@mastra/client-js 0.0.0-tool-call-parts-20250630193309 → 0.0.0-transpile-packages-20250724123433
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.
- package/.turbo/turbo-build.log +8 -8
- package/CHANGELOG.md +256 -3
- package/LICENSE.md +11 -42
- package/README.md +1 -0
- package/dist/index.cjs +415 -276
- package/dist/index.d.cts +133 -42
- package/dist/index.d.ts +133 -42
- package/dist/index.js +416 -277
- package/package.json +6 -5
- package/src/client.ts +48 -2
- package/src/example.ts +45 -17
- package/src/index.test.ts +53 -5
- package/src/resources/agent.ts +309 -254
- package/src/resources/base.ts +4 -1
- package/src/resources/memory-thread.ts +18 -0
- package/src/resources/vNextNetwork.ts +22 -5
- package/src/resources/workflow.ts +42 -5
- package/src/types.ts +40 -4
package/src/resources/agent.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parsePartialJson,
|
|
3
3
|
processDataStream,
|
|
4
|
+
processTextStream,
|
|
4
5
|
type JSONValue,
|
|
5
6
|
type ReasoningUIPart,
|
|
6
7
|
type TextUIPart,
|
|
@@ -14,6 +15,7 @@ import type { JSONSchema7 } from 'json-schema';
|
|
|
14
15
|
import { ZodSchema } from 'zod';
|
|
15
16
|
import { zodToJsonSchema } from '../utils/zod-to-json-schema';
|
|
16
17
|
import { processClientTools } from '../utils/process-client-tools';
|
|
18
|
+
import { v4 as uuid } from '@lukeed/uuid';
|
|
17
19
|
|
|
18
20
|
import type {
|
|
19
21
|
GenerateParams,
|
|
@@ -144,12 +146,18 @@ export class Agent extends BaseResource {
|
|
|
144
146
|
});
|
|
145
147
|
|
|
146
148
|
if (response.finishReason === 'tool-calls') {
|
|
147
|
-
|
|
149
|
+
const toolCalls = (
|
|
148
150
|
response as unknown as {
|
|
149
151
|
toolCalls: { toolName: string; args: any; toolCallId: string }[];
|
|
150
152
|
messages: CoreMessage[];
|
|
151
153
|
}
|
|
152
|
-
).toolCalls
|
|
154
|
+
).toolCalls;
|
|
155
|
+
|
|
156
|
+
if (!toolCalls || !Array.isArray(toolCalls)) {
|
|
157
|
+
return response;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
for (const toolCall of toolCalls) {
|
|
153
161
|
const clientTool = params.clientTools?.[toolCall.toolName] as Tool;
|
|
154
162
|
|
|
155
163
|
if (clientTool && clientTool.execute) {
|
|
@@ -198,6 +206,7 @@ export class Agent extends BaseResource {
|
|
|
198
206
|
onFinish,
|
|
199
207
|
getCurrentDate = () => new Date(),
|
|
200
208
|
lastMessage,
|
|
209
|
+
streamProtocol,
|
|
201
210
|
}: {
|
|
202
211
|
stream: ReadableStream<Uint8Array>;
|
|
203
212
|
update: (options: { message: UIMessage; data: JSONValue[] | undefined; replaceLastMessage: boolean }) => void;
|
|
@@ -206,6 +215,7 @@ export class Agent extends BaseResource {
|
|
|
206
215
|
generateId?: () => string;
|
|
207
216
|
getCurrentDate?: () => Date;
|
|
208
217
|
lastMessage: UIMessage | undefined;
|
|
218
|
+
streamProtocol: 'text' | 'data';
|
|
209
219
|
}) {
|
|
210
220
|
const replaceLastMessage = lastMessage?.role === 'assistant';
|
|
211
221
|
let step = replaceLastMessage
|
|
@@ -219,7 +229,7 @@ export class Agent extends BaseResource {
|
|
|
219
229
|
const message: UIMessage = replaceLastMessage
|
|
220
230
|
? structuredClone(lastMessage)
|
|
221
231
|
: {
|
|
222
|
-
id:
|
|
232
|
+
id: uuid(),
|
|
223
233
|
createdAt: getCurrentDate(),
|
|
224
234
|
role: 'assistant',
|
|
225
235
|
content: '',
|
|
@@ -279,7 +289,7 @@ export class Agent extends BaseResource {
|
|
|
279
289
|
// changes. This is why we need to add a revision id to ensure that the message
|
|
280
290
|
// is updated with SWR (without it, the changes get stuck in SWR and are not
|
|
281
291
|
// forwarded to rendering):
|
|
282
|
-
revisionId:
|
|
292
|
+
revisionId: uuid(),
|
|
283
293
|
} as UIMessage;
|
|
284
294
|
|
|
285
295
|
update({
|
|
@@ -289,297 +299,265 @@ export class Agent extends BaseResource {
|
|
|
289
299
|
});
|
|
290
300
|
}
|
|
291
301
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
message.parts.push(currentTextPart);
|
|
301
|
-
} else {
|
|
302
|
-
currentTextPart.text += value;
|
|
303
|
-
}
|
|
302
|
+
if (streamProtocol === 'text') {
|
|
303
|
+
await processTextStream({
|
|
304
|
+
stream,
|
|
305
|
+
onTextPart(value) {
|
|
306
|
+
message.content += value;
|
|
307
|
+
execUpdate();
|
|
308
|
+
},
|
|
309
|
+
});
|
|
304
310
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
311
|
+
onFinish?.({ message, finishReason, usage });
|
|
312
|
+
} else {
|
|
313
|
+
await processDataStream({
|
|
314
|
+
stream,
|
|
315
|
+
onTextPart(value) {
|
|
316
|
+
if (currentTextPart == null) {
|
|
317
|
+
currentTextPart = {
|
|
318
|
+
type: 'text',
|
|
319
|
+
text: value,
|
|
320
|
+
};
|
|
321
|
+
message.parts.push(currentTextPart);
|
|
322
|
+
} else {
|
|
323
|
+
currentTextPart.text += value;
|
|
313
324
|
}
|
|
314
|
-
} else {
|
|
315
|
-
currentReasoningTextDetail.text += value;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
if (currentReasoningPart == null) {
|
|
319
|
-
currentReasoningPart = {
|
|
320
|
-
type: 'reasoning',
|
|
321
|
-
reasoning: value,
|
|
322
|
-
details: [currentReasoningTextDetail],
|
|
323
|
-
};
|
|
324
|
-
message.parts.push(currentReasoningPart);
|
|
325
|
-
} else {
|
|
326
|
-
currentReasoningPart.reasoning += value;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
message.reasoning = (message.reasoning ?? '') + value;
|
|
330
|
-
|
|
331
|
-
execUpdate();
|
|
332
|
-
},
|
|
333
|
-
onReasoningSignaturePart(value) {
|
|
334
|
-
if (currentReasoningTextDetail != null) {
|
|
335
|
-
currentReasoningTextDetail.signature = value.signature;
|
|
336
|
-
}
|
|
337
|
-
},
|
|
338
|
-
onRedactedReasoningPart(value) {
|
|
339
|
-
if (currentReasoningPart == null) {
|
|
340
|
-
currentReasoningPart = {
|
|
341
|
-
type: 'reasoning',
|
|
342
|
-
reasoning: '',
|
|
343
|
-
details: [],
|
|
344
|
-
};
|
|
345
|
-
message.parts.push(currentReasoningPart);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
currentReasoningPart.details.push({
|
|
349
|
-
type: 'redacted',
|
|
350
|
-
data: value.data,
|
|
351
|
-
});
|
|
352
|
-
|
|
353
|
-
currentReasoningTextDetail = undefined;
|
|
354
325
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
326
|
+
message.content += value;
|
|
327
|
+
execUpdate();
|
|
328
|
+
},
|
|
329
|
+
onReasoningPart(value) {
|
|
330
|
+
if (currentReasoningTextDetail == null) {
|
|
331
|
+
currentReasoningTextDetail = { type: 'text', text: value };
|
|
332
|
+
if (currentReasoningPart != null) {
|
|
333
|
+
currentReasoningPart.details.push(currentReasoningTextDetail);
|
|
334
|
+
}
|
|
335
|
+
} else {
|
|
336
|
+
currentReasoningTextDetail.text += value;
|
|
337
|
+
}
|
|
363
338
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
339
|
+
if (currentReasoningPart == null) {
|
|
340
|
+
currentReasoningPart = {
|
|
341
|
+
type: 'reasoning',
|
|
342
|
+
reasoning: value,
|
|
343
|
+
details: [currentReasoningTextDetail],
|
|
344
|
+
};
|
|
345
|
+
message.parts.push(currentReasoningPart);
|
|
346
|
+
} else {
|
|
347
|
+
currentReasoningPart.reasoning += value;
|
|
348
|
+
}
|
|
371
349
|
|
|
372
|
-
|
|
373
|
-
},
|
|
374
|
-
onToolCallStreamingStartPart(value) {
|
|
375
|
-
if (message.toolInvocations == null) {
|
|
376
|
-
message.toolInvocations = [];
|
|
377
|
-
}
|
|
350
|
+
message.reasoning = (message.reasoning ?? '') + value;
|
|
378
351
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
}
|
|
352
|
+
execUpdate();
|
|
353
|
+
},
|
|
354
|
+
onReasoningSignaturePart(value) {
|
|
355
|
+
if (currentReasoningTextDetail != null) {
|
|
356
|
+
currentReasoningTextDetail.signature = value.signature;
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
onRedactedReasoningPart(value) {
|
|
360
|
+
if (currentReasoningPart == null) {
|
|
361
|
+
currentReasoningPart = {
|
|
362
|
+
type: 'reasoning',
|
|
363
|
+
reasoning: '',
|
|
364
|
+
details: [],
|
|
365
|
+
};
|
|
366
|
+
message.parts.push(currentReasoningPart);
|
|
367
|
+
}
|
|
386
368
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
toolName: value.toolName,
|
|
392
|
-
args: undefined,
|
|
393
|
-
} as const;
|
|
369
|
+
currentReasoningPart.details.push({
|
|
370
|
+
type: 'redacted',
|
|
371
|
+
data: value.data,
|
|
372
|
+
});
|
|
394
373
|
|
|
395
|
-
|
|
374
|
+
currentReasoningTextDetail = undefined;
|
|
396
375
|
|
|
397
|
-
|
|
376
|
+
execUpdate();
|
|
377
|
+
},
|
|
378
|
+
onFilePart(value) {
|
|
379
|
+
message.parts.push({
|
|
380
|
+
type: 'file',
|
|
381
|
+
mimeType: value.mimeType,
|
|
382
|
+
data: value.data,
|
|
383
|
+
});
|
|
398
384
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
385
|
+
execUpdate();
|
|
386
|
+
},
|
|
387
|
+
onSourcePart(value) {
|
|
388
|
+
message.parts.push({
|
|
389
|
+
type: 'source',
|
|
390
|
+
source: value,
|
|
391
|
+
});
|
|
403
392
|
|
|
404
|
-
|
|
393
|
+
execUpdate();
|
|
394
|
+
},
|
|
395
|
+
onToolCallStreamingStartPart(value) {
|
|
396
|
+
if (message.toolInvocations == null) {
|
|
397
|
+
message.toolInvocations = [];
|
|
398
|
+
}
|
|
405
399
|
|
|
406
|
-
|
|
400
|
+
// add the partial tool call to the map
|
|
401
|
+
partialToolCalls[value.toolCallId] = {
|
|
402
|
+
text: '',
|
|
403
|
+
step,
|
|
404
|
+
toolName: value.toolName,
|
|
405
|
+
index: message.toolInvocations.length,
|
|
406
|
+
};
|
|
407
407
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
408
|
+
const invocation = {
|
|
409
|
+
state: 'partial-call',
|
|
410
|
+
step,
|
|
411
|
+
toolCallId: value.toolCallId,
|
|
412
|
+
toolName: value.toolName,
|
|
413
|
+
args: undefined,
|
|
414
|
+
} as const;
|
|
415
415
|
|
|
416
|
-
|
|
416
|
+
message.toolInvocations.push(invocation);
|
|
417
417
|
|
|
418
|
-
|
|
418
|
+
updateToolInvocationPart(value.toolCallId, invocation);
|
|
419
419
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
state: 'call',
|
|
425
|
-
step,
|
|
426
|
-
...value,
|
|
427
|
-
} as const;
|
|
428
|
-
|
|
429
|
-
if (partialToolCalls[value.toolCallId] != null) {
|
|
430
|
-
// change the partial tool call to a full tool call
|
|
431
|
-
message.toolInvocations![partialToolCalls[value.toolCallId]!.index] = invocation;
|
|
432
|
-
} else {
|
|
433
|
-
if (message.toolInvocations == null) {
|
|
434
|
-
message.toolInvocations = [];
|
|
435
|
-
}
|
|
420
|
+
execUpdate();
|
|
421
|
+
},
|
|
422
|
+
onToolCallDeltaPart(value) {
|
|
423
|
+
const partialToolCall = partialToolCalls[value.toolCallId];
|
|
436
424
|
|
|
437
|
-
|
|
438
|
-
}
|
|
425
|
+
partialToolCall!.text += value.argsTextDelta;
|
|
439
426
|
|
|
440
|
-
|
|
427
|
+
const { value: partialArgs } = parsePartialJson(partialToolCall!.text);
|
|
441
428
|
|
|
442
|
-
|
|
429
|
+
const invocation = {
|
|
430
|
+
state: 'partial-call',
|
|
431
|
+
step: partialToolCall!.step,
|
|
432
|
+
toolCallId: value.toolCallId,
|
|
433
|
+
toolName: partialToolCall!.toolName,
|
|
434
|
+
args: partialArgs,
|
|
435
|
+
} as const;
|
|
443
436
|
|
|
444
|
-
|
|
445
|
-
// In the future we should make this non-blocking, which
|
|
446
|
-
// requires additional state management for error handling etc.
|
|
447
|
-
if (onToolCall) {
|
|
448
|
-
const result = await onToolCall({ toolCall: value });
|
|
449
|
-
if (result != null) {
|
|
450
|
-
const invocation = {
|
|
451
|
-
state: 'result',
|
|
452
|
-
step,
|
|
453
|
-
...value,
|
|
454
|
-
result,
|
|
455
|
-
} as const;
|
|
437
|
+
message.toolInvocations![partialToolCall!.index] = invocation;
|
|
456
438
|
|
|
457
|
-
|
|
458
|
-
message.toolInvocations![message.toolInvocations!.length - 1] = invocation;
|
|
439
|
+
updateToolInvocationPart(value.toolCallId, invocation);
|
|
459
440
|
|
|
460
|
-
|
|
441
|
+
execUpdate();
|
|
442
|
+
},
|
|
443
|
+
async onToolCallPart(value) {
|
|
444
|
+
const invocation = {
|
|
445
|
+
state: 'call',
|
|
446
|
+
step,
|
|
447
|
+
...value,
|
|
448
|
+
} as const;
|
|
449
|
+
|
|
450
|
+
if (partialToolCalls[value.toolCallId] != null) {
|
|
451
|
+
// change the partial tool call to a full tool call
|
|
452
|
+
message.toolInvocations![partialToolCalls[value.toolCallId]!.index] = invocation;
|
|
453
|
+
} else {
|
|
454
|
+
if (message.toolInvocations == null) {
|
|
455
|
+
message.toolInvocations = [];
|
|
456
|
+
}
|
|
461
457
|
|
|
462
|
-
|
|
458
|
+
message.toolInvocations.push(invocation);
|
|
463
459
|
}
|
|
464
|
-
}
|
|
465
|
-
},
|
|
466
|
-
onToolResultPart(value) {
|
|
467
|
-
const toolInvocations = message.toolInvocations;
|
|
468
460
|
|
|
469
|
-
|
|
470
|
-
throw new Error('tool_result must be preceded by a tool_call');
|
|
471
|
-
}
|
|
461
|
+
updateToolInvocationPart(value.toolCallId, invocation);
|
|
472
462
|
|
|
473
|
-
|
|
474
|
-
// and replace it with the result
|
|
475
|
-
const toolInvocationIndex = toolInvocations.findIndex(invocation => invocation.toolCallId === value.toolCallId);
|
|
463
|
+
execUpdate();
|
|
476
464
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
465
|
+
// invoke the onToolCall callback if it exists. This is blocking.
|
|
466
|
+
// In the future we should make this non-blocking, which
|
|
467
|
+
// requires additional state management for error handling etc.
|
|
468
|
+
if (onToolCall) {
|
|
469
|
+
const result = await onToolCall({ toolCall: value });
|
|
470
|
+
if (result != null) {
|
|
471
|
+
const invocation = {
|
|
472
|
+
state: 'result',
|
|
473
|
+
step,
|
|
474
|
+
...value,
|
|
475
|
+
result,
|
|
476
|
+
} as const;
|
|
480
477
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
state: 'result' as const,
|
|
484
|
-
...value,
|
|
485
|
-
} as const;
|
|
478
|
+
// store the result in the tool invocation
|
|
479
|
+
message.toolInvocations![message.toolInvocations!.length - 1] = invocation;
|
|
486
480
|
|
|
487
|
-
|
|
481
|
+
updateToolInvocationPart(value.toolCallId, invocation);
|
|
488
482
|
|
|
489
|
-
|
|
483
|
+
execUpdate();
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
},
|
|
487
|
+
onToolResultPart(value) {
|
|
488
|
+
const toolInvocations = message.toolInvocations;
|
|
490
489
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
data.push(...value);
|
|
495
|
-
execUpdate();
|
|
496
|
-
},
|
|
497
|
-
onMessageAnnotationsPart(value) {
|
|
498
|
-
if (messageAnnotations == null) {
|
|
499
|
-
messageAnnotations = [...value];
|
|
500
|
-
} else {
|
|
501
|
-
messageAnnotations.push(...value);
|
|
502
|
-
}
|
|
490
|
+
if (toolInvocations == null) {
|
|
491
|
+
throw new Error('tool_result must be preceded by a tool_call');
|
|
492
|
+
}
|
|
503
493
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
494
|
+
// find if there is any tool invocation with the same toolCallId
|
|
495
|
+
// and replace it with the result
|
|
496
|
+
const toolInvocationIndex = toolInvocations.findIndex(
|
|
497
|
+
invocation => invocation.toolCallId === value.toolCallId,
|
|
498
|
+
);
|
|
508
499
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
currentReasoningTextDetail = undefined;
|
|
513
|
-
},
|
|
514
|
-
onStartStepPart(value) {
|
|
515
|
-
// keep message id stable when we are updating an existing message:
|
|
516
|
-
if (!replaceLastMessage) {
|
|
517
|
-
message.id = value.messageId;
|
|
518
|
-
}
|
|
500
|
+
if (toolInvocationIndex === -1) {
|
|
501
|
+
throw new Error('tool_result must be preceded by a tool_call with the same toolCallId');
|
|
502
|
+
}
|
|
519
503
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
finishReason = value.finishReason;
|
|
526
|
-
if (value.usage != null) {
|
|
527
|
-
// usage = calculateLanguageModelUsage(value.usage);
|
|
528
|
-
usage = value.usage;
|
|
529
|
-
}
|
|
530
|
-
},
|
|
531
|
-
onErrorPart(error) {
|
|
532
|
-
throw new Error(error);
|
|
533
|
-
},
|
|
534
|
-
});
|
|
504
|
+
const invocation = {
|
|
505
|
+
...toolInvocations[toolInvocationIndex],
|
|
506
|
+
state: 'result' as const,
|
|
507
|
+
...value,
|
|
508
|
+
} as const;
|
|
535
509
|
|
|
536
|
-
|
|
537
|
-
}
|
|
510
|
+
toolInvocations[toolInvocationIndex] = invocation as ToolInvocation;
|
|
538
511
|
|
|
539
|
-
|
|
540
|
-
* Streams a response from the agent
|
|
541
|
-
* @param params - Stream parameters including prompt
|
|
542
|
-
* @returns Promise containing the enhanced Response object with processDataStream method
|
|
543
|
-
*/
|
|
544
|
-
async stream<T extends JSONSchema7 | ZodSchema | undefined = undefined>(
|
|
545
|
-
params: StreamParams<T>,
|
|
546
|
-
): Promise<
|
|
547
|
-
Response & {
|
|
548
|
-
processDataStream: (options?: Omit<Parameters<typeof processDataStream>[0], 'stream'>) => Promise<void>;
|
|
549
|
-
}
|
|
550
|
-
> {
|
|
551
|
-
const processedParams = {
|
|
552
|
-
...params,
|
|
553
|
-
output: params.output ? zodToJsonSchema(params.output) : undefined,
|
|
554
|
-
experimental_output: params.experimental_output ? zodToJsonSchema(params.experimental_output) : undefined,
|
|
555
|
-
runtimeContext: parseClientRuntimeContext(params.runtimeContext),
|
|
556
|
-
clientTools: processClientTools(params.clientTools),
|
|
557
|
-
};
|
|
512
|
+
updateToolInvocationPart(value.toolCallId, invocation as ToolInvocation);
|
|
558
513
|
|
|
559
|
-
|
|
560
|
-
|
|
514
|
+
execUpdate();
|
|
515
|
+
},
|
|
516
|
+
onDataPart(value) {
|
|
517
|
+
data.push(...value);
|
|
518
|
+
execUpdate();
|
|
519
|
+
},
|
|
520
|
+
onMessageAnnotationsPart(value) {
|
|
521
|
+
if (messageAnnotations == null) {
|
|
522
|
+
messageAnnotations = [...value];
|
|
523
|
+
} else {
|
|
524
|
+
messageAnnotations.push(...value);
|
|
525
|
+
}
|
|
561
526
|
|
|
562
|
-
|
|
563
|
-
|
|
527
|
+
execUpdate();
|
|
528
|
+
},
|
|
529
|
+
onFinishStepPart(value) {
|
|
530
|
+
step += 1;
|
|
564
531
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
532
|
+
// reset the current text and reasoning parts
|
|
533
|
+
currentTextPart = value.isContinued ? currentTextPart : undefined;
|
|
534
|
+
currentReasoningPart = undefined;
|
|
535
|
+
currentReasoningTextDetail = undefined;
|
|
536
|
+
},
|
|
537
|
+
onStartStepPart(value) {
|
|
538
|
+
// keep message id stable when we are updating an existing message:
|
|
539
|
+
if (!replaceLastMessage) {
|
|
540
|
+
message.id = value.messageId;
|
|
541
|
+
}
|
|
573
542
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
543
|
+
// add a step boundary part to the message
|
|
544
|
+
message.parts.push({ type: 'step-start' });
|
|
545
|
+
execUpdate();
|
|
546
|
+
},
|
|
547
|
+
onFinishMessagePart(value) {
|
|
548
|
+
finishReason = value.finishReason;
|
|
549
|
+
if (value.usage != null) {
|
|
550
|
+
// usage = calculateLanguageModelUsage(value.usage);
|
|
551
|
+
usage = value.usage;
|
|
552
|
+
}
|
|
553
|
+
},
|
|
554
|
+
onErrorPart(error) {
|
|
555
|
+
throw new Error(error);
|
|
556
|
+
},
|
|
579
557
|
});
|
|
580
|
-
};
|
|
581
558
|
|
|
582
|
-
|
|
559
|
+
onFinish?.({ message, finishReason, usage });
|
|
560
|
+
}
|
|
583
561
|
}
|
|
584
562
|
|
|
585
563
|
/**
|
|
@@ -599,6 +577,7 @@ export class Agent extends BaseResource {
|
|
|
599
577
|
}
|
|
600
578
|
|
|
601
579
|
try {
|
|
580
|
+
const streamProtocol = processedParams.output ? 'text' : 'data';
|
|
602
581
|
let toolCalls: ToolInvocation[] = [];
|
|
603
582
|
let finishReasonToolCalls = false;
|
|
604
583
|
let messages: UIMessage[] = [];
|
|
@@ -673,6 +652,24 @@ export class Agent extends BaseResource {
|
|
|
673
652
|
toolInvocation.result = result;
|
|
674
653
|
}
|
|
675
654
|
|
|
655
|
+
// write the tool result part to the stream
|
|
656
|
+
const writer = writable.getWriter();
|
|
657
|
+
|
|
658
|
+
try {
|
|
659
|
+
await writer.write(
|
|
660
|
+
new TextEncoder().encode(
|
|
661
|
+
'a:' +
|
|
662
|
+
JSON.stringify({
|
|
663
|
+
toolCallId: toolCall.toolCallId,
|
|
664
|
+
result,
|
|
665
|
+
}) +
|
|
666
|
+
'\n',
|
|
667
|
+
),
|
|
668
|
+
);
|
|
669
|
+
} finally {
|
|
670
|
+
writer.releaseLock();
|
|
671
|
+
}
|
|
672
|
+
|
|
676
673
|
// Convert messages to the correct format for the recursive call
|
|
677
674
|
const originalMessages = processedParams.messages;
|
|
678
675
|
const messageArray = Array.isArray(originalMessages) ? originalMessages : [originalMessages];
|
|
@@ -689,11 +686,14 @@ export class Agent extends BaseResource {
|
|
|
689
686
|
}
|
|
690
687
|
} else {
|
|
691
688
|
setTimeout(() => {
|
|
692
|
-
writable.
|
|
689
|
+
if (!writable.locked) {
|
|
690
|
+
writable.close();
|
|
691
|
+
}
|
|
693
692
|
}, 0);
|
|
694
693
|
}
|
|
695
694
|
},
|
|
696
695
|
lastMessage: undefined,
|
|
696
|
+
streamProtocol,
|
|
697
697
|
});
|
|
698
698
|
} catch (error) {
|
|
699
699
|
console.error('Error processing stream response:', error);
|
|
@@ -701,6 +701,61 @@ export class Agent extends BaseResource {
|
|
|
701
701
|
return response;
|
|
702
702
|
}
|
|
703
703
|
|
|
704
|
+
/**
|
|
705
|
+
* Streams a response from the agent
|
|
706
|
+
* @param params - Stream parameters including prompt
|
|
707
|
+
* @returns Promise containing the enhanced Response object with processDataStream and processTextStream methods
|
|
708
|
+
*/
|
|
709
|
+
async stream<T extends JSONSchema7 | ZodSchema | undefined = undefined>(
|
|
710
|
+
params: StreamParams<T>,
|
|
711
|
+
): Promise<
|
|
712
|
+
Response & {
|
|
713
|
+
processDataStream: (options?: Omit<Parameters<typeof processDataStream>[0], 'stream'>) => Promise<void>;
|
|
714
|
+
processTextStream: (options?: Omit<Parameters<typeof processTextStream>[0], 'stream'>) => Promise<void>;
|
|
715
|
+
}
|
|
716
|
+
> {
|
|
717
|
+
const processedParams = {
|
|
718
|
+
...params,
|
|
719
|
+
output: params.output ? zodToJsonSchema(params.output) : undefined,
|
|
720
|
+
experimental_output: params.experimental_output ? zodToJsonSchema(params.experimental_output) : undefined,
|
|
721
|
+
runtimeContext: parseClientRuntimeContext(params.runtimeContext),
|
|
722
|
+
clientTools: processClientTools(params.clientTools),
|
|
723
|
+
};
|
|
724
|
+
|
|
725
|
+
// Create a readable stream that will handle the response processing
|
|
726
|
+
const { readable, writable } = new TransformStream<Uint8Array, Uint8Array>();
|
|
727
|
+
// Start processing the response in the background
|
|
728
|
+
const response = await this.processStreamResponse(processedParams, writable);
|
|
729
|
+
|
|
730
|
+
// Create a new response with the readable stream
|
|
731
|
+
const streamResponse = new Response(readable, {
|
|
732
|
+
status: response.status,
|
|
733
|
+
statusText: response.statusText,
|
|
734
|
+
headers: response.headers,
|
|
735
|
+
}) as Response & {
|
|
736
|
+
processDataStream: (options?: Omit<Parameters<typeof processDataStream>[0], 'stream'>) => Promise<void>;
|
|
737
|
+
processTextStream: (options?: Omit<Parameters<typeof processTextStream>[0], 'stream'>) => Promise<void>;
|
|
738
|
+
};
|
|
739
|
+
|
|
740
|
+
// Add the processDataStream method to the response
|
|
741
|
+
streamResponse.processDataStream = async (options = {}) => {
|
|
742
|
+
await processDataStream({
|
|
743
|
+
stream: streamResponse.body as ReadableStream<Uint8Array>,
|
|
744
|
+
...options,
|
|
745
|
+
});
|
|
746
|
+
};
|
|
747
|
+
|
|
748
|
+
//Add the processTextStream method to the response
|
|
749
|
+
streamResponse.processTextStream = async options => {
|
|
750
|
+
await processTextStream({
|
|
751
|
+
stream: streamResponse.body as ReadableStream<Uint8Array>,
|
|
752
|
+
onTextPart: options?.onTextPart ?? (() => {}),
|
|
753
|
+
});
|
|
754
|
+
};
|
|
755
|
+
|
|
756
|
+
return streamResponse;
|
|
757
|
+
}
|
|
758
|
+
|
|
704
759
|
/**
|
|
705
760
|
* Gets details about a specific tool available to the agent
|
|
706
761
|
* @param toolId - ID of the tool to retrieve
|