@ai-sdk/workflow 1.0.0-beta.8 → 1.0.0-beta.94
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/workflow",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.94",
|
|
4
4
|
"description": "WorkflowAgent for building AI agents with AI SDK",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -26,17 +26,17 @@
|
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"ajv": "^8.
|
|
30
|
-
"@ai-sdk/provider": "4.0.0-beta.
|
|
31
|
-
"ai": "
|
|
32
|
-
"
|
|
29
|
+
"ajv": "^8.20.0",
|
|
30
|
+
"@ai-sdk/provider": "4.0.0-beta.19",
|
|
31
|
+
"@ai-sdk/provider-utils": "5.0.0-beta.49",
|
|
32
|
+
"ai": "7.0.0-beta.177"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@types/node": "
|
|
35
|
+
"@types/node": "22.19.19",
|
|
36
36
|
"@workflow/vitest": "4.0.1",
|
|
37
|
-
"tsup": "^8",
|
|
37
|
+
"tsup": "^8.5.1",
|
|
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
|
},
|
|
@@ -44,15 +44,17 @@
|
|
|
44
44
|
"zod": "^3.25.76 || ^4.1.8"
|
|
45
45
|
},
|
|
46
46
|
"engines": {
|
|
47
|
-
"node": ">=
|
|
47
|
+
"node": ">=22"
|
|
48
48
|
},
|
|
49
49
|
"publishConfig": {
|
|
50
|
-
"access": "public"
|
|
50
|
+
"access": "public",
|
|
51
|
+
"provenance": true
|
|
51
52
|
},
|
|
52
53
|
"homepage": "https://ai-sdk.dev/docs",
|
|
53
54
|
"repository": {
|
|
54
55
|
"type": "git",
|
|
55
|
-
"url": "
|
|
56
|
+
"url": "https://github.com/vercel/ai",
|
|
57
|
+
"directory": "packages/workflow"
|
|
56
58
|
},
|
|
57
59
|
"bugs": {
|
|
58
60
|
"url": "https://github.com/vercel/ai/issues"
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { LanguageModelV4ToolResultOutput } from '@ai-sdk/provider';
|
|
2
|
+
import type { Tool } from '@ai-sdk/provider-utils';
|
|
3
|
+
import type { ModelMessage } from 'ai';
|
|
4
|
+
import {
|
|
5
|
+
createDefaultDownloadFunction,
|
|
6
|
+
createToolModelOutput,
|
|
7
|
+
downloadAssets,
|
|
8
|
+
mapToolResultOutput,
|
|
9
|
+
type DownloadFunction,
|
|
10
|
+
} from 'ai/internal';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Converts a single tool result into a provider-level
|
|
14
|
+
* `LanguageModelV4ToolResultOutput`, honoring the tool's optional
|
|
15
|
+
* `toModelOutput` hook.
|
|
16
|
+
*
|
|
17
|
+
* Unlike `generateText`/`streamText`, `WorkflowAgent` assembles the
|
|
18
|
+
* `LanguageModelV4` prompt incrementally — appending one tool result at a time
|
|
19
|
+
* — instead of building AI-level `ModelMessage`s and converting the whole
|
|
20
|
+
* prompt once via `convertToLanguageModelPrompt`. This helper performs the
|
|
21
|
+
* equivalent per-result conversion using the shared `ai/internal` primitives:
|
|
22
|
+
*
|
|
23
|
+
* 1. `createToolModelOutput` — applies `tool.toModelOutput` (or the
|
|
24
|
+
* text/json/error fallback).
|
|
25
|
+
* 2. `downloadAssets` — for `content`-type outputs, downloads any file/image
|
|
26
|
+
* assets so URLs become bytes the provider can consume.
|
|
27
|
+
* 3. `mapToolResultOutput` — maps the AI-level `ToolResultOutput` to the
|
|
28
|
+
* provider-level output and converts legacy file types.
|
|
29
|
+
*/
|
|
30
|
+
export async function createLanguageModelToolResultOutput({
|
|
31
|
+
toolCallId,
|
|
32
|
+
toolName,
|
|
33
|
+
input,
|
|
34
|
+
output,
|
|
35
|
+
tool,
|
|
36
|
+
errorMode,
|
|
37
|
+
supportedUrls,
|
|
38
|
+
download = createDefaultDownloadFunction(),
|
|
39
|
+
provider,
|
|
40
|
+
}: {
|
|
41
|
+
toolCallId: string;
|
|
42
|
+
toolName: string;
|
|
43
|
+
input: unknown;
|
|
44
|
+
output: unknown;
|
|
45
|
+
tool: Tool | undefined;
|
|
46
|
+
errorMode: 'none' | 'text' | 'json';
|
|
47
|
+
supportedUrls: Record<string, RegExp[]>;
|
|
48
|
+
download?: DownloadFunction;
|
|
49
|
+
provider?: string;
|
|
50
|
+
}): Promise<LanguageModelV4ToolResultOutput> {
|
|
51
|
+
const modelOutput = await createToolModelOutput({
|
|
52
|
+
toolCallId,
|
|
53
|
+
input,
|
|
54
|
+
output,
|
|
55
|
+
tool,
|
|
56
|
+
errorMode,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const downloadedAssets =
|
|
60
|
+
modelOutput.type === 'content'
|
|
61
|
+
? await downloadAssets(
|
|
62
|
+
[
|
|
63
|
+
{
|
|
64
|
+
role: 'tool',
|
|
65
|
+
content: [
|
|
66
|
+
{
|
|
67
|
+
type: 'tool-result',
|
|
68
|
+
toolCallId,
|
|
69
|
+
toolName,
|
|
70
|
+
output: modelOutput,
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
} satisfies ModelMessage,
|
|
74
|
+
],
|
|
75
|
+
download,
|
|
76
|
+
supportedUrls,
|
|
77
|
+
)
|
|
78
|
+
: {};
|
|
79
|
+
|
|
80
|
+
return mapToolResultOutput({
|
|
81
|
+
output: modelOutput,
|
|
82
|
+
provider,
|
|
83
|
+
downloadedAssets,
|
|
84
|
+
});
|
|
85
|
+
}
|
package/src/do-stream-step.ts
CHANGED
|
@@ -2,9 +2,11 @@ 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
|
-
type Experimental_LanguageModelStreamPart as ModelCallStreamPart,
|
|
7
7
|
experimental_streamLanguageModelCall as streamModelCall,
|
|
8
|
+
gateway,
|
|
9
|
+
type Experimental_LanguageModelStreamPart as ModelCallStreamPart,
|
|
8
10
|
type FinishReason,
|
|
9
11
|
type LanguageModel,
|
|
10
12
|
type LanguageModelUsage,
|
|
@@ -15,13 +17,11 @@ import {
|
|
|
15
17
|
type ToolChoice,
|
|
16
18
|
type ToolSet,
|
|
17
19
|
} from 'ai';
|
|
18
|
-
import {
|
|
19
|
-
import type { ProviderOptions, TelemetrySettings } from './workflow-agent.js';
|
|
20
|
+
import type { ProviderOptions } from './workflow-agent.js';
|
|
20
21
|
import {
|
|
21
22
|
resolveSerializableTools,
|
|
22
23
|
type SerializableToolDef,
|
|
23
24
|
} from './serializable-schema.js';
|
|
24
|
-
|
|
25
25
|
export type { Experimental_LanguageModelStreamPart as ModelCallStreamPart } from 'ai';
|
|
26
26
|
|
|
27
27
|
export type ModelStopCondition = StopCondition<NoInfer<ToolSet>, any>;
|
|
@@ -54,9 +54,15 @@ export interface DoStreamStepOptions {
|
|
|
54
54
|
providerOptions?: ProviderOptions;
|
|
55
55
|
toolChoice?: ToolChoice<ToolSet>;
|
|
56
56
|
includeRawChunks?: boolean;
|
|
57
|
-
experimental_telemetry?: TelemetrySettings;
|
|
58
57
|
repairToolCall?: ToolCallRepairFunction<ToolSet>;
|
|
59
58
|
responseFormat?: LanguageModelV4CallOptions['responseFormat'];
|
|
59
|
+
runtimeContext?: Context;
|
|
60
|
+
toolsContext?: Record<string, Context | undefined>;
|
|
61
|
+
/**
|
|
62
|
+
* The step number for the returned StepResult. Defaults to 0 for direct
|
|
63
|
+
* callers; stream iterators pass their current index. See #15151.
|
|
64
|
+
*/
|
|
65
|
+
stepNumber?: number;
|
|
60
66
|
}
|
|
61
67
|
|
|
62
68
|
/**
|
|
@@ -90,7 +96,13 @@ export async function doStreamStep(
|
|
|
90
96
|
writable?: WritableStream<ModelCallStreamPart<ToolSet>>,
|
|
91
97
|
serializedTools?: Record<string, SerializableToolDef>,
|
|
92
98
|
options?: DoStreamStepOptions,
|
|
93
|
-
) {
|
|
99
|
+
): Promise<{
|
|
100
|
+
toolCalls: ParsedToolCall[];
|
|
101
|
+
finish: StreamFinish | undefined;
|
|
102
|
+
step: StepResult<ToolSet, any>;
|
|
103
|
+
chunks?: unknown[];
|
|
104
|
+
providerExecutedToolResults: Map<string, ProviderExecutedToolResult>;
|
|
105
|
+
}> {
|
|
94
106
|
'use step';
|
|
95
107
|
|
|
96
108
|
// Resolve model inside step (must happen here for serialization boundary)
|
|
@@ -115,6 +127,7 @@ export async function doStreamStep(
|
|
|
115
127
|
// pre-converted LanguageModelV4Prompt. standardizePrompt inside
|
|
116
128
|
// streamModelCall handles both formats.
|
|
117
129
|
messages: conversationPrompt as unknown as ModelMessage[],
|
|
130
|
+
allowSystemInMessages: true,
|
|
118
131
|
tools,
|
|
119
132
|
toolChoice: options?.toolChoice,
|
|
120
133
|
includeRawChunks: options?.includeRawChunks,
|
|
@@ -143,6 +156,7 @@ export async function doStreamStep(
|
|
|
143
156
|
// Aggregation for StepResult
|
|
144
157
|
let text = '';
|
|
145
158
|
const reasoningParts: Array<{ text: string }> = [];
|
|
159
|
+
const chunks: unknown[] = [];
|
|
146
160
|
let responseMetadata:
|
|
147
161
|
| { id?: string; timestamp?: Date; modelId?: string }
|
|
148
162
|
| undefined;
|
|
@@ -153,6 +167,14 @@ export async function doStreamStep(
|
|
|
153
167
|
|
|
154
168
|
try {
|
|
155
169
|
for await (const part of modelStream) {
|
|
170
|
+
if (
|
|
171
|
+
part.type !== 'model-call-start' &&
|
|
172
|
+
part.type !== 'model-call-end' &&
|
|
173
|
+
part.type !== 'model-call-response-metadata'
|
|
174
|
+
) {
|
|
175
|
+
chunks.push(part);
|
|
176
|
+
}
|
|
177
|
+
|
|
156
178
|
switch (part.type) {
|
|
157
179
|
case 'text-delta':
|
|
158
180
|
text += part.text;
|
|
@@ -234,14 +256,15 @@ export async function doStreamStep(
|
|
|
234
256
|
|
|
235
257
|
const step: StepResult<ToolSet, any> = {
|
|
236
258
|
callId: 'workflow-agent',
|
|
237
|
-
stepNumber: 0,
|
|
259
|
+
stepNumber: options?.stepNumber ?? 0,
|
|
238
260
|
model: {
|
|
239
261
|
provider: responseMetadata?.modelId?.split(':')[0] ?? 'unknown',
|
|
240
262
|
modelId: responseMetadata?.modelId ?? 'unknown',
|
|
241
263
|
},
|
|
242
264
|
functionId: undefined,
|
|
243
265
|
metadata: undefined,
|
|
244
|
-
|
|
266
|
+
runtimeContext: options?.runtimeContext ?? {},
|
|
267
|
+
toolsContext: options?.toolsContext ?? {},
|
|
245
268
|
content: [
|
|
246
269
|
...(text ? [{ type: 'text' as const, text }] : []),
|
|
247
270
|
...toolCalls
|
|
@@ -302,8 +325,21 @@ export async function doStreamStep(
|
|
|
302
325
|
},
|
|
303
326
|
totalTokens: 0,
|
|
304
327
|
} as LanguageModelUsage),
|
|
328
|
+
performance: {
|
|
329
|
+
effectiveOutputTokensPerSecond: 0,
|
|
330
|
+
outputTokensPerSecond: undefined,
|
|
331
|
+
inputTokensPerSecond: undefined,
|
|
332
|
+
effectiveTotalTokensPerSecond: 0,
|
|
333
|
+
stepTimeMs: 0,
|
|
334
|
+
responseTimeMs: 0,
|
|
335
|
+
toolExecutionMs: {},
|
|
336
|
+
timeToFirstOutputMs: undefined,
|
|
337
|
+
},
|
|
305
338
|
warnings,
|
|
306
|
-
request: {
|
|
339
|
+
request: {
|
|
340
|
+
body: '',
|
|
341
|
+
messages: [], // TODO implement step request messages
|
|
342
|
+
},
|
|
307
343
|
response: {
|
|
308
344
|
id: responseMetadata?.id ?? 'unknown',
|
|
309
345
|
timestamp: responseMetadata?.timestamp ?? new Date(),
|
|
@@ -317,6 +353,7 @@ export async function doStreamStep(
|
|
|
317
353
|
toolCalls,
|
|
318
354
|
finish,
|
|
319
355
|
step,
|
|
356
|
+
chunks,
|
|
320
357
|
providerExecutedToolResults,
|
|
321
358
|
};
|
|
322
359
|
}
|
package/src/index.ts
CHANGED
|
@@ -18,16 +18,18 @@ export {
|
|
|
18
18
|
type PrepareStepResult,
|
|
19
19
|
type ProviderOptions,
|
|
20
20
|
type WorkflowAgentOnAbortCallback,
|
|
21
|
+
type WorkflowAgentOnEndCallback,
|
|
21
22
|
type WorkflowAgentOnErrorCallback,
|
|
22
23
|
type WorkflowAgentOnFinishCallback,
|
|
24
|
+
type WorkflowAgentOnStepEndCallback,
|
|
23
25
|
type WorkflowAgentOnStepFinishCallback,
|
|
24
26
|
type StreamTextTransform,
|
|
25
|
-
type
|
|
27
|
+
type TelemetryOptions,
|
|
26
28
|
type ToolCallRepairFunction,
|
|
27
29
|
type WorkflowAgentOnStartCallback,
|
|
28
30
|
type WorkflowAgentOnStepStartCallback,
|
|
29
|
-
type
|
|
30
|
-
type
|
|
31
|
+
type WorkflowAgentOnToolExecutionStartCallback,
|
|
32
|
+
type WorkflowAgentOnToolExecutionEndCallback,
|
|
31
33
|
} from './workflow-agent.js';
|
|
32
34
|
|
|
33
35
|
export {
|
package/src/providers/mock.ts
CHANGED
|
@@ -47,13 +47,13 @@ export function mockTextModel(text: string) {
|
|
|
47
47
|
export function mockSequenceModel(responses: MockResponseDescriptor[]) {
|
|
48
48
|
return mockProvider({
|
|
49
49
|
doStream: async (options: any) => {
|
|
50
|
-
const
|
|
50
|
+
const responseIndex = Math.min(
|
|
51
51
|
options.prompt.filter((m: any) => m.role === 'assistant').length,
|
|
52
52
|
responses.length - 1,
|
|
53
53
|
);
|
|
54
|
-
const
|
|
54
|
+
const selectedResponse = responses[responseIndex];
|
|
55
55
|
const parts =
|
|
56
|
-
|
|
56
|
+
selectedResponse.type === 'text'
|
|
57
57
|
? [
|
|
58
58
|
{ type: 'stream-start', warnings: [] },
|
|
59
59
|
{
|
|
@@ -63,7 +63,7 @@ export function mockSequenceModel(responses: MockResponseDescriptor[]) {
|
|
|
63
63
|
timestamp: new Date(),
|
|
64
64
|
},
|
|
65
65
|
{ type: 'text-start', id: '1' },
|
|
66
|
-
{ type: 'text-delta', id: '1', delta:
|
|
66
|
+
{ type: 'text-delta', id: '1', delta: selectedResponse.text },
|
|
67
67
|
{ type: 'text-end', id: '1' },
|
|
68
68
|
{
|
|
69
69
|
type: 'finish',
|
|
@@ -84,9 +84,9 @@ export function mockSequenceModel(responses: MockResponseDescriptor[]) {
|
|
|
84
84
|
},
|
|
85
85
|
{
|
|
86
86
|
type: 'tool-call',
|
|
87
|
-
toolCallId: `call-${
|
|
88
|
-
toolName:
|
|
89
|
-
input:
|
|
87
|
+
toolCallId: `call-${responseIndex + 1}`,
|
|
88
|
+
toolName: selectedResponse.toolName,
|
|
89
|
+
input: selectedResponse.input,
|
|
90
90
|
},
|
|
91
91
|
{
|
|
92
92
|
type: 'finish',
|
|
@@ -100,7 +100,7 @@ export function mockSequenceModel(responses: MockResponseDescriptor[]) {
|
|
|
100
100
|
return {
|
|
101
101
|
stream: new ReadableStream({
|
|
102
102
|
start(c) {
|
|
103
|
-
for (const
|
|
103
|
+
for (const streamPart of parts as any[]) c.enqueue(streamPart);
|
|
104
104
|
c.close();
|
|
105
105
|
},
|
|
106
106
|
}),
|
|
@@ -23,6 +23,8 @@ export type SerializableToolDef = {
|
|
|
23
23
|
inputSchema: JSONSchema7;
|
|
24
24
|
/** Present on provider tools (e.g. anthropic.tools.webSearch). */
|
|
25
25
|
type?: 'provider';
|
|
26
|
+
/** Provider tool is executed by the provider. */
|
|
27
|
+
isProviderExecuted?: boolean;
|
|
26
28
|
/** Provider tool ID, e.g. 'anthropic.web_search_20250305'. */
|
|
27
29
|
id?: `${string}.${string}`;
|
|
28
30
|
/** Provider tool configuration args (maxUses, allowedDomains, etc.). */
|
|
@@ -41,7 +43,7 @@ export function serializeToolSet(
|
|
|
41
43
|
return Object.fromEntries(
|
|
42
44
|
Object.entries(tools).map(([name, t]) => {
|
|
43
45
|
const def: SerializableToolDef = {
|
|
44
|
-
description: t.description,
|
|
46
|
+
description: t.description as string, // TODO support tools with function descriptions
|
|
45
47
|
inputSchema: asSchema(t.inputSchema).jsonSchema as JSONSchema7,
|
|
46
48
|
};
|
|
47
49
|
|
|
@@ -49,6 +51,7 @@ export function serializeToolSet(
|
|
|
49
51
|
// them as provider-executed tools (e.g. anthropic webSearch).
|
|
50
52
|
if ((t as any).type === 'provider') {
|
|
51
53
|
def.type = 'provider';
|
|
54
|
+
def.isProviderExecuted = (t as any).isProviderExecuted ?? false;
|
|
52
55
|
def.id = (t as any).id;
|
|
53
56
|
def.args = (t as any).args;
|
|
54
57
|
}
|
|
@@ -81,6 +84,7 @@ export function resolveSerializableTools(
|
|
|
81
84
|
type: 'provider' as const,
|
|
82
85
|
id: t.id!,
|
|
83
86
|
args: t.args ?? {},
|
|
87
|
+
isProviderExecuted: t.isProviderExecuted ?? false,
|
|
84
88
|
inputSchema: jsonSchema(t.inputSchema),
|
|
85
89
|
}),
|
|
86
90
|
];
|