@ai-sdk/workflow 2.0.70 → 2.0.71

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": "2.0.70",
3
+ "version": "2.0.71",
4
4
  "type": "module",
5
5
  "description": "WorkflowAgent for building AI agents with AI SDK",
6
6
  "license": "Apache-2.0",
@@ -38,11 +38,11 @@
38
38
  "dependencies": {
39
39
  "@ai-sdk/provider": "4.0.27",
40
40
  "@ai-sdk/provider-utils": "5.0.59",
41
- "ai": "7.0.138",
41
+ "ai": "7.0.139",
42
42
  "ajv": "^8.20.0"
43
43
  },
44
44
  "devDependencies": {
45
- "@ai-sdk/otel": "1.0.138",
45
+ "@ai-sdk/otel": "1.0.139",
46
46
  "@opentelemetry/api": "1.9.1",
47
47
  "@opentelemetry/sdk-trace-base": "2.7.1",
48
48
  "@types/node": "22.19.19",
@@ -7,6 +7,7 @@ import {
7
7
  } from 'ai';
8
8
  import {
9
9
  asLanguageModelUsage,
10
+ convertToLanguageModelPrompt,
10
11
  parseToolCall,
11
12
  prepareLanguageModelCallOptions,
12
13
  prepareRetries,
@@ -14,6 +15,7 @@ import {
14
15
  prepareTools,
15
16
  resolveLanguageModel,
16
17
  } from 'ai/internal';
18
+ import { languageModelPromptToModelMessages } from './language-model-prompt-to-model-messages.js';
17
19
  import type {
18
20
  ModelCallOptions,
19
21
  ModelCallRawContentPart,
@@ -90,6 +92,16 @@ async function generateModelCall(
90
92
  AbortSignal.timeout(remaining),
91
93
  ]);
92
94
  const model = resolveLanguageModel(modelInit);
95
+ const modelPrompt = await convertToLanguageModelPrompt({
96
+ prompt: {
97
+ instructions: undefined,
98
+ messages: languageModelPromptToModelMessages(prompt),
99
+ },
100
+ supportedUrls: await model.supportedUrls,
101
+ download: options.download,
102
+ abortSignal,
103
+ provider: model.provider.split('.')[0],
104
+ });
93
105
  const tools = resolveSerializableTools(serializedTools);
94
106
  const toolChoice = prepareToolChoice({ toolChoice: options.toolChoice });
95
107
  const modelTools = await prepareTools({ tools });
@@ -125,7 +137,7 @@ async function generateModelCall(
125
137
  execute: () =>
126
138
  model.doGenerate({
127
139
  ...settings,
128
- prompt: [...prompt],
140
+ prompt: modelPrompt,
129
141
  tools: modelTools,
130
142
  toolChoice,
131
143
  responseFormat: options.responseFormat,
@@ -159,7 +171,7 @@ async function generateModelCall(
159
171
  tools,
160
172
  repairToolCall: options.repairToolCall,
161
173
  instructions:
162
- prompt
174
+ modelPrompt
163
175
  .filter(message => message.role === 'system')
164
176
  .map(message => message.content)
165
177
  .join('\n') || undefined,
@@ -10,10 +10,10 @@ import {
10
10
  NoOutputGeneratedError,
11
11
  wrapLanguageModel,
12
12
  type LanguageModel,
13
- type ModelMessage,
14
13
  type ToolSet,
15
14
  } from 'ai';
16
15
  import { isOutputChunk, prepareRetries } from 'ai/internal';
16
+ import { languageModelPromptToModelMessages } from './language-model-prompt-to-model-messages.js';
17
17
  import type { StreamTextTransform } from './workflow-agent.js';
18
18
  import {
19
19
  resolveSerializableTools,
@@ -178,21 +178,7 @@ async function streamModelStep(
178
178
  // streamModelCall expects Prompt (ModelMessage[]) but we pass the
179
179
  // pre-converted LanguageModelV4Prompt. standardizePrompt inside
180
180
  // streamModelCall handles both formats.
181
- messages: conversationPrompt.map(message =>
182
- message.role !== 'tool'
183
- ? message
184
- : {
185
- ...message,
186
- // Provider prompt approval responses have already been filtered by
187
- // convertToLanguageModelPrompt. Restore the marker expected by the
188
- // model-call helper when it converts these messages again.
189
- content: message.content.map(part =>
190
- part.type === 'tool-approval-response'
191
- ? { ...part, providerExecuted: true }
192
- : part,
193
- ),
194
- },
195
- ) as unknown as ModelMessage[],
181
+ messages: languageModelPromptToModelMessages(conversationPrompt),
196
182
  allowSystemInMessages: true,
197
183
  tools,
198
184
  toolChoice: options?.toolChoice,
@@ -211,6 +197,7 @@ async function streamModelStep(
211
197
  stopSequences: options?.stopSequences,
212
198
  seed: options?.seed,
213
199
  repairToolCall: options?.repairToolCall,
200
+ download: options?.download,
214
201
  ...languageModelCallTelemetry,
215
202
  }),
216
203
  );
@@ -0,0 +1,28 @@
1
+ import type { LanguageModelV4Prompt } from '@ai-sdk/provider';
2
+ import type { ModelMessage } from 'ai';
3
+
4
+ /**
5
+ * Prepares a workflow-side `LanguageModelV4Prompt` for a second, model-aware
6
+ * `convertToLanguageModelPrompt` pass inside a model step.
7
+ *
8
+ * The workflow-side conversion preserves URLs (it cannot download them) and
9
+ * filters out approval responses for tools that are not provider-executed.
10
+ * The remaining approval responses are restored with the `providerExecuted`
11
+ * marker that the second conversion expects.
12
+ */
13
+ export function languageModelPromptToModelMessages(
14
+ prompt: LanguageModelV4Prompt,
15
+ ): ModelMessage[] {
16
+ return prompt.map(message =>
17
+ message.role !== 'tool'
18
+ ? message
19
+ : {
20
+ ...message,
21
+ content: message.content.map(part =>
22
+ part.type === 'tool-approval-response'
23
+ ? { ...part, providerExecuted: true }
24
+ : part,
25
+ ),
26
+ },
27
+ ) as unknown as ModelMessage[];
28
+ }
@@ -143,6 +143,7 @@ export async function* modelCallIterator({
143
143
  includeRawChunks = false,
144
144
  timeoutAt,
145
145
  repairToolCall,
146
+ download,
146
147
  responseFormat,
147
148
  experimental_transform,
148
149
  experimental_sandbox: sandbox,
@@ -170,6 +171,7 @@ export async function* modelCallIterator({
170
171
  includeRawChunks?: boolean;
171
172
  timeoutAt?: number;
172
173
  repairToolCall?: ToolCallRepairFunction<ToolSet>;
174
+ download?: ModelCallOptions['download'];
173
175
  responseFormat?: LanguageModelV4CallOptions['responseFormat'];
174
176
  experimental_transform?:
175
177
  | StreamTextTransform<ToolSet>
@@ -380,6 +382,7 @@ export async function* modelCallIterator({
380
382
  includeRawChunks,
381
383
  timeoutAt,
382
384
  repairToolCall,
385
+ download,
383
386
  responseFormat,
384
387
  include,
385
388
  experimental_transform,
package/src/model-call.ts CHANGED
@@ -15,7 +15,11 @@ import type {
15
15
  ToolChoice,
16
16
  ToolSet,
17
17
  } from 'ai';
18
- import type { ProviderOptions, StreamTextTransform } from './workflow-agent.js';
18
+ import type {
19
+ DownloadFunction,
20
+ ProviderOptions,
21
+ StreamTextTransform,
22
+ } from './workflow-agent.js';
19
23
 
20
24
  export type ModelCallStreamPart<TTools extends ToolSet = ToolSet> =
21
25
  | Experimental_LanguageModelStreamPart<TTools>
@@ -62,6 +66,7 @@ export interface ModelCallOptions {
62
66
  toolChoice?: ToolChoice<ToolSet>;
63
67
  includeRawChunks?: boolean;
64
68
  repairToolCall?: ToolCallRepairFunction<ToolSet>;
69
+ download?: DownloadFunction;
65
70
  include?: {
66
71
  requestBody?: boolean;
67
72
  responseBody?: boolean;
@@ -578,6 +578,7 @@ export type WorkflowAgentOptions<
578
578
 
579
579
  /**
580
580
  * Default custom download function to use for URLs.
581
+ * Mark the function with `'use step'` when used in a workflow.
581
582
  *
582
583
  * Per-stream `experimental_download` values passed to `stream()` override this default.
583
584
  */
@@ -1074,6 +1075,7 @@ export type WorkflowAgentCallOptions<
1074
1075
  /**
1075
1076
  * Custom download function to use for URLs.
1076
1077
  * By default, files are downloaded if the model does not support the URL for the given media type.
1078
+ * Mark the function with `'use step'` when used in a workflow.
1077
1079
  */
1078
1080
  experimental_download?: DownloadFunction;
1079
1081
 
@@ -1834,7 +1836,8 @@ export class WorkflowAgent<
1834
1836
  ? { prompt: effectivePrompt }
1835
1837
  : { messages: effectiveMessages! }),
1836
1838
  } as Prompt);
1837
- const download = effectiveDownloadFromPrepare;
1839
+ const workflowDownload: DownloadFunction = async requests =>
1840
+ requests.map(() => null);
1838
1841
  const sandbox = options.experimental_sandbox ?? this.experimentalSandbox;
1839
1842
  // Model steps enforce the absolute deadline below. Avoid creating a native
1840
1843
  // timeout signal in the workflow VM, where timer APIs are unavailable.
@@ -1872,7 +1875,8 @@ export class WorkflowAgent<
1872
1875
  effectiveTelemetry,
1873
1876
  telemetryDispatcher,
1874
1877
  prompt,
1875
- download,
1878
+ download: effectiveDownloadFromPrepare,
1879
+ workflowDownload,
1876
1880
  sandbox,
1877
1881
  effectiveAbortSignal,
1878
1882
  timeoutAt,
@@ -1909,6 +1913,7 @@ export class WorkflowAgent<
1909
1913
  telemetryDispatcher,
1910
1914
  prompt,
1911
1915
  download,
1916
+ workflowDownload,
1912
1917
  sandbox,
1913
1918
  effectiveAbortSignal,
1914
1919
  timeoutAt,
@@ -2003,7 +2008,7 @@ export class WorkflowAgent<
2003
2008
  tool,
2004
2009
  errorMode: 'text',
2005
2010
  supportedUrls: {},
2006
- download,
2011
+ download: workflowDownload,
2007
2012
  }),
2008
2013
  });
2009
2014
  continue;
@@ -2061,7 +2066,7 @@ export class WorkflowAgent<
2061
2066
  tool,
2062
2067
  errorMode: 'text',
2063
2068
  supportedUrls: {},
2064
- download,
2069
+ download: workflowDownload,
2065
2070
  }),
2066
2071
  });
2067
2072
  continue;
@@ -2183,7 +2188,7 @@ export class WorkflowAgent<
2183
2188
  const modelPrompt = await convertToLanguageModelPrompt({
2184
2189
  prompt,
2185
2190
  supportedUrls: {},
2186
- download,
2191
+ download: workflowDownload,
2187
2192
  });
2188
2193
 
2189
2194
  // Merge generation settings: constructor defaults < prepareCall < stream options
@@ -2368,7 +2373,7 @@ export class WorkflowAgent<
2368
2373
  messages,
2369
2374
  resolvedContext,
2370
2375
  effectiveAbortSignal,
2371
- download,
2376
+ workflowDownload,
2372
2377
  stepSandbox,
2373
2378
  );
2374
2379
  result =
@@ -2546,6 +2551,7 @@ export class WorkflowAgent<
2546
2551
  repairToolCall: (options.repairToolCall ??
2547
2552
  options.experimental_repairToolCall ??
2548
2553
  this.repairToolCall) as ToolCallRepairFunction<ToolSet> | undefined,
2554
+ download,
2549
2555
  responseFormat: await (options.output ?? this.output)?.responseFormat,
2550
2556
  experimental_transform: options.experimental_transform as
2551
2557
  | StreamTextTransform<ToolSet>
@@ -2735,7 +2741,7 @@ export class WorkflowAgent<
2735
2741
  toolCall,
2736
2742
  capturedProviderToolResults,
2737
2743
  stepTools,
2738
- download,
2744
+ workflowDownload,
2739
2745
  ),
2740
2746
  })),
2741
2747
  );
@@ -2942,7 +2948,7 @@ export class WorkflowAgent<
2942
2948
  toolCall,
2943
2949
  capturedProviderToolResults,
2944
2950
  stepTools,
2945
- download,
2951
+ workflowDownload,
2946
2952
  ),
2947
2953
  })),
2948
2954
  );