@ai-sdk/workflow 1.0.0-canary.39 → 1.0.0-canary.41
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 +24 -0
- package/dist/index.d.mts +131 -55
- package/dist/index.mjs +160 -84
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/do-stream-step.ts +5 -2
- package/src/stream-text-iterator.ts +46 -26
- package/src/test/agent-e2e-workflows.ts +80 -0
- package/src/workflow-agent.ts +478 -220
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/workflow",
|
|
3
|
-
"version": "1.0.0-canary.
|
|
3
|
+
"version": "1.0.0-canary.41",
|
|
4
4
|
"description": "WorkflowAgent for building AI agents with AI SDK",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -28,15 +28,15 @@
|
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"ajv": "^8.18.0",
|
|
30
30
|
"@ai-sdk/provider": "4.0.0-canary.16",
|
|
31
|
-
"@ai-sdk/provider-utils": "5.0.0-canary.
|
|
32
|
-
"ai": "7.0.0-canary.
|
|
31
|
+
"@ai-sdk/provider-utils": "5.0.0-canary.34",
|
|
32
|
+
"ai": "7.0.0-canary.125"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/node": "20.17.24",
|
|
36
36
|
"@workflow/vitest": "4.0.1",
|
|
37
37
|
"tsup": "^8",
|
|
38
38
|
"typescript": "5.8.3",
|
|
39
|
-
"workflow": "4.2.
|
|
39
|
+
"workflow": "4.2.4",
|
|
40
40
|
"zod": "3.25.76",
|
|
41
41
|
"@vercel/ai-tsconfig": "0.0.0"
|
|
42
42
|
},
|
package/src/do-stream-step.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type {
|
|
|
2
2
|
LanguageModelV4CallOptions,
|
|
3
3
|
LanguageModelV4Prompt,
|
|
4
4
|
} from '@ai-sdk/provider';
|
|
5
|
+
import type { Context } from '@ai-sdk/provider-utils';
|
|
5
6
|
import {
|
|
6
7
|
experimental_streamLanguageModelCall as streamModelCall,
|
|
7
8
|
gateway,
|
|
@@ -56,6 +57,8 @@ export interface DoStreamStepOptions {
|
|
|
56
57
|
telemetry?: TelemetryOptions;
|
|
57
58
|
repairToolCall?: ToolCallRepairFunction<ToolSet>;
|
|
58
59
|
responseFormat?: LanguageModelV4CallOptions['responseFormat'];
|
|
60
|
+
runtimeContext?: Context;
|
|
61
|
+
toolsContext?: Record<string, Context | undefined>;
|
|
59
62
|
}
|
|
60
63
|
|
|
61
64
|
/**
|
|
@@ -241,8 +244,8 @@ export async function doStreamStep(
|
|
|
241
244
|
},
|
|
242
245
|
functionId: undefined,
|
|
243
246
|
metadata: undefined,
|
|
244
|
-
runtimeContext:
|
|
245
|
-
toolsContext: {},
|
|
247
|
+
runtimeContext: options?.runtimeContext ?? {},
|
|
248
|
+
toolsContext: options?.toolsContext ?? {},
|
|
246
249
|
content: [
|
|
247
250
|
...(text ? [{ type: 'text' as const, text }] : []),
|
|
248
251
|
...toolCalls
|
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
LanguageModelV4Prompt,
|
|
4
4
|
LanguageModelV4ToolResultPart,
|
|
5
5
|
} from '@ai-sdk/provider';
|
|
6
|
+
import type { Context } from '@ai-sdk/provider-utils';
|
|
6
7
|
import {
|
|
7
8
|
experimental_filterActiveTools as filterActiveTools,
|
|
8
9
|
type Experimental_LanguageModelStreamPart as ModelCallStreamPart,
|
|
@@ -43,8 +44,10 @@ export interface StreamTextIteratorYieldValue {
|
|
|
43
44
|
messages: LanguageModelV4Prompt;
|
|
44
45
|
/** The step result from the current step */
|
|
45
46
|
step?: StepResult<ToolSet, any>;
|
|
46
|
-
/** The current
|
|
47
|
-
|
|
47
|
+
/** The current runtime context shared across the agent loop */
|
|
48
|
+
runtimeContext?: Context;
|
|
49
|
+
/** The current per-tool context, keyed by tool name */
|
|
50
|
+
toolsContext?: Record<string, Context | undefined>;
|
|
48
51
|
/** Provider-executed tool results (keyed by tool call ID) */
|
|
49
52
|
providerExecutedToolResults?: Map<string, ProviderExecutedToolResult>;
|
|
50
53
|
}
|
|
@@ -62,7 +65,8 @@ export async function* streamTextIterator({
|
|
|
62
65
|
prepareStep,
|
|
63
66
|
generationSettings,
|
|
64
67
|
toolChoice,
|
|
65
|
-
|
|
68
|
+
runtimeContext,
|
|
69
|
+
toolsContext,
|
|
66
70
|
telemetry,
|
|
67
71
|
includeRawChunks = false,
|
|
68
72
|
repairToolCall,
|
|
@@ -79,7 +83,8 @@ export async function* streamTextIterator({
|
|
|
79
83
|
prepareStep?: PrepareStepCallback<any>;
|
|
80
84
|
generationSettings?: GenerationSettings;
|
|
81
85
|
toolChoice?: ToolChoice<ToolSet>;
|
|
82
|
-
|
|
86
|
+
runtimeContext?: Context;
|
|
87
|
+
toolsContext?: Record<string, Context | undefined>;
|
|
83
88
|
telemetry?: TelemetryOptions;
|
|
84
89
|
includeRawChunks?: boolean;
|
|
85
90
|
repairToolCall?: ToolCallRepairFunction<ToolSet>;
|
|
@@ -93,7 +98,9 @@ export async function* streamTextIterator({
|
|
|
93
98
|
let currentModel: LanguageModel = model;
|
|
94
99
|
let currentGenerationSettings = generationSettings ?? {};
|
|
95
100
|
let currentToolChoice = toolChoice;
|
|
96
|
-
let
|
|
101
|
+
let currentRuntimeContext: Context = runtimeContext ?? {};
|
|
102
|
+
let currentToolsContext: Record<string, Context | undefined> =
|
|
103
|
+
toolsContext ?? {};
|
|
97
104
|
let currentActiveTools: string[] | undefined;
|
|
98
105
|
|
|
99
106
|
const steps: StepResult<any, any>[] = [];
|
|
@@ -116,19 +123,20 @@ export async function* streamTextIterator({
|
|
|
116
123
|
stepNumber,
|
|
117
124
|
steps,
|
|
118
125
|
messages: conversationPrompt,
|
|
119
|
-
|
|
126
|
+
runtimeContext: currentRuntimeContext,
|
|
127
|
+
toolsContext: currentToolsContext as never,
|
|
120
128
|
});
|
|
121
129
|
|
|
122
130
|
// Apply any overrides from prepareStep
|
|
123
|
-
if (prepareResult
|
|
131
|
+
if (prepareResult?.model !== undefined) {
|
|
124
132
|
currentModel = prepareResult.model;
|
|
125
133
|
}
|
|
126
134
|
// Apply messages override BEFORE system so the system message
|
|
127
135
|
// isn't lost when messages replaces the prompt.
|
|
128
|
-
if (prepareResult
|
|
136
|
+
if (prepareResult?.messages !== undefined) {
|
|
129
137
|
conversationPrompt = [...prepareResult.messages];
|
|
130
138
|
}
|
|
131
|
-
if (prepareResult
|
|
139
|
+
if (prepareResult?.system !== undefined) {
|
|
132
140
|
// Update or prepend system message in the conversation prompt.
|
|
133
141
|
// Applied AFTER messages override so the system message isn't
|
|
134
142
|
// lost when messages replaces the prompt.
|
|
@@ -149,80 +157,86 @@ export async function* streamTextIterator({
|
|
|
149
157
|
});
|
|
150
158
|
}
|
|
151
159
|
}
|
|
152
|
-
if (prepareResult
|
|
153
|
-
|
|
160
|
+
if (prepareResult?.runtimeContext !== undefined) {
|
|
161
|
+
currentRuntimeContext = prepareResult.runtimeContext;
|
|
154
162
|
}
|
|
155
|
-
if (prepareResult
|
|
163
|
+
if (prepareResult?.toolsContext !== undefined) {
|
|
164
|
+
currentToolsContext = prepareResult.toolsContext as Record<
|
|
165
|
+
string,
|
|
166
|
+
Context | undefined
|
|
167
|
+
>;
|
|
168
|
+
}
|
|
169
|
+
if (prepareResult?.activeTools !== undefined) {
|
|
156
170
|
currentActiveTools = prepareResult.activeTools;
|
|
157
171
|
}
|
|
158
172
|
// Apply generation settings overrides
|
|
159
|
-
if (prepareResult
|
|
173
|
+
if (prepareResult?.maxOutputTokens !== undefined) {
|
|
160
174
|
currentGenerationSettings = {
|
|
161
175
|
...currentGenerationSettings,
|
|
162
176
|
maxOutputTokens: prepareResult.maxOutputTokens,
|
|
163
177
|
};
|
|
164
178
|
}
|
|
165
|
-
if (prepareResult
|
|
179
|
+
if (prepareResult?.temperature !== undefined) {
|
|
166
180
|
currentGenerationSettings = {
|
|
167
181
|
...currentGenerationSettings,
|
|
168
182
|
temperature: prepareResult.temperature,
|
|
169
183
|
};
|
|
170
184
|
}
|
|
171
|
-
if (prepareResult
|
|
185
|
+
if (prepareResult?.topP !== undefined) {
|
|
172
186
|
currentGenerationSettings = {
|
|
173
187
|
...currentGenerationSettings,
|
|
174
188
|
topP: prepareResult.topP,
|
|
175
189
|
};
|
|
176
190
|
}
|
|
177
|
-
if (prepareResult
|
|
191
|
+
if (prepareResult?.topK !== undefined) {
|
|
178
192
|
currentGenerationSettings = {
|
|
179
193
|
...currentGenerationSettings,
|
|
180
194
|
topK: prepareResult.topK,
|
|
181
195
|
};
|
|
182
196
|
}
|
|
183
|
-
if (prepareResult
|
|
197
|
+
if (prepareResult?.presencePenalty !== undefined) {
|
|
184
198
|
currentGenerationSettings = {
|
|
185
199
|
...currentGenerationSettings,
|
|
186
200
|
presencePenalty: prepareResult.presencePenalty,
|
|
187
201
|
};
|
|
188
202
|
}
|
|
189
|
-
if (prepareResult
|
|
203
|
+
if (prepareResult?.frequencyPenalty !== undefined) {
|
|
190
204
|
currentGenerationSettings = {
|
|
191
205
|
...currentGenerationSettings,
|
|
192
206
|
frequencyPenalty: prepareResult.frequencyPenalty,
|
|
193
207
|
};
|
|
194
208
|
}
|
|
195
|
-
if (prepareResult
|
|
209
|
+
if (prepareResult?.stopSequences !== undefined) {
|
|
196
210
|
currentGenerationSettings = {
|
|
197
211
|
...currentGenerationSettings,
|
|
198
212
|
stopSequences: prepareResult.stopSequences,
|
|
199
213
|
};
|
|
200
214
|
}
|
|
201
|
-
if (prepareResult
|
|
215
|
+
if (prepareResult?.seed !== undefined) {
|
|
202
216
|
currentGenerationSettings = {
|
|
203
217
|
...currentGenerationSettings,
|
|
204
218
|
seed: prepareResult.seed,
|
|
205
219
|
};
|
|
206
220
|
}
|
|
207
|
-
if (prepareResult
|
|
221
|
+
if (prepareResult?.maxRetries !== undefined) {
|
|
208
222
|
currentGenerationSettings = {
|
|
209
223
|
...currentGenerationSettings,
|
|
210
224
|
maxRetries: prepareResult.maxRetries,
|
|
211
225
|
};
|
|
212
226
|
}
|
|
213
|
-
if (prepareResult
|
|
227
|
+
if (prepareResult?.headers !== undefined) {
|
|
214
228
|
currentGenerationSettings = {
|
|
215
229
|
...currentGenerationSettings,
|
|
216
230
|
headers: prepareResult.headers,
|
|
217
231
|
};
|
|
218
232
|
}
|
|
219
|
-
if (prepareResult
|
|
233
|
+
if (prepareResult?.providerOptions !== undefined) {
|
|
220
234
|
currentGenerationSettings = {
|
|
221
235
|
...currentGenerationSettings,
|
|
222
236
|
providerOptions: prepareResult.providerOptions,
|
|
223
237
|
};
|
|
224
238
|
}
|
|
225
|
-
if (prepareResult
|
|
239
|
+
if (prepareResult?.toolChoice !== undefined) {
|
|
226
240
|
currentToolChoice = prepareResult.toolChoice;
|
|
227
241
|
}
|
|
228
242
|
}
|
|
@@ -233,6 +247,8 @@ export async function* streamTextIterator({
|
|
|
233
247
|
model: currentModel,
|
|
234
248
|
messages: conversationPrompt as unknown as ModelMessage[],
|
|
235
249
|
steps: [...steps],
|
|
250
|
+
runtimeContext: currentRuntimeContext,
|
|
251
|
+
toolsContext: currentToolsContext as never,
|
|
236
252
|
});
|
|
237
253
|
}
|
|
238
254
|
|
|
@@ -264,6 +280,8 @@ export async function* streamTextIterator({
|
|
|
264
280
|
telemetry,
|
|
265
281
|
repairToolCall,
|
|
266
282
|
responseFormat,
|
|
283
|
+
runtimeContext: currentRuntimeContext,
|
|
284
|
+
toolsContext: currentToolsContext,
|
|
267
285
|
},
|
|
268
286
|
);
|
|
269
287
|
|
|
@@ -308,7 +326,8 @@ export async function* streamTextIterator({
|
|
|
308
326
|
toolCalls,
|
|
309
327
|
messages: conversationPrompt,
|
|
310
328
|
step,
|
|
311
|
-
|
|
329
|
+
runtimeContext: currentRuntimeContext,
|
|
330
|
+
toolsContext: currentToolsContext,
|
|
312
331
|
providerExecutedToolResults,
|
|
313
332
|
};
|
|
314
333
|
|
|
@@ -380,7 +399,8 @@ export async function* streamTextIterator({
|
|
|
380
399
|
toolCalls: [],
|
|
381
400
|
messages: conversationPrompt,
|
|
382
401
|
step: lastStep,
|
|
383
|
-
|
|
402
|
+
runtimeContext: currentRuntimeContext,
|
|
403
|
+
toolsContext: currentToolsContext,
|
|
384
404
|
};
|
|
385
405
|
}
|
|
386
406
|
|
|
@@ -505,3 +505,83 @@ export async function agentToolInputSchemaE2e(a: number, b: number) {
|
|
|
505
505
|
lastStepText: result.steps[result.steps.length - 1]?.text,
|
|
506
506
|
};
|
|
507
507
|
}
|
|
508
|
+
|
|
509
|
+
// ============================================================================
|
|
510
|
+
// runtimeContext + toolsContext (end-to-end)
|
|
511
|
+
// ============================================================================
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Demonstrates the full context flow:
|
|
515
|
+
*
|
|
516
|
+
* - `runtimeContext` holds shared agent state (`tenantId`, `requestId`).
|
|
517
|
+
* `prepareStep` reads it and tags it with the current step number;
|
|
518
|
+
* `onFinish` receives the final value.
|
|
519
|
+
* - `toolsContext` holds per-tool, schema-validated context. The
|
|
520
|
+
* `lookupCustomer` tool declares `contextSchema`, so its entry is
|
|
521
|
+
* validated and the tool's `execute` only sees its own context.
|
|
522
|
+
*/
|
|
523
|
+
export async function agentRuntimeAndToolsContextE2e() {
|
|
524
|
+
'use workflow';
|
|
525
|
+
|
|
526
|
+
let onFinishRuntimeContext: Record<string, unknown> | undefined;
|
|
527
|
+
let onFinishToolsContext: Record<string, unknown> | undefined;
|
|
528
|
+
let toolReceivedContext: unknown;
|
|
529
|
+
|
|
530
|
+
const agent = new WorkflowAgent({
|
|
531
|
+
model: mockSequenceModel([
|
|
532
|
+
{
|
|
533
|
+
type: 'tool-call',
|
|
534
|
+
toolName: 'lookupCustomer',
|
|
535
|
+
input: JSON.stringify({ customerId: 'cust_123' }),
|
|
536
|
+
},
|
|
537
|
+
{ type: 'text', text: 'Customer cust_123 is eligible.' },
|
|
538
|
+
]),
|
|
539
|
+
tools: {
|
|
540
|
+
lookupCustomer: tool({
|
|
541
|
+
description: 'Look up customer account details.',
|
|
542
|
+
inputSchema: z.object({ customerId: z.string() }),
|
|
543
|
+
contextSchema: z.object({
|
|
544
|
+
apiKey: z.string(),
|
|
545
|
+
region: z.enum(['us', 'eu']),
|
|
546
|
+
}),
|
|
547
|
+
execute: async (input, { context }) => {
|
|
548
|
+
toolReceivedContext = context;
|
|
549
|
+
return { customerId: input.customerId, eligible: true };
|
|
550
|
+
},
|
|
551
|
+
}),
|
|
552
|
+
},
|
|
553
|
+
instructions: 'You look up customers.',
|
|
554
|
+
runtimeContext: {
|
|
555
|
+
tenantId: 'tenant_123',
|
|
556
|
+
requestId: 'req_abc',
|
|
557
|
+
},
|
|
558
|
+
toolsContext: {
|
|
559
|
+
lookupCustomer: {
|
|
560
|
+
apiKey: 'sk-test-key',
|
|
561
|
+
region: 'us',
|
|
562
|
+
},
|
|
563
|
+
},
|
|
564
|
+
prepareStep: ({ stepNumber, runtimeContext }) => ({
|
|
565
|
+
runtimeContext: { ...runtimeContext, lastStep: stepNumber },
|
|
566
|
+
}),
|
|
567
|
+
onFinish: ({ runtimeContext, toolsContext }) => {
|
|
568
|
+
onFinishRuntimeContext = runtimeContext;
|
|
569
|
+
onFinishToolsContext = toolsContext;
|
|
570
|
+
},
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
const result = await agent.stream({
|
|
574
|
+
messages: [
|
|
575
|
+
{ role: 'user', content: 'Is customer cust_123 eligible for support?' },
|
|
576
|
+
],
|
|
577
|
+
writable: getWritable(),
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
return {
|
|
581
|
+
stepCount: result.steps.length,
|
|
582
|
+
lastStepText: result.steps[result.steps.length - 1]?.text,
|
|
583
|
+
toolReceivedContext,
|
|
584
|
+
onFinishRuntimeContext,
|
|
585
|
+
onFinishToolsContext,
|
|
586
|
+
};
|
|
587
|
+
}
|