@ai-sdk/workflow 1.0.0-beta.9 → 1.0.0-beta.95
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 +747 -0
- package/dist/index.d.mts +190 -112
- package/dist/index.mjs +855 -340
- 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 +46 -9
- package/src/index.ts +5 -3
- package/src/providers/mock.ts +8 -8
- package/src/serializable-schema.ts +5 -1
- package/src/stream-text-iterator.ts +150 -62
- package/src/test/agent-e2e-workflows.ts +89 -9
- package/src/to-ui-message-chunk.ts +13 -10
- package/src/workflow-agent.ts +1157 -606
- 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,48 +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,
|
|
24
|
-
LanguageModel,
|
|
31
|
+
type LanguageModel,
|
|
32
|
+
type Prompt,
|
|
33
|
+
type TelemetryOptions as CoreTelemetryOptions,
|
|
34
|
+
type Instructions,
|
|
25
35
|
} from 'ai';
|
|
26
36
|
import {
|
|
37
|
+
createRestrictedTelemetryDispatcher,
|
|
38
|
+
collectToolApprovals,
|
|
27
39
|
convertToLanguageModelPrompt,
|
|
28
40
|
mergeAbortSignals,
|
|
29
41
|
mergeCallbacks,
|
|
30
42
|
standardizePrompt,
|
|
43
|
+
validateApprovedToolApprovals,
|
|
31
44
|
} from 'ai/internal';
|
|
45
|
+
import { createLanguageModelToolResultOutput } from './create-language-model-tool-result-output.js';
|
|
32
46
|
import { streamTextIterator } from './stream-text-iterator.js';
|
|
33
|
-
import type { CompatibleLanguageModel } from './types.js';
|
|
34
47
|
|
|
35
48
|
// Re-export for consumers
|
|
36
49
|
export type { CompatibleLanguageModel } from './types.js';
|
|
37
50
|
|
|
38
51
|
/**
|
|
39
52
|
* Callback function to be called after each step completes.
|
|
40
|
-
* Alias for the AI SDK's
|
|
53
|
+
* Alias for the AI SDK's GenerateTextOnStepEndCallback, using
|
|
41
54
|
* WorkflowAgent-consistent naming.
|
|
42
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
|
+
*/
|
|
43
67
|
export type WorkflowAgentOnStepFinishCallback<
|
|
44
68
|
TTools extends ToolSet = ToolSet,
|
|
45
|
-
|
|
69
|
+
TRuntimeContext extends Context = Context,
|
|
70
|
+
> = WorkflowAgentOnStepEndCallback<TTools, TRuntimeContext>;
|
|
46
71
|
|
|
47
72
|
/**
|
|
48
73
|
* Infer the type of the tools of a workflow agent.
|
|
49
74
|
*/
|
|
50
75
|
export type InferWorkflowAgentTools<WORKFLOW_AGENT> =
|
|
51
|
-
WORKFLOW_AGENT extends WorkflowAgent<infer TOOLS> ? TOOLS : never;
|
|
76
|
+
WORKFLOW_AGENT extends WorkflowAgent<infer TOOLS, any> ? TOOLS : never;
|
|
52
77
|
|
|
53
78
|
/**
|
|
54
79
|
* Infer the UI message type of a workflow agent.
|
|
@@ -89,54 +114,14 @@ export interface OutputSpecification<OUTPUT, PARTIAL> {
|
|
|
89
114
|
*/
|
|
90
115
|
export type ProviderOptions = SharedV4ProviderOptions;
|
|
91
116
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Identifier for this function. Used to group telemetry data by function.
|
|
103
|
-
*/
|
|
104
|
-
functionId?: string;
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Additional information to include in the telemetry data.
|
|
108
|
-
*/
|
|
109
|
-
metadata?: Record<
|
|
110
|
-
string,
|
|
111
|
-
| string
|
|
112
|
-
| number
|
|
113
|
-
| boolean
|
|
114
|
-
| Array<string | number | boolean>
|
|
115
|
-
| null
|
|
116
|
-
| undefined
|
|
117
|
-
>;
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Enable or disable input recording. Enabled by default.
|
|
121
|
-
*
|
|
122
|
-
* You might want to disable input recording to avoid recording sensitive
|
|
123
|
-
* information, to reduce data transfers, or to increase performance.
|
|
124
|
-
*/
|
|
125
|
-
recordInputs?: boolean;
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Enable or disable output recording. Enabled by default.
|
|
129
|
-
*
|
|
130
|
-
* You might want to disable output recording to avoid recording sensitive
|
|
131
|
-
* information, to reduce data transfers, or to increase performance.
|
|
132
|
-
*/
|
|
133
|
-
recordOutputs?: boolean;
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Custom tracer for the telemetry.
|
|
137
|
-
*/
|
|
138
|
-
tracer?: unknown;
|
|
139
|
-
}
|
|
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>;
|
|
140
125
|
|
|
141
126
|
/**
|
|
142
127
|
* A transformation that is applied to the stream.
|
|
@@ -253,7 +238,10 @@ export interface GenerationSettings {
|
|
|
253
238
|
/**
|
|
254
239
|
* Information passed to the prepareStep callback.
|
|
255
240
|
*/
|
|
256
|
-
export interface PrepareStepInfo<
|
|
241
|
+
export interface PrepareStepInfo<
|
|
242
|
+
TTools extends ToolSet = ToolSet,
|
|
243
|
+
TRuntimeContext extends Context = Context,
|
|
244
|
+
> {
|
|
257
245
|
/**
|
|
258
246
|
* The current model configuration (string or function).
|
|
259
247
|
* The function should return a LanguageModelV4 instance.
|
|
@@ -268,7 +256,7 @@ export interface PrepareStepInfo<TTools extends ToolSet = ToolSet> {
|
|
|
268
256
|
/**
|
|
269
257
|
* All previous steps with their results.
|
|
270
258
|
*/
|
|
271
|
-
steps: StepResult<TTools,
|
|
259
|
+
steps: StepResult<TTools, TRuntimeContext>[];
|
|
272
260
|
|
|
273
261
|
/**
|
|
274
262
|
* The messages that will be sent to the model.
|
|
@@ -277,16 +265,29 @@ export interface PrepareStepInfo<TTools extends ToolSet = ToolSet> {
|
|
|
277
265
|
messages: LanguageModelV4Prompt;
|
|
278
266
|
|
|
279
267
|
/**
|
|
280
|
-
* 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.
|
|
281
271
|
*/
|
|
282
|
-
|
|
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.
|
|
279
|
+
*/
|
|
280
|
+
toolsContext: InferToolSetContext<TTools>;
|
|
283
281
|
}
|
|
284
282
|
|
|
285
283
|
/**
|
|
286
284
|
* Return type from the prepareStep callback.
|
|
287
285
|
* All properties are optional - only return the ones you want to override.
|
|
288
286
|
*/
|
|
289
|
-
export interface PrepareStepResult
|
|
287
|
+
export interface PrepareStepResult<
|
|
288
|
+
TTools extends ToolSet = ToolSet,
|
|
289
|
+
TRuntimeContext extends Context = Context,
|
|
290
|
+
> extends Partial<GenerationSettings> {
|
|
290
291
|
/**
|
|
291
292
|
* Override the model for this step.
|
|
292
293
|
*/
|
|
@@ -315,32 +316,53 @@ export interface PrepareStepResult extends Partial<GenerationSettings> {
|
|
|
315
316
|
activeTools?: string[];
|
|
316
317
|
|
|
317
318
|
/**
|
|
318
|
-
*
|
|
319
|
-
*
|
|
319
|
+
* Updated runtime context for the current and subsequent steps.
|
|
320
|
+
* Returning a value replaces the agent's runtime context.
|
|
321
|
+
*/
|
|
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.
|
|
320
327
|
*/
|
|
321
|
-
|
|
328
|
+
toolsContext?: InferToolSetContext<TTools>;
|
|
322
329
|
}
|
|
323
330
|
|
|
324
331
|
/**
|
|
325
332
|
* Callback function called before each step in the agent loop.
|
|
326
333
|
* Use this to modify settings, manage context, or implement dynamic behavior.
|
|
327
334
|
*/
|
|
328
|
-
export type PrepareStepCallback<
|
|
329
|
-
|
|
330
|
-
|
|
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>;
|
|
331
344
|
|
|
332
345
|
/**
|
|
333
346
|
* Options passed to the prepareCall callback.
|
|
334
347
|
*/
|
|
335
348
|
export interface PrepareCallOptions<
|
|
336
349
|
TTools extends ToolSet = ToolSet,
|
|
350
|
+
TRuntimeContext extends Context = Context,
|
|
337
351
|
> extends Partial<GenerationSettings> {
|
|
338
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,172 +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
|
-
|
|
397
|
+
TRuntimeContext extends Context = Context,
|
|
398
|
+
> = GenerationSettings &
|
|
399
|
+
WorkflowAgentToolsContextParameter<TTools> & {
|
|
400
|
+
/**
|
|
401
|
+
* The id of the agent.
|
|
402
|
+
*/
|
|
403
|
+
id?: string;
|
|
374
404
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
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;
|
|
382
412
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
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;
|
|
389
419
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
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;
|
|
395
425
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
426
|
+
/**
|
|
427
|
+
* Optional system prompt to guide the agent's behavior.
|
|
428
|
+
* @deprecated Use `instructions` instead.
|
|
429
|
+
*/
|
|
430
|
+
system?: string;
|
|
401
431
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
432
|
+
/**
|
|
433
|
+
* The tool choice strategy. Default: 'auto'.
|
|
434
|
+
*/
|
|
435
|
+
toolChoice?: ToolChoice<TTools>;
|
|
406
436
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
437
|
+
/**
|
|
438
|
+
* Optional telemetry configuration.
|
|
439
|
+
*/
|
|
440
|
+
telemetry?: TelemetryOptions<TRuntimeContext, TTools>;
|
|
411
441
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
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;
|
|
420
456
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
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>>;
|
|
430
466
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
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>>;
|
|
438
474
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
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>;
|
|
446
482
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
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>;
|
|
453
489
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
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;
|
|
460
496
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
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>;
|
|
469
505
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
506
|
+
/**
|
|
507
|
+
* Callback function to be called after each step completes.
|
|
508
|
+
*/
|
|
509
|
+
onStepEnd?: WorkflowAgentOnStepEndCallback<TTools, TRuntimeContext>;
|
|
474
510
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
511
|
+
/**
|
|
512
|
+
* Callback function to be called after each step completes.
|
|
513
|
+
*
|
|
514
|
+
* @deprecated Use `onStepEnd` instead.
|
|
515
|
+
*/
|
|
516
|
+
onStepFinish?: WorkflowAgentOnStepFinishCallback<TTools, TRuntimeContext>;
|
|
479
517
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
518
|
+
/**
|
|
519
|
+
* Callback that is called when the LLM response and all request tool executions are finished.
|
|
520
|
+
*/
|
|
521
|
+
onEnd?: WorkflowAgentOnEndCallback<TTools, TRuntimeContext>;
|
|
484
522
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
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>;
|
|
489
529
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
530
|
+
/**
|
|
531
|
+
* Callback called when the agent starts streaming, before any LLM calls.
|
|
532
|
+
*/
|
|
533
|
+
experimental_onStart?: WorkflowAgentOnStartCallback<
|
|
534
|
+
TTools,
|
|
535
|
+
TRuntimeContext
|
|
536
|
+
>;
|
|
494
537
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
538
|
+
/**
|
|
539
|
+
* Callback called before each step (LLM call) begins.
|
|
540
|
+
*/
|
|
541
|
+
experimental_onStepStart?: WorkflowAgentOnStepStartCallback<
|
|
542
|
+
TTools,
|
|
543
|
+
TRuntimeContext
|
|
544
|
+
>;
|
|
499
545
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
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
|
+
};
|
|
507
563
|
|
|
508
564
|
/**
|
|
509
565
|
* Callback that is called when the LLM response and all request tool executions are finished.
|
|
510
566
|
*/
|
|
511
|
-
export type
|
|
567
|
+
export type WorkflowAgentOnEndCallback<
|
|
512
568
|
TTools extends ToolSet = ToolSet,
|
|
569
|
+
TRuntimeContext extends Context = Context,
|
|
513
570
|
OUTPUT = never,
|
|
514
571
|
> = (event: {
|
|
515
572
|
/**
|
|
516
573
|
* Details for all steps.
|
|
517
574
|
*/
|
|
518
|
-
readonly steps: StepResult<TTools,
|
|
575
|
+
readonly steps: StepResult<TTools, TRuntimeContext>[];
|
|
519
576
|
|
|
520
577
|
/**
|
|
521
578
|
* The final messages including all tool calls and results.
|
|
@@ -532,15 +589,25 @@ export type WorkflowAgentOnFinishCallback<
|
|
|
532
589
|
*/
|
|
533
590
|
readonly finishReason: FinishReason;
|
|
534
591
|
|
|
592
|
+
/**
|
|
593
|
+
* The total token usage across all steps.
|
|
594
|
+
*/
|
|
595
|
+
readonly usage: LanguageModelUsage;
|
|
596
|
+
|
|
535
597
|
/**
|
|
536
598
|
* The total token usage across all steps.
|
|
537
599
|
*/
|
|
538
600
|
readonly totalUsage: LanguageModelUsage;
|
|
539
601
|
|
|
540
602
|
/**
|
|
541
|
-
*
|
|
603
|
+
* The runtime context at the end of the agent loop.
|
|
542
604
|
*/
|
|
543
|
-
readonly
|
|
605
|
+
readonly runtimeContext: TRuntimeContext;
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* The per-tool context at the end of the agent loop.
|
|
609
|
+
*/
|
|
610
|
+
readonly toolsContext: InferToolSetContext<TTools>;
|
|
544
611
|
|
|
545
612
|
/**
|
|
546
613
|
* The generated structured output. It uses the `output` specification.
|
|
@@ -549,6 +616,17 @@ export type WorkflowAgentOnFinishCallback<
|
|
|
549
616
|
readonly output: OUTPUT;
|
|
550
617
|
}) => PromiseLike<void> | void;
|
|
551
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
|
+
|
|
552
630
|
/**
|
|
553
631
|
* Callback that is invoked when an error occurs during streaming.
|
|
554
632
|
*/
|
|
@@ -570,36 +648,57 @@ export type WorkflowAgentOnAbortCallback<TTools extends ToolSet = ToolSet> =
|
|
|
570
648
|
/**
|
|
571
649
|
* Callback that is called when the agent starts streaming, before any LLM calls.
|
|
572
650
|
*/
|
|
573
|
-
export type WorkflowAgentOnStartCallback
|
|
651
|
+
export type WorkflowAgentOnStartCallback<
|
|
652
|
+
TTools extends ToolSet = ToolSet,
|
|
653
|
+
TRuntimeContext extends Context = Context,
|
|
654
|
+
> = (event: {
|
|
574
655
|
/** The model being used */
|
|
575
656
|
readonly model: LanguageModel;
|
|
576
657
|
/** The messages being sent */
|
|
577
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>;
|
|
578
663
|
}) => PromiseLike<void> | void;
|
|
579
664
|
|
|
580
665
|
/**
|
|
581
666
|
* Callback that is called before each step (LLM call) begins.
|
|
582
667
|
*/
|
|
583
|
-
export type WorkflowAgentOnStepStartCallback<
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
668
|
+
export type WorkflowAgentOnStepStartCallback<
|
|
669
|
+
TTools extends ToolSet = ToolSet,
|
|
670
|
+
TRuntimeContext extends Context = Context,
|
|
671
|
+
> = (event: {
|
|
672
|
+
/** The current step number (0-based) */
|
|
673
|
+
readonly stepNumber: number;
|
|
674
|
+
/** The model being used for this step */
|
|
675
|
+
readonly model: LanguageModel;
|
|
676
|
+
/** The messages being sent for this step */
|
|
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>;
|
|
684
|
+
}) => PromiseLike<void> | void;
|
|
594
685
|
|
|
595
686
|
/**
|
|
596
687
|
* Callback that is called before a tool's execute function runs.
|
|
597
688
|
*/
|
|
598
|
-
export type
|
|
689
|
+
export type WorkflowAgentOnToolExecutionStartCallback<
|
|
690
|
+
TTools extends ToolSet = ToolSet,
|
|
691
|
+
> = (event: {
|
|
599
692
|
/** The tool call being executed */
|
|
600
693
|
readonly toolCall: ToolCall;
|
|
601
694
|
/** The current step number (0-based) */
|
|
602
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;
|
|
603
702
|
}) => PromiseLike<void> | void;
|
|
604
703
|
|
|
605
704
|
/**
|
|
@@ -607,7 +706,9 @@ export type WorkflowAgentOnToolCallStartCallback = (event: {
|
|
|
607
706
|
* Uses a discriminated union pattern: check `success` to determine
|
|
608
707
|
* whether `output` or `error` is available.
|
|
609
708
|
*/
|
|
610
|
-
export type
|
|
709
|
+
export type WorkflowAgentOnToolExecutionEndCallback<
|
|
710
|
+
TTools extends ToolSet = ToolSet,
|
|
711
|
+
> = (
|
|
611
712
|
event:
|
|
612
713
|
| {
|
|
613
714
|
/** The tool call that was executed */
|
|
@@ -616,6 +717,12 @@ export type WorkflowAgentOnToolCallFinishCallback = (
|
|
|
616
717
|
readonly stepNumber: number;
|
|
617
718
|
/** Execution time in milliseconds */
|
|
618
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;
|
|
619
726
|
/** Whether the tool call succeeded */
|
|
620
727
|
readonly success: true;
|
|
621
728
|
/** The tool result */
|
|
@@ -629,6 +736,12 @@ export type WorkflowAgentOnToolCallFinishCallback = (
|
|
|
629
736
|
readonly stepNumber: number;
|
|
630
737
|
/** Execution time in milliseconds */
|
|
631
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;
|
|
632
745
|
/** Whether the tool call succeeded */
|
|
633
746
|
readonly success: false;
|
|
634
747
|
/** The error that occurred */
|
|
@@ -642,6 +755,7 @@ export type WorkflowAgentOnToolCallFinishCallback = (
|
|
|
642
755
|
*/
|
|
643
756
|
export type WorkflowAgentStreamOptions<
|
|
644
757
|
TTools extends ToolSet = ToolSet,
|
|
758
|
+
TRuntimeContext extends Context = Context,
|
|
645
759
|
OUTPUT = never,
|
|
646
760
|
PARTIAL_OUTPUT = never,
|
|
647
761
|
> = Partial<GenerationSettings> &
|
|
@@ -714,13 +828,6 @@ export type WorkflowAgentStreamOptions<
|
|
|
714
828
|
| StopCondition<NoInfer<ToolSet>, any>
|
|
715
829
|
| Array<StopCondition<NoInfer<ToolSet>, any>>;
|
|
716
830
|
|
|
717
|
-
/**
|
|
718
|
-
* Maximum number of sequential LLM calls (steps), e.g. when you use tool calls.
|
|
719
|
-
* A maximum number can be set to prevent infinite loops in the case of misconfigured tools.
|
|
720
|
-
* By default, it's unlimited (the agent loops until completion).
|
|
721
|
-
*/
|
|
722
|
-
maxSteps?: number;
|
|
723
|
-
|
|
724
831
|
/**
|
|
725
832
|
* The tool choice strategy. Default: 'auto'.
|
|
726
833
|
* Overrides the toolChoice from the constructor if provided.
|
|
@@ -731,19 +838,37 @@ export type WorkflowAgentStreamOptions<
|
|
|
731
838
|
* Limits the tools that are available for the model to call without
|
|
732
839
|
* changing the tool call and result types in the result.
|
|
733
840
|
*/
|
|
734
|
-
activeTools?:
|
|
841
|
+
activeTools?: ActiveTools<NoInfer<TTools>>;
|
|
842
|
+
|
|
843
|
+
/**
|
|
844
|
+
* Optional telemetry configuration.
|
|
845
|
+
*/
|
|
846
|
+
telemetry?: TelemetryOptions<TRuntimeContext, TTools>;
|
|
735
847
|
|
|
736
848
|
/**
|
|
737
|
-
*
|
|
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.
|
|
738
858
|
*/
|
|
739
|
-
|
|
859
|
+
runtimeContext?: TRuntimeContext;
|
|
740
860
|
|
|
741
861
|
/**
|
|
742
|
-
*
|
|
743
|
-
*
|
|
744
|
-
*
|
|
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.
|
|
745
870
|
*/
|
|
746
|
-
|
|
871
|
+
toolsContext?: InferToolSetContext<TTools>;
|
|
747
872
|
|
|
748
873
|
/**
|
|
749
874
|
* Optional specification for parsing structured outputs from the LLM response.
|
|
@@ -801,7 +926,14 @@ export type WorkflowAgentStreamOptions<
|
|
|
801
926
|
/**
|
|
802
927
|
* Callback function to be called after each step completes.
|
|
803
928
|
*/
|
|
804
|
-
|
|
929
|
+
onStepEnd?: WorkflowAgentOnStepEndCallback<TTools, TRuntimeContext>;
|
|
930
|
+
|
|
931
|
+
/**
|
|
932
|
+
* Callback function to be called after each step completes.
|
|
933
|
+
*
|
|
934
|
+
* @deprecated Use `onStepEnd` instead.
|
|
935
|
+
*/
|
|
936
|
+
onStepFinish?: WorkflowAgentOnStepFinishCallback<TTools, TRuntimeContext>;
|
|
805
937
|
|
|
806
938
|
/**
|
|
807
939
|
* Callback that is invoked when an error occurs during streaming.
|
|
@@ -813,7 +945,15 @@ export type WorkflowAgentStreamOptions<
|
|
|
813
945
|
* Callback that is called when the LLM response and all request tool executions
|
|
814
946
|
* (for tools that have an `execute` function) are finished.
|
|
815
947
|
*/
|
|
816
|
-
|
|
948
|
+
onEnd?: WorkflowAgentOnEndCallback<TTools, TRuntimeContext, OUTPUT>;
|
|
949
|
+
|
|
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>;
|
|
817
957
|
|
|
818
958
|
/**
|
|
819
959
|
* Callback that is called when the operation is aborted.
|
|
@@ -823,22 +963,28 @@ export type WorkflowAgentStreamOptions<
|
|
|
823
963
|
/**
|
|
824
964
|
* Callback called when the agent starts streaming, before any LLM calls.
|
|
825
965
|
*/
|
|
826
|
-
experimental_onStart?: WorkflowAgentOnStartCallback
|
|
966
|
+
experimental_onStart?: WorkflowAgentOnStartCallback<
|
|
967
|
+
TTools,
|
|
968
|
+
TRuntimeContext
|
|
969
|
+
>;
|
|
827
970
|
|
|
828
971
|
/**
|
|
829
972
|
* Callback called before each step (LLM call) begins.
|
|
830
973
|
*/
|
|
831
|
-
experimental_onStepStart?: WorkflowAgentOnStepStartCallback
|
|
974
|
+
experimental_onStepStart?: WorkflowAgentOnStepStartCallback<
|
|
975
|
+
TTools,
|
|
976
|
+
TRuntimeContext
|
|
977
|
+
>;
|
|
832
978
|
|
|
833
979
|
/**
|
|
834
980
|
* Callback called before a tool's execute function runs.
|
|
835
981
|
*/
|
|
836
|
-
|
|
982
|
+
onToolExecutionStart?: WorkflowAgentOnToolExecutionStartCallback<TTools>;
|
|
837
983
|
|
|
838
984
|
/**
|
|
839
985
|
* Callback called after a tool execution completes.
|
|
840
986
|
*/
|
|
841
|
-
|
|
987
|
+
onToolExecutionEnd?: WorkflowAgentOnToolExecutionEndCallback<TTools>;
|
|
842
988
|
|
|
843
989
|
/**
|
|
844
990
|
* Callback function called before each step in the agent loop.
|
|
@@ -858,7 +1004,7 @@ export type WorkflowAgentStreamOptions<
|
|
|
858
1004
|
* }
|
|
859
1005
|
* ```
|
|
860
1006
|
*/
|
|
861
|
-
prepareStep?: PrepareStepCallback<TTools>;
|
|
1007
|
+
prepareStep?: PrepareStepCallback<TTools, TRuntimeContext>;
|
|
862
1008
|
|
|
863
1009
|
/**
|
|
864
1010
|
* Timeout in milliseconds for the stream operation.
|
|
@@ -910,6 +1056,12 @@ export interface ToolResult {
|
|
|
910
1056
|
output: unknown;
|
|
911
1057
|
}
|
|
912
1058
|
|
|
1059
|
+
type WorkflowToolExecutionResult = {
|
|
1060
|
+
modelResult: LanguageModelV4ToolResultPart;
|
|
1061
|
+
rawOutput: unknown;
|
|
1062
|
+
isError: boolean;
|
|
1063
|
+
};
|
|
1064
|
+
|
|
913
1065
|
/**
|
|
914
1066
|
* Result of the WorkflowAgent.stream method.
|
|
915
1067
|
*/
|
|
@@ -988,7 +1140,10 @@ export interface WorkflowAgentStreamResult<
|
|
|
988
1140
|
* });
|
|
989
1141
|
* ```
|
|
990
1142
|
*/
|
|
991
|
-
export class WorkflowAgent<
|
|
1143
|
+
export class WorkflowAgent<
|
|
1144
|
+
TBaseTools extends ToolSet = ToolSet,
|
|
1145
|
+
TRuntimeContext extends Context = Context,
|
|
1146
|
+
> {
|
|
992
1147
|
/**
|
|
993
1148
|
* The id of the agent.
|
|
994
1149
|
*/
|
|
@@ -999,51 +1154,63 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
999
1154
|
* The tool set configured for this agent.
|
|
1000
1155
|
*/
|
|
1001
1156
|
public readonly tools: TBaseTools;
|
|
1002
|
-
private instructions?:
|
|
1003
|
-
| string
|
|
1004
|
-
| SystemModelMessage
|
|
1005
|
-
| Array<SystemModelMessage>;
|
|
1157
|
+
private instructions?: Instructions;
|
|
1006
1158
|
private generationSettings: GenerationSettings;
|
|
1007
1159
|
private toolChoice?: ToolChoice<TBaseTools>;
|
|
1008
|
-
private telemetry?:
|
|
1009
|
-
private
|
|
1160
|
+
private telemetry?: TelemetryOptions<TRuntimeContext, TBaseTools>;
|
|
1161
|
+
private runtimeContext?: TRuntimeContext;
|
|
1162
|
+
private toolsContext?: InferToolSetContext<TBaseTools>;
|
|
1010
1163
|
private stopWhen?:
|
|
1011
1164
|
| StopCondition<ToolSet, any>
|
|
1012
1165
|
| Array<StopCondition<ToolSet, any>>;
|
|
1013
|
-
private activeTools?:
|
|
1166
|
+
private activeTools?: ActiveTools<TBaseTools>;
|
|
1014
1167
|
private output?: OutputSpecification<any, any>;
|
|
1015
1168
|
private experimentalRepairToolCall?: ToolCallRepairFunction<TBaseTools>;
|
|
1016
1169
|
private experimentalDownload?: DownloadFunction;
|
|
1017
|
-
private prepareStep?: PrepareStepCallback<TBaseTools>;
|
|
1018
|
-
private
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
private
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
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
|
|
1182
|
+
>;
|
|
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>) {
|
|
1027
1192
|
this.id = options.id;
|
|
1028
1193
|
this.model = options.model;
|
|
1029
1194
|
this.tools = (options.tools ?? {}) as TBaseTools;
|
|
1030
1195
|
// `instructions` takes precedence over deprecated `system`
|
|
1031
1196
|
this.instructions = options.instructions ?? options.system;
|
|
1032
1197
|
this.toolChoice = options.toolChoice;
|
|
1033
|
-
this.telemetry = options.
|
|
1034
|
-
this.
|
|
1198
|
+
this.telemetry = options.telemetry;
|
|
1199
|
+
this.runtimeContext = options.runtimeContext;
|
|
1200
|
+
this.toolsContext = options.toolsContext;
|
|
1035
1201
|
this.stopWhen = options.stopWhen;
|
|
1036
1202
|
this.activeTools = options.activeTools;
|
|
1037
1203
|
this.output = options.output;
|
|
1038
1204
|
this.experimentalRepairToolCall = options.experimental_repairToolCall;
|
|
1039
1205
|
this.experimentalDownload = options.experimental_download;
|
|
1040
1206
|
this.prepareStep = options.prepareStep;
|
|
1041
|
-
this.
|
|
1042
|
-
|
|
1207
|
+
this.constructorOnStepEnd = options.onStepEnd ?? options.onStepFinish;
|
|
1208
|
+
const { onFinish, onEnd = onFinish } = options;
|
|
1209
|
+
this.constructorOnEnd = onEnd;
|
|
1043
1210
|
this.constructorOnStart = options.experimental_onStart;
|
|
1044
1211
|
this.constructorOnStepStart = options.experimental_onStepStart;
|
|
1045
|
-
this.
|
|
1046
|
-
this.
|
|
1212
|
+
this.constructorOnToolExecutionStart = options.onToolExecutionStart;
|
|
1213
|
+
this.constructorOnToolExecutionEnd = options.onToolExecutionEnd;
|
|
1047
1214
|
this.prepareCall = options.prepareCall;
|
|
1048
1215
|
|
|
1049
1216
|
// Extract generation settings
|
|
@@ -1072,8 +1239,15 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1072
1239
|
OUTPUT = never,
|
|
1073
1240
|
PARTIAL_OUTPUT = never,
|
|
1074
1241
|
>(
|
|
1075
|
-
options: WorkflowAgentStreamOptions<
|
|
1242
|
+
options: WorkflowAgentStreamOptions<
|
|
1243
|
+
TTools,
|
|
1244
|
+
TRuntimeContext,
|
|
1245
|
+
OUTPUT,
|
|
1246
|
+
PARTIAL_OUTPUT
|
|
1247
|
+
>,
|
|
1076
1248
|
): Promise<WorkflowAgentStreamResult<TTools, OUTPUT>> {
|
|
1249
|
+
const { onFinish, onEnd = onFinish } = options;
|
|
1250
|
+
|
|
1077
1251
|
// Call prepareCall to transform parameters before the agent loop
|
|
1078
1252
|
let effectiveModel: LanguageModel = this.model;
|
|
1079
1253
|
let effectiveInstructions = options.system ?? this.instructions;
|
|
@@ -1081,11 +1255,16 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1081
1255
|
options.prompt;
|
|
1082
1256
|
let effectiveMessages: Array<ModelMessage> | undefined = options.messages;
|
|
1083
1257
|
let effectiveGenerationSettings = { ...this.generationSettings };
|
|
1084
|
-
let
|
|
1085
|
-
|
|
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
|
+
>;
|
|
1086
1266
|
let effectiveToolChoiceFromPrepare = options.toolChoice ?? this.toolChoice;
|
|
1087
|
-
let effectiveTelemetryFromPrepare =
|
|
1088
|
-
options.experimental_telemetry ?? this.telemetry;
|
|
1267
|
+
let effectiveTelemetryFromPrepare = options.telemetry ?? this.telemetry;
|
|
1089
1268
|
|
|
1090
1269
|
// Resolve messages for prepareCall: use messages directly, or convert prompt
|
|
1091
1270
|
const resolvedMessagesForPrepareCall: ModelMessage[] =
|
|
@@ -1101,11 +1280,12 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1101
1280
|
tools: this.tools,
|
|
1102
1281
|
instructions: effectiveInstructions,
|
|
1103
1282
|
toolChoice: effectiveToolChoiceFromPrepare as ToolChoice<TBaseTools>,
|
|
1104
|
-
|
|
1105
|
-
|
|
1283
|
+
telemetry: effectiveTelemetryFromPrepare,
|
|
1284
|
+
runtimeContext: effectiveRuntimeContext,
|
|
1285
|
+
toolsContext: effectiveToolsContext as InferToolSetContext<TBaseTools>,
|
|
1106
1286
|
messages: resolvedMessagesForPrepareCall,
|
|
1107
1287
|
...effectiveGenerationSettings,
|
|
1108
|
-
} as PrepareCallOptions<TBaseTools>);
|
|
1288
|
+
} as PrepareCallOptions<TBaseTools, TRuntimeContext>);
|
|
1109
1289
|
|
|
1110
1290
|
if (prepared.model !== undefined) effectiveModel = prepared.model;
|
|
1111
1291
|
if (prepared.instructions !== undefined)
|
|
@@ -1114,13 +1294,18 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1114
1294
|
effectiveMessages = prepared.messages as Array<ModelMessage>;
|
|
1115
1295
|
effectivePrompt = undefined; // messages from prepareCall take precedence
|
|
1116
1296
|
}
|
|
1117
|
-
if (prepared.
|
|
1118
|
-
|
|
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
|
+
>;
|
|
1119
1304
|
if (prepared.toolChoice !== undefined)
|
|
1120
1305
|
effectiveToolChoiceFromPrepare =
|
|
1121
1306
|
prepared.toolChoice as ToolChoice<TBaseTools>;
|
|
1122
|
-
if (prepared.
|
|
1123
|
-
effectiveTelemetryFromPrepare = prepared.
|
|
1307
|
+
if (prepared.telemetry !== undefined)
|
|
1308
|
+
effectiveTelemetryFromPrepare = prepared.telemetry;
|
|
1124
1309
|
if (prepared.maxOutputTokens !== undefined)
|
|
1125
1310
|
effectiveGenerationSettings.maxOutputTokens = prepared.maxOutputTokens;
|
|
1126
1311
|
if (prepared.temperature !== undefined)
|
|
@@ -1144,61 +1329,234 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1144
1329
|
effectiveGenerationSettings.providerOptions = prepared.providerOptions;
|
|
1145
1330
|
}
|
|
1146
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
|
+
|
|
1147
1343
|
const prompt = await standardizePrompt({
|
|
1148
1344
|
system: effectiveInstructions,
|
|
1345
|
+
allowSystemInMessages: true, // TODO: consider exposing this as a parameter
|
|
1149
1346
|
...(effectivePrompt != null
|
|
1150
1347
|
? { prompt: effectivePrompt }
|
|
1151
1348
|
: { messages: effectiveMessages! }),
|
|
1152
|
-
});
|
|
1349
|
+
} as Prompt);
|
|
1350
|
+
const download = options.experimental_download ?? this.experimentalDownload;
|
|
1153
1351
|
|
|
1154
1352
|
// Process tool approval responses before starting the agent loop.
|
|
1155
1353
|
// This mirrors how stream-text.ts handles tool-approval-response parts:
|
|
1156
1354
|
// approved tools are executed, denied tools get denial results, and
|
|
1157
1355
|
// approval parts are stripped from the messages.
|
|
1158
|
-
|
|
1159
|
-
|
|
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
|
+
collected,
|
|
1370
|
+
}),
|
|
1371
|
+
);
|
|
1372
|
+
const deniedToolApprovals = collectedApprovals.deniedToolApprovals.map(
|
|
1373
|
+
collected => ({
|
|
1374
|
+
toolCallId: collected.toolCall.toolCallId,
|
|
1375
|
+
toolName: collected.toolCall.toolName,
|
|
1376
|
+
input: collected.toolCall.input,
|
|
1377
|
+
reason: collected.approvalResponse.reason,
|
|
1378
|
+
}),
|
|
1379
|
+
);
|
|
1160
1380
|
|
|
1161
1381
|
if (approvedToolApprovals.length > 0 || deniedToolApprovals.length > 0) {
|
|
1162
1382
|
const _toolResultMessages: ModelMessage[] = [];
|
|
1163
|
-
const toolResultContent:
|
|
1164
|
-
|
|
1383
|
+
const toolResultContent: LanguageModelV4ToolResultPart[] = [];
|
|
1384
|
+
const approvedRawResults: Array<{
|
|
1165
1385
|
toolCallId: string;
|
|
1166
1386
|
toolName: string;
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
| { type: 'json'; value: JSONValue }
|
|
1170
|
-
| { type: 'execution-denied'; reason: string | undefined };
|
|
1387
|
+
input: unknown;
|
|
1388
|
+
output: unknown;
|
|
1171
1389
|
}> = [];
|
|
1172
1390
|
|
|
1173
1391
|
// Execute approved tools
|
|
1174
1392
|
for (const approval of approvedToolApprovals) {
|
|
1175
1393
|
const tool = (this.tools as ToolSet)[approval.toolName];
|
|
1176
1394
|
if (tool && typeof tool.execute === 'function') {
|
|
1395
|
+
if (!tool.needsApproval) {
|
|
1396
|
+
const reason = `Tool "${approval.toolName}" does not require approval`;
|
|
1397
|
+
toolResultContent.push({
|
|
1398
|
+
type: 'tool-result' as const,
|
|
1399
|
+
toolCallId: approval.toolCallId,
|
|
1400
|
+
toolName: approval.toolName,
|
|
1401
|
+
output: await createLanguageModelToolResultOutput({
|
|
1402
|
+
toolCallId: approval.toolCallId,
|
|
1403
|
+
toolName: approval.toolName,
|
|
1404
|
+
input: approval.input,
|
|
1405
|
+
output: reason,
|
|
1406
|
+
tool,
|
|
1407
|
+
errorMode: 'text',
|
|
1408
|
+
supportedUrls: {},
|
|
1409
|
+
download,
|
|
1410
|
+
}),
|
|
1411
|
+
});
|
|
1412
|
+
continue;
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
// Re-validate through the shared core implementation: input schema,
|
|
1416
|
+
// HMAC signature (when configured), and approval policy. It throws on
|
|
1417
|
+
// invalid input/signature; convert that to a denial result so the
|
|
1418
|
+
// agent loop can continue gracefully.
|
|
1419
|
+
let revalidationReason: string | undefined;
|
|
1420
|
+
try {
|
|
1421
|
+
const { deniedToolApprovals: policyDenied } =
|
|
1422
|
+
await validateApprovedToolApprovals({
|
|
1423
|
+
approvedToolApprovals: [approval.collected],
|
|
1424
|
+
tools: this.tools as ToolSet,
|
|
1425
|
+
toolApproval: undefined,
|
|
1426
|
+
messages: prompt.messages,
|
|
1427
|
+
toolsContext:
|
|
1428
|
+
effectiveToolsContext as InferToolSetContext<ToolSet>,
|
|
1429
|
+
runtimeContext: effectiveRuntimeContext,
|
|
1430
|
+
});
|
|
1431
|
+
if (policyDenied.length > 0) {
|
|
1432
|
+
revalidationReason =
|
|
1433
|
+
policyDenied[0].approvalResponse.reason ??
|
|
1434
|
+
'Tool approval denied';
|
|
1435
|
+
}
|
|
1436
|
+
} catch (error) {
|
|
1437
|
+
revalidationReason = getErrorMessage(error);
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
if (revalidationReason != null) {
|
|
1441
|
+
toolResultContent.push({
|
|
1442
|
+
type: 'tool-result' as const,
|
|
1443
|
+
toolCallId: approval.toolCallId,
|
|
1444
|
+
toolName: approval.toolName,
|
|
1445
|
+
output: await createLanguageModelToolResultOutput({
|
|
1446
|
+
toolCallId: approval.toolCallId,
|
|
1447
|
+
toolName: approval.toolName,
|
|
1448
|
+
input: approval.input,
|
|
1449
|
+
output: revalidationReason,
|
|
1450
|
+
tool,
|
|
1451
|
+
errorMode: 'text',
|
|
1452
|
+
supportedUrls: {},
|
|
1453
|
+
download,
|
|
1454
|
+
}),
|
|
1455
|
+
});
|
|
1456
|
+
continue;
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1177
1459
|
try {
|
|
1178
1460
|
const { execute } = tool;
|
|
1179
|
-
const
|
|
1461
|
+
const resolvedContext = await resolveToolContext({
|
|
1462
|
+
toolName: approval.toolName,
|
|
1463
|
+
tool,
|
|
1464
|
+
toolsContext: effectiveToolsContext,
|
|
1465
|
+
});
|
|
1466
|
+
const toolCallEvent: ToolCall = {
|
|
1467
|
+
type: 'tool-call',
|
|
1180
1468
|
toolCallId: approval.toolCallId,
|
|
1181
|
-
|
|
1182
|
-
|
|
1469
|
+
toolName: approval.toolName,
|
|
1470
|
+
input: approval.input,
|
|
1471
|
+
};
|
|
1472
|
+
const messages = prompt.messages as unknown as ModelMessage[];
|
|
1473
|
+
await telemetryDispatcher.onToolExecutionStart?.({
|
|
1474
|
+
toolCall: toolCallEvent,
|
|
1475
|
+
stepNumber: 0,
|
|
1476
|
+
messages,
|
|
1477
|
+
toolContext: resolvedContext,
|
|
1478
|
+
});
|
|
1479
|
+
const startTime = Date.now();
|
|
1480
|
+
const executeApprovedTool = () =>
|
|
1481
|
+
execute(approval.input, {
|
|
1482
|
+
toolCallId: approval.toolCallId,
|
|
1483
|
+
messages: [],
|
|
1484
|
+
context: resolvedContext,
|
|
1485
|
+
});
|
|
1486
|
+
const toolResult =
|
|
1487
|
+
telemetryDispatcher.executeTool != null
|
|
1488
|
+
? await telemetryDispatcher.executeTool({
|
|
1489
|
+
callId: 'workflow-agent',
|
|
1490
|
+
toolCallId: approval.toolCallId,
|
|
1491
|
+
execute: executeApprovedTool,
|
|
1492
|
+
})
|
|
1493
|
+
: await executeApprovedTool();
|
|
1494
|
+
await telemetryDispatcher.onToolExecutionEnd?.({
|
|
1495
|
+
toolCall: toolCallEvent,
|
|
1496
|
+
stepNumber: 0,
|
|
1497
|
+
durationMs: Date.now() - startTime,
|
|
1498
|
+
messages,
|
|
1499
|
+
toolContext: resolvedContext,
|
|
1500
|
+
success: true,
|
|
1501
|
+
output: toolResult,
|
|
1183
1502
|
});
|
|
1184
1503
|
toolResultContent.push({
|
|
1185
1504
|
type: 'tool-result' as const,
|
|
1186
1505
|
toolCallId: approval.toolCallId,
|
|
1187
1506
|
toolName: approval.toolName,
|
|
1188
|
-
output:
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1507
|
+
output: await createLanguageModelToolResultOutput({
|
|
1508
|
+
toolCallId: approval.toolCallId,
|
|
1509
|
+
toolName: approval.toolName,
|
|
1510
|
+
input: approval.input,
|
|
1511
|
+
output: toolResult,
|
|
1512
|
+
tool,
|
|
1513
|
+
errorMode: 'none',
|
|
1514
|
+
supportedUrls: {},
|
|
1515
|
+
download,
|
|
1516
|
+
}),
|
|
1517
|
+
});
|
|
1518
|
+
approvedRawResults.push({
|
|
1519
|
+
toolCallId: approval.toolCallId,
|
|
1520
|
+
toolName: approval.toolName,
|
|
1521
|
+
input: approval.input,
|
|
1522
|
+
output: toolResult,
|
|
1192
1523
|
});
|
|
1193
1524
|
} catch (error) {
|
|
1525
|
+
const errorMessage = getErrorMessage(error);
|
|
1526
|
+
await telemetryDispatcher.onToolExecutionEnd?.({
|
|
1527
|
+
toolCall: {
|
|
1528
|
+
type: 'tool-call',
|
|
1529
|
+
toolCallId: approval.toolCallId,
|
|
1530
|
+
toolName: approval.toolName,
|
|
1531
|
+
input: approval.input,
|
|
1532
|
+
},
|
|
1533
|
+
stepNumber: 0,
|
|
1534
|
+
durationMs: 0,
|
|
1535
|
+
messages: prompt.messages as unknown as ModelMessage[],
|
|
1536
|
+
toolContext: undefined,
|
|
1537
|
+
success: false,
|
|
1538
|
+
error,
|
|
1539
|
+
});
|
|
1194
1540
|
toolResultContent.push({
|
|
1195
1541
|
type: 'tool-result' as const,
|
|
1196
1542
|
toolCallId: approval.toolCallId,
|
|
1197
1543
|
toolName: approval.toolName,
|
|
1198
|
-
output: {
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1544
|
+
output: await createLanguageModelToolResultOutput({
|
|
1545
|
+
toolCallId: approval.toolCallId,
|
|
1546
|
+
toolName: approval.toolName,
|
|
1547
|
+
input: approval.input,
|
|
1548
|
+
output: errorMessage,
|
|
1549
|
+
tool,
|
|
1550
|
+
errorMode: 'text',
|
|
1551
|
+
supportedUrls: {},
|
|
1552
|
+
download,
|
|
1553
|
+
}),
|
|
1554
|
+
});
|
|
1555
|
+
approvedRawResults.push({
|
|
1556
|
+
toolCallId: approval.toolCallId,
|
|
1557
|
+
toolName: approval.toolName,
|
|
1558
|
+
input: approval.input,
|
|
1559
|
+
output: errorMessage,
|
|
1202
1560
|
});
|
|
1203
1561
|
}
|
|
1204
1562
|
}
|
|
@@ -1253,22 +1611,12 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1253
1611
|
// can transition approved/denied tool parts to the correct state
|
|
1254
1612
|
// and properly separate them from the subsequent model step.
|
|
1255
1613
|
if (options.writable && toolResultContent.length > 0) {
|
|
1256
|
-
const approvedResults = toolResultContent
|
|
1257
|
-
.filter(r => r.output.type !== 'execution-denied')
|
|
1258
|
-
.map(r => ({
|
|
1259
|
-
toolCallId: r.toolCallId,
|
|
1260
|
-
toolName: r.toolName,
|
|
1261
|
-
input: approvedToolApprovals.find(
|
|
1262
|
-
a => a.toolCallId === r.toolCallId,
|
|
1263
|
-
)?.input,
|
|
1264
|
-
output: 'value' in r.output ? r.output.value : undefined,
|
|
1265
|
-
}));
|
|
1266
1614
|
const deniedResults = toolResultContent
|
|
1267
1615
|
.filter(r => r.output.type === 'execution-denied')
|
|
1268
1616
|
.map(r => ({ toolCallId: r.toolCallId }));
|
|
1269
1617
|
await writeApprovalToolResults(
|
|
1270
1618
|
options.writable,
|
|
1271
|
-
|
|
1619
|
+
approvedRawResults,
|
|
1272
1620
|
deniedResults,
|
|
1273
1621
|
);
|
|
1274
1622
|
}
|
|
@@ -1277,14 +1625,12 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1277
1625
|
const modelPrompt = await convertToLanguageModelPrompt({
|
|
1278
1626
|
prompt,
|
|
1279
1627
|
supportedUrls: {},
|
|
1280
|
-
download
|
|
1628
|
+
download,
|
|
1281
1629
|
});
|
|
1282
1630
|
|
|
1283
1631
|
const effectiveAbortSignal = mergeAbortSignals(
|
|
1284
1632
|
options.abortSignal ?? effectiveGenerationSettings.abortSignal,
|
|
1285
|
-
options.timeout
|
|
1286
|
-
? AbortSignal.timeout(options.timeout)
|
|
1287
|
-
: undefined,
|
|
1633
|
+
options.timeout,
|
|
1288
1634
|
);
|
|
1289
1635
|
|
|
1290
1636
|
// Merge generation settings: constructor defaults < prepareCall < stream options
|
|
@@ -1321,52 +1667,59 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1321
1667
|
};
|
|
1322
1668
|
|
|
1323
1669
|
// Merge constructor + stream callbacks (constructor first, then stream)
|
|
1324
|
-
const
|
|
1325
|
-
this.
|
|
1326
|
-
|
|
|
1670
|
+
const mergedOnStepEnd = mergeCallbacks(
|
|
1671
|
+
this.constructorOnStepEnd as
|
|
1672
|
+
| WorkflowAgentOnStepEndCallback<TTools, TRuntimeContext>
|
|
1327
1673
|
| undefined,
|
|
1328
|
-
options.onStepFinish,
|
|
1674
|
+
options.onStepEnd ?? options.onStepFinish,
|
|
1329
1675
|
);
|
|
1330
|
-
const
|
|
1331
|
-
this.
|
|
1332
|
-
|
|
|
1676
|
+
const mergedOnEnd = mergeCallbacks(
|
|
1677
|
+
this.constructorOnEnd as
|
|
1678
|
+
| WorkflowAgentOnEndCallback<TTools, TRuntimeContext, OUTPUT>
|
|
1333
1679
|
| undefined,
|
|
1334
|
-
|
|
1680
|
+
onEnd,
|
|
1335
1681
|
);
|
|
1336
1682
|
const mergedOnStart = mergeCallbacks(
|
|
1337
|
-
this.constructorOnStart
|
|
1683
|
+
this.constructorOnStart as
|
|
1684
|
+
| WorkflowAgentOnStartCallback<TTools, TRuntimeContext>
|
|
1685
|
+
| undefined,
|
|
1338
1686
|
options.experimental_onStart,
|
|
1339
1687
|
);
|
|
1340
1688
|
const mergedOnStepStart = mergeCallbacks(
|
|
1341
|
-
this.constructorOnStepStart
|
|
1689
|
+
this.constructorOnStepStart as
|
|
1690
|
+
| WorkflowAgentOnStepStartCallback<TTools, TRuntimeContext>
|
|
1691
|
+
| undefined,
|
|
1342
1692
|
options.experimental_onStepStart,
|
|
1343
1693
|
);
|
|
1344
|
-
const
|
|
1345
|
-
this.
|
|
1346
|
-
options.
|
|
1694
|
+
const mergedOnToolExecutionStart = mergeCallbacks(
|
|
1695
|
+
this.constructorOnToolExecutionStart,
|
|
1696
|
+
options.onToolExecutionStart,
|
|
1347
1697
|
);
|
|
1348
|
-
const
|
|
1349
|
-
this.
|
|
1350
|
-
options.
|
|
1698
|
+
const mergedOnToolExecutionEnd = mergeCallbacks(
|
|
1699
|
+
this.constructorOnToolExecutionEnd,
|
|
1700
|
+
options.onToolExecutionEnd,
|
|
1351
1701
|
);
|
|
1352
1702
|
|
|
1353
1703
|
// Determine effective tool choice
|
|
1354
1704
|
const effectiveToolChoice = effectiveToolChoiceFromPrepare;
|
|
1355
1705
|
|
|
1356
|
-
// Merge telemetry settings
|
|
1357
|
-
const effectiveTelemetry = effectiveTelemetryFromPrepare;
|
|
1358
|
-
|
|
1359
1706
|
// Filter tools if activeTools is specified (stream-level overrides constructor default)
|
|
1360
1707
|
const effectiveActiveTools = options.activeTools ?? this.activeTools;
|
|
1361
1708
|
const effectiveTools =
|
|
1362
1709
|
effectiveActiveTools && effectiveActiveTools.length > 0
|
|
1363
|
-
?
|
|
1710
|
+
? (filterActiveTools({
|
|
1711
|
+
tools: this.tools,
|
|
1712
|
+
activeTools: effectiveActiveTools,
|
|
1713
|
+
}) ?? this.tools)
|
|
1364
1714
|
: this.tools;
|
|
1715
|
+
const effectiveModelInfo = getModelInfo(effectiveModel);
|
|
1365
1716
|
|
|
1366
1717
|
// Initialize context
|
|
1367
|
-
let
|
|
1718
|
+
let runtimeContext: TRuntimeContext = effectiveRuntimeContext;
|
|
1719
|
+
let toolsContext: Record<string, Context | undefined> =
|
|
1720
|
+
effectiveToolsContext;
|
|
1368
1721
|
|
|
1369
|
-
const steps: StepResult<TTools,
|
|
1722
|
+
const steps: StepResult<TTools, TRuntimeContext>[] = [];
|
|
1370
1723
|
|
|
1371
1724
|
// Track tool calls and results from the last step for the result
|
|
1372
1725
|
let lastStepToolCalls: ToolCall[] = [];
|
|
@@ -1377,17 +1730,45 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1377
1730
|
await mergedOnStart({
|
|
1378
1731
|
model: effectiveModel,
|
|
1379
1732
|
messages: prompt.messages,
|
|
1733
|
+
runtimeContext,
|
|
1734
|
+
toolsContext: toolsContext as unknown as InferToolSetContext<TTools>,
|
|
1380
1735
|
});
|
|
1381
1736
|
}
|
|
1737
|
+
await telemetryDispatcher.onStart?.({
|
|
1738
|
+
callId: 'workflow-agent',
|
|
1739
|
+
operationId: 'ai.workflowAgent.stream',
|
|
1740
|
+
provider: effectiveModelInfo.provider,
|
|
1741
|
+
modelId: effectiveModelInfo.modelId,
|
|
1742
|
+
system: undefined,
|
|
1743
|
+
messages: prompt.messages,
|
|
1744
|
+
tools: effectiveTools,
|
|
1745
|
+
toolChoice: effectiveToolChoice,
|
|
1746
|
+
activeTools: effectiveActiveTools as never,
|
|
1747
|
+
maxOutputTokens: mergedGenerationSettings.maxOutputTokens,
|
|
1748
|
+
temperature: mergedGenerationSettings.temperature,
|
|
1749
|
+
topP: mergedGenerationSettings.topP,
|
|
1750
|
+
topK: mergedGenerationSettings.topK,
|
|
1751
|
+
presencePenalty: mergedGenerationSettings.presencePenalty,
|
|
1752
|
+
frequencyPenalty: mergedGenerationSettings.frequencyPenalty,
|
|
1753
|
+
stopSequences: mergedGenerationSettings.stopSequences,
|
|
1754
|
+
seed: mergedGenerationSettings.seed,
|
|
1755
|
+
maxRetries: mergedGenerationSettings.maxRetries ?? 2,
|
|
1756
|
+
timeout: undefined,
|
|
1757
|
+
headers: mergedGenerationSettings.headers,
|
|
1758
|
+
providerOptions: mergedGenerationSettings.providerOptions,
|
|
1759
|
+
output: (options.output ?? this.output) as never,
|
|
1760
|
+
runtimeContext,
|
|
1761
|
+
toolsContext: toolsContext as unknown as InferToolSetContext<TTools>,
|
|
1762
|
+
});
|
|
1382
1763
|
|
|
1383
|
-
// Helper to wrap executeTool with
|
|
1764
|
+
// Helper to wrap executeTool with onToolExecutionStart/onToolExecutionEnd callbacks
|
|
1384
1765
|
const executeToolWithCallbacks = async (
|
|
1385
1766
|
toolCall: { toolCallId: string; toolName: string; input: unknown },
|
|
1386
1767
|
tools: ToolSet,
|
|
1387
1768
|
messages: LanguageModelV4Prompt,
|
|
1388
|
-
|
|
1769
|
+
perToolContexts: Record<string, Context | undefined>,
|
|
1389
1770
|
currentStepNumber: number = 0,
|
|
1390
|
-
): Promise<
|
|
1771
|
+
): Promise<WorkflowToolExecutionResult> => {
|
|
1391
1772
|
const toolCallEvent: ToolCall = {
|
|
1392
1773
|
type: 'tool-call',
|
|
1393
1774
|
toolCallId: toolCall.toolCallId,
|
|
@@ -1395,62 +1776,172 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1395
1776
|
input: toolCall.input,
|
|
1396
1777
|
};
|
|
1397
1778
|
|
|
1398
|
-
|
|
1399
|
-
|
|
1779
|
+
const tool = tools[toolCall.toolName];
|
|
1780
|
+
const resolvedContext = tool
|
|
1781
|
+
? await resolveToolContext({
|
|
1782
|
+
toolName: toolCall.toolName,
|
|
1783
|
+
tool,
|
|
1784
|
+
toolsContext: perToolContexts,
|
|
1785
|
+
})
|
|
1786
|
+
: undefined;
|
|
1787
|
+
const modelMessages = getToolCallbackMessages(messages);
|
|
1788
|
+
|
|
1789
|
+
if (mergedOnToolExecutionStart) {
|
|
1790
|
+
await mergedOnToolExecutionStart({
|
|
1400
1791
|
toolCall: toolCallEvent,
|
|
1401
1792
|
stepNumber: currentStepNumber,
|
|
1793
|
+
messages: modelMessages,
|
|
1794
|
+
toolContext: resolvedContext as
|
|
1795
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
1796
|
+
| undefined,
|
|
1402
1797
|
});
|
|
1403
1798
|
}
|
|
1799
|
+
await telemetryDispatcher.onToolExecutionStart?.({
|
|
1800
|
+
toolCall: toolCallEvent,
|
|
1801
|
+
stepNumber: currentStepNumber,
|
|
1802
|
+
messages: modelMessages,
|
|
1803
|
+
toolContext: resolvedContext as
|
|
1804
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
1805
|
+
| undefined,
|
|
1806
|
+
});
|
|
1404
1807
|
|
|
1405
1808
|
const startTime = Date.now();
|
|
1406
|
-
let result:
|
|
1809
|
+
let result: WorkflowToolExecutionResult;
|
|
1407
1810
|
try {
|
|
1408
|
-
|
|
1811
|
+
const execute = () =>
|
|
1812
|
+
executeTool(toolCall, tools, messages, resolvedContext, download);
|
|
1813
|
+
result =
|
|
1814
|
+
telemetryDispatcher.executeTool != null
|
|
1815
|
+
? await telemetryDispatcher.executeTool({
|
|
1816
|
+
callId: 'workflow-agent',
|
|
1817
|
+
toolCallId: toolCall.toolCallId,
|
|
1818
|
+
execute,
|
|
1819
|
+
})
|
|
1820
|
+
: await execute();
|
|
1409
1821
|
} catch (err) {
|
|
1410
1822
|
const durationMs = Date.now() - startTime;
|
|
1411
|
-
if (
|
|
1412
|
-
await
|
|
1823
|
+
if (mergedOnToolExecutionEnd) {
|
|
1824
|
+
await mergedOnToolExecutionEnd({
|
|
1413
1825
|
toolCall: toolCallEvent,
|
|
1414
1826
|
stepNumber: currentStepNumber,
|
|
1415
1827
|
durationMs,
|
|
1828
|
+
messages: modelMessages,
|
|
1829
|
+
toolContext: resolvedContext as
|
|
1830
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
1831
|
+
| undefined,
|
|
1416
1832
|
success: false,
|
|
1417
1833
|
error: err,
|
|
1418
1834
|
});
|
|
1419
1835
|
}
|
|
1836
|
+
await telemetryDispatcher.onToolExecutionEnd?.({
|
|
1837
|
+
toolCall: toolCallEvent,
|
|
1838
|
+
stepNumber: currentStepNumber,
|
|
1839
|
+
durationMs,
|
|
1840
|
+
messages: modelMessages,
|
|
1841
|
+
toolContext: resolvedContext as
|
|
1842
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
1843
|
+
| undefined,
|
|
1844
|
+
success: false,
|
|
1845
|
+
error: err,
|
|
1846
|
+
});
|
|
1420
1847
|
throw err;
|
|
1421
1848
|
}
|
|
1422
1849
|
|
|
1423
1850
|
const durationMs = Date.now() - startTime;
|
|
1424
|
-
if (
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
'type' in result.output &&
|
|
1428
|
-
(result.output.type === 'error-text' ||
|
|
1429
|
-
result.output.type === 'error-json');
|
|
1430
|
-
if (isError) {
|
|
1431
|
-
await mergedOnToolCallFinish({
|
|
1851
|
+
if (mergedOnToolExecutionEnd) {
|
|
1852
|
+
if (result.isError) {
|
|
1853
|
+
await mergedOnToolExecutionEnd({
|
|
1432
1854
|
toolCall: toolCallEvent,
|
|
1433
1855
|
stepNumber: currentStepNumber,
|
|
1434
1856
|
durationMs,
|
|
1857
|
+
messages: modelMessages,
|
|
1858
|
+
toolContext: resolvedContext as
|
|
1859
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
1860
|
+
| undefined,
|
|
1435
1861
|
success: false,
|
|
1436
|
-
error:
|
|
1862
|
+
error: result.rawOutput,
|
|
1437
1863
|
});
|
|
1438
1864
|
} else {
|
|
1439
|
-
await
|
|
1865
|
+
await mergedOnToolExecutionEnd({
|
|
1440
1866
|
toolCall: toolCallEvent,
|
|
1441
1867
|
stepNumber: currentStepNumber,
|
|
1442
1868
|
durationMs,
|
|
1869
|
+
messages: modelMessages,
|
|
1870
|
+
toolContext: resolvedContext as
|
|
1871
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
1872
|
+
| undefined,
|
|
1443
1873
|
success: true,
|
|
1444
|
-
output:
|
|
1445
|
-
result.output && 'value' in result.output
|
|
1446
|
-
? result.output.value
|
|
1447
|
-
: undefined,
|
|
1874
|
+
output: result.rawOutput,
|
|
1448
1875
|
});
|
|
1449
1876
|
}
|
|
1450
1877
|
}
|
|
1878
|
+
if (result.isError) {
|
|
1879
|
+
await telemetryDispatcher.onToolExecutionEnd?.({
|
|
1880
|
+
toolCall: toolCallEvent,
|
|
1881
|
+
stepNumber: currentStepNumber,
|
|
1882
|
+
durationMs,
|
|
1883
|
+
messages: modelMessages,
|
|
1884
|
+
toolContext: resolvedContext as
|
|
1885
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
1886
|
+
| undefined,
|
|
1887
|
+
success: false,
|
|
1888
|
+
error: result.rawOutput,
|
|
1889
|
+
});
|
|
1890
|
+
} else {
|
|
1891
|
+
await telemetryDispatcher.onToolExecutionEnd?.({
|
|
1892
|
+
toolCall: toolCallEvent,
|
|
1893
|
+
stepNumber: currentStepNumber,
|
|
1894
|
+
durationMs,
|
|
1895
|
+
messages: modelMessages,
|
|
1896
|
+
toolContext: resolvedContext as
|
|
1897
|
+
| InferToolSetContext<TTools>[keyof InferToolSetContext<TTools>]
|
|
1898
|
+
| undefined,
|
|
1899
|
+
success: true,
|
|
1900
|
+
output: result.rawOutput,
|
|
1901
|
+
});
|
|
1902
|
+
}
|
|
1451
1903
|
return result;
|
|
1452
1904
|
};
|
|
1453
1905
|
|
|
1906
|
+
const recordProviderExecutedToolTelemetry = async (
|
|
1907
|
+
toolCall: { toolCallId: string; toolName: string; input: unknown },
|
|
1908
|
+
result: WorkflowToolExecutionResult,
|
|
1909
|
+
messages: LanguageModelV4Prompt,
|
|
1910
|
+
currentStepNumber: number,
|
|
1911
|
+
) => {
|
|
1912
|
+
const toolCallEvent: ToolCall = {
|
|
1913
|
+
type: 'tool-call',
|
|
1914
|
+
toolCallId: toolCall.toolCallId,
|
|
1915
|
+
toolName: toolCall.toolName,
|
|
1916
|
+
input: toolCall.input,
|
|
1917
|
+
};
|
|
1918
|
+
const modelMessages = getToolCallbackMessages(messages);
|
|
1919
|
+
|
|
1920
|
+
await telemetryDispatcher.onToolExecutionStart?.({
|
|
1921
|
+
toolCall: toolCallEvent,
|
|
1922
|
+
stepNumber: currentStepNumber,
|
|
1923
|
+
messages: modelMessages,
|
|
1924
|
+
toolContext: undefined,
|
|
1925
|
+
});
|
|
1926
|
+
|
|
1927
|
+
await telemetryDispatcher.onToolExecutionEnd?.({
|
|
1928
|
+
toolCall: toolCallEvent,
|
|
1929
|
+
stepNumber: currentStepNumber,
|
|
1930
|
+
durationMs: 0,
|
|
1931
|
+
messages: modelMessages,
|
|
1932
|
+
toolContext: undefined,
|
|
1933
|
+
...(result.isError
|
|
1934
|
+
? {
|
|
1935
|
+
success: false as const,
|
|
1936
|
+
error: result.rawOutput,
|
|
1937
|
+
}
|
|
1938
|
+
: {
|
|
1939
|
+
success: true as const,
|
|
1940
|
+
output: result.rawOutput,
|
|
1941
|
+
}),
|
|
1942
|
+
});
|
|
1943
|
+
};
|
|
1944
|
+
|
|
1454
1945
|
// Check for abort before starting
|
|
1455
1946
|
if (mergedGenerationSettings.abortSignal?.aborted) {
|
|
1456
1947
|
if (options.onAbort) {
|
|
@@ -1471,17 +1962,19 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1471
1962
|
writable: options.writable,
|
|
1472
1963
|
prompt: modelPrompt,
|
|
1473
1964
|
stopConditions: options.stopWhen ?? this.stopWhen,
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
onStepStart: mergedOnStepStart,
|
|
1965
|
+
|
|
1966
|
+
onStepEnd: mergedOnStepEnd as any,
|
|
1967
|
+
onStepStart: mergedOnStepStart as any,
|
|
1477
1968
|
onError: options.onError,
|
|
1478
|
-
prepareStep:
|
|
1479
|
-
|
|
1480
|
-
|
|
1969
|
+
prepareStep: (options.prepareStep ??
|
|
1970
|
+
(this.prepareStep as
|
|
1971
|
+
| PrepareStepCallback<ToolSet, TRuntimeContext>
|
|
1972
|
+
| undefined)) as any,
|
|
1481
1973
|
generationSettings: mergedGenerationSettings,
|
|
1482
1974
|
toolChoice: effectiveToolChoice as ToolChoice<ToolSet>,
|
|
1483
|
-
|
|
1484
|
-
|
|
1975
|
+
runtimeContext,
|
|
1976
|
+
toolsContext,
|
|
1977
|
+
telemetry: effectiveTelemetry,
|
|
1485
1978
|
includeRawChunks: options.includeRawChunks ?? false,
|
|
1486
1979
|
repairToolCall: (options.experimental_repairToolCall ??
|
|
1487
1980
|
this.experimentalRepairToolCall) as
|
|
@@ -1511,25 +2004,34 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1511
2004
|
toolCalls,
|
|
1512
2005
|
messages: iterMessages,
|
|
1513
2006
|
step,
|
|
1514
|
-
|
|
2007
|
+
runtimeContext: yieldedRuntimeContext,
|
|
2008
|
+
toolsContext: yieldedToolsContext,
|
|
1515
2009
|
providerExecutedToolResults,
|
|
1516
2010
|
} = result.value;
|
|
1517
2011
|
// Capture current step number before pushing (0-based)
|
|
1518
2012
|
const currentStepNumber = steps.length;
|
|
1519
2013
|
if (step) {
|
|
1520
|
-
steps.push(step as unknown as StepResult<TTools,
|
|
2014
|
+
steps.push(step as unknown as StepResult<TTools, TRuntimeContext>);
|
|
1521
2015
|
}
|
|
1522
|
-
if (
|
|
1523
|
-
|
|
2016
|
+
if (yieldedRuntimeContext !== undefined) {
|
|
2017
|
+
runtimeContext = yieldedRuntimeContext as TRuntimeContext;
|
|
2018
|
+
}
|
|
2019
|
+
if (yieldedToolsContext !== undefined) {
|
|
2020
|
+
toolsContext = yieldedToolsContext;
|
|
1524
2021
|
}
|
|
1525
2022
|
|
|
1526
2023
|
// Only execute tools if there are tool calls
|
|
1527
2024
|
if (toolCalls.length > 0) {
|
|
2025
|
+
const invalidToolCalls = toolCalls.filter(tc => tc.invalid === true);
|
|
2026
|
+
const validToolCalls = toolCalls.filter(tc => tc.invalid !== true);
|
|
2027
|
+
|
|
1528
2028
|
// Separate provider-executed tool calls from client-executed ones
|
|
1529
|
-
const nonProviderToolCalls =
|
|
2029
|
+
const nonProviderToolCalls = validToolCalls.filter(
|
|
1530
2030
|
tc => !tc.providerExecuted,
|
|
1531
2031
|
);
|
|
1532
|
-
const providerToolCalls =
|
|
2032
|
+
const providerToolCalls = validToolCalls.filter(
|
|
2033
|
+
tc => tc.providerExecuted,
|
|
2034
|
+
);
|
|
1533
2035
|
|
|
1534
2036
|
// Check which tools need approval (can be async)
|
|
1535
2037
|
const approvalNeeded = await Promise.all(
|
|
@@ -1539,10 +2041,16 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1539
2041
|
if (tool.needsApproval == null) return false;
|
|
1540
2042
|
if (typeof tool.needsApproval === 'boolean')
|
|
1541
2043
|
return tool.needsApproval;
|
|
2044
|
+
const resolvedContext = await resolveToolContext({
|
|
2045
|
+
toolName: tc.toolName,
|
|
2046
|
+
tool,
|
|
2047
|
+
toolsContext:
|
|
2048
|
+
toolsContext as unknown as InferToolSetContext<TTools>,
|
|
2049
|
+
});
|
|
1542
2050
|
return tool.needsApproval(tc.input, {
|
|
1543
2051
|
toolCallId: tc.toolCallId,
|
|
1544
2052
|
messages: iterMessages as unknown as ModelMessage[],
|
|
1545
|
-
context:
|
|
2053
|
+
context: resolvedContext,
|
|
1546
2054
|
});
|
|
1547
2055
|
}),
|
|
1548
2056
|
);
|
|
@@ -1573,27 +2081,49 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1573
2081
|
// Execute any executable tools that were also called in this step
|
|
1574
2082
|
const executableResults = await Promise.all(
|
|
1575
2083
|
executableToolCalls.map(
|
|
1576
|
-
(toolCall): Promise<
|
|
2084
|
+
(toolCall): Promise<WorkflowToolExecutionResult> =>
|
|
1577
2085
|
executeToolWithCallbacks(
|
|
1578
2086
|
toolCall,
|
|
1579
2087
|
effectiveTools as ToolSet,
|
|
1580
2088
|
iterMessages,
|
|
1581
|
-
|
|
2089
|
+
toolsContext,
|
|
1582
2090
|
currentStepNumber,
|
|
1583
2091
|
),
|
|
1584
2092
|
),
|
|
1585
2093
|
);
|
|
1586
2094
|
|
|
1587
2095
|
// Collect provider tool results
|
|
1588
|
-
const providerResults:
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
2096
|
+
const providerResults: WorkflowToolExecutionResult[] =
|
|
2097
|
+
await Promise.all(
|
|
2098
|
+
providerToolCalls.map(toolCall =>
|
|
2099
|
+
resolveProviderToolResult(
|
|
2100
|
+
toolCall,
|
|
2101
|
+
providerExecutedToolResults,
|
|
2102
|
+
effectiveTools as ToolSet,
|
|
2103
|
+
download,
|
|
2104
|
+
),
|
|
1593
2105
|
),
|
|
1594
2106
|
);
|
|
2107
|
+
await Promise.all(
|
|
2108
|
+
providerToolCalls.map((toolCall, index) =>
|
|
2109
|
+
recordProviderExecutedToolTelemetry(
|
|
2110
|
+
toolCall,
|
|
2111
|
+
providerResults[index],
|
|
2112
|
+
iterMessages,
|
|
2113
|
+
currentStepNumber,
|
|
2114
|
+
),
|
|
2115
|
+
),
|
|
2116
|
+
);
|
|
1595
2117
|
|
|
1596
|
-
const
|
|
2118
|
+
const continuationInvalidResults = invalidToolCalls.map(
|
|
2119
|
+
createInvalidToolResult,
|
|
2120
|
+
);
|
|
2121
|
+
const resolvedResults: LanguageModelV4ToolResultPart[] = [
|
|
2122
|
+
...executableResults.map(result => result.modelResult),
|
|
2123
|
+
...providerResults.map(result => result.modelResult),
|
|
2124
|
+
...continuationInvalidResults,
|
|
2125
|
+
];
|
|
2126
|
+
const executedResults = [...executableResults, ...providerResults];
|
|
1597
2127
|
|
|
1598
2128
|
const allToolCalls: ToolCall[] = toolCalls.map(tc => ({
|
|
1599
2129
|
type: 'tool-call' as const,
|
|
@@ -1602,13 +2132,14 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1602
2132
|
input: tc.input,
|
|
1603
2133
|
}));
|
|
1604
2134
|
|
|
1605
|
-
const allToolResults: ToolResult[] =
|
|
2135
|
+
const allToolResults: ToolResult[] = executedResults.map(r => ({
|
|
1606
2136
|
type: 'tool-result' as const,
|
|
1607
|
-
toolCallId: r.toolCallId,
|
|
1608
|
-
toolName: r.toolName,
|
|
1609
|
-
input: toolCalls.find(
|
|
1610
|
-
|
|
1611
|
-
|
|
2137
|
+
toolCallId: r.modelResult.toolCallId,
|
|
2138
|
+
toolName: r.modelResult.toolName,
|
|
2139
|
+
input: toolCalls.find(
|
|
2140
|
+
tc => tc.toolCallId === r.modelResult.toolCallId,
|
|
2141
|
+
)?.input,
|
|
2142
|
+
output: r.rawOutput,
|
|
1612
2143
|
}));
|
|
1613
2144
|
|
|
1614
2145
|
if (resolvedResults.length > 0) {
|
|
@@ -1620,18 +2151,33 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1620
2151
|
|
|
1621
2152
|
const messages = iterMessages as unknown as ModelMessage[];
|
|
1622
2153
|
|
|
1623
|
-
if (
|
|
2154
|
+
if (mergedOnEnd && !wasAborted) {
|
|
1624
2155
|
const lastStep = steps[steps.length - 1];
|
|
1625
|
-
|
|
2156
|
+
const totalUsage = aggregateUsage(steps);
|
|
2157
|
+
await mergedOnEnd({
|
|
1626
2158
|
steps,
|
|
1627
2159
|
messages,
|
|
1628
2160
|
text: lastStep?.text ?? '',
|
|
1629
2161
|
finishReason: lastStep?.finishReason ?? 'other',
|
|
1630
|
-
|
|
1631
|
-
|
|
2162
|
+
usage: totalUsage,
|
|
2163
|
+
totalUsage,
|
|
2164
|
+
runtimeContext,
|
|
2165
|
+
toolsContext:
|
|
2166
|
+
toolsContext as unknown as InferToolSetContext<TTools>,
|
|
1632
2167
|
output: undefined as OUTPUT,
|
|
1633
2168
|
});
|
|
1634
2169
|
}
|
|
2170
|
+
if (!wasAborted && steps.length > 0) {
|
|
2171
|
+
const telemetrySteps = steps.map(normalizeStepForTelemetry);
|
|
2172
|
+
const lastStep = telemetrySteps[telemetrySteps.length - 1];
|
|
2173
|
+
const totalUsage = aggregateUsage(steps);
|
|
2174
|
+
await telemetryDispatcher.onEnd?.({
|
|
2175
|
+
...lastStep,
|
|
2176
|
+
steps: telemetrySteps,
|
|
2177
|
+
usage: totalUsage,
|
|
2178
|
+
totalUsage,
|
|
2179
|
+
});
|
|
2180
|
+
}
|
|
1635
2181
|
|
|
1636
2182
|
// Emit tool-approval-request chunks for tools that need approval
|
|
1637
2183
|
// so useChat can show the approval UI
|
|
@@ -1674,40 +2220,67 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1674
2220
|
// Execute client tools (all have execute functions at this point)
|
|
1675
2221
|
const clientToolResults = await Promise.all(
|
|
1676
2222
|
nonProviderToolCalls.map(
|
|
1677
|
-
(toolCall): Promise<
|
|
2223
|
+
(toolCall): Promise<WorkflowToolExecutionResult> =>
|
|
1678
2224
|
executeToolWithCallbacks(
|
|
1679
2225
|
toolCall,
|
|
1680
2226
|
effectiveTools as ToolSet,
|
|
1681
2227
|
iterMessages,
|
|
1682
|
-
|
|
2228
|
+
toolsContext,
|
|
1683
2229
|
currentStepNumber,
|
|
1684
2230
|
),
|
|
1685
2231
|
),
|
|
1686
2232
|
);
|
|
1687
2233
|
|
|
1688
2234
|
// For provider-executed tools, use the results from the stream
|
|
1689
|
-
const providerToolResults:
|
|
1690
|
-
|
|
1691
|
-
|
|
2235
|
+
const providerToolResults: WorkflowToolExecutionResult[] =
|
|
2236
|
+
await Promise.all(
|
|
2237
|
+
providerToolCalls.map(toolCall =>
|
|
2238
|
+
resolveProviderToolResult(
|
|
2239
|
+
toolCall,
|
|
2240
|
+
providerExecutedToolResults,
|
|
2241
|
+
effectiveTools as ToolSet,
|
|
2242
|
+
download,
|
|
2243
|
+
),
|
|
2244
|
+
),
|
|
1692
2245
|
);
|
|
2246
|
+
await Promise.all(
|
|
2247
|
+
providerToolCalls.map((toolCall, index) =>
|
|
2248
|
+
recordProviderExecutedToolTelemetry(
|
|
2249
|
+
toolCall,
|
|
2250
|
+
providerToolResults[index],
|
|
2251
|
+
iterMessages,
|
|
2252
|
+
currentStepNumber,
|
|
2253
|
+
),
|
|
2254
|
+
),
|
|
2255
|
+
);
|
|
2256
|
+
const continuationInvalidToolResults = invalidToolCalls.map(
|
|
2257
|
+
createInvalidToolResult,
|
|
2258
|
+
);
|
|
1693
2259
|
|
|
1694
|
-
// Combine results in the original order
|
|
1695
|
-
|
|
2260
|
+
// Combine executable/provider results in the original order,
|
|
2261
|
+
// while preserving invalid tool calls as error results for the
|
|
2262
|
+
// next model step without emitting them as synthetic UI success.
|
|
2263
|
+
const executedToolResults = toolCalls.flatMap(tc => {
|
|
1696
2264
|
const clientResult = clientToolResults.find(
|
|
1697
|
-
r => r.toolCallId === tc.toolCallId,
|
|
2265
|
+
r => r.modelResult.toolCallId === tc.toolCallId,
|
|
1698
2266
|
);
|
|
1699
|
-
if (clientResult) return clientResult;
|
|
2267
|
+
if (clientResult) return [clientResult];
|
|
1700
2268
|
const providerResult = providerToolResults.find(
|
|
2269
|
+
r => r.modelResult.toolCallId === tc.toolCallId,
|
|
2270
|
+
);
|
|
2271
|
+
if (providerResult) return [providerResult];
|
|
2272
|
+
return [];
|
|
2273
|
+
});
|
|
2274
|
+
const continuationToolResults = toolCalls.flatMap(tc => {
|
|
2275
|
+
const invalidResult = continuationInvalidToolResults.find(
|
|
1701
2276
|
r => r.toolCallId === tc.toolCallId,
|
|
1702
2277
|
);
|
|
1703
|
-
if (
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
output: { type: 'text' as const, value: '' },
|
|
1710
|
-
};
|
|
2278
|
+
if (invalidResult) return [invalidResult];
|
|
2279
|
+
const executedResult = executedToolResults.find(
|
|
2280
|
+
r => r.modelResult.toolCallId === tc.toolCallId,
|
|
2281
|
+
);
|
|
2282
|
+
if (executedResult) return [executedResult.modelResult];
|
|
2283
|
+
return [];
|
|
1711
2284
|
});
|
|
1712
2285
|
|
|
1713
2286
|
// Write tool results and step boundaries to the stream so the
|
|
@@ -1716,12 +2289,13 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1716
2289
|
if (options.writable) {
|
|
1717
2290
|
await writeToolResultsWithStepBoundary(
|
|
1718
2291
|
options.writable,
|
|
1719
|
-
|
|
1720
|
-
toolCallId: r.toolCallId,
|
|
1721
|
-
toolName: r.toolName,
|
|
1722
|
-
input: toolCalls.find(
|
|
1723
|
-
|
|
1724
|
-
|
|
2292
|
+
executedToolResults.map(r => ({
|
|
2293
|
+
toolCallId: r.modelResult.toolCallId,
|
|
2294
|
+
toolName: r.modelResult.toolName,
|
|
2295
|
+
input: toolCalls.find(
|
|
2296
|
+
tc => tc.toolCallId === r.modelResult.toolCallId,
|
|
2297
|
+
)?.input,
|
|
2298
|
+
output: r.rawOutput,
|
|
1725
2299
|
})),
|
|
1726
2300
|
);
|
|
1727
2301
|
}
|
|
@@ -1733,15 +2307,17 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1733
2307
|
toolName: tc.toolName,
|
|
1734
2308
|
input: tc.input,
|
|
1735
2309
|
}));
|
|
1736
|
-
lastStepToolResults =
|
|
2310
|
+
lastStepToolResults = executedToolResults.map(r => ({
|
|
1737
2311
|
type: 'tool-result' as const,
|
|
1738
|
-
toolCallId: r.toolCallId,
|
|
1739
|
-
toolName: r.toolName,
|
|
1740
|
-
input: toolCalls.find(
|
|
1741
|
-
|
|
2312
|
+
toolCallId: r.modelResult.toolCallId,
|
|
2313
|
+
toolName: r.modelResult.toolName,
|
|
2314
|
+
input: toolCalls.find(
|
|
2315
|
+
tc => tc.toolCallId === r.modelResult.toolCallId,
|
|
2316
|
+
)?.input,
|
|
2317
|
+
output: r.rawOutput,
|
|
1742
2318
|
}));
|
|
1743
2319
|
|
|
1744
|
-
result = await iterator.next(
|
|
2320
|
+
result = await iterator.next(continuationToolResults);
|
|
1745
2321
|
} else {
|
|
1746
2322
|
// Final step with no tool calls - reset tracking
|
|
1747
2323
|
lastStepToolCalls = [];
|
|
@@ -1766,7 +2342,8 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1766
2342
|
// Call onError for non-abort errors (including tool execution errors)
|
|
1767
2343
|
await options.onError({ error });
|
|
1768
2344
|
}
|
|
1769
|
-
|
|
2345
|
+
await telemetryDispatcher.onError?.(error);
|
|
2346
|
+
// Don't throw yet - we want to call onEnd first
|
|
1770
2347
|
}
|
|
1771
2348
|
|
|
1772
2349
|
// Use the final messages from the iterator, or fall back to standardized messages
|
|
@@ -1799,19 +2376,33 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1799
2376
|
}
|
|
1800
2377
|
}
|
|
1801
2378
|
|
|
1802
|
-
// Call
|
|
1803
|
-
if (
|
|
2379
|
+
// Call onEnd callback if provided (always call, even on errors, but not on abort)
|
|
2380
|
+
if (mergedOnEnd && !wasAborted) {
|
|
1804
2381
|
const lastStep = steps[steps.length - 1];
|
|
1805
|
-
|
|
2382
|
+
const totalUsage = aggregateUsage(steps);
|
|
2383
|
+
await mergedOnEnd({
|
|
1806
2384
|
steps,
|
|
1807
2385
|
messages: messages as ModelMessage[],
|
|
1808
2386
|
text: lastStep?.text ?? '',
|
|
1809
2387
|
finishReason: lastStep?.finishReason ?? 'other',
|
|
1810
|
-
|
|
1811
|
-
|
|
2388
|
+
usage: totalUsage,
|
|
2389
|
+
totalUsage,
|
|
2390
|
+
runtimeContext,
|
|
2391
|
+
toolsContext: toolsContext as unknown as InferToolSetContext<TTools>,
|
|
1812
2392
|
output: experimentalOutput,
|
|
1813
2393
|
});
|
|
1814
2394
|
}
|
|
2395
|
+
if (!wasAborted && steps.length > 0) {
|
|
2396
|
+
const telemetrySteps = steps.map(normalizeStepForTelemetry);
|
|
2397
|
+
const lastStep = telemetrySteps[telemetrySteps.length - 1];
|
|
2398
|
+
const totalUsage = aggregateUsage(steps);
|
|
2399
|
+
await telemetryDispatcher.onEnd?.({
|
|
2400
|
+
...lastStep,
|
|
2401
|
+
steps: telemetrySteps,
|
|
2402
|
+
usage: totalUsage,
|
|
2403
|
+
totalUsage,
|
|
2404
|
+
});
|
|
2405
|
+
}
|
|
1815
2406
|
|
|
1816
2407
|
// Re-throw any error that occurred
|
|
1817
2408
|
if (encounteredError) {
|
|
@@ -1845,6 +2436,25 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
|
|
|
1845
2436
|
}
|
|
1846
2437
|
}
|
|
1847
2438
|
|
|
2439
|
+
function getModelInfo(model: LanguageModel): {
|
|
2440
|
+
provider: string;
|
|
2441
|
+
modelId: string;
|
|
2442
|
+
} {
|
|
2443
|
+
return typeof model === 'string'
|
|
2444
|
+
? { provider: model.split('/')[0] ?? 'gateway', modelId: model }
|
|
2445
|
+
: { provider: model.provider, modelId: model.modelId };
|
|
2446
|
+
}
|
|
2447
|
+
|
|
2448
|
+
function normalizeStepForTelemetry<
|
|
2449
|
+
TOOLS extends ToolSet,
|
|
2450
|
+
RUNTIME_CONTEXT extends Context,
|
|
2451
|
+
>(step: StepResult<TOOLS, RUNTIME_CONTEXT>) {
|
|
2452
|
+
return {
|
|
2453
|
+
...step,
|
|
2454
|
+
model: step.model ?? { provider: 'unknown', modelId: 'unknown' },
|
|
2455
|
+
};
|
|
2456
|
+
}
|
|
2457
|
+
|
|
1848
2458
|
/**
|
|
1849
2459
|
* Filter tools to only include the specified active tools.
|
|
1850
2460
|
*/
|
|
@@ -1965,6 +2575,33 @@ async function writeApprovalToolResults(
|
|
|
1965
2575
|
}
|
|
1966
2576
|
}
|
|
1967
2577
|
|
|
2578
|
+
/**
|
|
2579
|
+
* Resolve the per-tool context that gets passed into a tool's `execute`
|
|
2580
|
+
* (and `needsApproval`) function. When the tool declares a `contextSchema`,
|
|
2581
|
+
* the entry is validated against it.
|
|
2582
|
+
*/
|
|
2583
|
+
async function resolveToolContext({
|
|
2584
|
+
toolName,
|
|
2585
|
+
tool,
|
|
2586
|
+
toolsContext,
|
|
2587
|
+
}: {
|
|
2588
|
+
toolName: string;
|
|
2589
|
+
tool: ToolSet[string];
|
|
2590
|
+
toolsContext: Record<string, Context | undefined> | undefined;
|
|
2591
|
+
}): Promise<unknown> {
|
|
2592
|
+
const contextSchema = (tool as { contextSchema?: unknown }).contextSchema;
|
|
2593
|
+
const entry = toolsContext?.[toolName];
|
|
2594
|
+
if (contextSchema == null) {
|
|
2595
|
+
return entry;
|
|
2596
|
+
}
|
|
2597
|
+
|
|
2598
|
+
return await validateTypes({
|
|
2599
|
+
value: entry,
|
|
2600
|
+
schema: contextSchema as Parameters<typeof validateTypes>[0]['schema'],
|
|
2601
|
+
context: { field: 'tool context', entityName: toolName },
|
|
2602
|
+
});
|
|
2603
|
+
}
|
|
2604
|
+
|
|
1968
2605
|
function aggregateUsage(steps: StepResult<any, any>[]): LanguageModelUsage {
|
|
1969
2606
|
let inputTokens = 0;
|
|
1970
2607
|
let outputTokens = 0;
|
|
@@ -1979,43 +2616,15 @@ function aggregateUsage(steps: StepResult<any, any>[]): LanguageModelUsage {
|
|
|
1979
2616
|
} as LanguageModelUsage;
|
|
1980
2617
|
}
|
|
1981
2618
|
|
|
1982
|
-
function
|
|
1983
|
-
|
|
1984
|
-
activeTools: string[],
|
|
1985
|
-
): ToolSet {
|
|
1986
|
-
const filtered: ToolSet = {};
|
|
1987
|
-
for (const toolName of activeTools) {
|
|
1988
|
-
if (toolName in tools) {
|
|
1989
|
-
filtered[toolName] = tools[toolName];
|
|
1990
|
-
}
|
|
1991
|
-
}
|
|
1992
|
-
return filtered;
|
|
1993
|
-
}
|
|
1994
|
-
|
|
1995
|
-
// Matches AI SDK's getErrorMessage from @ai-sdk/provider-utils
|
|
1996
|
-
function getErrorMessage(error: unknown): string {
|
|
1997
|
-
if (error == null) {
|
|
1998
|
-
return 'unknown error';
|
|
1999
|
-
}
|
|
2000
|
-
|
|
2001
|
-
if (typeof error === 'string') {
|
|
2002
|
-
return error;
|
|
2003
|
-
}
|
|
2004
|
-
|
|
2005
|
-
if (error instanceof Error) {
|
|
2006
|
-
return error.message;
|
|
2007
|
-
}
|
|
2008
|
-
|
|
2009
|
-
return JSON.stringify(error);
|
|
2010
|
-
}
|
|
2011
|
-
|
|
2012
|
-
function resolveProviderToolResult(
|
|
2013
|
-
toolCall: { toolCallId: string; toolName: string },
|
|
2619
|
+
async function resolveProviderToolResult(
|
|
2620
|
+
toolCall: { toolCallId: string; toolName: string; input: unknown },
|
|
2014
2621
|
providerExecutedToolResults?: Map<
|
|
2015
2622
|
string,
|
|
2016
2623
|
{ toolCallId: string; toolName: string; result: unknown; isError?: boolean }
|
|
2017
2624
|
>,
|
|
2018
|
-
|
|
2625
|
+
tools?: ToolSet,
|
|
2626
|
+
download?: DownloadFunction,
|
|
2627
|
+
): Promise<WorkflowToolExecutionResult> {
|
|
2019
2628
|
const streamResult = providerExecutedToolResults?.get(toolCall.toolCallId);
|
|
2020
2629
|
if (!streamResult) {
|
|
2021
2630
|
console.warn(
|
|
@@ -2023,45 +2632,79 @@ function resolveProviderToolResult(
|
|
|
2023
2632
|
`did not receive a result from the stream. This may indicate a provider issue.`,
|
|
2024
2633
|
);
|
|
2025
2634
|
return {
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2635
|
+
modelResult: {
|
|
2636
|
+
type: 'tool-result' as const,
|
|
2637
|
+
toolCallId: toolCall.toolCallId,
|
|
2638
|
+
toolName: toolCall.toolName,
|
|
2639
|
+
output: {
|
|
2640
|
+
type: 'text' as const,
|
|
2641
|
+
value: '',
|
|
2642
|
+
},
|
|
2032
2643
|
},
|
|
2644
|
+
rawOutput: '',
|
|
2645
|
+
isError: false,
|
|
2033
2646
|
};
|
|
2034
2647
|
}
|
|
2035
2648
|
|
|
2036
2649
|
const result = streamResult.result;
|
|
2037
|
-
const
|
|
2650
|
+
const errorMode = streamResult.isError
|
|
2651
|
+
? typeof result === 'string'
|
|
2652
|
+
? 'text'
|
|
2653
|
+
: 'json'
|
|
2654
|
+
: 'none';
|
|
2655
|
+
|
|
2656
|
+
return {
|
|
2657
|
+
modelResult: {
|
|
2658
|
+
type: 'tool-result' as const,
|
|
2659
|
+
toolCallId: toolCall.toolCallId,
|
|
2660
|
+
toolName: toolCall.toolName,
|
|
2661
|
+
output: await createLanguageModelToolResultOutput({
|
|
2662
|
+
toolCallId: toolCall.toolCallId,
|
|
2663
|
+
toolName: toolCall.toolName,
|
|
2664
|
+
input: toolCall.input,
|
|
2665
|
+
output: result,
|
|
2666
|
+
tool: tools?.[toolCall.toolName],
|
|
2667
|
+
errorMode,
|
|
2668
|
+
supportedUrls: {},
|
|
2669
|
+
download,
|
|
2670
|
+
}),
|
|
2671
|
+
},
|
|
2672
|
+
rawOutput: result,
|
|
2673
|
+
isError: streamResult.isError === true,
|
|
2674
|
+
};
|
|
2675
|
+
}
|
|
2038
2676
|
|
|
2677
|
+
function createInvalidToolResult(toolCall: {
|
|
2678
|
+
toolCallId: string;
|
|
2679
|
+
toolName: string;
|
|
2680
|
+
error?: unknown;
|
|
2681
|
+
}): LanguageModelV4ToolResultPart {
|
|
2039
2682
|
return {
|
|
2040
2683
|
type: 'tool-result' as const,
|
|
2041
2684
|
toolCallId: toolCall.toolCallId,
|
|
2042
2685
|
toolName: toolCall.toolName,
|
|
2043
|
-
output:
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
: streamResult.isError
|
|
2048
|
-
? {
|
|
2049
|
-
type: 'error-json' as const,
|
|
2050
|
-
value: result as JSONValue,
|
|
2051
|
-
}
|
|
2052
|
-
: {
|
|
2053
|
-
type: 'json' as const,
|
|
2054
|
-
value: result as JSONValue,
|
|
2055
|
-
},
|
|
2686
|
+
output: {
|
|
2687
|
+
type: 'error-text' as const,
|
|
2688
|
+
value: getErrorMessage(toolCall.error),
|
|
2689
|
+
},
|
|
2056
2690
|
};
|
|
2057
2691
|
}
|
|
2058
2692
|
|
|
2693
|
+
function getToolCallbackMessages(
|
|
2694
|
+
messages: LanguageModelV4Prompt,
|
|
2695
|
+
): ModelMessage[] {
|
|
2696
|
+
const withoutAssistantToolCall =
|
|
2697
|
+
messages.at(-1)?.role === 'assistant' ? messages.slice(0, -1) : messages;
|
|
2698
|
+
return withoutAssistantToolCall as unknown as ModelMessage[];
|
|
2699
|
+
}
|
|
2700
|
+
|
|
2059
2701
|
async function executeTool(
|
|
2060
2702
|
toolCall: { toolCallId: string; toolName: string; input: unknown },
|
|
2061
2703
|
tools: ToolSet,
|
|
2062
2704
|
messages: LanguageModelV4Prompt,
|
|
2063
|
-
|
|
2064
|
-
|
|
2705
|
+
context?: unknown,
|
|
2706
|
+
download?: DownloadFunction,
|
|
2707
|
+
): Promise<WorkflowToolExecutionResult> {
|
|
2065
2708
|
const tool = tools[toolCall.toolName];
|
|
2066
2709
|
if (!tool) throw new Error(`Tool "${toolCall.toolName}" not found`);
|
|
2067
2710
|
if (typeof tool.execute !== 'function') {
|
|
@@ -2072,6 +2715,7 @@ async function executeTool(
|
|
|
2072
2715
|
}
|
|
2073
2716
|
// Input is already parsed and validated by streamModelCall's parseToolCall
|
|
2074
2717
|
const parsedInput = toolCall.input;
|
|
2718
|
+
let toolResult: unknown;
|
|
2075
2719
|
|
|
2076
2720
|
try {
|
|
2077
2721
|
// Extract execute function to avoid binding `this` to the tool object.
|
|
@@ -2080,149 +2724,56 @@ async function executeTool(
|
|
|
2080
2724
|
// When the execute function is a workflow step (marked with 'use step'),
|
|
2081
2725
|
// the step system captures `this` for serialization, causing failures.
|
|
2082
2726
|
const { execute } = tool;
|
|
2083
|
-
|
|
2727
|
+
toolResult = await execute(parsedInput, {
|
|
2084
2728
|
toolCallId: toolCall.toolCallId,
|
|
2085
2729
|
// Pass the conversation messages to the tool so it has context about the conversation
|
|
2086
2730
|
messages,
|
|
2087
|
-
// Pass context to the tool
|
|
2088
|
-
context
|
|
2731
|
+
// Pass per-tool context to the tool (resolved from `toolsContext`)
|
|
2732
|
+
context,
|
|
2089
2733
|
});
|
|
2090
|
-
|
|
2091
|
-
// Use the appropriate output type based on the result
|
|
2092
|
-
// AI SDK supports 'text' for strings and 'json' for objects
|
|
2093
|
-
const output =
|
|
2094
|
-
typeof toolResult === 'string'
|
|
2095
|
-
? { type: 'text' as const, value: toolResult }
|
|
2096
|
-
: { type: 'json' as const, value: toolResult };
|
|
2097
|
-
|
|
2098
|
-
return {
|
|
2099
|
-
type: 'tool-result' as const,
|
|
2100
|
-
toolCallId: toolCall.toolCallId,
|
|
2101
|
-
toolName: toolCall.toolName,
|
|
2102
|
-
output,
|
|
2103
|
-
};
|
|
2104
2734
|
} catch (error) {
|
|
2105
2735
|
// Convert tool errors to error-text results sent back to the model,
|
|
2106
2736
|
// allowing the agent to recover rather than killing the entire stream.
|
|
2107
2737
|
// This aligns with AI SDK's streamText behavior for individual tool failures.
|
|
2738
|
+
const errorMessage = getErrorMessage(error);
|
|
2108
2739
|
return {
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2740
|
+
modelResult: {
|
|
2741
|
+
type: 'tool-result',
|
|
2742
|
+
toolCallId: toolCall.toolCallId,
|
|
2743
|
+
toolName: toolCall.toolName,
|
|
2744
|
+
output: await createLanguageModelToolResultOutput({
|
|
2745
|
+
toolCallId: toolCall.toolCallId,
|
|
2746
|
+
toolName: toolCall.toolName,
|
|
2747
|
+
input: parsedInput,
|
|
2748
|
+
output: errorMessage,
|
|
2749
|
+
tool,
|
|
2750
|
+
errorMode: 'text',
|
|
2751
|
+
supportedUrls: {},
|
|
2752
|
+
download,
|
|
2753
|
+
}),
|
|
2115
2754
|
},
|
|
2755
|
+
rawOutput: errorMessage,
|
|
2756
|
+
isError: true,
|
|
2116
2757
|
};
|
|
2117
2758
|
}
|
|
2118
|
-
}
|
|
2119
|
-
|
|
2120
|
-
/**
|
|
2121
|
-
* Collected tool approval information for a single tool call.
|
|
2122
|
-
*/
|
|
2123
|
-
interface CollectedApproval {
|
|
2124
|
-
toolCallId: string;
|
|
2125
|
-
toolName: string;
|
|
2126
|
-
input: unknown;
|
|
2127
|
-
approvalId: string;
|
|
2128
|
-
reason?: string;
|
|
2129
|
-
}
|
|
2130
|
-
|
|
2131
|
-
/**
|
|
2132
|
-
* Collect tool approval responses from model messages.
|
|
2133
|
-
* Mirrors the logic from `collectToolApprovals` in the AI SDK core
|
|
2134
|
-
* (`packages/ai/src/generate-text/collect-tool-approvals.ts`).
|
|
2135
|
-
*
|
|
2136
|
-
* Scans the last tool message for `tool-approval-response` parts,
|
|
2137
|
-
* matches them with `tool-approval-request` parts in assistant messages
|
|
2138
|
-
* and the corresponding `tool-call` parts.
|
|
2139
|
-
*/
|
|
2140
|
-
function collectToolApprovalsFromMessages(messages: ModelMessage[]): {
|
|
2141
|
-
approvedToolApprovals: CollectedApproval[];
|
|
2142
|
-
deniedToolApprovals: CollectedApproval[];
|
|
2143
|
-
} {
|
|
2144
|
-
const lastMessage = messages.at(-1);
|
|
2145
|
-
|
|
2146
|
-
if (lastMessage?.role !== 'tool') {
|
|
2147
|
-
return { approvedToolApprovals: [], deniedToolApprovals: [] };
|
|
2148
|
-
}
|
|
2149
|
-
|
|
2150
|
-
// Gather tool calls from assistant messages
|
|
2151
|
-
const toolCallsByToolCallId: Record<
|
|
2152
|
-
string,
|
|
2153
|
-
{ toolName: string; input: unknown }
|
|
2154
|
-
> = {};
|
|
2155
|
-
for (const message of messages) {
|
|
2156
|
-
if (message.role === 'assistant' && Array.isArray(message.content)) {
|
|
2157
|
-
for (const part of message.content as any[]) {
|
|
2158
|
-
if (part.type === 'tool-call') {
|
|
2159
|
-
toolCallsByToolCallId[part.toolCallId] = {
|
|
2160
|
-
toolName: part.toolName,
|
|
2161
|
-
input: part.input ?? part.args,
|
|
2162
|
-
};
|
|
2163
|
-
}
|
|
2164
|
-
}
|
|
2165
|
-
}
|
|
2166
|
-
}
|
|
2167
|
-
|
|
2168
|
-
// Gather approval requests from assistant messages
|
|
2169
|
-
const approvalRequestsByApprovalId: Record<
|
|
2170
|
-
string,
|
|
2171
|
-
{ approvalId: string; toolCallId: string }
|
|
2172
|
-
> = {};
|
|
2173
|
-
for (const message of messages) {
|
|
2174
|
-
if (message.role === 'assistant' && Array.isArray(message.content)) {
|
|
2175
|
-
for (const part of message.content as any[]) {
|
|
2176
|
-
if (part.type === 'tool-approval-request') {
|
|
2177
|
-
approvalRequestsByApprovalId[part.approvalId] = {
|
|
2178
|
-
approvalId: part.approvalId,
|
|
2179
|
-
toolCallId: part.toolCallId,
|
|
2180
|
-
};
|
|
2181
|
-
}
|
|
2182
|
-
}
|
|
2183
|
-
}
|
|
2184
|
-
}
|
|
2185
|
-
|
|
2186
|
-
// Gather existing tool results to avoid re-executing
|
|
2187
|
-
const existingToolResults = new Set<string>();
|
|
2188
|
-
for (const part of lastMessage.content as any[]) {
|
|
2189
|
-
if (part.type === 'tool-result') {
|
|
2190
|
-
existingToolResults.add(part.toolCallId);
|
|
2191
|
-
}
|
|
2192
|
-
}
|
|
2193
|
-
|
|
2194
|
-
const approvedToolApprovals: CollectedApproval[] = [];
|
|
2195
|
-
const deniedToolApprovals: CollectedApproval[] = [];
|
|
2196
|
-
|
|
2197
|
-
// Collect approval responses from the last tool message
|
|
2198
|
-
const approvalResponses = (lastMessage.content as any[]).filter(
|
|
2199
|
-
(part: any) => part.type === 'tool-approval-response',
|
|
2200
|
-
);
|
|
2201
|
-
|
|
2202
|
-
for (const response of approvalResponses) {
|
|
2203
|
-
const approvalRequest = approvalRequestsByApprovalId[response.approvalId];
|
|
2204
|
-
if (approvalRequest == null) continue;
|
|
2205
2759
|
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
if (toolCall == null) continue;
|
|
2211
|
-
|
|
2212
|
-
const approval: CollectedApproval = {
|
|
2213
|
-
toolCallId: approvalRequest.toolCallId,
|
|
2760
|
+
return {
|
|
2761
|
+
modelResult: {
|
|
2762
|
+
type: 'tool-result' as const,
|
|
2763
|
+
toolCallId: toolCall.toolCallId,
|
|
2214
2764
|
toolName: toolCall.toolName,
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2765
|
+
output: await createLanguageModelToolResultOutput({
|
|
2766
|
+
toolCallId: toolCall.toolCallId,
|
|
2767
|
+
toolName: toolCall.toolName,
|
|
2768
|
+
input: parsedInput,
|
|
2769
|
+
output: toolResult,
|
|
2770
|
+
tool,
|
|
2771
|
+
errorMode: 'none',
|
|
2772
|
+
supportedUrls: {},
|
|
2773
|
+
download,
|
|
2774
|
+
}),
|
|
2775
|
+
},
|
|
2776
|
+
rawOutput: toolResult,
|
|
2777
|
+
isError: false,
|
|
2778
|
+
};
|
|
2228
2779
|
}
|