@ai-sdk/workflow 2.0.28 → 2.0.30
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 +24 -0
- package/dist/index.js +291 -64
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/add-tool-results-to-conversation.ts +108 -0
- package/src/do-stream-step.ts +18 -0
- package/src/stream-text-iterator.ts +157 -34
- package/src/workflow-agent.ts +105 -39
package/src/workflow-agent.ts
CHANGED
|
@@ -50,10 +50,12 @@ import {
|
|
|
50
50
|
validateApprovedToolApprovals,
|
|
51
51
|
verifyToolApprovalSignature,
|
|
52
52
|
} from 'ai/internal';
|
|
53
|
+
import { addToolResultsToConversation } from './add-tool-results-to-conversation.js';
|
|
53
54
|
import { createLanguageModelToolResultOutput } from './create-language-model-tool-result-output.js';
|
|
54
55
|
import type {
|
|
55
56
|
ModelCallStreamPart,
|
|
56
57
|
ModelStopCondition,
|
|
58
|
+
ProviderExecutedToolResult,
|
|
57
59
|
} from './do-stream-step.js';
|
|
58
60
|
import { resolveToolContext } from './resolve-tool-context.js';
|
|
59
61
|
import { streamTextIterator } from './stream-text-iterator.js';
|
|
@@ -1281,11 +1283,24 @@ function addToolResultsToStep(
|
|
|
1281
1283
|
return;
|
|
1282
1284
|
}
|
|
1283
1285
|
|
|
1284
|
-
const
|
|
1286
|
+
const existingProviderResultIds = new Set(
|
|
1287
|
+
step.content.flatMap(part =>
|
|
1288
|
+
(part.type === 'tool-result' || part.type === 'tool-error') &&
|
|
1289
|
+
part.providerExecuted
|
|
1290
|
+
? [part.toolCallId]
|
|
1291
|
+
: [],
|
|
1292
|
+
),
|
|
1293
|
+
);
|
|
1294
|
+
|
|
1295
|
+
const toolOutputs = executedResults.flatMap(result => {
|
|
1285
1296
|
const toolCall = step.toolCalls.find(
|
|
1286
1297
|
toolCall => toolCall.toolCallId === result.modelResult.toolCallId,
|
|
1287
1298
|
);
|
|
1288
1299
|
|
|
1300
|
+
if (existingProviderResultIds.has(result.modelResult.toolCallId)) {
|
|
1301
|
+
return [];
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1289
1304
|
const common = {
|
|
1290
1305
|
toolCallId: result.modelResult.toolCallId,
|
|
1291
1306
|
toolName: result.modelResult.toolName,
|
|
@@ -1296,17 +1311,19 @@ function addToolResultsToStep(
|
|
|
1296
1311
|
: {}),
|
|
1297
1312
|
};
|
|
1298
1313
|
|
|
1299
|
-
return
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1314
|
+
return [
|
|
1315
|
+
result.isError
|
|
1316
|
+
? {
|
|
1317
|
+
type: 'tool-error' as const,
|
|
1318
|
+
...common,
|
|
1319
|
+
error: result.rawOutput,
|
|
1320
|
+
}
|
|
1321
|
+
: {
|
|
1322
|
+
type: 'tool-result' as const,
|
|
1323
|
+
...common,
|
|
1324
|
+
output: result.rawOutput,
|
|
1325
|
+
},
|
|
1326
|
+
];
|
|
1310
1327
|
});
|
|
1311
1328
|
|
|
1312
1329
|
step.content.push(...(toolOutputs as StepResult<ToolSet, any>['content']));
|
|
@@ -2335,7 +2352,11 @@ export class WorkflowAgent<
|
|
|
2335
2352
|
toolsContext: yieldedToolsContext,
|
|
2336
2353
|
experimental_sandbox: stepSandbox,
|
|
2337
2354
|
providerExecutedToolResults,
|
|
2355
|
+
providerExecutedToolResultPositions,
|
|
2338
2356
|
} = result.value;
|
|
2357
|
+
const capturedProviderToolResults =
|
|
2358
|
+
providerExecutedToolResults ??
|
|
2359
|
+
new Map<string, ProviderExecutedToolResult>();
|
|
2339
2360
|
const toolExecutionSandbox = stepSandbox ?? sandbox;
|
|
2340
2361
|
// Capture current step number before pushing (0-based)
|
|
2341
2362
|
const currentStepNumber = steps.length;
|
|
@@ -2349,8 +2370,10 @@ export class WorkflowAgent<
|
|
|
2349
2370
|
toolsContext = yieldedToolsContext;
|
|
2350
2371
|
}
|
|
2351
2372
|
|
|
2352
|
-
//
|
|
2353
|
-
|
|
2373
|
+
// Process client tool calls and any provider results captured in this
|
|
2374
|
+
// response. Deferred provider results may arrive without a matching
|
|
2375
|
+
// tool call in the current step.
|
|
2376
|
+
if (toolCalls.length > 0 || capturedProviderToolResults.size > 0) {
|
|
2354
2377
|
const invalidToolCalls = toolCalls.filter(tc => tc.invalid === true);
|
|
2355
2378
|
const validToolCalls = toolCalls.filter(tc => tc.invalid !== true);
|
|
2356
2379
|
|
|
@@ -2361,6 +2384,29 @@ export class WorkflowAgent<
|
|
|
2361
2384
|
const providerToolCalls = validToolCalls.filter(
|
|
2362
2385
|
tc => tc.providerExecuted,
|
|
2363
2386
|
);
|
|
2387
|
+
const providerToolCallsForResults = [
|
|
2388
|
+
...providerToolCalls,
|
|
2389
|
+
...[...capturedProviderToolResults.values()].flatMap(
|
|
2390
|
+
providerResult =>
|
|
2391
|
+
providerToolCalls.some(
|
|
2392
|
+
toolCall => toolCall.toolCallId === providerResult.toolCallId,
|
|
2393
|
+
)
|
|
2394
|
+
? []
|
|
2395
|
+
: [
|
|
2396
|
+
{
|
|
2397
|
+
type: 'tool-call' as const,
|
|
2398
|
+
toolCallId: providerResult.toolCallId,
|
|
2399
|
+
toolName: providerResult.toolName,
|
|
2400
|
+
input: toolCalls.find(
|
|
2401
|
+
toolCall =>
|
|
2402
|
+
toolCall.toolCallId === providerResult.toolCallId,
|
|
2403
|
+
)?.input,
|
|
2404
|
+
providerExecuted: true,
|
|
2405
|
+
dynamic: providerResult.dynamic,
|
|
2406
|
+
},
|
|
2407
|
+
],
|
|
2408
|
+
),
|
|
2409
|
+
];
|
|
2364
2410
|
|
|
2365
2411
|
// Check which tools need approval (can be async)
|
|
2366
2412
|
const approvalNeeded = await Promise.all(
|
|
@@ -2424,11 +2470,11 @@ export class WorkflowAgent<
|
|
|
2424
2470
|
|
|
2425
2471
|
// Collect provider tool results
|
|
2426
2472
|
const providerResultEntries = await Promise.all(
|
|
2427
|
-
|
|
2473
|
+
providerToolCallsForResults.map(async toolCall => ({
|
|
2428
2474
|
toolCall,
|
|
2429
2475
|
result: await resolveProviderToolResult(
|
|
2430
2476
|
toolCall,
|
|
2431
|
-
|
|
2477
|
+
capturedProviderToolResults,
|
|
2432
2478
|
effectiveTools as ToolSet,
|
|
2433
2479
|
download,
|
|
2434
2480
|
),
|
|
@@ -2452,9 +2498,9 @@ export class WorkflowAgent<
|
|
|
2452
2498
|
({ result }) => (result == null ? [] : [result]),
|
|
2453
2499
|
);
|
|
2454
2500
|
|
|
2455
|
-
const continuationInvalidResults = invalidToolCalls
|
|
2456
|
-
|
|
2457
|
-
|
|
2501
|
+
const continuationInvalidResults = invalidToolCalls
|
|
2502
|
+
.filter(toolCall => !toolCall.providerExecuted)
|
|
2503
|
+
.map(createInvalidToolResult);
|
|
2458
2504
|
const resolvedResults: LanguageModelV4ToolResultPart[] = [
|
|
2459
2505
|
...executableResults.map(result => result.modelResult),
|
|
2460
2506
|
...providerResults.map(result => result.modelResult),
|
|
@@ -2481,12 +2527,21 @@ export class WorkflowAgent<
|
|
|
2481
2527
|
|
|
2482
2528
|
addToolResultsToStep(step, executedResults);
|
|
2483
2529
|
|
|
2484
|
-
|
|
2485
|
-
iterMessages
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2530
|
+
const responseMessages = addToolResultsToConversation({
|
|
2531
|
+
messages: iterMessages,
|
|
2532
|
+
toolResults: resolvedResults,
|
|
2533
|
+
providerExecutedToolCallIds: new Set(
|
|
2534
|
+
providerToolCallsForResults.map(
|
|
2535
|
+
toolCall => toolCall.toolCallId,
|
|
2536
|
+
),
|
|
2537
|
+
),
|
|
2538
|
+
providerExecutedToolResultPositions,
|
|
2539
|
+
});
|
|
2540
|
+
step?.response.messages.push(
|
|
2541
|
+
...(responseMessages as unknown as NonNullable<
|
|
2542
|
+
typeof step
|
|
2543
|
+
>['response']['messages']),
|
|
2544
|
+
);
|
|
2490
2545
|
|
|
2491
2546
|
const messages = iterMessages as unknown as ModelMessage[];
|
|
2492
2547
|
const lastStep = steps[steps.length - 1];
|
|
@@ -2609,11 +2664,11 @@ export class WorkflowAgent<
|
|
|
2609
2664
|
|
|
2610
2665
|
// For provider-executed tools, use the results from the stream
|
|
2611
2666
|
const providerToolResultEntries = await Promise.all(
|
|
2612
|
-
|
|
2667
|
+
providerToolCallsForResults.map(async toolCall => ({
|
|
2613
2668
|
toolCall,
|
|
2614
2669
|
result: await resolveProviderToolResult(
|
|
2615
2670
|
toolCall,
|
|
2616
|
-
|
|
2671
|
+
capturedProviderToolResults,
|
|
2617
2672
|
effectiveTools as ToolSet,
|
|
2618
2673
|
download,
|
|
2619
2674
|
),
|
|
@@ -2636,9 +2691,9 @@ export class WorkflowAgent<
|
|
|
2636
2691
|
const providerToolResults = providerToolResultEntries.flatMap(
|
|
2637
2692
|
({ result }) => (result == null ? [] : [result]),
|
|
2638
2693
|
);
|
|
2639
|
-
const continuationInvalidToolResults = invalidToolCalls
|
|
2640
|
-
|
|
2641
|
-
|
|
2694
|
+
const continuationInvalidToolResults = invalidToolCalls
|
|
2695
|
+
.filter(toolCall => !toolCall.providerExecuted)
|
|
2696
|
+
.map(createInvalidToolResult);
|
|
2642
2697
|
|
|
2643
2698
|
// Combine executable/provider results in the original order,
|
|
2644
2699
|
// while preserving invalid tool calls as error results for the
|
|
@@ -2654,6 +2709,14 @@ export class WorkflowAgent<
|
|
|
2654
2709
|
if (providerResult) return [providerResult];
|
|
2655
2710
|
return [];
|
|
2656
2711
|
});
|
|
2712
|
+
const currentToolCallIds = new Set(
|
|
2713
|
+
toolCalls.map(toolCall => toolCall.toolCallId),
|
|
2714
|
+
);
|
|
2715
|
+
executedToolResults.push(
|
|
2716
|
+
...providerToolResults.filter(
|
|
2717
|
+
result => !currentToolCallIds.has(result.modelResult.toolCallId),
|
|
2718
|
+
),
|
|
2719
|
+
);
|
|
2657
2720
|
const continuationToolResults = toolCalls.flatMap(tc => {
|
|
2658
2721
|
const invalidResult = continuationInvalidToolResults.find(
|
|
2659
2722
|
r => r.toolCallId === tc.toolCallId,
|
|
@@ -2665,6 +2728,13 @@ export class WorkflowAgent<
|
|
|
2665
2728
|
if (executedResult) return [executedResult.modelResult];
|
|
2666
2729
|
return [];
|
|
2667
2730
|
});
|
|
2731
|
+
continuationToolResults.push(
|
|
2732
|
+
...providerToolResults.flatMap(result =>
|
|
2733
|
+
currentToolCallIds.has(result.modelResult.toolCallId)
|
|
2734
|
+
? []
|
|
2735
|
+
: [result.modelResult],
|
|
2736
|
+
),
|
|
2737
|
+
);
|
|
2668
2738
|
|
|
2669
2739
|
// Write tool results and step boundaries to the stream so the UI can
|
|
2670
2740
|
// transition tool parts to the appropriate output state and properly
|
|
@@ -3136,10 +3206,7 @@ function aggregateUsage(steps: StepResult<any, any>[]): LanguageModelUsage {
|
|
|
3136
3206
|
|
|
3137
3207
|
async function resolveProviderToolResult(
|
|
3138
3208
|
toolCall: { toolCallId: string; toolName: string; input: unknown },
|
|
3139
|
-
providerExecutedToolResults?: Map<
|
|
3140
|
-
string,
|
|
3141
|
-
{ toolCallId: string; toolName: string; result: unknown; isError?: boolean }
|
|
3142
|
-
>,
|
|
3209
|
+
providerExecutedToolResults?: Map<string, ProviderExecutedToolResult>,
|
|
3143
3210
|
tools?: ToolSet,
|
|
3144
3211
|
download?: DownloadFunction,
|
|
3145
3212
|
): Promise<WorkflowToolExecutionResult | undefined> {
|
|
@@ -3174,11 +3241,7 @@ async function resolveProviderToolResult(
|
|
|
3174
3241
|
}
|
|
3175
3242
|
|
|
3176
3243
|
const result = streamResult.result;
|
|
3177
|
-
const errorMode = streamResult.isError
|
|
3178
|
-
? typeof result === 'string'
|
|
3179
|
-
? 'text'
|
|
3180
|
-
: 'json'
|
|
3181
|
-
: 'none';
|
|
3244
|
+
const errorMode = streamResult.isError ? 'json' : 'none';
|
|
3182
3245
|
|
|
3183
3246
|
return {
|
|
3184
3247
|
modelResult: {
|
|
@@ -3195,6 +3258,9 @@ async function resolveProviderToolResult(
|
|
|
3195
3258
|
supportedUrls: {},
|
|
3196
3259
|
download,
|
|
3197
3260
|
}),
|
|
3261
|
+
...(streamResult.providerMetadata != null
|
|
3262
|
+
? { providerOptions: streamResult.providerMetadata }
|
|
3263
|
+
: {}),
|
|
3198
3264
|
},
|
|
3199
3265
|
rawOutput: result,
|
|
3200
3266
|
isError: streamResult.isError === true,
|