@ai-sdk/workflow 1.0.0-beta.2 → 1.0.0-beta.20

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.2",
3
+ "version": "1.0.0-beta.20",
4
4
  "description": "WorkflowAgent for building AI agents with AI SDK",
5
5
  "license": "Apache-2.0",
6
6
  "main": "./dist/index.js",
@@ -27,9 +27,9 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "ajv": "^8.18.0",
30
+ "ai": "7.0.0-beta.106",
30
31
  "@ai-sdk/provider": "4.0.0-beta.12",
31
- "@ai-sdk/provider-utils": "5.0.0-beta.20",
32
- "ai": "7.0.0-beta.91"
32
+ "@ai-sdk/provider-utils": "5.0.0-beta.22"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/node": "20.17.24",
@@ -16,12 +16,11 @@ import {
16
16
  type ToolSet,
17
17
  } from 'ai';
18
18
  import { gateway } from 'ai';
19
- import type { ProviderOptions, TelemetrySettings } from './workflow-agent.js';
19
+ import type { ProviderOptions, TelemetryOptions } from './workflow-agent.js';
20
20
  import {
21
21
  resolveSerializableTools,
22
22
  type SerializableToolDef,
23
23
  } from './serializable-schema.js';
24
- import type { CompatibleLanguageModel } from './types.js';
25
24
 
26
25
  export type { Experimental_LanguageModelStreamPart as ModelCallStreamPart } from 'ai';
27
26
 
@@ -55,7 +54,7 @@ export interface DoStreamStepOptions {
55
54
  providerOptions?: ProviderOptions;
56
55
  toolChoice?: ToolChoice<ToolSet>;
57
56
  includeRawChunks?: boolean;
58
- experimental_telemetry?: TelemetrySettings;
57
+ experimental_telemetry?: TelemetryOptions;
59
58
  repairToolCall?: ToolCallRepairFunction<ToolSet>;
60
59
  responseFormat?: LanguageModelV4CallOptions['responseFormat'];
61
60
  }
