@ai-sdk/workflow 1.0.0-canary.71 → 1.0.0-canary.73
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 +18 -0
- package/dist/index.d.mts +26 -3
- package/dist/index.mjs +44 -32
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/do-stream-step.ts +1 -1
- package/src/index.ts +1 -0
- package/src/providers/mock.ts +8 -8
- package/src/to-ui-message-chunk.ts +8 -8
- package/src/workflow-agent.ts +59 -17
- package/src/workflow-chat-transport.ts +15 -13
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/workflow",
|
|
3
|
-
"version": "1.0.0-canary.
|
|
3
|
+
"version": "1.0.0-canary.73",
|
|
4
4
|
"description": "WorkflowAgent for building AI agents with AI SDK",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"ajv": "^8.20.0",
|
|
30
30
|
"@ai-sdk/provider": "4.0.0-canary.17",
|
|
31
31
|
"@ai-sdk/provider-utils": "5.0.0-canary.44",
|
|
32
|
-
"ai": "7.0.0-canary.
|
|
32
|
+
"ai": "7.0.0-canary.156"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/node": "22.19.19",
|
package/src/do-stream-step.ts
CHANGED
package/src/index.ts
CHANGED
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
|
}),
|
|
@@ -191,20 +191,20 @@ export function toUIMessageChunk(
|
|
|
191
191
|
// standard ModelCallStreamPart types but are written by the
|
|
192
192
|
// WorkflowAgent between tool execution and the next model step
|
|
193
193
|
// to ensure proper message splitting in convertToModelMessages.
|
|
194
|
-
const
|
|
195
|
-
if (
|
|
194
|
+
const passthroughPart = part as any;
|
|
195
|
+
if (passthroughPart.type === 'tool-approval-request') {
|
|
196
196
|
return {
|
|
197
197
|
type: 'tool-approval-request',
|
|
198
|
-
approvalId:
|
|
199
|
-
toolCallId:
|
|
198
|
+
approvalId: passthroughPart.approvalId,
|
|
199
|
+
toolCallId: passthroughPart.toolCallId,
|
|
200
200
|
} as UIMessageChunk;
|
|
201
201
|
}
|
|
202
202
|
if (
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
203
|
+
passthroughPart.type === 'finish-step' ||
|
|
204
|
+
passthroughPart.type === 'start-step' ||
|
|
205
|
+
passthroughPart.type === 'tool-output-denied'
|
|
206
206
|
) {
|
|
207
|
-
return
|
|
207
|
+
return passthroughPart as UIMessageChunk;
|
|
208
208
|
}
|
|
209
209
|
return undefined;
|
|
210
210
|
}
|
package/src/workflow-agent.ts
CHANGED
|
@@ -497,6 +497,13 @@ export type WorkflowAgentOptions<
|
|
|
497
497
|
/**
|
|
498
498
|
* Callback that is called when the LLM response and all request tool executions are finished.
|
|
499
499
|
*/
|
|
500
|
+
onEnd?: WorkflowAgentOnEndCallback<TTools, TRuntimeContext>;
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Callback that is called when the LLM response and all request tool executions are finished.
|
|
504
|
+
*
|
|
505
|
+
* @deprecated Use `onEnd` instead.
|
|
506
|
+
*/
|
|
500
507
|
onFinish?: WorkflowAgentOnFinishCallback<TTools, TRuntimeContext>;
|
|
501
508
|
|
|
502
509
|
/**
|
|
@@ -536,7 +543,7 @@ export type WorkflowAgentOptions<
|
|
|
536
543
|
/**
|
|
537
544
|
* Callback that is called when the LLM response and all request tool executions are finished.
|
|
538
545
|
*/
|
|
539
|
-
export type
|
|
546
|
+
export type WorkflowAgentOnEndCallback<
|
|
540
547
|
TTools extends ToolSet = ToolSet,
|
|
541
548
|
TRuntimeContext extends Context = Context,
|
|
542
549
|
OUTPUT = never,
|
|
@@ -561,6 +568,11 @@ export type WorkflowAgentOnFinishCallback<
|
|
|
561
568
|
*/
|
|
562
569
|
readonly finishReason: FinishReason;
|
|
563
570
|
|
|
571
|
+
/**
|
|
572
|
+
* The total token usage across all steps.
|
|
573
|
+
*/
|
|
574
|
+
readonly usage: LanguageModelUsage;
|
|
575
|
+
|
|
564
576
|
/**
|
|
565
577
|
* The total token usage across all steps.
|
|
566
578
|
*/
|
|
@@ -583,6 +595,17 @@ export type WorkflowAgentOnFinishCallback<
|
|
|
583
595
|
readonly output: OUTPUT;
|
|
584
596
|
}) => PromiseLike<void> | void;
|
|
585
597
|
|
|
598
|
+
/**
|
|
599
|
+
* Callback that is called when the LLM response and all request tool executions are finished.
|
|
600
|
+
*
|
|
601
|
+
* @deprecated Use `WorkflowAgentOnEndCallback` instead.
|
|
602
|
+
*/
|
|
603
|
+
export type WorkflowAgentOnFinishCallback<
|
|
604
|
+
TTools extends ToolSet = ToolSet,
|
|
605
|
+
TRuntimeContext extends Context = Context,
|
|
606
|
+
OUTPUT = never,
|
|
607
|
+
> = WorkflowAgentOnEndCallback<TTools, TRuntimeContext, OUTPUT>;
|
|
608
|
+
|
|
586
609
|
/**
|
|
587
610
|
* Callback that is invoked when an error occurs during streaming.
|
|
588
611
|
*/
|
|
@@ -894,6 +917,14 @@ export type WorkflowAgentStreamOptions<
|
|
|
894
917
|
* Callback that is called when the LLM response and all request tool executions
|
|
895
918
|
* (for tools that have an `execute` function) are finished.
|
|
896
919
|
*/
|
|
920
|
+
onEnd?: WorkflowAgentOnEndCallback<TTools, TRuntimeContext, OUTPUT>;
|
|
921
|
+
|
|
922
|
+
/**
|
|
923
|
+
* Callback that is called when the LLM response and all request tool executions
|
|
924
|
+
* (for tools that have an `execute` function) are finished.
|
|
925
|
+
*
|
|
926
|
+
* @deprecated Use `onEnd` instead.
|
|
927
|
+
*/
|
|
897
928
|
onFinish?: WorkflowAgentOnFinishCallback<TTools, TRuntimeContext, OUTPUT>;
|
|
898
929
|
|
|
899
930
|
/**
|
|
@@ -1107,7 +1138,7 @@ export class WorkflowAgent<
|
|
|
1107
1138
|
TBaseTools,
|
|
1108
1139
|
TRuntimeContext
|
|
1109
1140
|
>;
|
|
1110
|
-
private
|
|
1141
|
+
private constructorOnEnd?: WorkflowAgentOnEndCallback<
|
|
1111
1142
|
TBaseTools,
|
|
1112
1143
|
TRuntimeContext
|
|
1113
1144
|
>;
|
|
@@ -1140,7 +1171,8 @@ export class WorkflowAgent<
|
|
|
1140
1171
|
this.experimentalDownload = options.experimental_download;
|
|
1141
1172
|
this.prepareStep = options.prepareStep;
|
|
1142
1173
|
this.constructorOnStepFinish = options.onStepFinish;
|
|
1143
|
-
|
|
1174
|
+
const { onFinish, onEnd = onFinish } = options;
|
|
1175
|
+
this.constructorOnEnd = onEnd;
|
|
1144
1176
|
this.constructorOnStart = options.experimental_onStart;
|
|
1145
1177
|
this.constructorOnStepStart = options.experimental_onStepStart;
|
|
1146
1178
|
this.constructorOnToolExecutionStart = options.onToolExecutionStart;
|
|
@@ -1180,6 +1212,8 @@ export class WorkflowAgent<
|
|
|
1180
1212
|
PARTIAL_OUTPUT
|
|
1181
1213
|
>,
|
|
1182
1214
|
): Promise<WorkflowAgentStreamResult<TTools, OUTPUT>> {
|
|
1215
|
+
const { onFinish, onEnd = onFinish } = options;
|
|
1216
|
+
|
|
1183
1217
|
// Call prepareCall to transform parameters before the agent loop
|
|
1184
1218
|
let effectiveModel: LanguageModel = this.model;
|
|
1185
1219
|
let effectiveInstructions = options.system ?? this.instructions;
|
|
@@ -1505,11 +1539,11 @@ export class WorkflowAgent<
|
|
|
1505
1539
|
| undefined,
|
|
1506
1540
|
options.onStepFinish,
|
|
1507
1541
|
);
|
|
1508
|
-
const
|
|
1509
|
-
this.
|
|
1510
|
-
|
|
|
1542
|
+
const mergedOnEnd = mergeCallbacks(
|
|
1543
|
+
this.constructorOnEnd as
|
|
1544
|
+
| WorkflowAgentOnEndCallback<TTools, TRuntimeContext, OUTPUT>
|
|
1511
1545
|
| undefined,
|
|
1512
|
-
|
|
1546
|
+
onEnd,
|
|
1513
1547
|
);
|
|
1514
1548
|
const mergedOnStart = mergeCallbacks(
|
|
1515
1549
|
this.constructorOnStart as
|
|
@@ -2006,14 +2040,16 @@ export class WorkflowAgent<
|
|
|
2006
2040
|
|
|
2007
2041
|
const messages = iterMessages as unknown as ModelMessage[];
|
|
2008
2042
|
|
|
2009
|
-
if (
|
|
2043
|
+
if (mergedOnEnd && !wasAborted) {
|
|
2010
2044
|
const lastStep = steps[steps.length - 1];
|
|
2011
|
-
|
|
2045
|
+
const totalUsage = aggregateUsage(steps);
|
|
2046
|
+
await mergedOnEnd({
|
|
2012
2047
|
steps,
|
|
2013
2048
|
messages,
|
|
2014
2049
|
text: lastStep?.text ?? '',
|
|
2015
2050
|
finishReason: lastStep?.finishReason ?? 'other',
|
|
2016
|
-
|
|
2051
|
+
usage: totalUsage,
|
|
2052
|
+
totalUsage,
|
|
2017
2053
|
runtimeContext,
|
|
2018
2054
|
toolsContext:
|
|
2019
2055
|
toolsContext as unknown as InferToolSetContext<TTools>,
|
|
@@ -2023,10 +2059,12 @@ export class WorkflowAgent<
|
|
|
2023
2059
|
if (!wasAborted && steps.length > 0) {
|
|
2024
2060
|
const telemetrySteps = steps.map(normalizeStepForTelemetry);
|
|
2025
2061
|
const lastStep = telemetrySteps[telemetrySteps.length - 1];
|
|
2062
|
+
const totalUsage = aggregateUsage(steps);
|
|
2026
2063
|
await telemetryDispatcher.onEnd?.({
|
|
2027
2064
|
...lastStep,
|
|
2028
2065
|
steps: telemetrySteps,
|
|
2029
|
-
|
|
2066
|
+
usage: totalUsage,
|
|
2067
|
+
totalUsage,
|
|
2030
2068
|
});
|
|
2031
2069
|
}
|
|
2032
2070
|
|
|
@@ -2181,7 +2219,7 @@ export class WorkflowAgent<
|
|
|
2181
2219
|
await options.onError({ error });
|
|
2182
2220
|
}
|
|
2183
2221
|
await telemetryDispatcher.onError?.(error);
|
|
2184
|
-
// Don't throw yet - we want to call
|
|
2222
|
+
// Don't throw yet - we want to call onEnd first
|
|
2185
2223
|
}
|
|
2186
2224
|
|
|
2187
2225
|
// Use the final messages from the iterator, or fall back to standardized messages
|
|
@@ -2214,15 +2252,17 @@ export class WorkflowAgent<
|
|
|
2214
2252
|
}
|
|
2215
2253
|
}
|
|
2216
2254
|
|
|
2217
|
-
// Call
|
|
2218
|
-
if (
|
|
2255
|
+
// Call onEnd callback if provided (always call, even on errors, but not on abort)
|
|
2256
|
+
if (mergedOnEnd && !wasAborted) {
|
|
2219
2257
|
const lastStep = steps[steps.length - 1];
|
|
2220
|
-
|
|
2258
|
+
const totalUsage = aggregateUsage(steps);
|
|
2259
|
+
await mergedOnEnd({
|
|
2221
2260
|
steps,
|
|
2222
2261
|
messages: messages as ModelMessage[],
|
|
2223
2262
|
text: lastStep?.text ?? '',
|
|
2224
2263
|
finishReason: lastStep?.finishReason ?? 'other',
|
|
2225
|
-
|
|
2264
|
+
usage: totalUsage,
|
|
2265
|
+
totalUsage,
|
|
2226
2266
|
runtimeContext,
|
|
2227
2267
|
toolsContext: toolsContext as unknown as InferToolSetContext<TTools>,
|
|
2228
2268
|
output: experimentalOutput,
|
|
@@ -2231,10 +2271,12 @@ export class WorkflowAgent<
|
|
|
2231
2271
|
if (!wasAborted && steps.length > 0) {
|
|
2232
2272
|
const telemetrySteps = steps.map(normalizeStepForTelemetry);
|
|
2233
2273
|
const lastStep = telemetrySteps[telemetrySteps.length - 1];
|
|
2274
|
+
const totalUsage = aggregateUsage(steps);
|
|
2234
2275
|
await telemetryDispatcher.onEnd?.({
|
|
2235
2276
|
...lastStep,
|
|
2236
2277
|
steps: telemetrySteps,
|
|
2237
|
-
|
|
2278
|
+
usage: totalUsage,
|
|
2279
|
+
totalUsage,
|
|
2238
2280
|
});
|
|
2239
2281
|
}
|
|
2240
2282
|
|
|
@@ -216,7 +216,7 @@ export class WorkflowChatTransport<
|
|
|
216
216
|
: undefined;
|
|
217
217
|
|
|
218
218
|
const url = requestConfig?.api ?? this.api;
|
|
219
|
-
const
|
|
219
|
+
const response = await this.fetch(url, {
|
|
220
220
|
method: 'POST',
|
|
221
221
|
body: JSON.stringify(
|
|
222
222
|
requestConfig?.body ?? { messages, ...options.body },
|
|
@@ -226,13 +226,13 @@ export class WorkflowChatTransport<
|
|
|
226
226
|
signal: abortSignal,
|
|
227
227
|
});
|
|
228
228
|
|
|
229
|
-
if (!
|
|
229
|
+
if (!response.ok || !response.body) {
|
|
230
230
|
throw new Error(
|
|
231
|
-
`Failed to fetch chat: ${
|
|
231
|
+
`Failed to fetch chat: ${response.status} ${await response.text()}`,
|
|
232
232
|
);
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
-
const workflowRunId =
|
|
235
|
+
const workflowRunId = response.headers.get('x-workflow-run-id');
|
|
236
236
|
if (!workflowRunId) {
|
|
237
237
|
throw new Error(
|
|
238
238
|
'Workflow run ID not found in "x-workflow-run-id" response header',
|
|
@@ -242,12 +242,12 @@ export class WorkflowChatTransport<
|
|
|
242
242
|
// Notify the caller that the chat POST request was sent.
|
|
243
243
|
// This is useful for tracking the chat history on the client
|
|
244
244
|
// side and allows for inspecting response headers.
|
|
245
|
-
await this.onChatSendMessage?.(
|
|
245
|
+
await this.onChatSendMessage?.(response, options);
|
|
246
246
|
|
|
247
247
|
// Flush the initial stream until the end or an error occurs
|
|
248
248
|
try {
|
|
249
249
|
const chunkStream = parseJsonEventStream({
|
|
250
|
-
stream:
|
|
250
|
+
stream: response.body,
|
|
251
251
|
schema: uiMessageChunkSchema,
|
|
252
252
|
});
|
|
253
253
|
for await (const chunk of createAsyncIterableStream(chunkStream)) {
|
|
@@ -292,8 +292,8 @@ export class WorkflowChatTransport<
|
|
|
292
292
|
async reconnectToStream(
|
|
293
293
|
options: ReconnectToStreamOptions & ChatRequestOptions,
|
|
294
294
|
): Promise<ReadableStream<UIMessageChunk> | null> {
|
|
295
|
-
const
|
|
296
|
-
return convertAsyncIteratorToReadableStream(
|
|
295
|
+
const reconnectIterator = this.reconnectToStreamIterator(options);
|
|
296
|
+
return convertAsyncIteratorToReadableStream(reconnectIterator);
|
|
297
297
|
}
|
|
298
298
|
|
|
299
299
|
private async *reconnectToStreamIterator(
|
|
@@ -344,15 +344,15 @@ export class WorkflowChatTransport<
|
|
|
344
344
|
: chunkIndex;
|
|
345
345
|
|
|
346
346
|
const url = `${baseUrl}?startIndex=${startIndex}`;
|
|
347
|
-
const
|
|
347
|
+
const response = await this.fetch(url, {
|
|
348
348
|
headers: requestConfig?.headers,
|
|
349
349
|
credentials: requestConfig?.credentials,
|
|
350
350
|
signal: options.abortSignal,
|
|
351
351
|
});
|
|
352
352
|
|
|
353
|
-
if (!
|
|
353
|
+
if (!response.ok || !response.body) {
|
|
354
354
|
throw new Error(
|
|
355
|
-
`Failed to fetch chat: ${
|
|
355
|
+
`Failed to fetch chat: ${response.status} ${await response.text()}`,
|
|
356
356
|
);
|
|
357
357
|
}
|
|
358
358
|
|
|
@@ -365,7 +365,9 @@ export class WorkflowChatTransport<
|
|
|
365
365
|
// resume from (explicitStartIndex + chunks received).
|
|
366
366
|
chunkIndex = explicitStartIndex;
|
|
367
367
|
} else if (useExplicitStartIndex && explicitStartIndex < 0) {
|
|
368
|
-
const tailIndexHeader =
|
|
368
|
+
const tailIndexHeader = response.headers.get(
|
|
369
|
+
'x-workflow-stream-tail-index',
|
|
370
|
+
);
|
|
369
371
|
const tailIndex =
|
|
370
372
|
tailIndexHeader !== null ? parseInt(tailIndexHeader, 10) : NaN;
|
|
371
373
|
|
|
@@ -389,7 +391,7 @@ export class WorkflowChatTransport<
|
|
|
389
391
|
|
|
390
392
|
try {
|
|
391
393
|
const chunkStream = parseJsonEventStream({
|
|
392
|
-
stream:
|
|
394
|
+
stream: response.body,
|
|
393
395
|
schema: uiMessageChunkSchema,
|
|
394
396
|
});
|
|
395
397
|
for await (const chunk of createAsyncIterableStream(chunkStream)) {
|