@mastra/client-js 0.0.0-ai-v5-20250625173645 → 0.0.0-ai-v5-20250710191716

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,16 @@
1
- import { processDataStream } from '@ai-sdk/ui-utils';
2
- import { type GenerateReturn } from '@mastra/core';
1
+ import {
2
+ parsePartialJson,
3
+ processDataStream,
4
+ processTextStream,
5
+ type JSONValue,
6
+ type ReasoningUIPart,
7
+ type TextUIPart,
8
+ type ToolInvocation,
9
+ type ToolInvocationUIPart,
10
+ type UIMessage,
11
+ type UseChatOptions,
12
+ } from '@ai-sdk/ui-utils';
13
+ import { Tool, type CoreMessage, type GenerateReturn } from '@mastra/core';
3
14
  import type { JSONSchema7 } from 'json-schema';
4
15
  import { ZodSchema } from 'zod';
5
16
  import { zodToJsonSchema } from '../utils/zod-to-json-schema';
@@ -17,6 +28,7 @@ import type {
17
28
  import { BaseResource } from './base';
18
29
  import type { RuntimeContext } from '@mastra/core/runtime-context';
19
30
  import { parseClientRuntimeContext } from '../utils';
31
+ import { MessageList } from '@mastra/core/agent';
20
32
 
21
33
  export class AgentVoice extends BaseResource {
22
34
  constructor(
@@ -105,16 +117,16 @@ export class Agent extends BaseResource {
105
117
  * @param params - Generation parameters including prompt
106
118
  * @returns Promise containing the generated response
107
119
  */
108
- generate<T extends JSONSchema7 | ZodSchema | undefined = undefined>(
120
+ async generate<T extends JSONSchema7 | ZodSchema | undefined = undefined>(
109
121
  params: GenerateParams<T> & { output?: never; experimental_output?: never },
110
122
  ): Promise<GenerateReturn<T>>;
111
- generate<T extends JSONSchema7 | ZodSchema | undefined = undefined>(
123
+ async generate<T extends JSONSchema7 | ZodSchema | undefined = undefined>(
112
124
  params: GenerateParams<T> & { output: T; experimental_output?: never },
113
125
  ): Promise<GenerateReturn<T>>;
114
- generate<T extends JSONSchema7 | ZodSchema | undefined = undefined>(
126
+ async generate<T extends JSONSchema7 | ZodSchema | undefined = undefined>(
115
127
  params: GenerateParams<T> & { output?: never; experimental_output: T },
116
128
  ): Promise<GenerateReturn<T>>;
117
- generate<T extends JSONSchema7 | ZodSchema | undefined = undefined>(
129
+ async generate<T extends JSONSchema7 | ZodSchema | undefined = undefined>(
118
130
  params: GenerateParams<T>,
119
131
  ): Promise<GenerateReturn<T>> {
120
132
  const processedParams = {
@@ -125,22 +137,580 @@ export class Agent extends BaseResource {
125
137
  clientTools: processClientTools(params.clientTools),
126
138
  };
127
139
 
128
- return this.request(`/api/agents/${this.agentId}/generate`, {
140
+ const { runId, resourceId, threadId, runtimeContext } = processedParams as GenerateParams;
141
+
142
+ const response: GenerateReturn<T> = await this.request(`/api/agents/${this.agentId}/generate`, {
143
+ method: 'POST',
144
+ body: processedParams,
145
+ });
146
+
147
+ if (response.finishReason === 'tool-calls') {
148
+ const toolCalls = (
149
+ response as unknown as {
150
+ toolCalls: { toolName: string; args: any; toolCallId: string }[];
151
+ messages: CoreMessage[];
152
+ }
153
+ ).toolCalls;
154
+
155
+ if (!toolCalls || !Array.isArray(toolCalls)) {
156
+ return response;
157
+ }
158
+
159
+ for (const toolCall of toolCalls) {
160
+ const clientTool = params.clientTools?.[toolCall.toolName] as Tool;
161
+
162
+ if (clientTool && clientTool.execute) {
163
+ const result = await clientTool.execute(
164
+ { context: toolCall?.args, runId, resourceId, threadId, runtimeContext: runtimeContext as RuntimeContext },
165
+ {
166
+ messages: (response as unknown as { messages: CoreMessage[] }).messages,
167
+ toolCallId: toolCall?.toolCallId,
168
+ },
169
+ );
170
+
171
+ const updatedMessages = [
172
+ {
173
+ role: 'user',
174
+ content: params.messages,
175
+ },
176
+ ...(response.response as unknown as { messages: CoreMessage[] }).messages,
177
+ {
178
+ role: 'tool',
179
+ content: [
180
+ {
181
+ type: 'tool-result',
182
+ toolCallId: toolCall.toolCallId,
183
+ toolName: toolCall.toolName,
184
+ result,
185
+ },
186
+ ],
187
+ },
188
+ ];
189
+ // @ts-ignore
190
+ return this.generate({
191
+ ...params,
192
+ messages: updatedMessages,
193
+ });
194
+ }
195
+ }
196
+ }
197
+
198
+ return response;
199
+ }
200
+
201
+ private async processChatResponse({
202
+ stream,
203
+ update,
204
+ onToolCall,
205
+ onFinish,
206
+ getCurrentDate = () => new Date(),
207
+ lastMessage,
208
+ streamProtocol,
209
+ }: {
210
+ stream: ReadableStream<Uint8Array>;
211
+ update: (options: { message: UIMessage; data: JSONValue[] | undefined; replaceLastMessage: boolean }) => void;
212
+ onToolCall?: UseChatOptions['onToolCall'];
213
+ onFinish?: (options: { message: UIMessage | undefined; finishReason: string; usage: string }) => void;
214
+ generateId?: () => string;
215
+ getCurrentDate?: () => Date;
216
+ lastMessage: UIMessage | undefined;
217
+ streamProtocol: 'text' | 'data';
218
+ }) {
219
+ const replaceLastMessage = lastMessage?.role === 'assistant';
220
+ let step = replaceLastMessage
221
+ ? 1 +
222
+ // find max step in existing tool invocations:
223
+ (lastMessage.toolInvocations?.reduce((max, toolInvocation) => {
224
+ return Math.max(max, toolInvocation.step ?? 0);
225
+ }, 0) ?? 0)
226
+ : 0;
227
+
228
+ const message: UIMessage = replaceLastMessage
229
+ ? structuredClone(lastMessage)
230
+ : {
231
+ id: crypto.randomUUID(),
232
+ createdAt: getCurrentDate(),
233
+ role: 'assistant',
234
+ content: '',
235
+ parts: [],
236
+ };
237
+
238
+ let currentTextPart: TextUIPart | undefined = undefined;
239
+ let currentReasoningPart: ReasoningUIPart | undefined = undefined;
240
+ let currentReasoningTextDetail: { type: 'text'; text: string; signature?: string } | undefined = undefined;
241
+
242
+ function updateToolInvocationPart(toolCallId: string, invocation: ToolInvocation) {
243
+ const part = message.parts.find(
244
+ part => part.type === 'tool-invocation' && part.toolInvocation.toolCallId === toolCallId,
245
+ ) as ToolInvocationUIPart | undefined;
246
+
247
+ if (part != null) {
248
+ part.toolInvocation = invocation;
249
+ } else {
250
+ message.parts.push({
251
+ type: 'tool-invocation',
252
+ toolInvocation: invocation,
253
+ });
254
+ }
255
+ }
256
+
257
+ const data: JSONValue[] = [];
258
+
259
+ // keep list of current message annotations for message
260
+ let messageAnnotations: JSONValue[] | undefined = replaceLastMessage ? lastMessage?.annotations : undefined;
261
+
262
+ // keep track of partial tool calls
263
+ const partialToolCalls: Record<string, { text: string; step: number; index: number; toolName: string }> = {};
264
+
265
+ let usage: any = {
266
+ completionTokens: NaN,
267
+ promptTokens: NaN,
268
+ totalTokens: NaN,
269
+ };
270
+ let finishReason: string = 'unknown';
271
+
272
+ function execUpdate() {
273
+ // make a copy of the data array to ensure UI is updated (SWR)
274
+ const copiedData = [...data];
275
+
276
+ // keeps the currentMessage up to date with the latest annotations,
277
+ // even if annotations preceded the message creation
278
+ if (messageAnnotations?.length) {
279
+ message.annotations = messageAnnotations;
280
+ }
281
+
282
+ const copiedMessage = {
283
+ // deep copy the message to ensure that deep changes (msg attachments) are updated
284
+ // with SolidJS. SolidJS uses referential integration of sub-objects to detect changes.
285
+ ...structuredClone(message),
286
+ // add a revision id to ensure that the message is updated with SWR. SWR uses a
287
+ // hashing approach by default to detect changes, but it only works for shallow
288
+ // changes. This is why we need to add a revision id to ensure that the message
289
+ // is updated with SWR (without it, the changes get stuck in SWR and are not
290
+ // forwarded to rendering):
291
+ revisionId: crypto.randomUUID(),
292
+ } as UIMessage;
293
+
294
+ update({
295
+ message: copiedMessage,
296
+ data: copiedData,
297
+ replaceLastMessage,
298
+ });
299
+ }
300
+
301
+ if (streamProtocol === 'text') {
302
+ await processTextStream({
303
+ stream,
304
+ onTextPart(value) {
305
+ message.content += value;
306
+ execUpdate();
307
+ },
308
+ });
309
+
310
+ onFinish?.({ message, finishReason, usage });
311
+ } else {
312
+ await processDataStream({
313
+ stream,
314
+ onTextPart(value) {
315
+ if (currentTextPart == null) {
316
+ currentTextPart = {
317
+ type: 'text',
318
+ text: value,
319
+ };
320
+ message.parts.push(currentTextPart);
321
+ } else {
322
+ currentTextPart.text += value;
323
+ }
324
+
325
+ message.content += value;
326
+ execUpdate();
327
+ },
328
+ onReasoningPart(value) {
329
+ if (currentReasoningTextDetail == null) {
330
+ currentReasoningTextDetail = { type: 'text', text: value };
331
+ if (currentReasoningPart != null) {
332
+ currentReasoningPart.details.push(currentReasoningTextDetail);
333
+ }
334
+ } else {
335
+ currentReasoningTextDetail.text += value;
336
+ }
337
+
338
+ if (currentReasoningPart == null) {
339
+ currentReasoningPart = {
340
+ type: 'reasoning',
341
+ reasoning: value,
342
+ details: [currentReasoningTextDetail],
343
+ };
344
+ message.parts.push(currentReasoningPart);
345
+ } else {
346
+ currentReasoningPart.reasoning += value;
347
+ }
348
+
349
+ message.reasoning = (message.reasoning ?? '') + value;
350
+
351
+ execUpdate();
352
+ },
353
+ onReasoningSignaturePart(value) {
354
+ if (currentReasoningTextDetail != null) {
355
+ currentReasoningTextDetail.signature = value.signature;
356
+ }
357
+ },
358
+ onRedactedReasoningPart(value) {
359
+ if (currentReasoningPart == null) {
360
+ currentReasoningPart = {
361
+ type: 'reasoning',
362
+ reasoning: '',
363
+ details: [],
364
+ };
365
+ message.parts.push(currentReasoningPart);
366
+ }
367
+
368
+ currentReasoningPart.details.push({
369
+ type: 'redacted',
370
+ data: value.data,
371
+ });
372
+
373
+ currentReasoningTextDetail = undefined;
374
+
375
+ execUpdate();
376
+ },
377
+ onFilePart(value) {
378
+ message.parts.push({
379
+ type: 'file',
380
+ mimeType: value.mimeType,
381
+ data: value.data,
382
+ });
383
+
384
+ execUpdate();
385
+ },
386
+ onSourcePart(value) {
387
+ message.parts.push({
388
+ type: 'source',
389
+ source: value,
390
+ });
391
+
392
+ execUpdate();
393
+ },
394
+ onToolCallStreamingStartPart(value) {
395
+ if (message.toolInvocations == null) {
396
+ message.toolInvocations = [];
397
+ }
398
+
399
+ // add the partial tool call to the map
400
+ partialToolCalls[value.toolCallId] = {
401
+ text: '',
402
+ step,
403
+ toolName: value.toolName,
404
+ index: message.toolInvocations.length,
405
+ };
406
+
407
+ const invocation = {
408
+ state: 'partial-call',
409
+ step,
410
+ toolCallId: value.toolCallId,
411
+ toolName: value.toolName,
412
+ args: undefined,
413
+ } as const;
414
+
415
+ message.toolInvocations.push(invocation);
416
+
417
+ updateToolInvocationPart(value.toolCallId, invocation);
418
+
419
+ execUpdate();
420
+ },
421
+ onToolCallDeltaPart(value) {
422
+ const partialToolCall = partialToolCalls[value.toolCallId];
423
+
424
+ partialToolCall!.text += value.argsTextDelta;
425
+
426
+ const { value: partialArgs } = parsePartialJson(partialToolCall!.text);
427
+
428
+ const invocation = {
429
+ state: 'partial-call',
430
+ step: partialToolCall!.step,
431
+ toolCallId: value.toolCallId,
432
+ toolName: partialToolCall!.toolName,
433
+ args: partialArgs,
434
+ } as const;
435
+
436
+ message.toolInvocations![partialToolCall!.index] = invocation;
437
+
438
+ updateToolInvocationPart(value.toolCallId, invocation);
439
+
440
+ execUpdate();
441
+ },
442
+ async onToolCallPart(value) {
443
+ const invocation = {
444
+ state: 'call',
445
+ step,
446
+ ...value,
447
+ } as const;
448
+
449
+ if (partialToolCalls[value.toolCallId] != null) {
450
+ // change the partial tool call to a full tool call
451
+ message.toolInvocations![partialToolCalls[value.toolCallId]!.index] = invocation;
452
+ } else {
453
+ if (message.toolInvocations == null) {
454
+ message.toolInvocations = [];
455
+ }
456
+
457
+ message.toolInvocations.push(invocation);
458
+ }
459
+
460
+ updateToolInvocationPart(value.toolCallId, invocation);
461
+
462
+ execUpdate();
463
+
464
+ // invoke the onToolCall callback if it exists. This is blocking.
465
+ // In the future we should make this non-blocking, which
466
+ // requires additional state management for error handling etc.
467
+ if (onToolCall) {
468
+ const result = await onToolCall({ toolCall: value });
469
+ if (result != null) {
470
+ const invocation = {
471
+ state: 'result',
472
+ step,
473
+ ...value,
474
+ result,
475
+ } as const;
476
+
477
+ // store the result in the tool invocation
478
+ message.toolInvocations![message.toolInvocations!.length - 1] = invocation;
479
+
480
+ updateToolInvocationPart(value.toolCallId, invocation);
481
+
482
+ execUpdate();
483
+ }
484
+ }
485
+ },
486
+ onToolResultPart(value) {
487
+ const toolInvocations = message.toolInvocations;
488
+
489
+ if (toolInvocations == null) {
490
+ throw new Error('tool_result must be preceded by a tool_call');
491
+ }
492
+
493
+ // find if there is any tool invocation with the same toolCallId
494
+ // and replace it with the result
495
+ const toolInvocationIndex = toolInvocations.findIndex(
496
+ invocation => invocation.toolCallId === value.toolCallId,
497
+ );
498
+
499
+ if (toolInvocationIndex === -1) {
500
+ throw new Error('tool_result must be preceded by a tool_call with the same toolCallId');
501
+ }
502
+
503
+ const invocation = {
504
+ ...toolInvocations[toolInvocationIndex],
505
+ state: 'result' as const,
506
+ ...value,
507
+ } as const;
508
+
509
+ toolInvocations[toolInvocationIndex] = invocation as ToolInvocation;
510
+
511
+ updateToolInvocationPart(value.toolCallId, invocation as ToolInvocation);
512
+
513
+ execUpdate();
514
+ },
515
+ onDataPart(value) {
516
+ data.push(...value);
517
+ execUpdate();
518
+ },
519
+ onMessageAnnotationsPart(value) {
520
+ if (messageAnnotations == null) {
521
+ messageAnnotations = [...value];
522
+ } else {
523
+ messageAnnotations.push(...value);
524
+ }
525
+
526
+ execUpdate();
527
+ },
528
+ onFinishStepPart(value) {
529
+ step += 1;
530
+
531
+ // reset the current text and reasoning parts
532
+ currentTextPart = value.isContinued ? currentTextPart : undefined;
533
+ currentReasoningPart = undefined;
534
+ currentReasoningTextDetail = undefined;
535
+ },
536
+ onStartStepPart(value) {
537
+ // keep message id stable when we are updating an existing message:
538
+ if (!replaceLastMessage) {
539
+ message.id = value.messageId;
540
+ }
541
+
542
+ // add a step boundary part to the message
543
+ message.parts.push({ type: 'step-start' });
544
+ execUpdate();
545
+ },
546
+ onFinishMessagePart(value) {
547
+ finishReason = value.finishReason;
548
+ if (value.usage != null) {
549
+ // usage = calculateLanguageModelUsage(value.usage);
550
+ usage = value.usage;
551
+ }
552
+ },
553
+ onErrorPart(error) {
554
+ throw new Error(error);
555
+ },
556
+ });
557
+
558
+ onFinish?.({ message, finishReason, usage });
559
+ }
560
+ }
561
+
562
+ /**
563
+ * Processes the stream response and handles tool calls
564
+ */
565
+ private async processStreamResponse(processedParams: any, writable: WritableStream<Uint8Array>) {
566
+ const response: Response & {
567
+ processDataStream: (options?: Omit<Parameters<typeof processDataStream>[0], 'stream'>) => Promise<void>;
568
+ } = await this.request(`/api/agents/${this.agentId}/stream`, {
129
569
  method: 'POST',
130
570
  body: processedParams,
571
+ stream: true,
131
572
  });
573
+
574
+ if (!response.body) {
575
+ throw new Error('No response body');
576
+ }
577
+
578
+ try {
579
+ const streamProtocol = processedParams.output ? 'text' : 'data';
580
+ let toolCalls: ToolInvocation[] = [];
581
+ let finishReasonToolCalls = false;
582
+ let messages: UIMessage[] = [];
583
+ let hasProcessedToolCalls = false;
584
+
585
+ // Use tee() to split the stream into two branches
586
+ const [streamForWritable, streamForProcessing] = response.body.tee();
587
+
588
+ // Pipe one branch to the writable stream
589
+ streamForWritable
590
+ .pipeTo(writable, {
591
+ preventClose: true,
592
+ })
593
+ .catch(error => {
594
+ console.error('Error piping to writable stream:', error);
595
+ });
596
+
597
+ // Process the other branch for chat response handling
598
+ this.processChatResponse({
599
+ stream: streamForProcessing,
600
+ update: ({ message }) => {
601
+ messages.push(message);
602
+ },
603
+ onFinish: async ({ finishReason, message }) => {
604
+ if (finishReason === 'tool-calls') {
605
+ const toolCall = [...(message?.parts ?? [])]
606
+ .reverse()
607
+ .find(part => part.type === 'tool-invocation')?.toolInvocation;
608
+ if (toolCall) {
609
+ toolCalls.push(toolCall);
610
+ }
611
+
612
+ // Handle tool calls if needed
613
+ for (const toolCall of toolCalls) {
614
+ const clientTool = processedParams.clientTools?.[toolCall.toolName] as Tool;
615
+ if (clientTool && clientTool.execute) {
616
+ const result = await clientTool.execute(
617
+ {
618
+ context: toolCall?.args,
619
+ runId: processedParams.runId,
620
+ resourceId: processedParams.resourceId,
621
+ threadId: processedParams.threadId,
622
+ runtimeContext: processedParams.runtimeContext as RuntimeContext,
623
+ },
624
+ {
625
+ messages: (response as unknown as { messages: CoreMessage[] }).messages,
626
+ toolCallId: toolCall?.toolCallId,
627
+ },
628
+ );
629
+
630
+ const lastMessage: UIMessage = JSON.parse(JSON.stringify(messages[messages.length - 1]));
631
+
632
+ const toolInvocationPart = lastMessage?.parts?.find(
633
+ part => part.type === 'tool-invocation' && part.toolInvocation?.toolCallId === toolCall.toolCallId,
634
+ ) as ToolInvocationUIPart | undefined;
635
+
636
+ if (toolInvocationPart) {
637
+ toolInvocationPart.toolInvocation = {
638
+ ...toolInvocationPart.toolInvocation,
639
+ state: 'result',
640
+ result,
641
+ };
642
+ }
643
+
644
+ const toolInvocation = lastMessage?.toolInvocations?.find(
645
+ toolInvocation => toolInvocation.toolCallId === toolCall.toolCallId,
646
+ ) as ToolInvocation | undefined;
647
+
648
+ if (toolInvocation) {
649
+ toolInvocation.state = 'result';
650
+ // @ts-ignore
651
+ toolInvocation.result = result;
652
+ }
653
+
654
+ // write the tool result part to the stream
655
+ const writer = writable.getWriter();
656
+
657
+ try {
658
+ await writer.write(
659
+ new TextEncoder().encode(
660
+ 'a:' +
661
+ JSON.stringify({
662
+ toolCallId: toolCall.toolCallId,
663
+ result,
664
+ }) +
665
+ '\n',
666
+ ),
667
+ );
668
+ } finally {
669
+ writer.releaseLock();
670
+ }
671
+
672
+ // Convert messages to the correct format for the recursive call
673
+ const originalMessages = processedParams.messages;
674
+ const messageArray = Array.isArray(originalMessages) ? originalMessages : [originalMessages];
675
+
676
+ // Recursively call stream with updated messages
677
+ this.processStreamResponse(
678
+ {
679
+ ...processedParams,
680
+ messages: [...messageArray, ...messages, lastMessage],
681
+ },
682
+ writable,
683
+ );
684
+ }
685
+ }
686
+ } else {
687
+ setTimeout(() => {
688
+ if (!writable.locked) {
689
+ writable.close();
690
+ }
691
+ }, 0);
692
+ }
693
+ },
694
+ lastMessage: undefined,
695
+ streamProtocol,
696
+ });
697
+ } catch (error) {
698
+ console.error('Error processing stream response:', error);
699
+ }
700
+ return response;
132
701
  }
133
702
 
134
703
  /**
135
704
  * Streams a response from the agent
136
705
  * @param params - Stream parameters including prompt
137
- * @returns Promise containing the enhanced Response object with processDataStream method
706
+ * @returns Promise containing the enhanced Response object with processDataStream and processTextStream methods
138
707
  */
139
708
  async stream<T extends JSONSchema7 | ZodSchema | undefined = undefined>(
140
709
  params: StreamParams<T>,
141
710
  ): Promise<
142
711
  Response & {
143
712
  processDataStream: (options?: Omit<Parameters<typeof processDataStream>[0], 'stream'>) => Promise<void>;
713
+ processTextStream: (options?: Omit<Parameters<typeof processTextStream>[0], 'stream'>) => Promise<void>;
144
714
  }
145
715
  > {
146
716
  const processedParams = {
@@ -151,26 +721,38 @@ export class Agent extends BaseResource {
151
721
  clientTools: processClientTools(params.clientTools),
152
722
  };
153
723
 
154
- const response: Response & {
155
- processDataStream: (options?: Omit<Parameters<typeof processDataStream>[0], 'stream'>) => Promise<void>;
156
- } = await this.request(`/api/agents/${this.agentId}/stream`, {
157
- method: 'POST',
158
- body: processedParams,
159
- stream: true,
160
- });
724
+ // Create a readable stream that will handle the response processing
725
+ const { readable, writable } = new TransformStream<Uint8Array, Uint8Array>();
726
+ // Start processing the response in the background
727
+ const response = await this.processStreamResponse(processedParams, writable);
161
728
 
162
- if (!response.body) {
163
- throw new Error('No response body');
164
- }
729
+ // Create a new response with the readable stream
730
+ const streamResponse = new Response(readable, {
731
+ status: response.status,
732
+ statusText: response.statusText,
733
+ headers: response.headers,
734
+ }) as Response & {
735
+ processDataStream: (options?: Omit<Parameters<typeof processDataStream>[0], 'stream'>) => Promise<void>;
736
+ processTextStream: (options?: Omit<Parameters<typeof processTextStream>[0], 'stream'>) => Promise<void>;
737
+ };
165
738
 
166
- response.processDataStream = async (options = {}) => {
739
+ // Add the processDataStream method to the response
740
+ streamResponse.processDataStream = async (options = {}) => {
167
741
  await processDataStream({
168
- stream: response.body as ReadableStream<Uint8Array>,
742
+ stream: streamResponse.body as ReadableStream<Uint8Array>,
169
743
  ...options,
170
744
  });
171
745
  };
172
746
 
173
- return response;
747
+ //Add the processTextStream method to the response
748
+ streamResponse.processTextStream = async options => {
749
+ await processTextStream({
750
+ stream: streamResponse.body as ReadableStream<Uint8Array>,
751
+ onTextPart: options?.onTextPart ?? (() => {}),
752
+ });
753
+ };
754
+
755
+ return streamResponse;
174
756
  }
175
757
 
176
758
  /**
@@ -30,6 +30,7 @@ export class BaseResource {
30
30
  // TODO: Bring this back once we figure out what we/users need to do to make this work with cross-origin requests
31
31
  // 'x-mastra-client-type': 'js',
32
32
  },
33
+ signal: this.options.abortSignal,
33
34
  body:
34
35
  options.body instanceof FormData ? options.body : options.body ? JSON.stringify(options.body) : undefined,
35
36
  });