@@ -87,10 +86,7 @@ export interface StreamFinish {
87
86
 
88
87
  export async function doStreamStep(
89
88
  conversationPrompt: LanguageModelV4Prompt,
90
- modelInit:
91
- | string
92
- | CompatibleLanguageModel
93
- | (() => Promise<CompatibleLanguageModel>),
89
+ modelInit: LanguageModel,
94
90
  writable?: WritableStream<ModelCallStreamPart<ToolSet>>,
95
91
  serializedTools?: Record<string, SerializableToolDef>,
96
92
  options?: DoStreamStepOptions,
@@ -98,22 +94,10 @@ export async function doStreamStep(
98
94
  'use step';
99
95
 
100
96
  // Resolve model inside step (must happen here for serialization boundary)
101
- let model: CompatibleLanguageModel;
102
- if (typeof modelInit === 'string') {
103
- model = gateway.languageModel(modelInit) as CompatibleLanguageModel;
104
- } else if (typeof modelInit === 'function') {
105
- model = await modelInit();
106
- } else if (
107
- typeof modelInit === 'object' &&
108
- modelInit !== null &&
109
- 'modelId' in modelInit
110
- ) {
111
- model = modelInit;
112
- } else {
113
- throw new Error(
114
- 'Invalid "model initialization" argument. Must be a string, a LanguageModel instance, or a function that returns a LanguageModel instance.',
115
- );
116
- }
97
+ const model: LanguageModel =
98
+ typeof modelInit === 'string'
99
+ ? gateway.languageModel(modelInit)
100
+ : modelInit;
117
101
 
118
102
  // Reconstruct tools from serializable definitions with Ajv validation.
119
103
  // Tools are serialized before crossing the step boundary because zod schemas
@@ -126,7 +110,7 @@ export async function doStreamStep(
126
110
  // model.doStream(), retry logic, and stream part transformation
127
111
  // (tool call parsing, finish reason mapping, file wrapping).
128
112
  const { stream: modelStream } = await streamModelCall({
129
- model: model as LanguageModel,
113
+ model,
130
114
  // streamModelCall expects Prompt (ModelMessage[]) but we pass the
131
115
  // pre-converted LanguageModelV4Prompt. standardizePrompt inside
132
116
  // streamModelCall handles both formats.
@@ -257,7 +241,8 @@ export async function doStreamStep(
257
241
  },
258
242
  functionId: undefined,
259
243
  metadata: undefined,
260
- context: undefined,
244
+ runtimeContext: undefined,
245
+ toolsContext: {},
261
246
  content: [
262
247
  ...(text ? [{ type: 'text' as const, text }] : []),
263
248
  ...toolCalls
package/src/index.ts CHANGED
@@ -17,11 +17,12 @@ export {
17
17
  type PrepareStepInfo,
18
18
  type PrepareStepResult,
19
19
  type ProviderOptions,
20
- type StreamTextOnAbortCallback,
21
- type StreamTextOnErrorCallback,
22
- type StreamTextOnFinishCallback,
20
+ type WorkflowAgentOnAbortCallback,
21
+ type WorkflowAgentOnErrorCallback,
22
+ type WorkflowAgentOnFinishCallback,
23
+ type WorkflowAgentOnStepFinishCallback,
23
24
  type StreamTextTransform,
24
- type TelemetrySettings,
25
+ type TelemetryOptions,
25
26
  type ToolCallRepairFunction,
26
27
  type WorkflowAgentOnStartCallback,
27
28
  type WorkflowAgentOnStepStartCallback,
@@ -6,19 +6,55 @@ export type MockResponseDescriptor =
6
6
 
7
7
  /**
8
8
  * Mock model that returns a fixed text response.
9
- * Same 'use step' pattern as real providers (anthropic, openai, etc.).
10
- * Only captures `text` (string) — fully serializable across step boundary.
11
9
  */
12
10
  export function mockTextModel(text: string) {
13
- return async () => {
14
- 'use step';
15
- // Bind closure var at step body level so SWC plugin detects it
16
- const _text = text;
17
- return mockProvider({
18
- doStream: async () => ({
19
- stream: new ReadableStream({
20
- start(c) {
21
- for (const v of [
11
+ return mockProvider({
12
+ doStream: async () => ({
13
+ stream: new ReadableStream({
14
+ start(c) {
15
+ for (const v of [
16
+ { type: 'stream-start', warnings: [] },
17
+ {
18
+ type: 'response-metadata',
19
+ id: 'r',
20
+ modelId: 'mock',
21
+ timestamp: new Date(),
22
+ },
23
+ { type: 'text-start', id: '1' },
24
+ { type: 'text-delta', id: '1', delta: text },
25
+ { type: 'text-end', id: '1' },
26
+ {
27
+ type: 'finish',
28
+ finishReason: { unified: 'stop', raw: 'stop' },
29
+ usage: {
30
+ inputTokens: { total: 5, noCache: 5 },
31
+ outputTokens: { total: 10, text: 10 },
32
+ },
33
+ },
34
+ ] as any[])
35
+ c.enqueue(v);
36
+ c.close();
37
+ },
38
+ }),
39
+ }),
40
+ });
41
+ }
42
+
43
+ /**
44
+ * Mock model that plays through a sequence of responses.
45
+ * Determines which response to return by counting assistant messages in the prompt.
46
+ */
47
+ export function mockSequenceModel(responses: MockResponseDescriptor[]) {
48
+ return mockProvider({
49
+ doStream: async (options: any) => {
50
+ const idx = Math.min(
51
+ options.prompt.filter((m: any) => m.role === 'assistant').length,
52
+ responses.length - 1,
53
+ );
54
+ const r = responses[idx];
55
+ const parts =
56
+ r.type === 'text'
57
+ ? [
22
58
  { type: 'stream-start', warnings: [] },
23
59
  {
24
60
  type: 'response-metadata',
@@ -27,7 +63,7 @@ export function mockTextModel(text: string) {
27
63
  timestamp: new Date(),
28
64
  },
29
65
  { type: 'text-start', id: '1' },
30
- { type: 'text-delta', id: '1', delta: _text },
66
+ { type: 'text-delta', id: '1', delta: r.text },
31
67
  { type: 'text-end', id: '1' },
32
68
  {
33
69
  type: 'finish',
@@ -37,87 +73,38 @@ export function mockTextModel(text: string) {
37
73
  outputTokens: { total: 10, text: 10 },
38
74
  },
39
75
  },
40
- ] as any[])
41
- c.enqueue(v);
76
+ ]
77
+ : [
78
+ { type: 'stream-start', warnings: [] },
79
+ {
80
+ type: 'response-metadata',
81
+ id: 'r',
82
+ modelId: 'mock',
83
+ timestamp: new Date(),
84
+ },
85
+ {
86
+ type: 'tool-call',
87
+ toolCallId: `call-${idx + 1}`,
88
+ toolName: r.toolName,
89
+ input: r.input,
90
+ },
91
+ {
92
+ type: 'finish',
93
+ finishReason: { unified: 'tool-calls', raw: undefined },
94
+ usage: {
95
+ inputTokens: { total: 5, noCache: 5 },
96
+ outputTokens: { total: 10, text: 10 },
97
+ },
98
+ },
99
+ ];
100
+ return {
101
+ stream: new ReadableStream({
102
+ start(c) {
103
+ for (const p of parts as any[]) c.enqueue(p);
42
104
  c.close();
43
105
  },
44
106
  }),
45
- }),
46
- });
47
- };
48
- }
49
-
50
- /**
51
- * Mock model that plays through a sequence of responses.
52
- * Determines which response to return by counting assistant messages in the prompt.
53
- * Only captures `responses` (array of plain objects) — fully serializable.
54
- */
55
- export function mockSequenceModel(responses: MockResponseDescriptor[]) {
56
- return async () => {
57
- 'use step';
58
- // Bind closure var at step body level so SWC plugin detects it
59
- const _responses = responses;
60
- return mockProvider({
61
- doStream: async (options: any) => {
62
- const idx = Math.min(
63
- options.prompt.filter((m: any) => m.role === 'assistant').length,
64
- _responses.length - 1,
65
- );
66
- const r = _responses[idx];
67
- const parts =
68
- r.type === 'text'
69
- ? [
70
- { type: 'stream-start', warnings: [] },
71
- {
72
- type: 'response-metadata',
73
- id: 'r',
74
- modelId: 'mock',
75
- timestamp: new Date(),
76
- },
77
- { type: 'text-start', id: '1' },
78
- { type: 'text-delta', id: '1', delta: r.text },
79
- { type: 'text-end', id: '1' },
80
- {
81
- type: 'finish',
82
- finishReason: { unified: 'stop', raw: 'stop' },
83
- usage: {
84
- inputTokens: { total: 5, noCache: 5 },
85
- outputTokens: { total: 10, text: 10 },
86
- },
87
- },
88
- ]
89
- : [
90
- { type: 'stream-start', warnings: [] },
91
- {
92
- type: 'response-metadata',
93
- id: 'r',
94
- modelId: 'mock',
95
- timestamp: new Date(),
96
- },
97
- {
98
- type: 'tool-call',
99
- toolCallId: `call-${idx + 1}`,
100
- toolName: r.toolName,
101
- input: r.input,
102
- },
103
- {
104
- type: 'finish',
105
- finishReason: { unified: 'tool-calls', raw: undefined },
106
- usage: {
107
- inputTokens: { total: 5, noCache: 5 },
108
- outputTokens: { total: 10, text: 10 },
109
- },
110
- },
111
- ];
112
- return {
113
- stream: new ReadableStream({
114
- start(c) {
115
- for (const p of parts as any[]) c.enqueue(p);
116
- c.close();
117
- },
118
- }),
119
- };
120
- },
121
- });
122
- };
107
+ };
108
+ },
109
+ });
123
110
  }
@@ -5,9 +5,9 @@ import type {
5
5
  } from '@ai-sdk/provider';
6
6
  import type {
7
7
  Experimental_LanguageModelStreamPart as ModelCallStreamPart,
8
+ LanguageModel,
8
9
  ModelMessage,
9
10
  StepResult,
10
- StreamTextOnStepFinishCallback,
11
11
  ToolCallRepairFunction,
12
12
  ToolChoice,
13
13
  ToolSet,
@@ -22,11 +22,11 @@ import { serializeToolSet } from './serializable-schema.js';
22
22
  import type {
23
23
  GenerationSettings,
24
24
  PrepareStepCallback,
25
- StreamTextOnErrorCallback,
26
- TelemetrySettings,
25
+ WorkflowAgentOnErrorCallback,
26
+ WorkflowAgentOnStepFinishCallback,
27
+ TelemetryOptions,
27
28
  WorkflowAgentOnStepStartCallback,
28
29
  } from './workflow-agent.js';
29
- import type { CompatibleLanguageModel } from './types.js';
30
30
 
31
31
  // Re-export for consumers
32
32
  export type { ProviderExecutedToolResult } from './do-stream-step.js';
@@ -55,7 +55,6 @@ export async function* streamTextIterator({
55
55
  writable,
56
56
  model,
57
57
  stopConditions,
58
- maxSteps,
59
58
  onStepFinish,
60
59
  onStepStart,
61
60
  onError,
@@ -71,20 +70,16 @@ export async function* streamTextIterator({
71
70
  prompt: LanguageModelV4Prompt;
72
71
  tools: ToolSet;
73
72
  writable?: WritableStream<ModelCallStreamPart<ToolSet>>;
74
- model:
75
- | string
76
- | CompatibleLanguageModel
77
- | (() => Promise<CompatibleLanguageModel>);
73
+ model: LanguageModel;
78
74
  stopConditions?: ModelStopCondition[] | ModelStopCondition;
79
- maxSteps?: number;
80
- onStepFinish?: StreamTextOnStepFinishCallback<any, any>;
75
+ onStepFinish?: WorkflowAgentOnStepFinishCallback<any>;
81
76
  onStepStart?: WorkflowAgentOnStepStartCallback;
82
- onError?: StreamTextOnErrorCallback;
77
+ onError?: WorkflowAgentOnErrorCallback;
83
78
  prepareStep?: PrepareStepCallback<any>;
84
79
  generationSettings?: GenerationSettings;
85
80
  toolChoice?: ToolChoice<ToolSet>;
86
81
  experimental_context?: unknown;
87
- experimental_telemetry?: TelemetrySettings;
82
+ experimental_telemetry?: TelemetryOptions;
88
83
  includeRawChunks?: boolean;
89
84
  repairToolCall?: ToolCallRepairFunction<ToolSet>;
90
85
  responseFormat?: LanguageModelV4CallOptions['responseFormat'];
@@ -94,10 +89,7 @@ export async function* streamTextIterator({
94
89
  LanguageModelV4ToolResultPart[]
95
90
  > {
96
91
  let conversationPrompt = [...prompt]; // Create a mutable copy
97
- let currentModel:
98
- | string
99
- | CompatibleLanguageModel
100
- | (() => Promise<CompatibleLanguageModel>) = model;
92
+ let currentModel: LanguageModel = model;
101
93
  let currentGenerationSettings = generationSettings ?? {};
102
94
  let currentToolChoice = toolChoice;
103
95
  let currentContext = experimental_context;
@@ -110,16 +102,7 @@ export async function* streamTextIterator({
110
102
  let lastStep: StepResult<any, any> | undefined;
111
103
  let lastStepWasToolCalls = false;
112
104
 
113
- // Default maxSteps to Infinity to preserve backwards compatibility
114
- // (agent loops until completion unless explicitly limited)
115
- const effectiveMaxSteps = maxSteps ?? Infinity;
116
-
117
105
  while (!done) {
118
- // Check if we've exceeded the maximum number of steps
119
- if (stepNumber >= effectiveMaxSteps) {
120
- break;
121
- }
122
-
123
106
  // Check for abort signal
124
107
  if (currentGenerationSettings.abortSignal?.aborted) {
125
108
  break;
@@ -248,6 +231,7 @@ export async function* streamTextIterator({
248
231
  stepNumber,
249
232
  model: currentModel,
250
233
  messages: conversationPrompt as unknown as ModelMessage[],
234
+ steps: [...steps],
251
235
  });
252
236
  }
253
237
 
package/src/telemetry.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { TelemetrySettings } from './workflow-agent.js';
1
+ import type { TelemetryOptions } from './workflow-agent.js';
2
2
 
3
3
  // Minimal OTel type shims so we don't depend on @opentelemetry/api at compile time.
4
4
  type Attributes = Record<string, unknown>;
@@ -62,7 +62,7 @@ async function ensureOtelApi(): Promise<OtelApi | null> {
62
62
  * Returns a no-op–equivalent `null` when telemetry is disabled, so callers
63
63
  * don't need a separate init step.
64
64
  */
65
- function getTracer(telemetry?: TelemetrySettings): Tracer | null {
65
+ function getTracer(telemetry?: TelemetryOptions): Tracer | null {
66
66
  if (!telemetry?.isEnabled || !otelApi) return null;
67
67
  if (telemetry.tracer) return telemetry.tracer as Tracer;
68
68
  return otelApi.trace.getTracer('ai');
@@ -76,7 +76,7 @@ function getTracer(telemetry?: TelemetrySettings): Tracer | null {
76
76
  */
77
77
  function assembleOperationName(
78
78
  operationId: string,
79
- telemetry?: TelemetrySettings,
79
+ telemetry?: TelemetryOptions,
80
80
  ): Attributes {
81
81
  return {
82
82
  'operation.name': `${operationId}${
@@ -94,7 +94,7 @@ function assembleOperationName(
94
94
  */
95
95
  function buildAttributes(
96
96
  operationId: string,
97
- telemetry: TelemetrySettings | undefined,
97
+ telemetry: TelemetryOptions | undefined,
98
98
  extra?: Attributes,
99
99
  ): Attributes {
100
100
  if (!telemetry?.isEnabled) return {};
@@ -154,7 +154,7 @@ function recordErrorOnSpan(span: Span, error: unknown): void {
154
154
  */
155
155
  export async function recordSpan<T>(options: {
156
156
  name: string;
157
- telemetry?: TelemetrySettings;
157
+ telemetry?: TelemetryOptions;
158
158
  attributes?: Attributes;
159
159
  fn: (span?: Span) => PromiseLike<T> | T;
160
160
  }): Promise<T> {