@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/workflow",
3
- "version": "1.0.0-beta.9",
3
+ "version": "1.0.0-beta.95",
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.18.0",
30
- "@ai-sdk/provider": "4.0.0-beta.12",
31
- "@ai-sdk/provider-utils": "5.0.0-beta.20",
32
- "ai": "7.0.0-beta.95"
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.178"
33
33
  },
34
34
  "devDependencies": {
35
- "@types/node": "20.17.24",
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.0",
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": ">=18"
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": "git+https://github.com/vercel/ai.git"
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
+ }
@@ -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 { gateway } from 'ai';
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
- context: undefined,
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: { body: '' },
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 TelemetrySettings,
27
+ type TelemetryOptions,
26
28
  type ToolCallRepairFunction,
27
29
  type WorkflowAgentOnStartCallback,
28
30
  type WorkflowAgentOnStepStartCallback,
29
- type WorkflowAgentOnToolCallStartCallback,
30
- type WorkflowAgentOnToolCallFinishCallback,
31
+ type WorkflowAgentOnToolExecutionStartCallback,
32
+ type WorkflowAgentOnToolExecutionEndCallback,
31
33
  } from './workflow-agent.js';
32
34
 
33
35
  export {
@@ -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 idx = Math.min(
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 r = responses[idx];
54
+ const selectedResponse = responses[responseIndex];
55
55
  const parts =
56
- r.type === 'text'
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: r.text },
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-${idx + 1}`,
88
- toolName: r.toolName,
89
- input: r.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 p of parts as any[]) c.enqueue(p);
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
  ];