@ai-sdk/workflow 2.0.70 → 2.0.72
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 +20 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +130 -79
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/do-generate-step.ts +14 -2
- package/src/do-stream-step.ts +3 -16
- package/src/language-model-prompt-to-model-messages.ts +28 -0
- package/src/model-call-iterator.ts +3 -0
- package/src/model-call.ts +6 -1
- package/src/test/concurrency-workflows.ts +66 -0
- package/src/workflow-agent.ts +170 -120
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/workflow",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.72",
|
|
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.
|
|
41
|
+
"ai": "7.0.140",
|
|
42
42
|
"ajv": "^8.20.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@ai-sdk/otel": "1.0.
|
|
45
|
+
"@ai-sdk/otel": "1.0.140",
|
|
46
46
|
"@opentelemetry/api": "1.9.1",
|
|
47
47
|
"@opentelemetry/sdk-trace-base": "2.7.1",
|
|
48
48
|
"@types/node": "22.19.19",
|
package/src/do-generate-step.ts
CHANGED
|
@@ -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:
|
|
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
|
-
|
|
174
|
+
modelPrompt
|
|
163
175
|
.filter(message => message.role === 'system')
|
|
164
176
|
.map(message => message.content)
|
|
165
177
|
.join('\n') || undefined,
|
package/src/do-stream-step.ts
CHANGED
|
@@ -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
|
|
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 {
|
|
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;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { tool, type ModelMessage } from 'ai';
|
|
2
|
+
import { createHook } from 'workflow';
|
|
3
|
+
import { z } from 'zod/v4';
|
|
4
|
+
import { mockTextModel } from '../providers/mock.js';
|
|
5
|
+
import { WorkflowAgent } from '../workflow-agent.js';
|
|
6
|
+
|
|
7
|
+
async function completeAction(id: string) {
|
|
8
|
+
'use step';
|
|
9
|
+
return id;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function resumeSequentialApprovals(mode: 'generate' | 'stream') {
|
|
13
|
+
'use workflow';
|
|
14
|
+
const events: string[] = [];
|
|
15
|
+
const agent = new WorkflowAgent({
|
|
16
|
+
model: mockTextModel('Done.'),
|
|
17
|
+
toolCallConcurrency: 1,
|
|
18
|
+
tools: {
|
|
19
|
+
action: tool({
|
|
20
|
+
inputSchema: z.object({ id: z.string() }),
|
|
21
|
+
needsApproval: true,
|
|
22
|
+
execute: async ({ id }) => {
|
|
23
|
+
events.push(`start:${id}`);
|
|
24
|
+
if (id === 'first') {
|
|
25
|
+
using hook = createHook<string>();
|
|
26
|
+
await hook;
|
|
27
|
+
}
|
|
28
|
+
const result = await completeAction(id);
|
|
29
|
+
events.push(`end:${id}`);
|
|
30
|
+
return result;
|
|
31
|
+
},
|
|
32
|
+
}),
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
const ids = ['first', 'second'];
|
|
36
|
+
const messages: ModelMessage[] = [
|
|
37
|
+
{ role: 'user', content: 'Run the tools.' },
|
|
38
|
+
{
|
|
39
|
+
role: 'assistant',
|
|
40
|
+
content: ids.flatMap(id => [
|
|
41
|
+
{
|
|
42
|
+
type: 'tool-call' as const,
|
|
43
|
+
toolCallId: id,
|
|
44
|
+
toolName: 'action',
|
|
45
|
+
input: { id },
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
type: 'tool-approval-request' as const,
|
|
49
|
+
toolCallId: id,
|
|
50
|
+
approvalId: `approval-${id}`,
|
|
51
|
+
},
|
|
52
|
+
]),
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
role: 'tool',
|
|
56
|
+
content: [...ids].reverse().map(id => ({
|
|
57
|
+
type: 'tool-approval-response' as const,
|
|
58
|
+
approvalId: `approval-${id}`,
|
|
59
|
+
approved: true,
|
|
60
|
+
})),
|
|
61
|
+
},
|
|
62
|
+
];
|
|
63
|
+
if (mode === 'generate') await agent.generate({ messages });
|
|
64
|
+
else await agent.stream({ messages });
|
|
65
|
+
return events;
|
|
66
|
+
}
|