@ai-sdk/workflow 1.0.0-beta.1 → 1.0.0-beta.100
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/CHANGELOG.md +839 -0
- package/dist/index.d.mts +301 -127
- package/dist/index.mjs +926 -379
- package/dist/index.mjs.map +1 -1
- package/package.json +13 -11
- package/src/create-language-model-tool-result-output.ts +85 -0
- package/src/do-stream-step.ts +52 -31
- package/src/index.ts +9 -6
- package/src/providers/mock.ts +79 -92
- package/src/serializable-schema.ts +5 -1
- package/src/stream-text-iterator.ts +157 -74
- package/src/test/agent-e2e-workflows.ts +89 -9
- package/src/to-ui-message-chunk.ts +13 -10
- package/src/workflow-agent.ts +1525 -804
- package/src/workflow-chat-transport.ts +17 -15
- package/src/telemetry.ts +0 -199
package/src/workflow-agent.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
JSONValue,
|
|
3
2
|
LanguageModelV4CallOptions,
|
|
4
3
|
LanguageModelV4Prompt,
|
|
5
4
|
LanguageModelV4StreamPart,
|
|
@@ -7,38 +6,74 @@ import type {
|
|
|
7
6
|
SharedV4ProviderOptions,
|
|
8
7
|
} from '@ai-sdk/provider';
|
|
9
8
|
import {
|
|
10
|
-
|
|
9
|
+
getErrorMessage,
|
|
10
|
+
validateTypes,
|
|
11
|
+
type Context,
|
|
12
|
+
type HasRequiredKey,
|
|
13
|
+
type InferToolSetContext,
|
|
14
|
+
} from '@ai-sdk/provider-utils';
|
|
15
|
+
import {
|
|
16
|
+
Output,
|
|
17
|
+
experimental_filterActiveTools as filterActiveTools,
|
|
11
18
|
type FinishReason,
|
|
12
19
|
type LanguageModelResponseMetadata,
|
|
13
20
|
type LanguageModelUsage,
|
|
21
|
+
type Experimental_LanguageModelStreamPart as ModelCallStreamPart,
|
|
14
22
|
type ModelMessage,
|
|
15
|
-
Output,
|
|
16
23
|
type StepResult,
|
|
17
24
|
type StopCondition,
|
|
18
|
-
type
|
|
19
|
-
type
|
|
25
|
+
type GenerateTextOnStepEndCallback,
|
|
26
|
+
type ActiveTools,
|
|
20
27
|
type ToolCallRepairFunction,
|
|
21
28
|
type ToolChoice,
|
|
22
29
|
type ToolSet,
|
|
23
30
|
type UIMessage,
|
|
31
|
+
type LanguageModel,
|
|
32
|
+
type Prompt,
|
|
33
|
+
type TelemetryOptions as CoreTelemetryOptions,
|
|
34
|
+
type Instructions,
|
|
24
35
|
} from 'ai';
|
|
25
36
|
import {
|
|
37
|
+
createRestrictedTelemetryDispatcher,
|
|
38
|
+
collectToolApprovals,
|
|
26
39
|
convertToLanguageModelPrompt,
|
|
27
40
|
mergeAbortSignals,
|
|
28
41
|
mergeCallbacks,
|
|
29
42
|
standardizePrompt,
|
|
43
|
+
validateApprovedToolApprovals,
|
|
30
44
|
} from 'ai/internal';
|
|
45
|
+
import { createLanguageModelToolResultOutput } from './create-language-model-tool-result-output.js';
|
|
31
46
|
import { streamTextIterator } from './stream-text-iterator.js';
|
|
32
|
-
import type { CompatibleLanguageModel } from './types.js';
|
|
33
47
|
|
|
34
48
|
// Re-export for consumers
|
|
35
49
|
export type { CompatibleLanguageModel } from './types.js';
|
|
36
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Callback function to be called after each step completes.
|
|
53
|
+
* Alias for the AI SDK's GenerateTextOnStepEndCallback, using
|
|
54
|
+
* WorkflowAgent-consistent naming.
|
|
55
|
+
*/
|
|
56
|
+
export type WorkflowAgentOnStepEndCallback<
|
|
57
|
+
TTools extends ToolSet = ToolSet,
|
|
58
|
+
TRuntimeContext extends Context = Context,
|
|
59
|
+
> = GenerateTextOnStepEndCallback<TTools, TRuntimeContext>;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Callback function to be called after each step completes.
|
|
63
|
+
* Deprecated alias for `WorkflowAgentOnStepEndCallback`.
|
|
64
|
+
*
|
|
65
|
+
* @deprecated Use `WorkflowAgentOnStepEndCallback` instead.
|
|
66
|
+
*/
|
|
67
|
+
export type WorkflowAgentOnStepFinishCallback<
|
|
68
|
+
TTools extends ToolSet = ToolSet,
|
|
69
|
+
TRuntimeContext extends Context = Context,
|
|
70
|
+
> = WorkflowAgentOnStepEndCallback<TTools, TRuntimeContext>;
|
|
71
|
+
|
|
37
72
|
/**
|
|
38
73
|
* Infer the type of the tools of a workflow agent.
|
|
39
74
|
*/
|
|
40
75
|
export type InferWorkflowAgentTools<WORKFLOW_AGENT> =
|
|
41
|
-
WORKFLOW_AGENT extends WorkflowAgent<infer TOOLS> ? TOOLS : never;
|
|
76
|
+
WORKFLOW_AGENT extends WorkflowAgent<infer TOOLS, any> ? TOOLS : never;
|
|
42
77
|
|
|
43
78
|
/**
|
|
44
79
|
* Infer the UI message type of a workflow agent.
|
|
@@ -79,54 +114,14 @@ export interface OutputSpecification<OUTPUT, PARTIAL> {
|
|
|
79
114
|
*/
|
|
80
115
|
export type ProviderOptions = SharedV4ProviderOptions;
|
|
81
116
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Identifier for this function. Used to group telemetry data by function.
|
|
93
|
-
*/
|
|
94
|
-
functionId?: string;
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Additional information to include in the telemetry data.
|
|
98
|
-
*/
|
|
99
|
-
metadata?: Record<
|
|
100
|
-
string,
|
|
101
|
-
| string
|
|
102
|
-
| number
|
|
103
|
-
| boolean
|
|
104
|
-
| Array<string | number | boolean>
|
|
105
|
-
| null
|
|
106
|
-
| undefined
|
|
107
|
-
>;
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Enable or disable input recording. Enabled by default.
|
|
111
|
-
*
|
|
112
|
-
* You might want to disable input recording to avoid recording sensitive
|
|
113
|
-
* information, to reduce data transfers, or to increase performance.
|
|
114
|
-
*/
|
|
115
|
-
recordInputs?: boolean;
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Enable or disable output recording. Enabled by default.
|
|
119
|
-
*
|
|
120
|
-
* You might want to disable output recording to avoid recording sensitive
|
|
121
|
-
* information, to reduce data transfers, or to increase performance.
|
|
122
|
-
*/
|
|
123
|
-
recordOutputs?: boolean;
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Custom tracer for the telemetry.
|
|
127
|
-
*/
|
|
128
|
-
tracer?: unknown;
|
|
129
|
-
}
|
|
117
|
+
type WorkflowAgentToolsContextParameter<TTools extends ToolSet> =
|
|
118
|
+
HasRequiredKey<InferToolSetContext<TTools>> extends true
|
|
119
|
+
? { toolsContext: InferToolSetContext<TTools> }
|
|
120
|
+
: { toolsContext?: never };
|
|
121
|
+
export type TelemetryOptions<
|
|
122
|
+
TRuntimeContext extends Context = Context,
|
|
123
|
+
TTools extends ToolSet = ToolSet,
|
|
124
|
+
> = CoreTelemetryOptions<TRuntimeContext, TTools>;
|
|
130
125
|
|
|
131
126
|
/**
|
|
132
127
|
* A transformation that is applied to the stream.
|
|
@@ -243,15 +238,15 @@ export interface GenerationSettings {
|
|
|
243
238
|
/**
|
|
244
239
|
* Information passed to the prepareStep callback.
|
|
245
240
|
*/
|
|
246
|
-
export interface PrepareStepInfo<
|
|
241
|
+
export interface PrepareStepInfo<
|
|
242
|
+
TTools extends ToolSet = ToolSet,
|
|
243
|
+
TRuntimeContext extends Context = Context,
|
|
244
|
+
> {
|
|
247
245
|
/**
|
|
248
246
|
* The current model configuration (string or function).
|
|
249
247
|
* The function should return a LanguageModelV4 instance.
|
|
250
248
|
*/
|
|
251
|
-
model:
|
|
252
|
-
| string
|
|
253
|
-
| CompatibleLanguageModel
|
|
254
|
-
| (() => Promise<CompatibleLanguageModel>);
|
|
249
|
+
model: LanguageModel;
|
|
255
250
|
|
|
256
251
|
/**
|
|
257
252
|
* The current step number (0-indexed).
|
|
@@ -261,7 +256,7 @@ export interface PrepareStepInfo<TTools extends ToolSet = ToolSet> {
|
|
|
261
256
|
/**
|
|
262
257
|
* All previous steps with their results.
|
|
263
258
|
*/
|
|
264
|
-
steps: StepResult<TTools,
|
|
259
|
+
steps: StepResult<TTools, TRuntimeContext>[];
|
|
265
260
|
|
|
266
261
|
/**
|
|
267
262
|
* The messages that will be sent to the model.
|
|
@@ -270,24 +265,33 @@ export interface PrepareStepInfo<TTools extends ToolSet = ToolSet> {
|
|
|
270
265
|
messages: LanguageModelV4Prompt;
|
|
271
266
|
|
|
272
267
|
/**
|
|
273
|
-
* The context
|
|
268
|
+
* The runtime context that flows through the agent loop.
|
|
269
|
+
* Treat the value as immutable; return a new `runtimeContext` from
|
|
270
|
+
* `prepareStep` to update it for the current and subsequent steps.
|
|
271
|
+
*/
|
|
272
|
+
runtimeContext: TRuntimeContext;
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Per-tool context, keyed by tool name. Each tool receives only its own
|
|
276
|
+
* validated entry as `context` during execution.
|
|
277
|
+
* Treat the value as immutable; return a new `toolsContext` from
|
|
278
|
+
* `prepareStep` to update it for the current and subsequent steps.
|
|
274
279
|
*/
|
|
275
|
-
|
|
280
|
+
toolsContext: InferToolSetContext<TTools>;
|
|
276
281
|
}
|
|
277
282
|
|
|
278
283
|
/**
|
|
279
284
|
* Return type from the prepareStep callback.
|
|
280
285
|
* All properties are optional - only return the ones you want to override.
|
|
281
286
|
*/
|
|
282
|
-
export interface PrepareStepResult
|
|
287
|
+
export interface PrepareStepResult<
|
|
288
|
+
TTools extends ToolSet = ToolSet,
|
|
289
|
+
TRuntimeContext extends Context = Context,
|
|
290
|
+
> extends Partial<GenerationSettings> {
|
|
283
291
|
/**
|
|
284
292
|
* Override the model for this step.
|
|
285
|
-
* The function should return a LanguageModelV4 instance.
|
|
286
293
|
*/
|
|
287
|
-
model?:
|
|
288
|
-
| string
|
|
289
|
-
| CompatibleLanguageModel
|
|
290
|
-
| (() => Promise<CompatibleLanguageModel>);
|
|
294
|
+
model?: LanguageModel;
|
|
291
295
|
|
|
292
296
|
/**
|
|
293
297
|
* Override the system message for this step.
|
|
@@ -312,35 +316,53 @@ export interface PrepareStepResult extends Partial<GenerationSettings> {
|
|
|
312
316
|
activeTools?: string[];
|
|
313
317
|
|
|
314
318
|
/**
|
|
315
|
-
*
|
|
316
|
-
*
|
|
319
|
+
* Updated runtime context for the current and subsequent steps.
|
|
320
|
+
* Returning a value replaces the agent's runtime context.
|
|
317
321
|
*/
|
|
318
|
-
|
|
322
|
+
runtimeContext?: TRuntimeContext;
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Updated per-tool context for the current and subsequent steps.
|
|
326
|
+
* Returning a value replaces the agent's tools context.
|
|
327
|
+
*/
|
|
328
|
+
toolsContext?: InferToolSetContext<TTools>;
|
|
319
329
|
}
|
|
320
330
|
|
|
321
331
|
/**
|
|
322
332
|
* Callback function called before each step in the agent loop.
|
|
323
333
|
* Use this to modify settings, manage context, or implement dynamic behavior.
|
|
324
334
|
*/
|
|
325
|
-
export type PrepareStepCallback<
|
|
326
|
-
|
|
327
|
-
|
|
335
|
+
export type PrepareStepCallback<
|
|
336
|
+
TTools extends ToolSet = ToolSet,
|
|
337
|
+
TRuntimeContext extends Context = Context,
|
|
338
|
+
> = (
|
|
339
|
+
info: PrepareStepInfo<TTools, TRuntimeContext>,
|
|
340
|
+
) =>
|
|
341
|
+
| PrepareStepResult<TTools, TRuntimeContext>
|
|
342
|
+
| undefined
|
|
343
|
+
| Promise<PrepareStepResult<TTools, TRuntimeContext> | undefined>;
|
|
328
344
|
|
|
329
345
|
/**
|
|
330
346
|
* Options passed to the prepareCall callback.
|
|
331
347
|
*/
|
|
332
348
|
export interface PrepareCallOptions<
|
|
333
349
|
TTools extends ToolSet = ToolSet,
|
|
350
|
+
TRuntimeContext extends Context = Context,
|
|
334
351
|
> extends Partial<GenerationSettings> {
|
|
335
|
-
model:
|
|
336
|
-
| string
|
|
337
|
-
| CompatibleLanguageModel
|
|
338
|
-
| (() => Promise<CompatibleLanguageModel>);
|
|
352
|
+
model: LanguageModel;
|
|
339
353
|
tools: TTools;
|
|
340
|
-
instructions?:
|
|
354
|
+
instructions?: Instructions;
|
|
341
355
|
toolChoice?: ToolChoice<TTools>;
|
|
342
|
-
|
|
343
|
-
|
|
356
|
+
telemetry?: TelemetryOptions<TRuntimeContext, TTools>;
|
|
357
|
+
/**
|
|
358
|
+
* Runtime context that flows through the agent loop.
|
|
359
|
+
* Treat as immutable; return a new `runtimeContext` to update it for the call.
|
|
360
|
+
*/
|
|
361
|
+
runtimeContext?: TRuntimeContext;
|
|
362
|
+
/**
|
|
363
|
+
* Per-tool context, keyed by tool name.
|
|
364
|
+
*/
|
|
365
|
+
toolsContext?: InferToolSetContext<TTools>;
|
|
344
366
|
messages: ModelMessage[];
|
|
345
367
|
}
|
|
346
368
|
|
|
@@ -350,130 +372,207 @@ export interface PrepareCallOptions<
|
|
|
350
372
|
* Note: `tools` cannot be overridden via prepareCall because they are
|
|
351
373
|
* bound at construction time for type safety.
|
|
352
374
|
*/
|
|
353
|
-
export type PrepareCallResult<
|
|
354
|
-
|
|
355
|
-
|
|
375
|
+
export type PrepareCallResult<
|
|
376
|
+
TTools extends ToolSet = ToolSet,
|
|
377
|
+
TRuntimeContext extends Context = Context,
|
|
378
|
+
> = Partial<Omit<PrepareCallOptions<TTools, TRuntimeContext>, 'tools'>>;
|
|
356
379
|
|
|
357
380
|
/**
|
|
358
381
|
* Callback called once before the agent loop starts to transform call parameters.
|
|
359
382
|
*/
|
|
360
|
-
export type PrepareCallCallback<
|
|
361
|
-
|
|
362
|
-
|
|
383
|
+
export type PrepareCallCallback<
|
|
384
|
+
TTools extends ToolSet = ToolSet,
|
|
385
|
+
TRuntimeContext extends Context = Context,
|
|
386
|
+
> = (
|
|
387
|
+
options: PrepareCallOptions<TTools, TRuntimeContext>,
|
|
388
|
+
) =>
|
|
389
|
+
| PrepareCallResult<TTools, TRuntimeContext>
|
|
390
|
+
| Promise<PrepareCallResult<TTools, TRuntimeContext>>;
|
|
363
391
|
|
|
364
392
|
/**
|
|
365
393
|
* Configuration options for creating a {@link WorkflowAgent} instance.
|
|
366
394
|
*/
|
|
367
|
-
export
|
|
395
|
+
export type WorkflowAgentOptions<
|
|
368
396
|
TTools extends ToolSet = ToolSet,
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
model:
|
|
377
|
-
| string
|
|
378
|
-
| CompatibleLanguageModel
|
|
379
|
-
| (() => Promise<CompatibleLanguageModel>);
|
|
397
|
+
TRuntimeContext extends Context = Context,
|
|
398
|
+
> = GenerationSettings &
|
|
399
|
+
WorkflowAgentToolsContextParameter<TTools> & {
|
|
400
|
+
/**
|
|
401
|
+
* The id of the agent.
|
|
402
|
+
*/
|
|
403
|
+
id?: string;
|
|
380
404
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
405
|
+
/**
|
|
406
|
+
* The model provider to use for the agent.
|
|
407
|
+
*
|
|
408
|
+
* This should be a string compatible with the Vercel AI Gateway (e.g., 'anthropic/claude-opus'),
|
|
409
|
+
* or a LanguageModelV4 instance from a provider.
|
|
410
|
+
*/
|
|
411
|
+
model: LanguageModel;
|
|
387
412
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
413
|
+
/**
|
|
414
|
+
* A set of tools available to the agent.
|
|
415
|
+
* Tools can be implemented as workflow steps for automatic retries and persistence,
|
|
416
|
+
* or as regular workflow-level logic using core library features like sleep() and Hooks.
|
|
417
|
+
*/
|
|
418
|
+
tools?: TTools;
|
|
393
419
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
420
|
+
/**
|
|
421
|
+
* Agent instructions. Can be a string, a SystemModelMessage, or an array of SystemModelMessages.
|
|
422
|
+
* Supports provider-specific options (e.g., caching) when using the SystemModelMessage form.
|
|
423
|
+
*/
|
|
424
|
+
instructions?: Instructions;
|
|
399
425
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
426
|
+
/**
|
|
427
|
+
* Optional system prompt to guide the agent's behavior.
|
|
428
|
+
* @deprecated Use `instructions` instead.
|
|
429
|
+
*/
|
|
430
|
+
system?: string;
|
|
404
431
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
432
|
+
/**
|
|
433
|
+
* The tool choice strategy. Default: 'auto'.
|
|
434
|
+
*/
|
|
435
|
+
toolChoice?: ToolChoice<TTools>;
|
|
409
436
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
* Experimental (can break in patch releases).
|
|
415
|
-
* @default undefined
|
|
416
|
-
*/
|
|
417
|
-
experimental_context?: unknown;
|
|
437
|
+
/**
|
|
438
|
+
* Optional telemetry configuration.
|
|
439
|
+
*/
|
|
440
|
+
telemetry?: TelemetryOptions<TRuntimeContext, TTools>;
|
|
418
441
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
442
|
+
/**
|
|
443
|
+
* Default runtime context for every stream call on this agent.
|
|
444
|
+
*
|
|
445
|
+
* The runtime context flows through `prepareStep`, lifecycle callbacks,
|
|
446
|
+
* and step results.
|
|
447
|
+
* Treat as immutable; return a new `runtimeContext` from `prepareStep`
|
|
448
|
+
* to update it between steps.
|
|
449
|
+
*
|
|
450
|
+
* In workflow context, keep values serializable so they can cross workflow
|
|
451
|
+
* and step boundaries.
|
|
452
|
+
*
|
|
453
|
+
* Per-stream `runtimeContext` values passed to `stream()` override this default.
|
|
454
|
+
*/
|
|
455
|
+
runtimeContext?: TRuntimeContext;
|
|
427
456
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
457
|
+
/**
|
|
458
|
+
* Default stop condition for the agent loop. When the condition is an array,
|
|
459
|
+
* any of the conditions can be met to stop the generation.
|
|
460
|
+
*
|
|
461
|
+
* Per-stream `stopWhen` values passed to `stream()` override this default.
|
|
462
|
+
*/
|
|
463
|
+
stopWhen?:
|
|
464
|
+
| StopCondition<NoInfer<ToolSet>, any>
|
|
465
|
+
| Array<StopCondition<NoInfer<ToolSet>, any>>;
|
|
432
466
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
467
|
+
/**
|
|
468
|
+
* Default set of active tools that limits which tools the model can call,
|
|
469
|
+
* without changing the tool call and result types in the result.
|
|
470
|
+
*
|
|
471
|
+
* Per-stream `activeTools` values passed to `stream()` override this default.
|
|
472
|
+
*/
|
|
473
|
+
activeTools?: ActiveTools<NoInfer<TTools>>;
|
|
437
474
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
475
|
+
/**
|
|
476
|
+
* Default output specification for structured outputs.
|
|
477
|
+
* Use `Output.object({ schema })` for structured output or `Output.text()` for text output.
|
|
478
|
+
*
|
|
479
|
+
* Per-stream `output` values passed to `stream()` override this default.
|
|
480
|
+
*/
|
|
481
|
+
output?: OutputSpecification<any, any>;
|
|
442
482
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
483
|
+
/**
|
|
484
|
+
* Default function that attempts to repair a tool call that failed to parse.
|
|
485
|
+
*
|
|
486
|
+
* Per-stream `experimental_repairToolCall` values passed to `stream()` override this default.
|
|
487
|
+
*/
|
|
488
|
+
experimental_repairToolCall?: ToolCallRepairFunction<TTools>;
|
|
447
489
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
490
|
+
/**
|
|
491
|
+
* Default custom download function to use for URLs.
|
|
492
|
+
*
|
|
493
|
+
* Per-stream `experimental_download` values passed to `stream()` override this default.
|
|
494
|
+
*/
|
|
495
|
+
experimental_download?: DownloadFunction;
|
|
452
496
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
497
|
+
/**
|
|
498
|
+
* Default callback function called before each step in the agent loop.
|
|
499
|
+
* Use this to modify settings, manage context, or inject messages dynamically
|
|
500
|
+
* for every stream call on this agent instance.
|
|
501
|
+
*
|
|
502
|
+
* Per-stream `prepareStep` values passed to `stream()` override this default.
|
|
503
|
+
*/
|
|
504
|
+
prepareStep?: PrepareStepCallback<TTools, TRuntimeContext>;
|
|
457
505
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
506
|
+
/**
|
|
507
|
+
* Callback function to be called after each step completes.
|
|
508
|
+
*/
|
|
509
|
+
onStepEnd?: WorkflowAgentOnStepEndCallback<TTools, TRuntimeContext>;
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Callback function to be called after each step completes.
|
|
513
|
+
*
|
|
514
|
+
* @deprecated Use `onStepEnd` instead.
|
|
515
|
+
*/
|
|
516
|
+
onStepFinish?: WorkflowAgentOnStepFinishCallback<TTools, TRuntimeContext>;
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Callback that is called when the LLM response and all request tool executions are finished.
|
|
520
|
+
*/
|
|
521
|
+
onEnd?: WorkflowAgentOnEndCallback<TTools, TRuntimeContext>;
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Callback that is called when the LLM response and all request tool executions are finished.
|
|
525
|
+
*
|
|
526
|
+
* @deprecated Use `onEnd` instead.
|
|
527
|
+
*/
|
|
528
|
+
onFinish?: WorkflowAgentOnFinishCallback<TTools, TRuntimeContext>;
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Callback called when the agent starts streaming, before any LLM calls.
|
|
532
|
+
*/
|
|
533
|
+
experimental_onStart?: WorkflowAgentOnStartCallback<
|
|
534
|
+
TTools,
|
|
535
|
+
TRuntimeContext
|
|
536
|
+
>;
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Callback called before each step (LLM call) begins.
|
|
540
|
+
*/
|
|
541
|
+
experimental_onStepStart?: WorkflowAgentOnStepStartCallback<
|
|
542
|
+
TTools,
|
|
543
|
+
TRuntimeContext
|
|
544
|
+
>;
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Callback called before a tool's execute function runs.
|
|
548
|
+
*/
|
|
549
|
+
onToolExecutionStart?: WorkflowAgentOnToolExecutionStartCallback<TTools>;
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Callback called after a tool execution completes.
|
|
553
|
+
*/
|
|
554
|
+
onToolExecutionEnd?: WorkflowAgentOnToolExecutionEndCallback<TTools>;
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Prepare the parameters for the stream call.
|
|
558
|
+
* Called once before the agent loop starts. Use this to transform
|
|
559
|
+
* model, tools, instructions, or other settings based on runtime context.
|
|
560
|
+
*/
|
|
561
|
+
prepareCall?: PrepareCallCallback<TTools, TRuntimeContext>;
|
|
562
|
+
};
|
|
465
563
|
|
|
466
564
|
/**
|
|
467
565
|
* Callback that is called when the LLM response and all request tool executions are finished.
|
|
468
566
|
*/
|
|
469
|
-
export type
|
|
567
|
+
export type WorkflowAgentOnEndCallback<
|
|
470
568
|
TTools extends ToolSet = ToolSet,
|
|
569
|
+
TRuntimeContext extends Context = Context,
|
|
471
570
|
OUTPUT = never,
|
|
472
571
|
> = (event: {
|
|
473
572
|
/**
|
|
474
573
|
* Details for all steps.
|
|
475
574
|
*/
|
|
476
|
-
readonly steps: StepResult<TTools,
|
|
575
|
+
readonly steps: StepResult<TTools, TRuntimeContext>[];
|
|
477
576
|
|
|
478
577
|
/**
|
|
479
578
|
* The final messages including all tool calls and results.
|
|
@@ -490,15 +589,25 @@ export type StreamTextOnFinishCallback<
|
|
|
490
589
|
*/
|
|
491
590
|
readonly finishReason: FinishReason;
|
|
492
591
|
|
|
592
|
+
/**
|
|
593
|
+
* The total token usage across all steps.
|
|
594
|
+
*/
|
|
595
|
+
readonly usage: LanguageModelUsage;
|
|
596
|
+
|
|
493
597
|
/**
|
|
494
598
|
* The total token usage across all steps.
|
|
495
599
|
*/
|
|
496
600
|
readonly totalUsage: LanguageModelUsage;
|
|
497
601
|
|
|
498
602
|
/**
|
|
499
|
-
*
|
|
603
|
+
* The runtime context at the end of the agent loop.
|
|
604
|
+
*/
|
|
605
|
+
readonly runtimeContext: TRuntimeContext;
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* The per-tool context at the end of the agent loop.
|
|
500
609
|
*/
|
|
501
|
-
readonly
|
|
610
|
+
readonly toolsContext: InferToolSetContext<TTools>;
|
|
502
611
|
|
|
503
612
|
/**
|
|
504
613
|
* The generated structured output. It uses the `output` specification.
|
|
@@ -507,17 +616,28 @@ export type StreamTextOnFinishCallback<
|
|
|
507
616
|
readonly output: OUTPUT;
|
|
508
617
|
}) => PromiseLike<void> | void;
|
|
509
618
|
|
|
619
|
+
/**
|
|
620
|
+
* Callback that is called when the LLM response and all request tool executions are finished.
|
|
621
|
+
*
|
|
622
|
+
* @deprecated Use `WorkflowAgentOnEndCallback` instead.
|
|
623
|
+
*/
|
|
624
|
+
export type WorkflowAgentOnFinishCallback<
|
|
625
|
+
TTools extends ToolSet = ToolSet,
|
|
626
|
+
TRuntimeContext extends Context = Context,
|
|
627
|
+
OUTPUT = never,
|
|
628
|
+
> = WorkflowAgentOnEndCallback<TTools, TRuntimeContext, OUTPUT>;
|
|
629
|
+
|
|
510
630
|
/**
|
|
511
631
|
* Callback that is invoked when an error occurs during streaming.
|
|
512
632
|
*/
|
|
513
|
-
export type
|
|
633
|
+
export type WorkflowAgentOnErrorCallback = (event: {
|
|
514
634
|
error: unknown;
|
|
515
635
|
}) => PromiseLike<void> | void;
|
|
516
636
|
|
|
517
637
|
/**
|
|
518
638
|
* Callback that is set using the `onAbort` option.
|
|
519
639
|
*/
|
|
520
|
-
export type
|
|
640
|
+
export type WorkflowAgentOnAbortCallback<TTools extends ToolSet = ToolSet> =
|
|
521
641
|
(event: {
|
|
522
642
|
/**
|
|
523
643
|
* Details for all previously finished steps.
|
|
@@ -528,266 +648,383 @@ export type StreamTextOnAbortCallback<TTools extends ToolSet = ToolSet> =
|
|
|
528
648
|
/**
|
|
529
649
|
* Callback that is called when the agent starts streaming, before any LLM calls.
|
|
530
650
|
*/
|
|
531
|
-
export type WorkflowAgentOnStartCallback
|
|
651
|
+
export type WorkflowAgentOnStartCallback<
|
|
652
|
+
TTools extends ToolSet = ToolSet,
|
|
653
|
+
TRuntimeContext extends Context = Context,
|
|
654
|
+
> = (event: {
|
|
532
655
|
/** The model being used */
|
|
533
|
-
readonly model:
|
|
534
|
-
| string
|
|
535
|
-
| CompatibleLanguageModel
|
|
536
|
-
| (() => Promise<CompatibleLanguageModel>);
|
|
656
|
+
readonly model: LanguageModel;
|
|
537
657
|
/** The messages being sent */
|
|
538
658
|
readonly messages: ModelMessage[];
|
|
659
|
+
/** Shared runtime context for this agent loop */
|
|
660
|
+
readonly runtimeContext: TRuntimeContext;
|
|
661
|
+
/** Per-tool context map for this agent loop */
|
|
662
|
+
readonly toolsContext: InferToolSetContext<TTools>;
|
|
539
663
|
}) => PromiseLike<void> | void;
|
|
540
664
|
|
|
541
665
|
/**
|
|
542
666
|
* Callback that is called before each step (LLM call) begins.
|
|
543
667
|
*/
|
|
544
|
-
export type WorkflowAgentOnStepStartCallback
|
|
668
|
+
export type WorkflowAgentOnStepStartCallback<
|
|
669
|
+
TTools extends ToolSet = ToolSet,
|
|
670
|
+
TRuntimeContext extends Context = Context,
|
|
671
|
+
> = (event: {
|
|
545
672
|
/** The current step number (0-based) */
|
|
546
673
|
readonly stepNumber: number;
|
|
547
674
|
/** The model being used for this step */
|
|
548
|
-
readonly model:
|
|
549
|
-
| string
|
|
550
|
-
| CompatibleLanguageModel
|
|
551
|
-
| (() => Promise<CompatibleLanguageModel>);
|
|
675
|
+
readonly model: LanguageModel;
|
|
552
676
|
/** The messages being sent for this step */
|
|
553
677
|
readonly messages: ModelMessage[];
|
|
678
|
+
/** Results from all previously finished steps */
|
|
679
|
+
readonly steps: ReadonlyArray<StepResult<TTools, TRuntimeContext>>;
|
|
680
|
+
/** Shared runtime context for this step */
|
|
681
|
+
readonly runtimeContext: TRuntimeContext;
|
|
682
|
+
/** Per-tool context map for this step */
|
|
683
|
+
readonly toolsContext: InferToolSetContext<TTools>;
|
|
554
684
|
}) => PromiseLike<void> | void;
|
|
555
685
|
|
|
556
686
|
/**
|
|
557
687
|
* Callback that is called before a tool's execute function runs.
|
|
558
688
|
*/
|
|
559
|
-
export type
|
|
689
|
+
export type WorkflowAgentOnToolExecutionStartCallback<
|
|
690
|
+
TTools extends ToolSet = ToolSet,
|
|
691
|
+
> = (event: {
|
|
560
692
|
/** The tool call being executed */
|
|
561
693
|
readonly toolCall: ToolCall;
|
|
694
|
+
/** The current step number (0-based) */
|
|
695
|
+
readonly stepNumber: number;
|
|
696
|
+
/** Messages sent to the language model for the step that produced the call */
|
|
697
|
+
readonly messages: ModelMessage[];
|
|
698
|
+
/** Tool-specific context passed to the tool */
|
|
699
|
+
readonly toolContext:
|
|
700
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
701
|
+
| undefined;
|
|
562
702
|
}) => PromiseLike<void> | void;
|
|
563
703
|
|
|
564
704
|
/**
|
|
565
705
|
* Callback that is called after a tool execution completes.
|
|
706
|
+
* Uses a discriminated union pattern: check `success` to determine
|
|
707
|
+
* whether `output` or `error` is available.
|
|
566
708
|
*/
|
|
567
|
-
export type
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
709
|
+
export type WorkflowAgentOnToolExecutionEndCallback<
|
|
710
|
+
TTools extends ToolSet = ToolSet,
|
|
711
|
+
> = (
|
|
712
|
+
event:
|
|
713
|
+
| {
|
|
714
|
+
/** The tool call that was executed */
|
|
715
|
+
readonly toolCall: ToolCall;
|
|
716
|
+
/** The current step number (0-based) */
|
|
717
|
+
readonly stepNumber: number;
|
|
718
|
+
/** Execution time in milliseconds */
|
|
719
|
+
readonly durationMs: number;
|
|
720
|
+
/** Messages sent to the language model for the step that produced the call */
|
|
721
|
+
readonly messages: ModelMessage[];
|
|
722
|
+
/** Tool-specific context passed to the tool */
|
|
723
|
+
readonly toolContext:
|
|
724
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
725
|
+
| undefined;
|
|
726
|
+
/** Whether the tool call succeeded */
|
|
727
|
+
readonly success: true;
|
|
728
|
+
/** The tool result */
|
|
729
|
+
readonly output: unknown;
|
|
730
|
+
readonly error?: never;
|
|
731
|
+
}
|
|
732
|
+
| {
|
|
733
|
+
/** The tool call that was executed */
|
|
734
|
+
readonly toolCall: ToolCall;
|
|
735
|
+
/** The current step number (0-based) */
|
|
736
|
+
readonly stepNumber: number;
|
|
737
|
+
/** Execution time in milliseconds */
|
|
738
|
+
readonly durationMs: number;
|
|
739
|
+
/** Messages sent to the language model for the step that produced the call */
|
|
740
|
+
readonly messages: ModelMessage[];
|
|
741
|
+
/** Tool-specific context passed to the tool */
|
|
742
|
+
readonly toolContext:
|
|
743
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
744
|
+
| undefined;
|
|
745
|
+
/** Whether the tool call succeeded */
|
|
746
|
+
readonly success: false;
|
|
747
|
+
/** The error that occurred */
|
|
748
|
+
readonly error: unknown;
|
|
749
|
+
readonly output?: never;
|
|
750
|
+
},
|
|
751
|
+
) => PromiseLike<void> | void;
|
|
575
752
|
|
|
576
753
|
/**
|
|
577
754
|
* Options for the {@link WorkflowAgent.stream} method.
|
|
578
755
|
*/
|
|
579
|
-
export
|
|
756
|
+
export type WorkflowAgentStreamOptions<
|
|
580
757
|
TTools extends ToolSet = ToolSet,
|
|
758
|
+
TRuntimeContext extends Context = Context,
|
|
581
759
|
OUTPUT = never,
|
|
582
760
|
PARTIAL_OUTPUT = never,
|
|
583
|
-
>
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
761
|
+
> = Partial<GenerationSettings> &
|
|
762
|
+
(
|
|
763
|
+
| {
|
|
764
|
+
/**
|
|
765
|
+
* A prompt. It can be either a text prompt or a list of messages.
|
|
766
|
+
*
|
|
767
|
+
* You can either use `prompt` or `messages` but not both.
|
|
768
|
+
*/
|
|
769
|
+
prompt: string | Array<ModelMessage>;
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* A list of messages.
|
|
773
|
+
*
|
|
774
|
+
* You can either use `prompt` or `messages` but not both.
|
|
775
|
+
*/
|
|
776
|
+
messages?: never;
|
|
777
|
+
}
|
|
778
|
+
| {
|
|
779
|
+
/**
|
|
780
|
+
* The conversation messages to process. Should follow the AI SDK's ModelMessage format.
|
|
781
|
+
*
|
|
782
|
+
* You can either use `prompt` or `messages` but not both.
|
|
783
|
+
*/
|
|
784
|
+
messages: Array<ModelMessage>;
|
|
785
|
+
|
|
786
|
+
/**
|
|
787
|
+
* A prompt. It can be either a text prompt or a list of messages.
|
|
788
|
+
*
|
|
789
|
+
* You can either use `prompt` or `messages` but not both.
|
|
790
|
+
*/
|
|
791
|
+
prompt?: never;
|
|
792
|
+
}
|
|
793
|
+
) & {
|
|
794
|
+
/**
|
|
795
|
+
* Optional system prompt override. If provided, overrides the system prompt from the constructor.
|
|
796
|
+
*/
|
|
797
|
+
system?: string;
|
|
588
798
|
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
799
|
+
/**
|
|
800
|
+
* A WritableStream that receives raw LanguageModelV4StreamPart chunks in real-time
|
|
801
|
+
* as the model generates them. This enables streaming to the client without
|
|
802
|
+
* coupling WorkflowAgent to UIMessageChunk format.
|
|
803
|
+
*
|
|
804
|
+
* Convert to UIMessageChunks at the response boundary using
|
|
805
|
+
* `createUIMessageChunkTransform()` from `@ai-sdk/workflow`.
|
|
806
|
+
*
|
|
807
|
+
* @example
|
|
808
|
+
* ```typescript
|
|
809
|
+
* // In the workflow:
|
|
810
|
+
* await agent.stream({
|
|
811
|
+
* messages,
|
|
812
|
+
* writable: getWritable<ModelCallStreamPart>(),
|
|
813
|
+
* });
|
|
814
|
+
*
|
|
815
|
+
* // In the route handler:
|
|
816
|
+
* return createUIMessageStreamResponse({
|
|
817
|
+
* stream: run.readable.pipeThrough(createModelCallToUIChunkTransform()),
|
|
818
|
+
* });
|
|
819
|
+
* ```
|
|
820
|
+
*/
|
|
821
|
+
writable?: WritableStream<ModelCallStreamPart<ToolSet>>;
|
|
593
822
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
*
|
|
602
|
-
* @example
|
|
603
|
-
* ```typescript
|
|
604
|
-
* // In the workflow:
|
|
605
|
-
* await agent.stream({
|
|
606
|
-
* messages,
|
|
607
|
-
* writable: getWritable<ModelCallStreamPart>(),
|
|
608
|
-
* });
|
|
609
|
-
*
|
|
610
|
-
* // In the route handler:
|
|
611
|
-
* return createUIMessageStreamResponse({
|
|
612
|
-
* stream: run.readable.pipeThrough(createModelCallToUIChunkTransform()),
|
|
613
|
-
* });
|
|
614
|
-
* ```
|
|
615
|
-
*/
|
|
616
|
-
writable?: WritableStream<ModelCallStreamPart<ToolSet>>;
|
|
823
|
+
/**
|
|
824
|
+
* Condition for stopping the generation when there are tool results in the last step.
|
|
825
|
+
* When the condition is an array, any of the conditions can be met to stop the generation.
|
|
826
|
+
*/
|
|
827
|
+
stopWhen?:
|
|
828
|
+
| StopCondition<NoInfer<ToolSet>, any>
|
|
829
|
+
| Array<StopCondition<NoInfer<ToolSet>, any>>;
|
|
617
830
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
| StopCondition<NoInfer<ToolSet>, any>
|
|
624
|
-
| Array<StopCondition<NoInfer<ToolSet>, any>>;
|
|
831
|
+
/**
|
|
832
|
+
* The tool choice strategy. Default: 'auto'.
|
|
833
|
+
* Overrides the toolChoice from the constructor if provided.
|
|
834
|
+
*/
|
|
835
|
+
toolChoice?: ToolChoice<TTools>;
|
|
625
836
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
maxSteps?: number;
|
|
837
|
+
/**
|
|
838
|
+
* Limits the tools that are available for the model to call without
|
|
839
|
+
* changing the tool call and result types in the result.
|
|
840
|
+
*/
|
|
841
|
+
activeTools?: ActiveTools<NoInfer<TTools>>;
|
|
632
842
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
toolChoice?: ToolChoice<TTools>;
|
|
843
|
+
/**
|
|
844
|
+
* Optional telemetry configuration.
|
|
845
|
+
*/
|
|
846
|
+
telemetry?: TelemetryOptions<TRuntimeContext, TTools>;
|
|
638
847
|
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
848
|
+
/**
|
|
849
|
+
* Runtime context that flows through the agent loop.
|
|
850
|
+
*
|
|
851
|
+
* Treat as immutable; return a new `runtimeContext` from `prepareStep`
|
|
852
|
+
* to update it between steps.
|
|
853
|
+
*
|
|
854
|
+
* In workflow context, keep values serializable so they can cross workflow
|
|
855
|
+
* and step boundaries.
|
|
856
|
+
*
|
|
857
|
+
* Overrides the constructor-level `runtimeContext` if provided.
|
|
858
|
+
*/
|
|
859
|
+
runtimeContext?: TRuntimeContext;
|
|
644
860
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
861
|
+
/**
|
|
862
|
+
* Per-tool context, keyed by tool name. Each tool receives only its own
|
|
863
|
+
* validated entry as `context` during execution. Tools that declare a
|
|
864
|
+
* `contextSchema` validate their entry against the schema.
|
|
865
|
+
*
|
|
866
|
+
* In workflow context, keep values serializable so they can cross workflow
|
|
867
|
+
* and step boundaries.
|
|
868
|
+
*
|
|
869
|
+
* Overrides the constructor-level `toolsContext` if provided.
|
|
870
|
+
*/
|
|
871
|
+
toolsContext?: InferToolSetContext<TTools>;
|
|
649
872
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
873
|
+
/**
|
|
874
|
+
* Optional specification for parsing structured outputs from the LLM response.
|
|
875
|
+
* Use `Output.object({ schema })` for structured output or `Output.text()` for text output.
|
|
876
|
+
*
|
|
877
|
+
* @example
|
|
878
|
+
* ```typescript
|
|
879
|
+
* import { Output } from '@workflow/ai';
|
|
880
|
+
* import { z } from 'zod';
|
|
881
|
+
*
|
|
882
|
+
* const result = await agent.stream({
|
|
883
|
+
* messages: [...],
|
|
884
|
+
* writable: getWritable(),
|
|
885
|
+
* output: Output.object({
|
|
886
|
+
* schema: z.object({
|
|
887
|
+
* sentiment: z.enum(['positive', 'negative', 'neutral']),
|
|
888
|
+
* confidence: z.number(),
|
|
889
|
+
* }),
|
|
890
|
+
* }),
|
|
891
|
+
* });
|
|
892
|
+
*
|
|
893
|
+
* console.log(result.output); // { sentiment: 'positive', confidence: 0.95 }
|
|
894
|
+
* ```
|
|
895
|
+
*/
|
|
896
|
+
output?: OutputSpecification<OUTPUT, PARTIAL_OUTPUT>;
|
|
656
897
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
* import { z } from 'zod';
|
|
665
|
-
*
|
|
666
|
-
* const result = await agent.stream({
|
|
667
|
-
* messages: [...],
|
|
668
|
-
* writable: getWritable(),
|
|
669
|
-
* output: Output.object({
|
|
670
|
-
* schema: z.object({
|
|
671
|
-
* sentiment: z.enum(['positive', 'negative', 'neutral']),
|
|
672
|
-
* confidence: z.number(),
|
|
673
|
-
* }),
|
|
674
|
-
* }),
|
|
675
|
-
* });
|
|
676
|
-
*
|
|
677
|
-
* console.log(result.output); // { sentiment: 'positive', confidence: 0.95 }
|
|
678
|
-
* ```
|
|
679
|
-
*/
|
|
680
|
-
output?: OutputSpecification<OUTPUT, PARTIAL_OUTPUT>;
|
|
898
|
+
/**
|
|
899
|
+
* Whether to include raw chunks from the provider in the stream.
|
|
900
|
+
* When enabled, you will receive raw chunks with type 'raw' that contain the unprocessed data from the provider.
|
|
901
|
+
* This allows access to cutting-edge provider features not yet wrapped by the AI SDK.
|
|
902
|
+
* Defaults to false.
|
|
903
|
+
*/
|
|
904
|
+
includeRawChunks?: boolean;
|
|
681
905
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
* Defaults to false.
|
|
687
|
-
*/
|
|
688
|
-
includeRawChunks?: boolean;
|
|
906
|
+
/**
|
|
907
|
+
* A function that attempts to repair a tool call that failed to parse.
|
|
908
|
+
*/
|
|
909
|
+
experimental_repairToolCall?: ToolCallRepairFunction<TTools>;
|
|
689
910
|
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
911
|
+
/**
|
|
912
|
+
* Optional stream transformations.
|
|
913
|
+
* They are applied in the order they are provided.
|
|
914
|
+
* The stream transformations must maintain the stream structure for streamText to work correctly.
|
|
915
|
+
*/
|
|
916
|
+
experimental_transform?:
|
|
917
|
+
| StreamTextTransform<TTools>
|
|
918
|
+
| Array<StreamTextTransform<TTools>>;
|
|
694
919
|
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
experimental_transform?:
|
|
701
|
-
| StreamTextTransform<TTools>
|
|
702
|
-
| Array<StreamTextTransform<TTools>>;
|
|
920
|
+
/**
|
|
921
|
+
* Custom download function to use for URLs.
|
|
922
|
+
* By default, files are downloaded if the model does not support the URL for the given media type.
|
|
923
|
+
*/
|
|
924
|
+
experimental_download?: DownloadFunction;
|
|
703
925
|
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
experimental_download?: DownloadFunction;
|
|
926
|
+
/**
|
|
927
|
+
* Callback function to be called after each step completes.
|
|
928
|
+
*/
|
|
929
|
+
onStepEnd?: WorkflowAgentOnStepEndCallback<TTools, TRuntimeContext>;
|
|
709
930
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
931
|
+
/**
|
|
932
|
+
* Callback function to be called after each step completes.
|
|
933
|
+
*
|
|
934
|
+
* @deprecated Use `onStepEnd` instead.
|
|
935
|
+
*/
|
|
936
|
+
onStepFinish?: WorkflowAgentOnStepFinishCallback<TTools, TRuntimeContext>;
|
|
714
937
|
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
938
|
+
/**
|
|
939
|
+
* Callback that is invoked when an error occurs during streaming.
|
|
940
|
+
* You can use it to log errors.
|
|
941
|
+
*/
|
|
942
|
+
onError?: WorkflowAgentOnErrorCallback;
|
|
720
943
|
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
944
|
+
/**
|
|
945
|
+
* Callback that is called when the LLM response and all request tool executions
|
|
946
|
+
* (for tools that have an `execute` function) are finished.
|
|
947
|
+
*/
|
|
948
|
+
onEnd?: WorkflowAgentOnEndCallback<TTools, TRuntimeContext, OUTPUT>;
|
|
726
949
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
950
|
+
/**
|
|
951
|
+
* Callback that is called when the LLM response and all request tool executions
|
|
952
|
+
* (for tools that have an `execute` function) are finished.
|
|
953
|
+
*
|
|
954
|
+
* @deprecated Use `onEnd` instead.
|
|
955
|
+
*/
|
|
956
|
+
onFinish?: WorkflowAgentOnFinishCallback<TTools, TRuntimeContext, OUTPUT>;
|
|
731
957
|
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
958
|
+
/**
|
|
959
|
+
* Callback that is called when the operation is aborted.
|
|
960
|
+
*/
|
|
961
|
+
onAbort?: WorkflowAgentOnAbortCallback<TTools>;
|
|
736
962
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
963
|
+
/**
|
|
964
|
+
* Callback called when the agent starts streaming, before any LLM calls.
|
|
965
|
+
*/
|
|
966
|
+
experimental_onStart?: WorkflowAgentOnStartCallback<
|
|
967
|
+
TTools,
|
|
968
|
+
TRuntimeContext
|
|
969
|
+
>;
|
|
741
970
|
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
971
|
+
/**
|
|
972
|
+
* Callback called before each step (LLM call) begins.
|
|
973
|
+
*/
|
|
974
|
+
experimental_onStepStart?: WorkflowAgentOnStepStartCallback<
|
|
975
|
+
TTools,
|
|
976
|
+
TRuntimeContext
|
|
977
|
+
>;
|
|
746
978
|
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
979
|
+
/**
|
|
980
|
+
* Callback called before a tool's execute function runs.
|
|
981
|
+
*/
|
|
982
|
+
onToolExecutionStart?: WorkflowAgentOnToolExecutionStartCallback<TTools>;
|
|
751
983
|
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
* @example
|
|
757
|
-
* ```typescript
|
|
758
|
-
* prepareStep: async ({ messages, stepNumber }) => {
|
|
759
|
-
* // Inject messages from a queue
|
|
760
|
-
* const queuedMessages = await getQueuedMessages();
|
|
761
|
-
* if (queuedMessages.length > 0) {
|
|
762
|
-
* return {
|
|
763
|
-
* messages: [...messages, ...queuedMessages],
|
|
764
|
-
* };
|
|
765
|
-
* }
|
|
766
|
-
* return {};
|
|
767
|
-
* }
|
|
768
|
-
* ```
|
|
769
|
-
*/
|
|
770
|
-
prepareStep?: PrepareStepCallback<TTools>;
|
|
984
|
+
/**
|
|
985
|
+
* Callback called after a tool execution completes.
|
|
986
|
+
*/
|
|
987
|
+
onToolExecutionEnd?: WorkflowAgentOnToolExecutionEndCallback<TTools>;
|
|
771
988
|
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
989
|
+
/**
|
|
990
|
+
* Callback function called before each step in the agent loop.
|
|
991
|
+
* Use this to modify settings, manage context, or inject messages dynamically.
|
|
992
|
+
*
|
|
993
|
+
* @example
|
|
994
|
+
* ```typescript
|
|
995
|
+
* prepareStep: async ({ messages, stepNumber }) => {
|
|
996
|
+
* // Inject messages from a queue
|
|
997
|
+
* const queuedMessages = await getQueuedMessages();
|
|
998
|
+
* if (queuedMessages.length > 0) {
|
|
999
|
+
* return {
|
|
1000
|
+
* messages: [...messages, ...queuedMessages],
|
|
1001
|
+
* };
|
|
1002
|
+
* }
|
|
1003
|
+
* return {};
|
|
1004
|
+
* }
|
|
1005
|
+
* ```
|
|
1006
|
+
*/
|
|
1007
|
+
prepareStep?: PrepareStepCallback<TTools, TRuntimeContext>;
|
|
778
1008
|
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
1009
|
+
/**
|
|
1010
|
+
* Timeout in milliseconds for the stream operation.
|
|
1011
|
+
* When specified, creates an AbortSignal that will abort the operation after the given time.
|
|
1012
|
+
* If both `timeout` and `abortSignal` are provided, whichever triggers first will abort.
|
|
1013
|
+
*/
|
|
1014
|
+
timeout?: number;
|
|
784
1015
|
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
1016
|
+
/**
|
|
1017
|
+
* Whether to send a 'finish' chunk to the writable stream when streaming completes.
|
|
1018
|
+
* @default true
|
|
1019
|
+
*/
|
|
1020
|
+
sendFinish?: boolean;
|
|
1021
|
+
|
|
1022
|
+
/**
|
|
1023
|
+
* Whether to prevent the writable stream from being closed after streaming completes.
|
|
1024
|
+
* @default false
|
|
1025
|
+
*/
|
|
1026
|
+
preventClose?: boolean;
|
|
1027
|
+
};
|
|
791
1028
|
|
|
792
1029
|
/**
|
|
793
1030
|
* A tool call made by the model. Matches the AI SDK's tool call shape.
|
|
@@ -819,6 +1056,12 @@ export interface ToolResult {
|
|
|
819
1056
|
output: unknown;
|
|
820
1057
|
}
|
|
821
1058
|
|
|
1059
|
+
type WorkflowToolExecutionResult = {
|
|
1060
|
+
modelResult: LanguageModelV4ToolResultPart;
|
|
1061
|
+
rawOutput: unknown;
|
|
1062
|
+
isError: boolean;
|
|
1063
|
+
};
|
|
1064
|
+
|
|
822
1065
|
/**
|
|
823
1066
|
* Result of the WorkflowAgent.stream method.
|
|
824
1067
|
*/
|
|
@@ -897,50 +1140,77 @@ export interface WorkflowAgentStreamResult<
|
|
|
897
1140
|
* });
|
|
898
1141
|
* ```
|
|
899
1142
|
*/
|
|
900
|
-
export class WorkflowAgent<
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
1143
|
+
export class WorkflowAgent<
|
|
1144
|
+
TBaseTools extends ToolSet = ToolSet,
|
|
1145
|
+
TRuntimeContext extends Context = Context,
|
|
1146
|
+
> {
|
|
1147
|
+
/**
|
|
1148
|
+
* The id of the agent.
|
|
1149
|
+
*/
|
|
1150
|
+
public readonly id: string | undefined;
|
|
1151
|
+
|
|
1152
|
+
private model: LanguageModel;
|
|
905
1153
|
/**
|
|
906
1154
|
* The tool set configured for this agent.
|
|
907
1155
|
*/
|
|
908
1156
|
public readonly tools: TBaseTools;
|
|
909
|
-
private instructions?:
|
|
910
|
-
| string
|
|
911
|
-
| SystemModelMessage
|
|
912
|
-
| Array<SystemModelMessage>;
|
|
1157
|
+
private instructions?: Instructions;
|
|
913
1158
|
private generationSettings: GenerationSettings;
|
|
914
1159
|
private toolChoice?: ToolChoice<TBaseTools>;
|
|
915
|
-
private telemetry?:
|
|
916
|
-
private
|
|
917
|
-
private
|
|
918
|
-
private
|
|
919
|
-
ToolSet,
|
|
920
|
-
any
|
|
1160
|
+
private telemetry?: TelemetryOptions<TRuntimeContext, TBaseTools>;
|
|
1161
|
+
private runtimeContext?: TRuntimeContext;
|
|
1162
|
+
private toolsContext?: InferToolSetContext<TBaseTools>;
|
|
1163
|
+
private stopWhen?:
|
|
1164
|
+
| StopCondition<ToolSet, any>
|
|
1165
|
+
| Array<StopCondition<ToolSet, any>>;
|
|
1166
|
+
private activeTools?: ActiveTools<TBaseTools>;
|
|
1167
|
+
private output?: OutputSpecification<any, any>;
|
|
1168
|
+
private experimentalRepairToolCall?: ToolCallRepairFunction<TBaseTools>;
|
|
1169
|
+
private experimentalDownload?: DownloadFunction;
|
|
1170
|
+
private prepareStep?: PrepareStepCallback<TBaseTools, TRuntimeContext>;
|
|
1171
|
+
private constructorOnStepEnd?: WorkflowAgentOnStepEndCallback<
|
|
1172
|
+
TBaseTools,
|
|
1173
|
+
TRuntimeContext
|
|
1174
|
+
>;
|
|
1175
|
+
private constructorOnEnd?: WorkflowAgentOnEndCallback<
|
|
1176
|
+
TBaseTools,
|
|
1177
|
+
TRuntimeContext
|
|
1178
|
+
>;
|
|
1179
|
+
private constructorOnStart?: WorkflowAgentOnStartCallback<
|
|
1180
|
+
TBaseTools,
|
|
1181
|
+
TRuntimeContext
|
|
921
1182
|
>;
|
|
922
|
-
private
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
private
|
|
927
|
-
private
|
|
928
|
-
|
|
929
|
-
|
|
1183
|
+
private constructorOnStepStart?: WorkflowAgentOnStepStartCallback<
|
|
1184
|
+
TBaseTools,
|
|
1185
|
+
TRuntimeContext
|
|
1186
|
+
>;
|
|
1187
|
+
private constructorOnToolExecutionStart?: WorkflowAgentOnToolExecutionStartCallback<TBaseTools>;
|
|
1188
|
+
private constructorOnToolExecutionEnd?: WorkflowAgentOnToolExecutionEndCallback<TBaseTools>;
|
|
1189
|
+
private prepareCall?: PrepareCallCallback<TBaseTools, TRuntimeContext>;
|
|
1190
|
+
|
|
1191
|
+
constructor(options: WorkflowAgentOptions<TBaseTools, TRuntimeContext>) {
|
|
1192
|
+
this.id = options.id;
|
|
930
1193
|
this.model = options.model;
|
|
931
1194
|
this.tools = (options.tools ?? {}) as TBaseTools;
|
|
932
1195
|
// `instructions` takes precedence over deprecated `system`
|
|
933
1196
|
this.instructions = options.instructions ?? options.system;
|
|
934
1197
|
this.toolChoice = options.toolChoice;
|
|
935
|
-
this.telemetry = options.
|
|
936
|
-
this.
|
|
1198
|
+
this.telemetry = options.telemetry;
|
|
1199
|
+
this.runtimeContext = options.runtimeContext;
|
|
1200
|
+
this.toolsContext = options.toolsContext;
|
|
1201
|
+
this.stopWhen = options.stopWhen;
|
|
1202
|
+
this.activeTools = options.activeTools;
|
|
1203
|
+
this.output = options.output;
|
|
1204
|
+
this.experimentalRepairToolCall = options.experimental_repairToolCall;
|
|
1205
|
+
this.experimentalDownload = options.experimental_download;
|
|
937
1206
|
this.prepareStep = options.prepareStep;
|
|
938
|
-
this.
|
|
939
|
-
|
|
1207
|
+
this.constructorOnStepEnd = options.onStepEnd ?? options.onStepFinish;
|
|
1208
|
+
const { onFinish, onEnd = onFinish } = options;
|
|
1209
|
+
this.constructorOnEnd = onEnd;
|
|
940
1210
|
this.constructorOnStart = options.experimental_onStart;
|
|
941
1211
|
this.constructorOnStepStart = options.experimental_onStepStart;
|
|
942
|
-
this.
|
|
943
|
-
this.
|
|
1212
|
+
this.constructorOnToolExecutionStart = options.onToolExecutionStart;
|
|
1213
|
+
this.constructorOnToolExecutionEnd = options.onToolExecutionEnd;
|
|
944
1214
|
this.prepareCall = options.prepareCall;
|
|
945
1215
|
|
|
946
1216
|
// Extract generation settings
|
|
@@ -969,21 +1239,40 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
969
1239
|
OUTPUT = never,
|
|
970
1240
|
PARTIAL_OUTPUT = never,
|
|
971
1241
|
>(
|
|
972
|
-
options: WorkflowAgentStreamOptions<
|
|
1242
|
+
options: WorkflowAgentStreamOptions<
|
|
1243
|
+
TTools,
|
|
1244
|
+
TRuntimeContext,
|
|
1245
|
+
OUTPUT,
|
|
1246
|
+
PARTIAL_OUTPUT
|
|
1247
|
+
>,
|
|
973
1248
|
): Promise<WorkflowAgentStreamResult<TTools, OUTPUT>> {
|
|
1249
|
+
const { onFinish, onEnd = onFinish } = options;
|
|
1250
|
+
|
|
974
1251
|
// Call prepareCall to transform parameters before the agent loop
|
|
975
|
-
let effectiveModel:
|
|
976
|
-
| string
|
|
977
|
-
| CompatibleLanguageModel
|
|
978
|
-
| (() => Promise<CompatibleLanguageModel>) = this.model;
|
|
1252
|
+
let effectiveModel: LanguageModel = this.model;
|
|
979
1253
|
let effectiveInstructions = options.system ?? this.instructions;
|
|
980
|
-
let
|
|
1254
|
+
let effectivePrompt: string | Array<ModelMessage> | undefined =
|
|
1255
|
+
options.prompt;
|
|
1256
|
+
let effectiveMessages: Array<ModelMessage> | undefined = options.messages;
|
|
981
1257
|
let effectiveGenerationSettings = { ...this.generationSettings };
|
|
982
|
-
let
|
|
983
|
-
|
|
1258
|
+
let effectiveRuntimeContext: TRuntimeContext = (options.runtimeContext ??
|
|
1259
|
+
this.runtimeContext ??
|
|
1260
|
+
{}) as TRuntimeContext;
|
|
1261
|
+
let effectiveToolsContext: Record<string, Context | undefined> =
|
|
1262
|
+
(options.toolsContext ?? this.toolsContext ?? {}) as unknown as Record<
|
|
1263
|
+
string,
|
|
1264
|
+
Context | undefined
|
|
1265
|
+
>;
|
|
984
1266
|
let effectiveToolChoiceFromPrepare = options.toolChoice ?? this.toolChoice;
|
|
985
|
-
let effectiveTelemetryFromPrepare =
|
|
986
|
-
|
|
1267
|
+
let effectiveTelemetryFromPrepare = options.telemetry ?? this.telemetry;
|
|
1268
|
+
|
|
1269
|
+
// Resolve messages for prepareCall: use messages directly, or convert prompt
|
|
1270
|
+
const resolvedMessagesForPrepareCall: ModelMessage[] =
|
|
1271
|
+
effectiveMessages ??
|
|
1272
|
+
(typeof effectivePrompt === 'string'
|
|
1273
|
+
? [{ role: 'user' as const, content: effectivePrompt }]
|
|
1274
|
+
: (effectivePrompt as ModelMessage[])) ??
|
|
1275
|
+
[];
|
|
987
1276
|
|
|
988
1277
|
if (this.prepareCall) {
|
|
989
1278
|
const prepared = await this.prepareCall({
|
|
@@ -991,25 +1280,32 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
991
1280
|
tools: this.tools,
|
|
992
1281
|
instructions: effectiveInstructions,
|
|
993
1282
|
toolChoice: effectiveToolChoiceFromPrepare as ToolChoice<TBaseTools>,
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1283
|
+
telemetry: effectiveTelemetryFromPrepare,
|
|
1284
|
+
runtimeContext: effectiveRuntimeContext,
|
|
1285
|
+
toolsContext: effectiveToolsContext as InferToolSetContext<TBaseTools>,
|
|
1286
|
+
messages: resolvedMessagesForPrepareCall,
|
|
997
1287
|
...effectiveGenerationSettings,
|
|
998
|
-
} as PrepareCallOptions<TBaseTools>);
|
|
1288
|
+
} as PrepareCallOptions<TBaseTools, TRuntimeContext>);
|
|
999
1289
|
|
|
1000
1290
|
if (prepared.model !== undefined) effectiveModel = prepared.model;
|
|
1001
1291
|
if (prepared.instructions !== undefined)
|
|
1002
1292
|
effectiveInstructions = prepared.instructions;
|
|
1003
|
-
if (prepared.messages !== undefined)
|
|
1004
|
-
effectiveMessages =
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1293
|
+
if (prepared.messages !== undefined) {
|
|
1294
|
+
effectiveMessages = prepared.messages as Array<ModelMessage>;
|
|
1295
|
+
effectivePrompt = undefined; // messages from prepareCall take precedence
|
|
1296
|
+
}
|
|
1297
|
+
if (prepared.runtimeContext !== undefined)
|
|
1298
|
+
effectiveRuntimeContext = prepared.runtimeContext;
|
|
1299
|
+
if (prepared.toolsContext !== undefined)
|
|
1300
|
+
effectiveToolsContext = prepared.toolsContext as Record<
|
|
1301
|
+
string,
|
|
1302
|
+
Context | undefined
|
|
1303
|
+
>;
|
|
1008
1304
|
if (prepared.toolChoice !== undefined)
|
|
1009
1305
|
effectiveToolChoiceFromPrepare =
|
|
1010
1306
|
prepared.toolChoice as ToolChoice<TBaseTools>;
|
|
1011
|
-
if (prepared.
|
|
1012
|
-
effectiveTelemetryFromPrepare = prepared.
|
|
1307
|
+
if (prepared.telemetry !== undefined)
|
|
1308
|
+
effectiveTelemetryFromPrepare = prepared.telemetry;
|
|
1013
1309
|
if (prepared.maxOutputTokens !== undefined)
|
|
1014
1310
|
effectiveGenerationSettings.maxOutputTokens = prepared.maxOutputTokens;
|
|
1015
1311
|
if (prepared.temperature !== undefined)
|
|
@@ -1033,59 +1329,257 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1033
1329
|
effectiveGenerationSettings.providerOptions = prepared.providerOptions;
|
|
1034
1330
|
}
|
|
1035
1331
|
|
|
1332
|
+
const effectiveTelemetry = effectiveTelemetryFromPrepare;
|
|
1333
|
+
const telemetryDispatcher = createRestrictedTelemetryDispatcher<
|
|
1334
|
+
any,
|
|
1335
|
+
any,
|
|
1336
|
+
any
|
|
1337
|
+
>({
|
|
1338
|
+
telemetry: effectiveTelemetry as any,
|
|
1339
|
+
includeRuntimeContext: effectiveTelemetry?.includeRuntimeContext,
|
|
1340
|
+
includeToolsContext: effectiveTelemetry?.includeToolsContext,
|
|
1341
|
+
}) as any;
|
|
1342
|
+
|
|
1036
1343
|
const prompt = await standardizePrompt({
|
|
1037
1344
|
system: effectiveInstructions,
|
|
1038
|
-
|
|
1039
|
-
|
|
1345
|
+
allowSystemInMessages: true, // TODO: consider exposing this as a parameter
|
|
1346
|
+
...(effectivePrompt != null
|
|
1347
|
+
? { prompt: effectivePrompt }
|
|
1348
|
+
: { messages: effectiveMessages! }),
|
|
1349
|
+
} as Prompt);
|
|
1350
|
+
const download = options.experimental_download ?? this.experimentalDownload;
|
|
1040
1351
|
|
|
1041
1352
|
// Process tool approval responses before starting the agent loop.
|
|
1042
1353
|
// This mirrors how stream-text.ts handles tool-approval-response parts:
|
|
1043
1354
|
// approved tools are executed, denied tools get denial results, and
|
|
1044
1355
|
// approval parts are stripped from the messages.
|
|
1045
|
-
|
|
1046
|
-
|
|
1356
|
+
// Use the AI SDK core collector so this path cannot drift from the
|
|
1357
|
+
// hardened generateText/streamText implementation. The collected approvals
|
|
1358
|
+
// are mapped to the flat shape used below; the original (nested) approval
|
|
1359
|
+
// is carried on `collected` for re-validation.
|
|
1360
|
+
const collectedApprovals = collectToolApprovals<ToolSet>({
|
|
1361
|
+
messages: prompt.messages,
|
|
1362
|
+
});
|
|
1363
|
+
const approvedToolApprovals = collectedApprovals.approvedToolApprovals.map(
|
|
1364
|
+
collected => ({
|
|
1365
|
+
toolCallId: collected.toolCall.toolCallId,
|
|
1366
|
+
toolName: collected.toolCall.toolName,
|
|
1367
|
+
input: collected.toolCall.input,
|
|
1368
|
+
reason: collected.approvalResponse.reason,
|
|
1369
|
+
providerExecuted: collected.toolCall.providerExecuted === true,
|
|
1370
|
+
collected,
|
|
1371
|
+
}),
|
|
1372
|
+
);
|
|
1373
|
+
const deniedToolApprovals = collectedApprovals.deniedToolApprovals.map(
|
|
1374
|
+
collected => ({
|
|
1375
|
+
toolCallId: collected.toolCall.toolCallId,
|
|
1376
|
+
toolName: collected.toolCall.toolName,
|
|
1377
|
+
input: collected.toolCall.input,
|
|
1378
|
+
reason: collected.approvalResponse.reason,
|
|
1379
|
+
providerExecuted: collected.toolCall.providerExecuted === true,
|
|
1380
|
+
}),
|
|
1381
|
+
);
|
|
1382
|
+
|
|
1383
|
+
// Approval ids of provider-executed tool calls. Provider-executed tools
|
|
1384
|
+
// (e.g. MCP via the Responses API) cannot be resolved locally — the
|
|
1385
|
+
// provider owns execution. We therefore skip them from local execution
|
|
1386
|
+
// and preserve their approval responses in the messages so the provider
|
|
1387
|
+
// receives the approval on the next call. The discriminator is sourced
|
|
1388
|
+
// from the original `tool-call` part (matching how core's stream-text.ts
|
|
1389
|
+
// decides), not from the response part which may be missing the flag.
|
|
1390
|
+
const providerExecutedApprovalIds = new Set<string>(
|
|
1391
|
+
[
|
|
1392
|
+
...collectedApprovals.approvedToolApprovals,
|
|
1393
|
+
...collectedApprovals.deniedToolApprovals,
|
|
1394
|
+
]
|
|
1395
|
+
.filter(collected => collected.toolCall.providerExecuted === true)
|
|
1396
|
+
.map(collected => collected.approvalResponse.approvalId),
|
|
1397
|
+
);
|
|
1047
1398
|
|
|
1048
1399
|
if (approvedToolApprovals.length > 0 || deniedToolApprovals.length > 0) {
|
|
1049
1400
|
const _toolResultMessages: ModelMessage[] = [];
|
|
1050
|
-
const toolResultContent:
|
|
1051
|
-
|
|
1401
|
+
const toolResultContent: LanguageModelV4ToolResultPart[] = [];
|
|
1402
|
+
const approvedRawResults: Array<{
|
|
1052
1403
|
toolCallId: string;
|
|
1053
1404
|
toolName: string;
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
| { type: 'json'; value: JSONValue }
|
|
1057
|
-
| { type: 'execution-denied'; reason: string | undefined };
|
|
1405
|
+
input: unknown;
|
|
1406
|
+
output: unknown;
|
|
1058
1407
|
}> = [];
|
|
1059
1408
|
|
|
1060
1409
|
// Execute approved tools
|
|
1061
1410
|
for (const approval of approvedToolApprovals) {
|
|
1411
|
+
// Provider-executed approvals are forwarded to the provider via the
|
|
1412
|
+
// preserved approval response below, not executed locally.
|
|
1413
|
+
if (approval.providerExecuted) {
|
|
1414
|
+
continue;
|
|
1415
|
+
}
|
|
1062
1416
|
const tool = (this.tools as ToolSet)[approval.toolName];
|
|
1063
1417
|
if (tool && typeof tool.execute === 'function') {
|
|
1418
|
+
if (!tool.needsApproval) {
|
|
1419
|
+
const reason = `Tool "${approval.toolName}" does not require approval`;
|
|
1420
|
+
toolResultContent.push({
|
|
1421
|
+
type: 'tool-result' as const,
|
|
1422
|
+
toolCallId: approval.toolCallId,
|
|
1423
|
+
toolName: approval.toolName,
|
|
1424
|
+
output: await createLanguageModelToolResultOutput({
|
|
1425
|
+
toolCallId: approval.toolCallId,
|
|
1426
|
+
toolName: approval.toolName,
|
|
1427
|
+
input: approval.input,
|
|
1428
|
+
output: reason,
|
|
1429
|
+
tool,
|
|
1430
|
+
errorMode: 'text',
|
|
1431
|
+
supportedUrls: {},
|
|
1432
|
+
download,
|
|
1433
|
+
}),
|
|
1434
|
+
});
|
|
1435
|
+
continue;
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
// Re-validate through the shared core implementation: input schema,
|
|
1439
|
+
// HMAC signature (when configured), and approval policy. It throws on
|
|
1440
|
+
// invalid input/signature; convert that to a denial result so the
|
|
1441
|
+
// agent loop can continue gracefully.
|
|
1442
|
+
let revalidationReason: string | undefined;
|
|
1443
|
+
try {
|
|
1444
|
+
const { deniedToolApprovals: policyDenied } =
|
|
1445
|
+
await validateApprovedToolApprovals({
|
|
1446
|
+
approvedToolApprovals: [approval.collected],
|
|
1447
|
+
tools: this.tools as ToolSet,
|
|
1448
|
+
toolApproval: undefined,
|
|
1449
|
+
messages: prompt.messages,
|
|
1450
|
+
toolsContext:
|
|
1451
|
+
effectiveToolsContext as InferToolSetContext<ToolSet>,
|
|
1452
|
+
runtimeContext: effectiveRuntimeContext,
|
|
1453
|
+
});
|
|
1454
|
+
if (policyDenied.length > 0) {
|
|
1455
|
+
revalidationReason =
|
|
1456
|
+
policyDenied[0].approvalResponse.reason ??
|
|
1457
|
+
'Tool approval denied';
|
|
1458
|
+
}
|
|
1459
|
+
} catch (error) {
|
|
1460
|
+
revalidationReason = getErrorMessage(error);
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
if (revalidationReason != null) {
|
|
1464
|
+
toolResultContent.push({
|
|
1465
|
+
type: 'tool-result' as const,
|
|
1466
|
+
toolCallId: approval.toolCallId,
|
|
1467
|
+
toolName: approval.toolName,
|
|
1468
|
+
output: await createLanguageModelToolResultOutput({
|
|
1469
|
+
toolCallId: approval.toolCallId,
|
|
1470
|
+
toolName: approval.toolName,
|
|
1471
|
+
input: approval.input,
|
|
1472
|
+
output: revalidationReason,
|
|
1473
|
+
tool,
|
|
1474
|
+
errorMode: 'text',
|
|
1475
|
+
supportedUrls: {},
|
|
1476
|
+
download,
|
|
1477
|
+
}),
|
|
1478
|
+
});
|
|
1479
|
+
continue;
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1064
1482
|
try {
|
|
1065
1483
|
const { execute } = tool;
|
|
1066
|
-
const
|
|
1484
|
+
const resolvedContext = await resolveToolContext({
|
|
1485
|
+
toolName: approval.toolName,
|
|
1486
|
+
tool,
|
|
1487
|
+
toolsContext: effectiveToolsContext,
|
|
1488
|
+
});
|
|
1489
|
+
const toolCallEvent: ToolCall = {
|
|
1490
|
+
type: 'tool-call',
|
|
1067
1491
|
toolCallId: approval.toolCallId,
|
|
1068
|
-
|
|
1069
|
-
|
|
1492
|
+
toolName: approval.toolName,
|
|
1493
|
+
input: approval.input,
|
|
1494
|
+
};
|
|
1495
|
+
const messages = prompt.messages as unknown as ModelMessage[];
|
|
1496
|
+
await telemetryDispatcher.onToolExecutionStart?.({
|
|
1497
|
+
toolCall: toolCallEvent,
|
|
1498
|
+
stepNumber: 0,
|
|
1499
|
+
messages,
|
|
1500
|
+
toolContext: resolvedContext,
|
|
1501
|
+
});
|
|
1502
|
+
const startTime = Date.now();
|
|
1503
|
+
const executeApprovedTool = () =>
|
|
1504
|
+
execute(approval.input, {
|
|
1505
|
+
toolCallId: approval.toolCallId,
|
|
1506
|
+
messages: [],
|
|
1507
|
+
context: resolvedContext,
|
|
1508
|
+
});
|
|
1509
|
+
const toolResult =
|
|
1510
|
+
telemetryDispatcher.executeTool != null
|
|
1511
|
+
? await telemetryDispatcher.executeTool({
|
|
1512
|
+
callId: 'workflow-agent',
|
|
1513
|
+
toolCallId: approval.toolCallId,
|
|
1514
|
+
execute: executeApprovedTool,
|
|
1515
|
+
})
|
|
1516
|
+
: await executeApprovedTool();
|
|
1517
|
+
await telemetryDispatcher.onToolExecutionEnd?.({
|
|
1518
|
+
toolCall: toolCallEvent,
|
|
1519
|
+
stepNumber: 0,
|
|
1520
|
+
durationMs: Date.now() - startTime,
|
|
1521
|
+
messages,
|
|
1522
|
+
toolContext: resolvedContext,
|
|
1523
|
+
success: true,
|
|
1524
|
+
output: toolResult,
|
|
1070
1525
|
});
|
|
1071
1526
|
toolResultContent.push({
|
|
1072
1527
|
type: 'tool-result' as const,
|
|
1073
1528
|
toolCallId: approval.toolCallId,
|
|
1074
1529
|
toolName: approval.toolName,
|
|
1075
|
-
output:
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1530
|
+
output: await createLanguageModelToolResultOutput({
|
|
1531
|
+
toolCallId: approval.toolCallId,
|
|
1532
|
+
toolName: approval.toolName,
|
|
1533
|
+
input: approval.input,
|
|
1534
|
+
output: toolResult,
|
|
1535
|
+
tool,
|
|
1536
|
+
errorMode: 'none',
|
|
1537
|
+
supportedUrls: {},
|
|
1538
|
+
download,
|
|
1539
|
+
}),
|
|
1540
|
+
});
|
|
1541
|
+
approvedRawResults.push({
|
|
1542
|
+
toolCallId: approval.toolCallId,
|
|
1543
|
+
toolName: approval.toolName,
|
|
1544
|
+
input: approval.input,
|
|
1545
|
+
output: toolResult,
|
|
1079
1546
|
});
|
|
1080
1547
|
} catch (error) {
|
|
1548
|
+
const errorMessage = getErrorMessage(error);
|
|
1549
|
+
await telemetryDispatcher.onToolExecutionEnd?.({
|
|
1550
|
+
toolCall: {
|
|
1551
|
+
type: 'tool-call',
|
|
1552
|
+
toolCallId: approval.toolCallId,
|
|
1553
|
+
toolName: approval.toolName,
|
|
1554
|
+
input: approval.input,
|
|
1555
|
+
},
|
|
1556
|
+
stepNumber: 0,
|
|
1557
|
+
durationMs: 0,
|
|
1558
|
+
messages: prompt.messages as unknown as ModelMessage[],
|
|
1559
|
+
toolContext: undefined,
|
|
1560
|
+
success: false,
|
|
1561
|
+
error,
|
|
1562
|
+
});
|
|
1081
1563
|
toolResultContent.push({
|
|
1082
1564
|
type: 'tool-result' as const,
|
|
1083
1565
|
toolCallId: approval.toolCallId,
|
|
1084
1566
|
toolName: approval.toolName,
|
|
1085
|
-
output: {
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1567
|
+
output: await createLanguageModelToolResultOutput({
|
|
1568
|
+
toolCallId: approval.toolCallId,
|
|
1569
|
+
toolName: approval.toolName,
|
|
1570
|
+
input: approval.input,
|
|
1571
|
+
output: errorMessage,
|
|
1572
|
+
tool,
|
|
1573
|
+
errorMode: 'text',
|
|
1574
|
+
supportedUrls: {},
|
|
1575
|
+
download,
|
|
1576
|
+
}),
|
|
1577
|
+
});
|
|
1578
|
+
approvedRawResults.push({
|
|
1579
|
+
toolCallId: approval.toolCallId,
|
|
1580
|
+
toolName: approval.toolName,
|
|
1581
|
+
input: approval.input,
|
|
1582
|
+
output: errorMessage,
|
|
1089
1583
|
});
|
|
1090
1584
|
}
|
|
1091
1585
|
}
|
|
@@ -1093,6 +1587,11 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1093
1587
|
|
|
1094
1588
|
// Create denial results for denied tools
|
|
1095
1589
|
for (const denial of deniedToolApprovals) {
|
|
1590
|
+
// Provider-executed denials are forwarded to the provider via the
|
|
1591
|
+
// preserved approval response below, not turned into a local result.
|
|
1592
|
+
if (denial.providerExecuted) {
|
|
1593
|
+
continue;
|
|
1594
|
+
}
|
|
1096
1595
|
toolResultContent.push({
|
|
1097
1596
|
type: 'tool-result' as const,
|
|
1098
1597
|
toolCallId: denial.toolCallId,
|
|
@@ -1104,20 +1603,33 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1104
1603
|
});
|
|
1105
1604
|
}
|
|
1106
1605
|
|
|
1107
|
-
// Strip approval parts
|
|
1606
|
+
// Strip approval parts that we resolved locally and inject tool results.
|
|
1607
|
+
// Provider-executed approval parts are preserved so the next call to
|
|
1608
|
+
// `convertToLanguageModelPrompt` forwards the approval response to the
|
|
1609
|
+
// provider (it only forwards responses flagged `providerExecuted`).
|
|
1108
1610
|
const cleanedMessages: ModelMessage[] = [];
|
|
1109
1611
|
for (const msg of prompt.messages) {
|
|
1110
1612
|
if (msg.role === 'assistant' && Array.isArray(msg.content)) {
|
|
1111
1613
|
const filtered = (msg.content as any[]).filter(
|
|
1112
|
-
(p: any) =>
|
|
1614
|
+
(p: any) =>
|
|
1615
|
+
p.type !== 'tool-approval-request' ||
|
|
1616
|
+
providerExecutedApprovalIds.has(p.approvalId),
|
|
1113
1617
|
);
|
|
1114
1618
|
if (filtered.length > 0) {
|
|
1115
1619
|
cleanedMessages.push({ ...msg, content: filtered });
|
|
1116
1620
|
}
|
|
1117
1621
|
} else if (msg.role === 'tool') {
|
|
1118
|
-
const filtered = (msg.content as any[]).
|
|
1119
|
-
(p
|
|
1120
|
-
|
|
1622
|
+
const filtered = (msg.content as any[]).flatMap((p: any) => {
|
|
1623
|
+
if (p.type !== 'tool-approval-response') {
|
|
1624
|
+
return [p];
|
|
1625
|
+
}
|
|
1626
|
+
if (!providerExecutedApprovalIds.has(p.approvalId)) {
|
|
1627
|
+
return [];
|
|
1628
|
+
}
|
|
1629
|
+
// Re-stamp `providerExecuted` so the conversion layer forwards the
|
|
1630
|
+
// response even if the client omitted the flag on the response part.
|
|
1631
|
+
return [{ ...p, providerExecuted: true }];
|
|
1632
|
+
});
|
|
1121
1633
|
if (filtered.length > 0) {
|
|
1122
1634
|
cleanedMessages.push({ ...msg, content: filtered });
|
|
1123
1635
|
}
|
|
@@ -1140,22 +1652,12 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1140
1652
|
// can transition approved/denied tool parts to the correct state
|
|
1141
1653
|
// and properly separate them from the subsequent model step.
|
|
1142
1654
|
if (options.writable && toolResultContent.length > 0) {
|
|
1143
|
-
const approvedResults = toolResultContent
|
|
1144
|
-
.filter(r => r.output.type !== 'execution-denied')
|
|
1145
|
-
.map(r => ({
|
|
1146
|
-
toolCallId: r.toolCallId,
|
|
1147
|
-
toolName: r.toolName,
|
|
1148
|
-
input: approvedToolApprovals.find(
|
|
1149
|
-
a => a.toolCallId === r.toolCallId,
|
|
1150
|
-
)?.input,
|
|
1151
|
-
output: 'value' in r.output ? r.output.value : undefined,
|
|
1152
|
-
}));
|
|
1153
1655
|
const deniedResults = toolResultContent
|
|
1154
1656
|
.filter(r => r.output.type === 'execution-denied')
|
|
1155
1657
|
.map(r => ({ toolCallId: r.toolCallId }));
|
|
1156
1658
|
await writeApprovalToolResults(
|
|
1157
1659
|
options.writable,
|
|
1158
|
-
|
|
1660
|
+
approvedRawResults,
|
|
1159
1661
|
deniedResults,
|
|
1160
1662
|
);
|
|
1161
1663
|
}
|
|
@@ -1164,14 +1666,12 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1164
1666
|
const modelPrompt = await convertToLanguageModelPrompt({
|
|
1165
1667
|
prompt,
|
|
1166
1668
|
supportedUrls: {},
|
|
1167
|
-
download
|
|
1669
|
+
download,
|
|
1168
1670
|
});
|
|
1169
1671
|
|
|
1170
1672
|
const effectiveAbortSignal = mergeAbortSignals(
|
|
1171
1673
|
options.abortSignal ?? effectiveGenerationSettings.abortSignal,
|
|
1172
|
-
options.timeout
|
|
1173
|
-
? AbortSignal.timeout(options.timeout)
|
|
1174
|
-
: undefined,
|
|
1674
|
+
options.timeout,
|
|
1175
1675
|
);
|
|
1176
1676
|
|
|
1177
1677
|
// Merge generation settings: constructor defaults < prepareCall < stream options
|
|
@@ -1208,51 +1708,59 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1208
1708
|
};
|
|
1209
1709
|
|
|
1210
1710
|
// Merge constructor + stream callbacks (constructor first, then stream)
|
|
1211
|
-
const
|
|
1212
|
-
this.
|
|
1213
|
-
|
|
|
1711
|
+
const mergedOnStepEnd = mergeCallbacks(
|
|
1712
|
+
this.constructorOnStepEnd as
|
|
1713
|
+
| WorkflowAgentOnStepEndCallback<TTools, TRuntimeContext>
|
|
1214
1714
|
| undefined,
|
|
1215
|
-
options.onStepFinish,
|
|
1715
|
+
options.onStepEnd ?? options.onStepFinish,
|
|
1216
1716
|
);
|
|
1217
|
-
const
|
|
1218
|
-
this.
|
|
1219
|
-
|
|
|
1717
|
+
const mergedOnEnd = mergeCallbacks(
|
|
1718
|
+
this.constructorOnEnd as
|
|
1719
|
+
| WorkflowAgentOnEndCallback<TTools, TRuntimeContext, OUTPUT>
|
|
1220
1720
|
| undefined,
|
|
1221
|
-
|
|
1721
|
+
onEnd,
|
|
1222
1722
|
);
|
|
1223
1723
|
const mergedOnStart = mergeCallbacks(
|
|
1224
|
-
this.constructorOnStart
|
|
1724
|
+
this.constructorOnStart as
|
|
1725
|
+
| WorkflowAgentOnStartCallback<TTools, TRuntimeContext>
|
|
1726
|
+
| undefined,
|
|
1225
1727
|
options.experimental_onStart,
|
|
1226
1728
|
);
|
|
1227
1729
|
const mergedOnStepStart = mergeCallbacks(
|
|
1228
|
-
this.constructorOnStepStart
|
|
1730
|
+
this.constructorOnStepStart as
|
|
1731
|
+
| WorkflowAgentOnStepStartCallback<TTools, TRuntimeContext>
|
|
1732
|
+
| undefined,
|
|
1229
1733
|
options.experimental_onStepStart,
|
|
1230
1734
|
);
|
|
1231
|
-
const
|
|
1232
|
-
this.
|
|
1233
|
-
options.
|
|
1735
|
+
const mergedOnToolExecutionStart = mergeCallbacks(
|
|
1736
|
+
this.constructorOnToolExecutionStart,
|
|
1737
|
+
options.onToolExecutionStart,
|
|
1234
1738
|
);
|
|
1235
|
-
const
|
|
1236
|
-
this.
|
|
1237
|
-
options.
|
|
1739
|
+
const mergedOnToolExecutionEnd = mergeCallbacks(
|
|
1740
|
+
this.constructorOnToolExecutionEnd,
|
|
1741
|
+
options.onToolExecutionEnd,
|
|
1238
1742
|
);
|
|
1239
1743
|
|
|
1240
1744
|
// Determine effective tool choice
|
|
1241
1745
|
const effectiveToolChoice = effectiveToolChoiceFromPrepare;
|
|
1242
1746
|
|
|
1243
|
-
//
|
|
1244
|
-
const
|
|
1245
|
-
|
|
1246
|
-
// Filter tools if activeTools is specified
|
|
1747
|
+
// Filter tools if activeTools is specified (stream-level overrides constructor default)
|
|
1748
|
+
const effectiveActiveTools = options.activeTools ?? this.activeTools;
|
|
1247
1749
|
const effectiveTools =
|
|
1248
|
-
|
|
1249
|
-
?
|
|
1750
|
+
effectiveActiveTools && effectiveActiveTools.length > 0
|
|
1751
|
+
? (filterActiveTools({
|
|
1752
|
+
tools: this.tools,
|
|
1753
|
+
activeTools: effectiveActiveTools,
|
|
1754
|
+
}) ?? this.tools)
|
|
1250
1755
|
: this.tools;
|
|
1756
|
+
const effectiveModelInfo = getModelInfo(effectiveModel);
|
|
1251
1757
|
|
|
1252
1758
|
// Initialize context
|
|
1253
|
-
let
|
|
1759
|
+
let runtimeContext: TRuntimeContext = effectiveRuntimeContext;
|
|
1760
|
+
let toolsContext: Record<string, Context | undefined> =
|
|
1761
|
+
effectiveToolsContext;
|
|
1254
1762
|
|
|
1255
|
-
const steps: StepResult<TTools,
|
|
1763
|
+
const steps: StepResult<TTools, TRuntimeContext>[] = [];
|
|
1256
1764
|
|
|
1257
1765
|
// Track tool calls and results from the last step for the result
|
|
1258
1766
|
let lastStepToolCalls: ToolCall[] = [];
|
|
@@ -1262,80 +1770,226 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1262
1770
|
if (mergedOnStart) {
|
|
1263
1771
|
await mergedOnStart({
|
|
1264
1772
|
model: effectiveModel,
|
|
1265
|
-
messages:
|
|
1773
|
+
messages: prompt.messages,
|
|
1774
|
+
runtimeContext,
|
|
1775
|
+
toolsContext: toolsContext as unknown as InferToolSetContext<TTools>,
|
|
1266
1776
|
});
|
|
1267
1777
|
}
|
|
1778
|
+
await telemetryDispatcher.onStart?.({
|
|
1779
|
+
callId: 'workflow-agent',
|
|
1780
|
+
operationId: 'ai.workflowAgent.stream',
|
|
1781
|
+
provider: effectiveModelInfo.provider,
|
|
1782
|
+
modelId: effectiveModelInfo.modelId,
|
|
1783
|
+
system: undefined,
|
|
1784
|
+
messages: prompt.messages,
|
|
1785
|
+
tools: effectiveTools,
|
|
1786
|
+
toolChoice: effectiveToolChoice,
|
|
1787
|
+
activeTools: effectiveActiveTools as never,
|
|
1788
|
+
maxOutputTokens: mergedGenerationSettings.maxOutputTokens,
|
|
1789
|
+
temperature: mergedGenerationSettings.temperature,
|
|
1790
|
+
topP: mergedGenerationSettings.topP,
|
|
1791
|
+
topK: mergedGenerationSettings.topK,
|
|
1792
|
+
presencePenalty: mergedGenerationSettings.presencePenalty,
|
|
1793
|
+
frequencyPenalty: mergedGenerationSettings.frequencyPenalty,
|
|
1794
|
+
stopSequences: mergedGenerationSettings.stopSequences,
|
|
1795
|
+
seed: mergedGenerationSettings.seed,
|
|
1796
|
+
maxRetries: mergedGenerationSettings.maxRetries ?? 2,
|
|
1797
|
+
timeout: undefined,
|
|
1798
|
+
headers: mergedGenerationSettings.headers,
|
|
1799
|
+
providerOptions: mergedGenerationSettings.providerOptions,
|
|
1800
|
+
output: (options.output ?? this.output) as never,
|
|
1801
|
+
runtimeContext,
|
|
1802
|
+
toolsContext: toolsContext as unknown as InferToolSetContext<TTools>,
|
|
1803
|
+
});
|
|
1268
1804
|
|
|
1269
|
-
// Helper to wrap executeTool with
|
|
1805
|
+
// Helper to wrap executeTool with onToolExecutionStart/onToolExecutionEnd callbacks
|
|
1270
1806
|
const executeToolWithCallbacks = async (
|
|
1271
1807
|
toolCall: { toolCallId: string; toolName: string; input: unknown },
|
|
1272
1808
|
tools: ToolSet,
|
|
1273
1809
|
messages: LanguageModelV4Prompt,
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1810
|
+
perToolContexts: Record<string, Context | undefined>,
|
|
1811
|
+
currentStepNumber: number = 0,
|
|
1812
|
+
): Promise<WorkflowToolExecutionResult> => {
|
|
1813
|
+
const toolCallEvent: ToolCall = {
|
|
1814
|
+
type: 'tool-call',
|
|
1815
|
+
toolCallId: toolCall.toolCallId,
|
|
1816
|
+
toolName: toolCall.toolName,
|
|
1817
|
+
input: toolCall.input,
|
|
1818
|
+
};
|
|
1819
|
+
|
|
1820
|
+
const tool = tools[toolCall.toolName];
|
|
1821
|
+
const resolvedContext = tool
|
|
1822
|
+
? await resolveToolContext({
|
|
1281
1823
|
toolName: toolCall.toolName,
|
|
1282
|
-
|
|
1283
|
-
|
|
1824
|
+
tool,
|
|
1825
|
+
toolsContext: perToolContexts,
|
|
1826
|
+
})
|
|
1827
|
+
: undefined;
|
|
1828
|
+
const modelMessages = getToolCallbackMessages(messages);
|
|
1829
|
+
|
|
1830
|
+
if (mergedOnToolExecutionStart) {
|
|
1831
|
+
await mergedOnToolExecutionStart({
|
|
1832
|
+
toolCall: toolCallEvent,
|
|
1833
|
+
stepNumber: currentStepNumber,
|
|
1834
|
+
messages: modelMessages,
|
|
1835
|
+
toolContext: resolvedContext as
|
|
1836
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
1837
|
+
| undefined,
|
|
1284
1838
|
});
|
|
1285
1839
|
}
|
|
1286
|
-
|
|
1840
|
+
await telemetryDispatcher.onToolExecutionStart?.({
|
|
1841
|
+
toolCall: toolCallEvent,
|
|
1842
|
+
stepNumber: currentStepNumber,
|
|
1843
|
+
messages: modelMessages,
|
|
1844
|
+
toolContext: resolvedContext as
|
|
1845
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
1846
|
+
| undefined,
|
|
1847
|
+
});
|
|
1848
|
+
|
|
1849
|
+
const startTime = Date.now();
|
|
1850
|
+
let result: WorkflowToolExecutionResult;
|
|
1287
1851
|
try {
|
|
1288
|
-
|
|
1852
|
+
const execute = () =>
|
|
1853
|
+
executeTool(toolCall, tools, messages, resolvedContext, download);
|
|
1854
|
+
result =
|
|
1855
|
+
telemetryDispatcher.executeTool != null
|
|
1856
|
+
? await telemetryDispatcher.executeTool({
|
|
1857
|
+
callId: 'workflow-agent',
|
|
1858
|
+
toolCallId: toolCall.toolCallId,
|
|
1859
|
+
execute,
|
|
1860
|
+
})
|
|
1861
|
+
: await execute();
|
|
1289
1862
|
} catch (err) {
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1863
|
+
const durationMs = Date.now() - startTime;
|
|
1864
|
+
if (mergedOnToolExecutionEnd) {
|
|
1865
|
+
await mergedOnToolExecutionEnd({
|
|
1866
|
+
toolCall: toolCallEvent,
|
|
1867
|
+
stepNumber: currentStepNumber,
|
|
1868
|
+
durationMs,
|
|
1869
|
+
messages: modelMessages,
|
|
1870
|
+
toolContext: resolvedContext as
|
|
1871
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
1872
|
+
| undefined,
|
|
1873
|
+
success: false,
|
|
1298
1874
|
error: err,
|
|
1299
1875
|
});
|
|
1300
1876
|
}
|
|
1877
|
+
await telemetryDispatcher.onToolExecutionEnd?.({
|
|
1878
|
+
toolCall: toolCallEvent,
|
|
1879
|
+
stepNumber: currentStepNumber,
|
|
1880
|
+
durationMs,
|
|
1881
|
+
messages: modelMessages,
|
|
1882
|
+
toolContext: resolvedContext as
|
|
1883
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
1884
|
+
| undefined,
|
|
1885
|
+
success: false,
|
|
1886
|
+
error: err,
|
|
1887
|
+
});
|
|
1301
1888
|
throw err;
|
|
1302
1889
|
}
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
(
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
:
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1890
|
+
|
|
1891
|
+
const durationMs = Date.now() - startTime;
|
|
1892
|
+
if (mergedOnToolExecutionEnd) {
|
|
1893
|
+
if (result.isError) {
|
|
1894
|
+
await mergedOnToolExecutionEnd({
|
|
1895
|
+
toolCall: toolCallEvent,
|
|
1896
|
+
stepNumber: currentStepNumber,
|
|
1897
|
+
durationMs,
|
|
1898
|
+
messages: modelMessages,
|
|
1899
|
+
toolContext: resolvedContext as
|
|
1900
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
1901
|
+
| undefined,
|
|
1902
|
+
success: false,
|
|
1903
|
+
error: result.rawOutput,
|
|
1904
|
+
});
|
|
1905
|
+
} else {
|
|
1906
|
+
await mergedOnToolExecutionEnd({
|
|
1907
|
+
toolCall: toolCallEvent,
|
|
1908
|
+
stepNumber: currentStepNumber,
|
|
1909
|
+
durationMs,
|
|
1910
|
+
messages: modelMessages,
|
|
1911
|
+
toolContext: resolvedContext as
|
|
1912
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
1913
|
+
| undefined,
|
|
1914
|
+
success: true,
|
|
1915
|
+
output: result.rawOutput,
|
|
1916
|
+
});
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
if (result.isError) {
|
|
1920
|
+
await telemetryDispatcher.onToolExecutionEnd?.({
|
|
1921
|
+
toolCall: toolCallEvent,
|
|
1922
|
+
stepNumber: currentStepNumber,
|
|
1923
|
+
durationMs,
|
|
1924
|
+
messages: modelMessages,
|
|
1925
|
+
toolContext: resolvedContext as
|
|
1926
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
1927
|
+
| undefined,
|
|
1928
|
+
success: false,
|
|
1929
|
+
error: result.rawOutput,
|
|
1930
|
+
});
|
|
1931
|
+
} else {
|
|
1932
|
+
await telemetryDispatcher.onToolExecutionEnd?.({
|
|
1933
|
+
toolCall: toolCallEvent,
|
|
1934
|
+
stepNumber: currentStepNumber,
|
|
1935
|
+
durationMs,
|
|
1936
|
+
messages: modelMessages,
|
|
1937
|
+
toolContext: resolvedContext as
|
|
1938
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
1939
|
+
| undefined,
|
|
1940
|
+
success: true,
|
|
1941
|
+
output: result.rawOutput,
|
|
1327
1942
|
});
|
|
1328
1943
|
}
|
|
1329
1944
|
return result;
|
|
1330
1945
|
};
|
|
1331
1946
|
|
|
1947
|
+
const recordProviderExecutedToolTelemetry = async (
|
|
1948
|
+
toolCall: { toolCallId: string; toolName: string; input: unknown },
|
|
1949
|
+
result: WorkflowToolExecutionResult,
|
|
1950
|
+
messages: LanguageModelV4Prompt,
|
|
1951
|
+
currentStepNumber: number,
|
|
1952
|
+
) => {
|
|
1953
|
+
const toolCallEvent: ToolCall = {
|
|
1954
|
+
type: 'tool-call',
|
|
1955
|
+
toolCallId: toolCall.toolCallId,
|
|
1956
|
+
toolName: toolCall.toolName,
|
|
1957
|
+
input: toolCall.input,
|
|
1958
|
+
};
|
|
1959
|
+
const modelMessages = getToolCallbackMessages(messages);
|
|
1960
|
+
|
|
1961
|
+
await telemetryDispatcher.onToolExecutionStart?.({
|
|
1962
|
+
toolCall: toolCallEvent,
|
|
1963
|
+
stepNumber: currentStepNumber,
|
|
1964
|
+
messages: modelMessages,
|
|
1965
|
+
toolContext: undefined,
|
|
1966
|
+
});
|
|
1967
|
+
|
|
1968
|
+
await telemetryDispatcher.onToolExecutionEnd?.({
|
|
1969
|
+
toolCall: toolCallEvent,
|
|
1970
|
+
stepNumber: currentStepNumber,
|
|
1971
|
+
durationMs: 0,
|
|
1972
|
+
messages: modelMessages,
|
|
1973
|
+
toolContext: undefined,
|
|
1974
|
+
...(result.isError
|
|
1975
|
+
? {
|
|
1976
|
+
success: false as const,
|
|
1977
|
+
error: result.rawOutput,
|
|
1978
|
+
}
|
|
1979
|
+
: {
|
|
1980
|
+
success: true as const,
|
|
1981
|
+
output: result.rawOutput,
|
|
1982
|
+
}),
|
|
1983
|
+
});
|
|
1984
|
+
};
|
|
1985
|
+
|
|
1332
1986
|
// Check for abort before starting
|
|
1333
1987
|
if (mergedGenerationSettings.abortSignal?.aborted) {
|
|
1334
1988
|
if (options.onAbort) {
|
|
1335
1989
|
await options.onAbort({ steps });
|
|
1336
1990
|
}
|
|
1337
1991
|
return {
|
|
1338
|
-
messages:
|
|
1992
|
+
messages: prompt.messages,
|
|
1339
1993
|
steps,
|
|
1340
1994
|
toolCalls: [],
|
|
1341
1995
|
toolResults: [],
|
|
@@ -1348,22 +2002,26 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1348
2002
|
tools: effectiveTools as ToolSet,
|
|
1349
2003
|
writable: options.writable,
|
|
1350
2004
|
prompt: modelPrompt,
|
|
1351
|
-
stopConditions: options.stopWhen,
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
onStepStart: mergedOnStepStart,
|
|
2005
|
+
stopConditions: options.stopWhen ?? this.stopWhen,
|
|
2006
|
+
|
|
2007
|
+
onStepEnd: mergedOnStepEnd as any,
|
|
2008
|
+
onStepStart: mergedOnStepStart as any,
|
|
1355
2009
|
onError: options.onError,
|
|
1356
|
-
prepareStep:
|
|
1357
|
-
|
|
1358
|
-
|
|
2010
|
+
prepareStep: (options.prepareStep ??
|
|
2011
|
+
(this.prepareStep as
|
|
2012
|
+
| PrepareStepCallback<ToolSet, TRuntimeContext>
|
|
2013
|
+
| undefined)) as any,
|
|
1359
2014
|
generationSettings: mergedGenerationSettings,
|
|
1360
2015
|
toolChoice: effectiveToolChoice as ToolChoice<ToolSet>,
|
|
1361
|
-
|
|
1362
|
-
|
|
2016
|
+
runtimeContext,
|
|
2017
|
+
toolsContext,
|
|
2018
|
+
telemetry: effectiveTelemetry,
|
|
1363
2019
|
includeRawChunks: options.includeRawChunks ?? false,
|
|
1364
|
-
repairToolCall:
|
|
1365
|
-
|
|
1366
|
-
|
|
2020
|
+
repairToolCall: (options.experimental_repairToolCall ??
|
|
2021
|
+
this.experimentalRepairToolCall) as
|
|
2022
|
+
| ToolCallRepairFunction<ToolSet>
|
|
2023
|
+
| undefined,
|
|
2024
|
+
responseFormat: await (options.output ?? this.output)?.responseFormat,
|
|
1367
2025
|
});
|
|
1368
2026
|
|
|
1369
2027
|
// Track the final conversation messages from the iterator
|
|
@@ -1387,23 +2045,34 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1387
2045
|
toolCalls,
|
|
1388
2046
|
messages: iterMessages,
|
|
1389
2047
|
step,
|
|
1390
|
-
|
|
2048
|
+
runtimeContext: yieldedRuntimeContext,
|
|
2049
|
+
toolsContext: yieldedToolsContext,
|
|
1391
2050
|
providerExecutedToolResults,
|
|
1392
2051
|
} = result.value;
|
|
2052
|
+
// Capture current step number before pushing (0-based)
|
|
2053
|
+
const currentStepNumber = steps.length;
|
|
1393
2054
|
if (step) {
|
|
1394
|
-
steps.push(step as unknown as StepResult<TTools,
|
|
2055
|
+
steps.push(step as unknown as StepResult<TTools, TRuntimeContext>);
|
|
2056
|
+
}
|
|
2057
|
+
if (yieldedRuntimeContext !== undefined) {
|
|
2058
|
+
runtimeContext = yieldedRuntimeContext as TRuntimeContext;
|
|
1395
2059
|
}
|
|
1396
|
-
if (
|
|
1397
|
-
|
|
2060
|
+
if (yieldedToolsContext !== undefined) {
|
|
2061
|
+
toolsContext = yieldedToolsContext;
|
|
1398
2062
|
}
|
|
1399
2063
|
|
|
1400
2064
|
// Only execute tools if there are tool calls
|
|
1401
2065
|
if (toolCalls.length > 0) {
|
|
2066
|
+
const invalidToolCalls = toolCalls.filter(tc => tc.invalid === true);
|
|
2067
|
+
const validToolCalls = toolCalls.filter(tc => tc.invalid !== true);
|
|
2068
|
+
|
|
1402
2069
|
// Separate provider-executed tool calls from client-executed ones
|
|
1403
|
-
const nonProviderToolCalls =
|
|
2070
|
+
const nonProviderToolCalls = validToolCalls.filter(
|
|
1404
2071
|
tc => !tc.providerExecuted,
|
|
1405
2072
|
);
|
|
1406
|
-
const providerToolCalls =
|
|
2073
|
+
const providerToolCalls = validToolCalls.filter(
|
|
2074
|
+
tc => tc.providerExecuted,
|
|
2075
|
+
);
|
|
1407
2076
|
|
|
1408
2077
|
// Check which tools need approval (can be async)
|
|
1409
2078
|
const approvalNeeded = await Promise.all(
|
|
@@ -1413,10 +2082,16 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1413
2082
|
if (tool.needsApproval == null) return false;
|
|
1414
2083
|
if (typeof tool.needsApproval === 'boolean')
|
|
1415
2084
|
return tool.needsApproval;
|
|
2085
|
+
const resolvedContext = await resolveToolContext({
|
|
2086
|
+
toolName: tc.toolName,
|
|
2087
|
+
tool,
|
|
2088
|
+
toolsContext:
|
|
2089
|
+
toolsContext as unknown as InferToolSetContext<TTools>,
|
|
2090
|
+
});
|
|
1416
2091
|
return tool.needsApproval(tc.input, {
|
|
1417
2092
|
toolCallId: tc.toolCallId,
|
|
1418
2093
|
messages: iterMessages as unknown as ModelMessage[],
|
|
1419
|
-
context:
|
|
2094
|
+
context: resolvedContext,
|
|
1420
2095
|
});
|
|
1421
2096
|
}),
|
|
1422
2097
|
);
|
|
@@ -1447,26 +2122,49 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1447
2122
|
// Execute any executable tools that were also called in this step
|
|
1448
2123
|
const executableResults = await Promise.all(
|
|
1449
2124
|
executableToolCalls.map(
|
|
1450
|
-
(toolCall): Promise<
|
|
2125
|
+
(toolCall): Promise<WorkflowToolExecutionResult> =>
|
|
1451
2126
|
executeToolWithCallbacks(
|
|
1452
2127
|
toolCall,
|
|
1453
2128
|
effectiveTools as ToolSet,
|
|
1454
2129
|
iterMessages,
|
|
1455
|
-
|
|
2130
|
+
toolsContext,
|
|
2131
|
+
currentStepNumber,
|
|
1456
2132
|
),
|
|
1457
2133
|
),
|
|
1458
2134
|
);
|
|
1459
2135
|
|
|
1460
2136
|
// Collect provider tool results
|
|
1461
|
-
const providerResults:
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
2137
|
+
const providerResults: WorkflowToolExecutionResult[] =
|
|
2138
|
+
await Promise.all(
|
|
2139
|
+
providerToolCalls.map(toolCall =>
|
|
2140
|
+
resolveProviderToolResult(
|
|
2141
|
+
toolCall,
|
|
2142
|
+
providerExecutedToolResults,
|
|
2143
|
+
effectiveTools as ToolSet,
|
|
2144
|
+
download,
|
|
2145
|
+
),
|
|
1466
2146
|
),
|
|
1467
2147
|
);
|
|
2148
|
+
await Promise.all(
|
|
2149
|
+
providerToolCalls.map((toolCall, index) =>
|
|
2150
|
+
recordProviderExecutedToolTelemetry(
|
|
2151
|
+
toolCall,
|
|
2152
|
+
providerResults[index],
|
|
2153
|
+
iterMessages,
|
|
2154
|
+
currentStepNumber,
|
|
2155
|
+
),
|
|
2156
|
+
),
|
|
2157
|
+
);
|
|
1468
2158
|
|
|
1469
|
-
const
|
|
2159
|
+
const continuationInvalidResults = invalidToolCalls.map(
|
|
2160
|
+
createInvalidToolResult,
|
|
2161
|
+
);
|
|
2162
|
+
const resolvedResults: LanguageModelV4ToolResultPart[] = [
|
|
2163
|
+
...executableResults.map(result => result.modelResult),
|
|
2164
|
+
...providerResults.map(result => result.modelResult),
|
|
2165
|
+
...continuationInvalidResults,
|
|
2166
|
+
];
|
|
2167
|
+
const executedResults = [...executableResults, ...providerResults];
|
|
1470
2168
|
|
|
1471
2169
|
const allToolCalls: ToolCall[] = toolCalls.map(tc => ({
|
|
1472
2170
|
type: 'tool-call' as const,
|
|
@@ -1475,13 +2173,14 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1475
2173
|
input: tc.input,
|
|
1476
2174
|
}));
|
|
1477
2175
|
|
|
1478
|
-
const allToolResults: ToolResult[] =
|
|
2176
|
+
const allToolResults: ToolResult[] = executedResults.map(r => ({
|
|
1479
2177
|
type: 'tool-result' as const,
|
|
1480
|
-
toolCallId: r.toolCallId,
|
|
1481
|
-
toolName: r.toolName,
|
|
1482
|
-
input: toolCalls.find(
|
|
1483
|
-
|
|
1484
|
-
|
|
2178
|
+
toolCallId: r.modelResult.toolCallId,
|
|
2179
|
+
toolName: r.modelResult.toolName,
|
|
2180
|
+
input: toolCalls.find(
|
|
2181
|
+
tc => tc.toolCallId === r.modelResult.toolCallId,
|
|
2182
|
+
)?.input,
|
|
2183
|
+
output: r.rawOutput,
|
|
1485
2184
|
}));
|
|
1486
2185
|
|
|
1487
2186
|
if (resolvedResults.length > 0) {
|
|
@@ -1493,18 +2192,33 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1493
2192
|
|
|
1494
2193
|
const messages = iterMessages as unknown as ModelMessage[];
|
|
1495
2194
|
|
|
1496
|
-
if (
|
|
2195
|
+
if (mergedOnEnd && !wasAborted) {
|
|
1497
2196
|
const lastStep = steps[steps.length - 1];
|
|
1498
|
-
|
|
2197
|
+
const totalUsage = aggregateUsage(steps);
|
|
2198
|
+
await mergedOnEnd({
|
|
1499
2199
|
steps,
|
|
1500
2200
|
messages,
|
|
1501
2201
|
text: lastStep?.text ?? '',
|
|
1502
2202
|
finishReason: lastStep?.finishReason ?? 'other',
|
|
1503
|
-
|
|
1504
|
-
|
|
2203
|
+
usage: totalUsage,
|
|
2204
|
+
totalUsage,
|
|
2205
|
+
runtimeContext,
|
|
2206
|
+
toolsContext:
|
|
2207
|
+
toolsContext as unknown as InferToolSetContext<TTools>,
|
|
1505
2208
|
output: undefined as OUTPUT,
|
|
1506
2209
|
});
|
|
1507
2210
|
}
|
|
2211
|
+
if (!wasAborted && steps.length > 0) {
|
|
2212
|
+
const telemetrySteps = steps.map(normalizeStepForTelemetry);
|
|
2213
|
+
const lastStep = telemetrySteps[telemetrySteps.length - 1];
|
|
2214
|
+
const totalUsage = aggregateUsage(steps);
|
|
2215
|
+
await telemetryDispatcher.onEnd?.({
|
|
2216
|
+
...lastStep,
|
|
2217
|
+
steps: telemetrySteps,
|
|
2218
|
+
usage: totalUsage,
|
|
2219
|
+
totalUsage,
|
|
2220
|
+
});
|
|
2221
|
+
}
|
|
1508
2222
|
|
|
1509
2223
|
// Emit tool-approval-request chunks for tools that need approval
|
|
1510
2224
|
// so useChat can show the approval UI
|
|
@@ -1547,39 +2261,67 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1547
2261
|
// Execute client tools (all have execute functions at this point)
|
|
1548
2262
|
const clientToolResults = await Promise.all(
|
|
1549
2263
|
nonProviderToolCalls.map(
|
|
1550
|
-
(toolCall): Promise<
|
|
2264
|
+
(toolCall): Promise<WorkflowToolExecutionResult> =>
|
|
1551
2265
|
executeToolWithCallbacks(
|
|
1552
2266
|
toolCall,
|
|
1553
2267
|
effectiveTools as ToolSet,
|
|
1554
2268
|
iterMessages,
|
|
1555
|
-
|
|
2269
|
+
toolsContext,
|
|
2270
|
+
currentStepNumber,
|
|
1556
2271
|
),
|
|
1557
2272
|
),
|
|
1558
2273
|
);
|
|
1559
2274
|
|
|
1560
2275
|
// For provider-executed tools, use the results from the stream
|
|
1561
|
-
const providerToolResults:
|
|
1562
|
-
|
|
1563
|
-
|
|
2276
|
+
const providerToolResults: WorkflowToolExecutionResult[] =
|
|
2277
|
+
await Promise.all(
|
|
2278
|
+
providerToolCalls.map(toolCall =>
|
|
2279
|
+
resolveProviderToolResult(
|
|
2280
|
+
toolCall,
|
|
2281
|
+
providerExecutedToolResults,
|
|
2282
|
+
effectiveTools as ToolSet,
|
|
2283
|
+
download,
|
|
2284
|
+
),
|
|
2285
|
+
),
|
|
1564
2286
|
);
|
|
2287
|
+
await Promise.all(
|
|
2288
|
+
providerToolCalls.map((toolCall, index) =>
|
|
2289
|
+
recordProviderExecutedToolTelemetry(
|
|
2290
|
+
toolCall,
|
|
2291
|
+
providerToolResults[index],
|
|
2292
|
+
iterMessages,
|
|
2293
|
+
currentStepNumber,
|
|
2294
|
+
),
|
|
2295
|
+
),
|
|
2296
|
+
);
|
|
2297
|
+
const continuationInvalidToolResults = invalidToolCalls.map(
|
|
2298
|
+
createInvalidToolResult,
|
|
2299
|
+
);
|
|
1565
2300
|
|
|
1566
|
-
// Combine results in the original order
|
|
1567
|
-
|
|
2301
|
+
// Combine executable/provider results in the original order,
|
|
2302
|
+
// while preserving invalid tool calls as error results for the
|
|
2303
|
+
// next model step without emitting them as synthetic UI success.
|
|
2304
|
+
const executedToolResults = toolCalls.flatMap(tc => {
|
|
1568
2305
|
const clientResult = clientToolResults.find(
|
|
1569
|
-
r => r.toolCallId === tc.toolCallId,
|
|
2306
|
+
r => r.modelResult.toolCallId === tc.toolCallId,
|
|
1570
2307
|
);
|
|
1571
|
-
if (clientResult) return clientResult;
|
|
2308
|
+
if (clientResult) return [clientResult];
|
|
1572
2309
|
const providerResult = providerToolResults.find(
|
|
2310
|
+
r => r.modelResult.toolCallId === tc.toolCallId,
|
|
2311
|
+
);
|
|
2312
|
+
if (providerResult) return [providerResult];
|
|
2313
|
+
return [];
|
|
2314
|
+
});
|
|
2315
|
+
const continuationToolResults = toolCalls.flatMap(tc => {
|
|
2316
|
+
const invalidResult = continuationInvalidToolResults.find(
|
|
1573
2317
|
r => r.toolCallId === tc.toolCallId,
|
|
1574
2318
|
);
|
|
1575
|
-
if (
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
output: { type: 'text' as const, value: '' },
|
|
1582
|
-
};
|
|
2319
|
+
if (invalidResult) return [invalidResult];
|
|
2320
|
+
const executedResult = executedToolResults.find(
|
|
2321
|
+
r => r.modelResult.toolCallId === tc.toolCallId,
|
|
2322
|
+
);
|
|
2323
|
+
if (executedResult) return [executedResult.modelResult];
|
|
2324
|
+
return [];
|
|
1583
2325
|
});
|
|
1584
2326
|
|
|
1585
2327
|
// Write tool results and step boundaries to the stream so the
|
|
@@ -1588,12 +2330,13 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1588
2330
|
if (options.writable) {
|
|
1589
2331
|
await writeToolResultsWithStepBoundary(
|
|
1590
2332
|
options.writable,
|
|
1591
|
-
|
|
1592
|
-
toolCallId: r.toolCallId,
|
|
1593
|
-
toolName: r.toolName,
|
|
1594
|
-
input: toolCalls.find(
|
|
1595
|
-
|
|
1596
|
-
|
|
2333
|
+
executedToolResults.map(r => ({
|
|
2334
|
+
toolCallId: r.modelResult.toolCallId,
|
|
2335
|
+
toolName: r.modelResult.toolName,
|
|
2336
|
+
input: toolCalls.find(
|
|
2337
|
+
tc => tc.toolCallId === r.modelResult.toolCallId,
|
|
2338
|
+
)?.input,
|
|
2339
|
+
output: r.rawOutput,
|
|
1597
2340
|
})),
|
|
1598
2341
|
);
|
|
1599
2342
|
}
|
|
@@ -1605,15 +2348,17 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1605
2348
|
toolName: tc.toolName,
|
|
1606
2349
|
input: tc.input,
|
|
1607
2350
|
}));
|
|
1608
|
-
lastStepToolResults =
|
|
2351
|
+
lastStepToolResults = executedToolResults.map(r => ({
|
|
1609
2352
|
type: 'tool-result' as const,
|
|
1610
|
-
toolCallId: r.toolCallId,
|
|
1611
|
-
toolName: r.toolName,
|
|
1612
|
-
input: toolCalls.find(
|
|
1613
|
-
|
|
2353
|
+
toolCallId: r.modelResult.toolCallId,
|
|
2354
|
+
toolName: r.modelResult.toolName,
|
|
2355
|
+
input: toolCalls.find(
|
|
2356
|
+
tc => tc.toolCallId === r.modelResult.toolCallId,
|
|
2357
|
+
)?.input,
|
|
2358
|
+
output: r.rawOutput,
|
|
1614
2359
|
}));
|
|
1615
2360
|
|
|
1616
|
-
result = await iterator.next(
|
|
2361
|
+
result = await iterator.next(continuationToolResults);
|
|
1617
2362
|
} else {
|
|
1618
2363
|
// Final step with no tool calls - reset tracking
|
|
1619
2364
|
lastStepToolCalls = [];
|
|
@@ -1638,21 +2383,23 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1638
2383
|
// Call onError for non-abort errors (including tool execution errors)
|
|
1639
2384
|
await options.onError({ error });
|
|
1640
2385
|
}
|
|
1641
|
-
|
|
2386
|
+
await telemetryDispatcher.onError?.(error);
|
|
2387
|
+
// Don't throw yet - we want to call onEnd first
|
|
1642
2388
|
}
|
|
1643
2389
|
|
|
1644
|
-
// Use the final messages from the iterator, or fall back to
|
|
2390
|
+
// Use the final messages from the iterator, or fall back to standardized messages
|
|
1645
2391
|
const messages = (finalMessages ??
|
|
1646
|
-
|
|
2392
|
+
prompt.messages) as unknown as ModelMessage[];
|
|
1647
2393
|
|
|
1648
|
-
// Parse structured output if output is specified
|
|
2394
|
+
// Parse structured output if output is specified (stream-level overrides constructor default)
|
|
2395
|
+
const effectiveOutput = options.output ?? this.output;
|
|
1649
2396
|
let experimentalOutput: OUTPUT = undefined as OUTPUT;
|
|
1650
|
-
if (
|
|
2397
|
+
if (effectiveOutput && steps.length > 0) {
|
|
1651
2398
|
const lastStep = steps[steps.length - 1];
|
|
1652
2399
|
const text = lastStep.text;
|
|
1653
2400
|
if (text) {
|
|
1654
2401
|
try {
|
|
1655
|
-
experimentalOutput = await
|
|
2402
|
+
experimentalOutput = await effectiveOutput.parseCompleteOutput(
|
|
1656
2403
|
{ text },
|
|
1657
2404
|
{
|
|
1658
2405
|
response: lastStep.response,
|
|
@@ -1670,19 +2417,33 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1670
2417
|
}
|
|
1671
2418
|
}
|
|
1672
2419
|
|
|
1673
|
-
// Call
|
|
1674
|
-
if (
|
|
2420
|
+
// Call onEnd callback if provided (always call, even on errors, but not on abort)
|
|
2421
|
+
if (mergedOnEnd && !wasAborted) {
|
|
1675
2422
|
const lastStep = steps[steps.length - 1];
|
|
1676
|
-
|
|
2423
|
+
const totalUsage = aggregateUsage(steps);
|
|
2424
|
+
await mergedOnEnd({
|
|
1677
2425
|
steps,
|
|
1678
2426
|
messages: messages as ModelMessage[],
|
|
1679
2427
|
text: lastStep?.text ?? '',
|
|
1680
2428
|
finishReason: lastStep?.finishReason ?? 'other',
|
|
1681
|
-
|
|
1682
|
-
|
|
2429
|
+
usage: totalUsage,
|
|
2430
|
+
totalUsage,
|
|
2431
|
+
runtimeContext,
|
|
2432
|
+
toolsContext: toolsContext as unknown as InferToolSetContext<TTools>,
|
|
1683
2433
|
output: experimentalOutput,
|
|
1684
2434
|
});
|
|
1685
2435
|
}
|
|
2436
|
+
if (!wasAborted && steps.length > 0) {
|
|
2437
|
+
const telemetrySteps = steps.map(normalizeStepForTelemetry);
|
|
2438
|
+
const lastStep = telemetrySteps[telemetrySteps.length - 1];
|
|
2439
|
+
const totalUsage = aggregateUsage(steps);
|
|
2440
|
+
await telemetryDispatcher.onEnd?.({
|
|
2441
|
+
...lastStep,
|
|
2442
|
+
steps: telemetrySteps,
|
|
2443
|
+
usage: totalUsage,
|
|
2444
|
+
totalUsage,
|
|
2445
|
+
});
|
|
2446
|
+
}
|
|
1686
2447
|
|
|
1687
2448
|
// Re-throw any error that occurred
|
|
1688
2449
|
if (encounteredError) {
|
|
@@ -1716,6 +2477,25 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1716
2477
|
}
|
|
1717
2478
|
}
|
|
1718
2479
|
|
|
2480
|
+
function getModelInfo(model: LanguageModel): {
|
|
2481
|
+
provider: string;
|
|
2482
|
+
modelId: string;
|
|
2483
|
+
} {
|
|
2484
|
+
return typeof model === 'string'
|
|
2485
|
+
? { provider: model.split('/')[0] ?? 'gateway', modelId: model }
|
|
2486
|
+
: { provider: model.provider, modelId: model.modelId };
|
|
2487
|
+
}
|
|
2488
|
+
|
|
2489
|
+
function normalizeStepForTelemetry<
|
|
2490
|
+
TOOLS extends ToolSet,
|
|
2491
|
+
RUNTIME_CONTEXT extends Context,
|
|
2492
|
+
>(step: StepResult<TOOLS, RUNTIME_CONTEXT>) {
|
|
2493
|
+
return {
|
|
2494
|
+
...step,
|
|
2495
|
+
model: step.model ?? { provider: 'unknown', modelId: 'unknown' },
|
|
2496
|
+
};
|
|
2497
|
+
}
|
|
2498
|
+
|
|
1719
2499
|
/**
|
|
1720
2500
|
* Filter tools to only include the specified active tools.
|
|
1721
2501
|
*/
|
|
@@ -1836,6 +2616,33 @@ async function writeApprovalToolResults(
|
|
|
1836
2616
|
}
|
|
1837
2617
|
}
|
|
1838
2618
|
|
|
2619
|
+
/**
|
|
2620
|
+
* Resolve the per-tool context that gets passed into a tool's `execute`
|
|
2621
|
+
* (and `needsApproval`) function. When the tool declares a `contextSchema`,
|
|
2622
|
+
* the entry is validated against it.
|
|
2623
|
+
*/
|
|
2624
|
+
async function resolveToolContext({
|
|
2625
|
+
toolName,
|
|
2626
|
+
tool,
|
|
2627
|
+
toolsContext,
|
|
2628
|
+
}: {
|
|
2629
|
+
toolName: string;
|
|
2630
|
+
tool: ToolSet[string];
|
|
2631
|
+
toolsContext: Record<string, Context | undefined> | undefined;
|
|
2632
|
+
}): Promise<unknown> {
|
|
2633
|
+
const contextSchema = (tool as { contextSchema?: unknown }).contextSchema;
|
|
2634
|
+
const entry = toolsContext?.[toolName];
|
|
2635
|
+
if (contextSchema == null) {
|
|
2636
|
+
return entry;
|
|
2637
|
+
}
|
|
2638
|
+
|
|
2639
|
+
return await validateTypes({
|
|
2640
|
+
value: entry,
|
|
2641
|
+
schema: contextSchema as Parameters<typeof validateTypes>[0]['schema'],
|
|
2642
|
+
context: { field: 'tool context', entityName: toolName },
|
|
2643
|
+
});
|
|
2644
|
+
}
|
|
2645
|
+
|
|
1839
2646
|
function aggregateUsage(steps: StepResult<any, any>[]): LanguageModelUsage {
|
|
1840
2647
|
let inputTokens = 0;
|
|
1841
2648
|
let outputTokens = 0;
|
|
@@ -1850,43 +2657,15 @@ function aggregateUsage(steps: StepResult<any, any>[]): LanguageModelUsage {
|
|
|
1850
2657
|
} as LanguageModelUsage;
|
|
1851
2658
|
}
|
|
1852
2659
|
|
|
1853
|
-
function
|
|
1854
|
-
|
|
1855
|
-
activeTools: string[],
|
|
1856
|
-
): ToolSet {
|
|
1857
|
-
const filtered: ToolSet = {};
|
|
1858
|
-
for (const toolName of activeTools) {
|
|
1859
|
-
if (toolName in tools) {
|
|
1860
|
-
filtered[toolName] = tools[toolName];
|
|
1861
|
-
}
|
|
1862
|
-
}
|
|
1863
|
-
return filtered;
|
|
1864
|
-
}
|
|
1865
|
-
|
|
1866
|
-
// Matches AI SDK's getErrorMessage from @ai-sdk/provider-utils
|
|
1867
|
-
function getErrorMessage(error: unknown): string {
|
|
1868
|
-
if (error == null) {
|
|
1869
|
-
return 'unknown error';
|
|
1870
|
-
}
|
|
1871
|
-
|
|
1872
|
-
if (typeof error === 'string') {
|
|
1873
|
-
return error;
|
|
1874
|
-
}
|
|
1875
|
-
|
|
1876
|
-
if (error instanceof Error) {
|
|
1877
|
-
return error.message;
|
|
1878
|
-
}
|
|
1879
|
-
|
|
1880
|
-
return JSON.stringify(error);
|
|
1881
|
-
}
|
|
1882
|
-
|
|
1883
|
-
function resolveProviderToolResult(
|
|
1884
|
-
toolCall: { toolCallId: string; toolName: string },
|
|
2660
|
+
async function resolveProviderToolResult(
|
|
2661
|
+
toolCall: { toolCallId: string; toolName: string; input: unknown },
|
|
1885
2662
|
providerExecutedToolResults?: Map<
|
|
1886
2663
|
string,
|
|
1887
2664
|
{ toolCallId: string; toolName: string; result: unknown; isError?: boolean }
|
|
1888
2665
|
>,
|
|
1889
|
-
|
|
2666
|
+
tools?: ToolSet,
|
|
2667
|
+
download?: DownloadFunction,
|
|
2668
|
+
): Promise<WorkflowToolExecutionResult> {
|
|
1890
2669
|
const streamResult = providerExecutedToolResults?.get(toolCall.toolCallId);
|
|
1891
2670
|
if (!streamResult) {
|
|
1892
2671
|
console.warn(
|
|
@@ -1894,45 +2673,79 @@ function resolveProviderToolResult(
|
|
|
1894
2673
|
`did not receive a result from the stream. This may indicate a provider issue.`,
|
|
1895
2674
|
);
|
|
1896
2675
|
return {
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
2676
|
+
modelResult: {
|
|
2677
|
+
type: 'tool-result' as const,
|
|
2678
|
+
toolCallId: toolCall.toolCallId,
|
|
2679
|
+
toolName: toolCall.toolName,
|
|
2680
|
+
output: {
|
|
2681
|
+
type: 'text' as const,
|
|
2682
|
+
value: '',
|
|
2683
|
+
},
|
|
1903
2684
|
},
|
|
2685
|
+
rawOutput: '',
|
|
2686
|
+
isError: false,
|
|
1904
2687
|
};
|
|
1905
2688
|
}
|
|
1906
2689
|
|
|
1907
2690
|
const result = streamResult.result;
|
|
1908
|
-
const
|
|
2691
|
+
const errorMode = streamResult.isError
|
|
2692
|
+
? typeof result === 'string'
|
|
2693
|
+
? 'text'
|
|
2694
|
+
: 'json'
|
|
2695
|
+
: 'none';
|
|
2696
|
+
|
|
2697
|
+
return {
|
|
2698
|
+
modelResult: {
|
|
2699
|
+
type: 'tool-result' as const,
|
|
2700
|
+
toolCallId: toolCall.toolCallId,
|
|
2701
|
+
toolName: toolCall.toolName,
|
|
2702
|
+
output: await createLanguageModelToolResultOutput({
|
|
2703
|
+
toolCallId: toolCall.toolCallId,
|
|
2704
|
+
toolName: toolCall.toolName,
|
|
2705
|
+
input: toolCall.input,
|
|
2706
|
+
output: result,
|
|
2707
|
+
tool: tools?.[toolCall.toolName],
|
|
2708
|
+
errorMode,
|
|
2709
|
+
supportedUrls: {},
|
|
2710
|
+
download,
|
|
2711
|
+
}),
|
|
2712
|
+
},
|
|
2713
|
+
rawOutput: result,
|
|
2714
|
+
isError: streamResult.isError === true,
|
|
2715
|
+
};
|
|
2716
|
+
}
|
|
1909
2717
|
|
|
2718
|
+
function createInvalidToolResult(toolCall: {
|
|
2719
|
+
toolCallId: string;
|
|
2720
|
+
toolName: string;
|
|
2721
|
+
error?: unknown;
|
|
2722
|
+
}): LanguageModelV4ToolResultPart {
|
|
1910
2723
|
return {
|
|
1911
2724
|
type: 'tool-result' as const,
|
|
1912
2725
|
toolCallId: toolCall.toolCallId,
|
|
1913
2726
|
toolName: toolCall.toolName,
|
|
1914
|
-
output:
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
: streamResult.isError
|
|
1919
|
-
? {
|
|
1920
|
-
type: 'error-json' as const,
|
|
1921
|
-
value: result as JSONValue,
|
|
1922
|
-
}
|
|
1923
|
-
: {
|
|
1924
|
-
type: 'json' as const,
|
|
1925
|
-
value: result as JSONValue,
|
|
1926
|
-
},
|
|
2727
|
+
output: {
|
|
2728
|
+
type: 'error-text' as const,
|
|
2729
|
+
value: getErrorMessage(toolCall.error),
|
|
2730
|
+
},
|
|
1927
2731
|
};
|
|
1928
2732
|
}
|
|
1929
2733
|
|
|
2734
|
+
function getToolCallbackMessages(
|
|
2735
|
+
messages: LanguageModelV4Prompt,
|
|
2736
|
+
): ModelMessage[] {
|
|
2737
|
+
const withoutAssistantToolCall =
|
|
2738
|
+
messages.at(-1)?.role === 'assistant' ? messages.slice(0, -1) : messages;
|
|
2739
|
+
return withoutAssistantToolCall as unknown as ModelMessage[];
|
|
2740
|
+
}
|
|
2741
|
+
|
|
1930
2742
|
async function executeTool(
|
|
1931
2743
|
toolCall: { toolCallId: string; toolName: string; input: unknown },
|
|
1932
2744
|
tools: ToolSet,
|
|
1933
2745
|
messages: LanguageModelV4Prompt,
|
|
1934
|
-
|
|
1935
|
-
|
|
2746
|
+
context?: unknown,
|
|
2747
|
+
download?: DownloadFunction,
|
|
2748
|
+
): Promise<WorkflowToolExecutionResult> {
|
|
1936
2749
|
const tool = tools[toolCall.toolName];
|
|
1937
2750
|
if (!tool) throw new Error(`Tool "${toolCall.toolName}" not found`);
|
|
1938
2751
|
if (typeof tool.execute !== 'function') {
|
|
@@ -1943,6 +2756,7 @@ async function executeTool(
|
|
|
1943
2756
|
}
|
|
1944
2757
|
// Input is already parsed and validated by streamModelCall's parseToolCall
|
|
1945
2758
|
const parsedInput = toolCall.input;
|
|
2759
|
+
let toolResult: unknown;
|
|
1946
2760
|
|
|
1947
2761
|
try {
|
|
1948
2762
|
// Extract execute function to avoid binding `this` to the tool object.
|
|
@@ -1951,149 +2765,56 @@ async function executeTool(
|
|
|
1951
2765
|
// When the execute function is a workflow step (marked with 'use step'),
|
|
1952
2766
|
// the step system captures `this` for serialization, causing failures.
|
|
1953
2767
|
const { execute } = tool;
|
|
1954
|
-
|
|
2768
|
+
toolResult = await execute(parsedInput, {
|
|
1955
2769
|
toolCallId: toolCall.toolCallId,
|
|
1956
2770
|
// Pass the conversation messages to the tool so it has context about the conversation
|
|
1957
2771
|
messages,
|
|
1958
|
-
// Pass context to the tool
|
|
1959
|
-
context
|
|
2772
|
+
// Pass per-tool context to the tool (resolved from `toolsContext`)
|
|
2773
|
+
context,
|
|
1960
2774
|
});
|
|
1961
|
-
|
|
1962
|
-
// Use the appropriate output type based on the result
|
|
1963
|
-
// AI SDK supports 'text' for strings and 'json' for objects
|
|
1964
|
-
const output =
|
|
1965
|
-
typeof toolResult === 'string'
|
|
1966
|
-
? { type: 'text' as const, value: toolResult }
|
|
1967
|
-
: { type: 'json' as const, value: toolResult };
|
|
1968
|
-
|
|
1969
|
-
return {
|
|
1970
|
-
type: 'tool-result' as const,
|
|
1971
|
-
toolCallId: toolCall.toolCallId,
|
|
1972
|
-
toolName: toolCall.toolName,
|
|
1973
|
-
output,
|
|
1974
|
-
};
|
|
1975
2775
|
} catch (error) {
|
|
1976
2776
|
// Convert tool errors to error-text results sent back to the model,
|
|
1977
2777
|
// allowing the agent to recover rather than killing the entire stream.
|
|
1978
2778
|
// This aligns with AI SDK's streamText behavior for individual tool failures.
|
|
2779
|
+
const errorMessage = getErrorMessage(error);
|
|
1979
2780
|
return {
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
2781
|
+
modelResult: {
|
|
2782
|
+
type: 'tool-result',
|
|
2783
|
+
toolCallId: toolCall.toolCallId,
|
|
2784
|
+
toolName: toolCall.toolName,
|
|
2785
|
+
output: await createLanguageModelToolResultOutput({
|
|
2786
|
+
toolCallId: toolCall.toolCallId,
|
|
2787
|
+
toolName: toolCall.toolName,
|
|
2788
|
+
input: parsedInput,
|
|
2789
|
+
output: errorMessage,
|
|
2790
|
+
tool,
|
|
2791
|
+
errorMode: 'text',
|
|
2792
|
+
supportedUrls: {},
|
|
2793
|
+
download,
|
|
2794
|
+
}),
|
|
1986
2795
|
},
|
|
2796
|
+
rawOutput: errorMessage,
|
|
2797
|
+
isError: true,
|
|
1987
2798
|
};
|
|
1988
2799
|
}
|
|
1989
|
-
}
|
|
1990
|
-
|
|
1991
|
-
/**
|
|
1992
|
-
* Collected tool approval information for a single tool call.
|
|
1993
|
-
*/
|
|
1994
|
-
interface CollectedApproval {
|
|
1995
|
-
toolCallId: string;
|
|
1996
|
-
toolName: string;
|
|
1997
|
-
input: unknown;
|
|
1998
|
-
approvalId: string;
|
|
1999
|
-
reason?: string;
|
|
2000
|
-
}
|
|
2001
|
-
|
|
2002
|
-
/**
|
|
2003
|
-
* Collect tool approval responses from model messages.
|
|
2004
|
-
* Mirrors the logic from `collectToolApprovals` in the AI SDK core
|
|
2005
|
-
* (`packages/ai/src/generate-text/collect-tool-approvals.ts`).
|
|
2006
|
-
*
|
|
2007
|
-
* Scans the last tool message for `tool-approval-response` parts,
|
|
2008
|
-
* matches them with `tool-approval-request` parts in assistant messages
|
|
2009
|
-
* and the corresponding `tool-call` parts.
|
|
2010
|
-
*/
|
|
2011
|
-
function collectToolApprovalsFromMessages(messages: ModelMessage[]): {
|
|
2012
|
-
approvedToolApprovals: CollectedApproval[];
|
|
2013
|
-
deniedToolApprovals: CollectedApproval[];
|
|
2014
|
-
} {
|
|
2015
|
-
const lastMessage = messages.at(-1);
|
|
2016
|
-
|
|
2017
|
-
if (lastMessage?.role !== 'tool') {
|
|
2018
|
-
return { approvedToolApprovals: [], deniedToolApprovals: [] };
|
|
2019
|
-
}
|
|
2020
|
-
|
|
2021
|
-
// Gather tool calls from assistant messages
|
|
2022
|
-
const toolCallsByToolCallId: Record<
|
|
2023
|
-
string,
|
|
2024
|
-
{ toolName: string; input: unknown }
|
|
2025
|
-
> = {};
|
|
2026
|
-
for (const message of messages) {
|
|
2027
|
-
if (message.role === 'assistant' && Array.isArray(message.content)) {
|
|
2028
|
-
for (const part of message.content as any[]) {
|
|
2029
|
-
if (part.type === 'tool-call') {
|
|
2030
|
-
toolCallsByToolCallId[part.toolCallId] = {
|
|
2031
|
-
toolName: part.toolName,
|
|
2032
|
-
input: part.input ?? part.args,
|
|
2033
|
-
};
|
|
2034
|
-
}
|
|
2035
|
-
}
|
|
2036
|
-
}
|
|
2037
|
-
}
|
|
2038
|
-
|
|
2039
|
-
// Gather approval requests from assistant messages
|
|
2040
|
-
const approvalRequestsByApprovalId: Record<
|
|
2041
|
-
string,
|
|
2042
|
-
{ approvalId: string; toolCallId: string }
|
|
2043
|
-
> = {};
|
|
2044
|
-
for (const message of messages) {
|
|
2045
|
-
if (message.role === 'assistant' && Array.isArray(message.content)) {
|
|
2046
|
-
for (const part of message.content as any[]) {
|
|
2047
|
-
if (part.type === 'tool-approval-request') {
|
|
2048
|
-
approvalRequestsByApprovalId[part.approvalId] = {
|
|
2049
|
-
approvalId: part.approvalId,
|
|
2050
|
-
toolCallId: part.toolCallId,
|
|
2051
|
-
};
|
|
2052
|
-
}
|
|
2053
|
-
}
|
|
2054
|
-
}
|
|
2055
|
-
}
|
|
2056
|
-
|
|
2057
|
-
// Gather existing tool results to avoid re-executing
|
|
2058
|
-
const existingToolResults = new Set<string>();
|
|
2059
|
-
for (const part of lastMessage.content as any[]) {
|
|
2060
|
-
if (part.type === 'tool-result') {
|
|
2061
|
-
existingToolResults.add(part.toolCallId);
|
|
2062
|
-
}
|
|
2063
|
-
}
|
|
2064
2800
|
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
const approvalResponses = (lastMessage.content as any[]).filter(
|
|
2070
|
-
(part: any) => part.type === 'tool-approval-response',
|
|
2071
|
-
);
|
|
2072
|
-
|
|
2073
|
-
for (const response of approvalResponses) {
|
|
2074
|
-
const approvalRequest = approvalRequestsByApprovalId[response.approvalId];
|
|
2075
|
-
if (approvalRequest == null) continue;
|
|
2076
|
-
|
|
2077
|
-
// Skip if there's already a tool result for this tool call
|
|
2078
|
-
if (existingToolResults.has(approvalRequest.toolCallId)) continue;
|
|
2079
|
-
|
|
2080
|
-
const toolCall = toolCallsByToolCallId[approvalRequest.toolCallId];
|
|
2081
|
-
if (toolCall == null) continue;
|
|
2082
|
-
|
|
2083
|
-
const approval: CollectedApproval = {
|
|
2084
|
-
toolCallId: approvalRequest.toolCallId,
|
|
2801
|
+
return {
|
|
2802
|
+
modelResult: {
|
|
2803
|
+
type: 'tool-result' as const,
|
|
2804
|
+
toolCallId: toolCall.toolCallId,
|
|
2085
2805
|
toolName: toolCall.toolName,
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2806
|
+
output: await createLanguageModelToolResultOutput({
|
|
2807
|
+
toolCallId: toolCall.toolCallId,
|
|
2808
|
+
toolName: toolCall.toolName,
|
|
2809
|
+
input: parsedInput,
|
|
2810
|
+
output: toolResult,
|
|
2811
|
+
tool,
|
|
2812
|
+
errorMode: 'none',
|
|
2813
|
+
supportedUrls: {},
|
|
2814
|
+
download,
|
|
2815
|
+
}),
|
|
2816
|
+
},
|
|
2817
|
+
rawOutput: toolResult,
|
|
2818
|
+
isError: false,
|
|
2819
|
+
};
|
|
2099
2820
|
}
|