@ai-sdk/workflow 2.0.57 → 2.0.59
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 +31 -0
- package/dist/index.d.ts +61 -43
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +681 -299
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/build-model-step-result.ts +329 -0
- package/src/do-generate-step.ts +212 -0
- package/src/do-stream-step.ts +50 -179
- package/src/index.ts +3 -0
- package/src/{stream-text-iterator.ts → model-call-iterator.ts} +117 -297
- package/src/model-call.ts +196 -0
- package/src/providers/mock-function-wrapper.ts +32 -0
- package/src/test/agent-e2e-workflows.ts +60 -1
- package/src/test/approval-workflows.ts +105 -0
- package/src/test/generation-boundary-model.ts +115 -0
- package/src/test/generation-boundary-workflows.ts +106 -0
- package/src/test/model-call-payload-workflow.ts +132 -0
- package/src/to-model-response-messages.ts +28 -0
- package/src/workflow-agent.ts +509 -201
- package/src/workflow-execution-result.ts +55 -0
package/src/workflow-agent.ts
CHANGED
|
@@ -18,6 +18,9 @@ import {
|
|
|
18
18
|
} from '@ai-sdk/provider-utils';
|
|
19
19
|
import {
|
|
20
20
|
Output,
|
|
21
|
+
isStepCount,
|
|
22
|
+
NoOutputGeneratedError,
|
|
23
|
+
type GenerateTextResult,
|
|
21
24
|
experimental_filterActiveTools as filterActiveTools,
|
|
22
25
|
type FinishReason,
|
|
23
26
|
type LanguageModelResponseMetadata,
|
|
@@ -41,6 +44,9 @@ import {
|
|
|
41
44
|
} from 'ai';
|
|
42
45
|
import {
|
|
43
46
|
createRestrictedTelemetryDispatcher,
|
|
47
|
+
DefaultGenerateTextResult,
|
|
48
|
+
addLanguageModelUsage,
|
|
49
|
+
createNullLanguageModelUsage,
|
|
44
50
|
collectToolApprovals,
|
|
45
51
|
convertToLanguageModelPrompt,
|
|
46
52
|
mergeAbortSignals,
|
|
@@ -59,7 +65,14 @@ import type {
|
|
|
59
65
|
ProviderExecutedToolResult,
|
|
60
66
|
} from './do-stream-step.js';
|
|
61
67
|
import { resolveToolContext } from './resolve-tool-context.js';
|
|
62
|
-
import {
|
|
68
|
+
import { toModelResponseMessages } from './to-model-response-messages.js';
|
|
69
|
+
import { modelCallIterator } from './model-call-iterator.js';
|
|
70
|
+
import {
|
|
71
|
+
resolveWorkflowStreamResult,
|
|
72
|
+
type WorkflowExecutionData,
|
|
73
|
+
type WorkflowExecutionResult,
|
|
74
|
+
type WorkflowExecutionOutcome,
|
|
75
|
+
} from './workflow-execution-result.js';
|
|
63
76
|
|
|
64
77
|
// Re-export for consumers
|
|
65
78
|
export type { CompatibleLanguageModel } from './types.js';
|
|
@@ -921,7 +934,7 @@ export type WorkflowAgentOnToolExecutionEndCallback<
|
|
|
921
934
|
/**
|
|
922
935
|
* Options for the {@link WorkflowAgent.stream} method.
|
|
923
936
|
*/
|
|
924
|
-
export type
|
|
937
|
+
export type WorkflowAgentCallOptions<
|
|
925
938
|
TTools extends ToolSet = ToolSet,
|
|
926
939
|
TRuntimeContext extends Context = Context,
|
|
927
940
|
OUTPUT = never,
|
|
@@ -960,7 +973,7 @@ export type WorkflowAgentStreamOptions<
|
|
|
960
973
|
}
|
|
961
974
|
) & {
|
|
962
975
|
/**
|
|
963
|
-
* Instructions override for this
|
|
976
|
+
* Instructions override for this call.
|
|
964
977
|
*/
|
|
965
978
|
instructions?: Instructions;
|
|
966
979
|
|
|
@@ -971,30 +984,6 @@ export type WorkflowAgentStreamOptions<
|
|
|
971
984
|
*/
|
|
972
985
|
system?: string;
|
|
973
986
|
|
|
974
|
-
/**
|
|
975
|
-
* A WritableStream that receives raw LanguageModelV4StreamPart chunks in real-time
|
|
976
|
-
* as the model generates them. This enables streaming to the client without
|
|
977
|
-
* coupling WorkflowAgent to UIMessageChunk format.
|
|
978
|
-
*
|
|
979
|
-
* Convert to UIMessageChunks at the response boundary using
|
|
980
|
-
* `createUIMessageChunkTransform()` from `@ai-sdk/workflow`.
|
|
981
|
-
*
|
|
982
|
-
* @example
|
|
983
|
-
* ```typescript
|
|
984
|
-
* // In the workflow:
|
|
985
|
-
* await agent.stream({
|
|
986
|
-
* messages,
|
|
987
|
-
* writable: getWritable<ModelCallStreamPart>(),
|
|
988
|
-
* });
|
|
989
|
-
*
|
|
990
|
-
* // In the route handler:
|
|
991
|
-
* return createUIMessageStreamResponse({
|
|
992
|
-
* stream: run.readable.pipeThrough(createModelCallToUIChunkTransform()),
|
|
993
|
-
* });
|
|
994
|
-
* ```
|
|
995
|
-
*/
|
|
996
|
-
writable?: WritableStream<ModelCallStreamPart<ToolSet>>;
|
|
997
|
-
|
|
998
987
|
/**
|
|
999
988
|
* Condition for stopping the generation when there are tool results in the last step.
|
|
1000
989
|
* When the condition is an array, any of the conditions can be met to stop the generation.
|
|
@@ -1070,14 +1059,6 @@ export type WorkflowAgentStreamOptions<
|
|
|
1070
1059
|
*/
|
|
1071
1060
|
output?: OutputSpecification<OUTPUT, PARTIAL_OUTPUT>;
|
|
1072
1061
|
|
|
1073
|
-
/**
|
|
1074
|
-
* Whether to include raw chunks from the provider in the stream.
|
|
1075
|
-
* When enabled, you will receive raw chunks with type 'raw' that contain the unprocessed data from the provider.
|
|
1076
|
-
* This allows access to cutting-edge provider features not yet wrapped by the AI SDK.
|
|
1077
|
-
* Defaults to false.
|
|
1078
|
-
*/
|
|
1079
|
-
includeRawChunks?: boolean;
|
|
1080
|
-
|
|
1081
1062
|
/**
|
|
1082
1063
|
* A function that attempts to repair a tool call that failed to parse.
|
|
1083
1064
|
*/
|
|
@@ -1090,15 +1071,6 @@ export type WorkflowAgentStreamOptions<
|
|
|
1090
1071
|
*/
|
|
1091
1072
|
experimental_repairToolCall?: ToolCallRepairFunction<TTools>;
|
|
1092
1073
|
|
|
1093
|
-
/**
|
|
1094
|
-
* Optional stream transformations.
|
|
1095
|
-
* They are applied in the order they are provided.
|
|
1096
|
-
* The stream transformations must maintain the stream structure for streamText to work correctly.
|
|
1097
|
-
*/
|
|
1098
|
-
experimental_transform?:
|
|
1099
|
-
| StreamTextTransform<TTools>
|
|
1100
|
-
| Array<StreamTextTransform<TTools>>;
|
|
1101
|
-
|
|
1102
1074
|
/**
|
|
1103
1075
|
* Custom download function to use for URLs.
|
|
1104
1076
|
* By default, files are downloaded if the model does not support the URL for the given media type.
|
|
@@ -1221,24 +1193,113 @@ export type WorkflowAgentStreamOptions<
|
|
|
1221
1193
|
prepareStep?: PrepareStepCallback<TTools, TRuntimeContext>;
|
|
1222
1194
|
|
|
1223
1195
|
/**
|
|
1224
|
-
* Timeout in milliseconds for
|
|
1196
|
+
* Timeout in milliseconds for model calls in this operation.
|
|
1225
1197
|
* When specified, creates an AbortSignal that will abort the operation after the given time.
|
|
1226
1198
|
* If both `timeout` and `abortSignal` are provided, whichever triggers first will abort.
|
|
1227
1199
|
*/
|
|
1228
1200
|
timeout?: number;
|
|
1201
|
+
};
|
|
1229
1202
|
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1203
|
+
/** Streaming options add transport controls to the shared call options. */
|
|
1204
|
+
export type WorkflowAgentStreamOptions<
|
|
1205
|
+
TTools extends ToolSet = ToolSet,
|
|
1206
|
+
TRuntimeContext extends Context = Context,
|
|
1207
|
+
OUTPUT = never,
|
|
1208
|
+
PARTIAL_OUTPUT = never,
|
|
1209
|
+
> = WorkflowAgentCallOptions<
|
|
1210
|
+
TTools,
|
|
1211
|
+
TRuntimeContext,
|
|
1212
|
+
OUTPUT,
|
|
1213
|
+
PARTIAL_OUTPUT
|
|
1214
|
+
> & {
|
|
1215
|
+
/**
|
|
1216
|
+
* A WritableStream that receives raw LanguageModelV4StreamPart chunks in real-time
|
|
1217
|
+
* as the model generates them. This enables streaming to the client without
|
|
1218
|
+
* coupling WorkflowAgent to UIMessageChunk format.
|
|
1219
|
+
*
|
|
1220
|
+
* Convert to UIMessageChunks at the response boundary using
|
|
1221
|
+
* `createUIMessageChunkTransform()` from `@ai-sdk/workflow`.
|
|
1222
|
+
*
|
|
1223
|
+
* @example
|
|
1224
|
+
* ```typescript
|
|
1225
|
+
* // In the workflow:
|
|
1226
|
+
* await agent.stream({
|
|
1227
|
+
* messages,
|
|
1228
|
+
* writable: getWritable<ModelCallStreamPart>(),
|
|
1229
|
+
* });
|
|
1230
|
+
*
|
|
1231
|
+
* // In the route handler:
|
|
1232
|
+
* return createUIMessageStreamResponse({
|
|
1233
|
+
* stream: run.readable.pipeThrough(createModelCallToUIChunkTransform()),
|
|
1234
|
+
* });
|
|
1235
|
+
* ```
|
|
1236
|
+
*/
|
|
1237
|
+
writable?: WritableStream<ModelCallStreamPart<ToolSet>>;
|
|
1235
1238
|
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1239
|
+
/**
|
|
1240
|
+
* Whether to include raw chunks from the provider in the stream.
|
|
1241
|
+
* When enabled, you will receive raw chunks with type 'raw' that contain the unprocessed data from the provider.
|
|
1242
|
+
* This allows access to cutting-edge provider features not yet wrapped by the AI SDK.
|
|
1243
|
+
* Defaults to false.
|
|
1244
|
+
*/
|
|
1245
|
+
includeRawChunks?: boolean;
|
|
1246
|
+
|
|
1247
|
+
/**
|
|
1248
|
+
* Optional stream transformations.
|
|
1249
|
+
* They are applied in the order they are provided.
|
|
1250
|
+
* The stream transformations must maintain the stream structure for streamText to work correctly.
|
|
1251
|
+
*/
|
|
1252
|
+
experimental_transform?:
|
|
1253
|
+
| StreamTextTransform<TTools>
|
|
1254
|
+
| Array<StreamTextTransform<TTools>>;
|
|
1255
|
+
|
|
1256
|
+
/**
|
|
1257
|
+
* Whether to send a 'finish' chunk to the writable stream when streaming completes.
|
|
1258
|
+
* @default true
|
|
1259
|
+
*/
|
|
1260
|
+
sendFinish?: boolean;
|
|
1261
|
+
|
|
1262
|
+
/**
|
|
1263
|
+
* Whether to prevent the writable stream from being closed after streaming completes.
|
|
1264
|
+
* @default false
|
|
1265
|
+
*/
|
|
1266
|
+
preventClose?: boolean;
|
|
1267
|
+
};
|
|
1268
|
+
|
|
1269
|
+
export type WorkflowAgentGenerateOptions<
|
|
1270
|
+
TTools extends ToolSet = ToolSet,
|
|
1271
|
+
TRuntimeContext extends Context = Context,
|
|
1272
|
+
OUTPUT = string,
|
|
1273
|
+
PARTIAL_OUTPUT = never,
|
|
1274
|
+
> = WorkflowAgentCallOptions<
|
|
1275
|
+
TTools,
|
|
1276
|
+
TRuntimeContext,
|
|
1277
|
+
OUTPUT,
|
|
1278
|
+
PARTIAL_OUTPUT
|
|
1279
|
+
> & {
|
|
1280
|
+
/** Request/response bodies and request messages are omitted by default. */
|
|
1281
|
+
include?: {
|
|
1282
|
+
requestBody?: boolean;
|
|
1283
|
+
responseBody?: boolean;
|
|
1284
|
+
requestMessages?: boolean;
|
|
1241
1285
|
};
|
|
1286
|
+
};
|
|
1287
|
+
|
|
1288
|
+
/** Core result semantics, available inside the workflow that runs generation. */
|
|
1289
|
+
export type WorkflowAgentGenerateResult<
|
|
1290
|
+
TTools extends ToolSet = ToolSet,
|
|
1291
|
+
TRuntimeContext extends Context = Context,
|
|
1292
|
+
OUTPUT = string,
|
|
1293
|
+
> = Omit<
|
|
1294
|
+
GenerateTextResult<TTools, TRuntimeContext, Output.Output>,
|
|
1295
|
+
'output'
|
|
1296
|
+
> & { readonly output: OUTPUT };
|
|
1297
|
+
|
|
1298
|
+
type DefaultGenerateOutput<OUTPUT> = [
|
|
1299
|
+
DefaultWorkflowAgentOutput<OUTPUT>,
|
|
1300
|
+
] extends [never]
|
|
1301
|
+
? string
|
|
1302
|
+
: OUTPUT;
|
|
1242
1303
|
|
|
1243
1304
|
/**
|
|
1244
1305
|
* A tool call made by the model. Matches the AI SDK's tool call shape.
|
|
@@ -1279,6 +1340,7 @@ type WorkflowToolExecutionResult = {
|
|
|
1279
1340
|
function addToolResultsToStep(
|
|
1280
1341
|
step: StepResult<ToolSet, any> | undefined,
|
|
1281
1342
|
executedResults: WorkflowToolExecutionResult[],
|
|
1343
|
+
mode: 'generate' | 'stream' = 'stream',
|
|
1282
1344
|
) {
|
|
1283
1345
|
if (step == null || executedResults.length === 0) {
|
|
1284
1346
|
return;
|
|
@@ -1306,6 +1368,14 @@ function addToolResultsToStep(
|
|
|
1306
1368
|
toolCallId: result.modelResult.toolCallId,
|
|
1307
1369
|
toolName: result.modelResult.toolName,
|
|
1308
1370
|
input: toolCall?.input,
|
|
1371
|
+
...(mode === 'generate'
|
|
1372
|
+
? {
|
|
1373
|
+
dynamic: toolCall?.dynamic === true,
|
|
1374
|
+
...(toolCall?.toolMetadata != null
|
|
1375
|
+
? { toolMetadata: toolCall.toolMetadata }
|
|
1376
|
+
: {}),
|
|
1377
|
+
}
|
|
1378
|
+
: {}),
|
|
1309
1379
|
...(toolCall?.dynamic === true ? { dynamic: true as const } : {}),
|
|
1310
1380
|
...(toolCall?.providerExecuted === true
|
|
1311
1381
|
? { providerExecuted: true }
|
|
@@ -1329,6 +1399,9 @@ function addToolResultsToStep(
|
|
|
1329
1399
|
|
|
1330
1400
|
step.content.push(...(toolOutputs as StepResult<ToolSet, any>['content']));
|
|
1331
1401
|
|
|
1402
|
+
// Core's generate step derives its tool lists from content getters.
|
|
1403
|
+
if (mode === 'generate') return;
|
|
1404
|
+
|
|
1332
1405
|
const toolResults = toolOutputs.filter(
|
|
1333
1406
|
result => result.type === 'tool-result',
|
|
1334
1407
|
);
|
|
@@ -1555,8 +1628,54 @@ export class WorkflowAgent<
|
|
|
1555
1628
|
};
|
|
1556
1629
|
}
|
|
1557
1630
|
|
|
1558
|
-
generate
|
|
1559
|
-
|
|
1631
|
+
async generate<
|
|
1632
|
+
TTools extends TBaseTools = TBaseTools,
|
|
1633
|
+
TOutput = DefaultGenerateOutput<OUTPUT>,
|
|
1634
|
+
TPartialOutput = DefaultWorkflowAgentOutput<PARTIAL_OUTPUT>,
|
|
1635
|
+
>(
|
|
1636
|
+
options: WorkflowAgentGenerateOptions<
|
|
1637
|
+
TTools,
|
|
1638
|
+
TRuntimeContext,
|
|
1639
|
+
TOutput,
|
|
1640
|
+
TPartialOutput
|
|
1641
|
+
>,
|
|
1642
|
+
): Promise<WorkflowAgentGenerateResult<TTools, TRuntimeContext, TOutput>> {
|
|
1643
|
+
const {
|
|
1644
|
+
data,
|
|
1645
|
+
outcome,
|
|
1646
|
+
abortReason,
|
|
1647
|
+
initialResponseMessages = [],
|
|
1648
|
+
} = await this.execute(options, 'generate');
|
|
1649
|
+
if (outcome.status === 'failed') throw outcome.error;
|
|
1650
|
+
if (outcome.status === 'aborted') {
|
|
1651
|
+
if (outcome.rejection != null) throw outcome.rejection.value;
|
|
1652
|
+
if (abortReason != null) throw abortReason.value;
|
|
1653
|
+
options.abortSignal?.throwIfAborted();
|
|
1654
|
+
throw new DOMException('Generation aborted.', 'AbortError');
|
|
1655
|
+
}
|
|
1656
|
+
const lastStep = data.steps.at(-1);
|
|
1657
|
+
if (lastStep == null) throw new NoOutputGeneratedError();
|
|
1658
|
+
let output: TOutput | undefined;
|
|
1659
|
+
if (
|
|
1660
|
+
lastStep.finishReason === 'stop' ||
|
|
1661
|
+
(lastStep.finishReason !== 'tool-calls' && lastStep.text.length > 0)
|
|
1662
|
+
) {
|
|
1663
|
+
const specification = options.output ?? this.output ?? Output.text();
|
|
1664
|
+
output = (await specification.parseCompleteOutput(
|
|
1665
|
+
{ text: lastStep.text },
|
|
1666
|
+
{
|
|
1667
|
+
response: lastStep.response,
|
|
1668
|
+
usage: lastStep.usage,
|
|
1669
|
+
finishReason: lastStep.finishReason,
|
|
1670
|
+
},
|
|
1671
|
+
)) as TOutput;
|
|
1672
|
+
}
|
|
1673
|
+
return new DefaultGenerateTextResult({
|
|
1674
|
+
initialResponseMessages,
|
|
1675
|
+
steps: data.steps as StepResult<TTools, TRuntimeContext>[],
|
|
1676
|
+
totalUsage: data.totalUsage,
|
|
1677
|
+
output,
|
|
1678
|
+
}) as WorkflowAgentGenerateResult<TTools, TRuntimeContext, TOutput>;
|
|
1560
1679
|
}
|
|
1561
1680
|
|
|
1562
1681
|
async stream<
|
|
@@ -1571,6 +1690,22 @@ export class WorkflowAgent<
|
|
|
1571
1690
|
TPartialOutput
|
|
1572
1691
|
>,
|
|
1573
1692
|
): Promise<WorkflowAgentStreamResult<TTools, TOutput>> {
|
|
1693
|
+
return resolveWorkflowStreamResult(await this.execute(options));
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
private async prepareInvocation<
|
|
1697
|
+
TTools extends TBaseTools = TBaseTools,
|
|
1698
|
+
TOutput = DefaultWorkflowAgentOutput<OUTPUT>,
|
|
1699
|
+
TPartialOutput = DefaultWorkflowAgentOutput<PARTIAL_OUTPUT>,
|
|
1700
|
+
>(
|
|
1701
|
+
options: WorkflowAgentStreamOptions<
|
|
1702
|
+
TTools,
|
|
1703
|
+
TRuntimeContext,
|
|
1704
|
+
TOutput,
|
|
1705
|
+
TPartialOutput
|
|
1706
|
+
> & { include?: WorkflowAgentGenerateOptions['include'] },
|
|
1707
|
+
mode: 'generate' | 'stream' = 'stream',
|
|
1708
|
+
) {
|
|
1574
1709
|
const { onFinish, onEnd = onFinish } = options;
|
|
1575
1710
|
|
|
1576
1711
|
// Call prepareCall to transform parameters before the agent loop
|
|
@@ -1590,7 +1725,10 @@ export class WorkflowAgent<
|
|
|
1590
1725
|
Context | undefined
|
|
1591
1726
|
>;
|
|
1592
1727
|
let effectiveToolChoiceFromPrepare = options.toolChoice ?? this.toolChoice;
|
|
1593
|
-
let effectiveStopWhenFromPrepare =
|
|
1728
|
+
let effectiveStopWhenFromPrepare =
|
|
1729
|
+
options.stopWhen ??
|
|
1730
|
+
this.stopWhen ??
|
|
1731
|
+
(mode === 'generate' ? isStepCount(20) : undefined);
|
|
1594
1732
|
let effectiveActiveToolsFromPrepare =
|
|
1595
1733
|
options.activeTools ?? this.activeTools;
|
|
1596
1734
|
let effectiveDownloadFromPrepare =
|
|
@@ -1720,6 +1858,70 @@ export class WorkflowAgent<
|
|
|
1720
1858
|
options.onToolExecutionEnd,
|
|
1721
1859
|
);
|
|
1722
1860
|
|
|
1861
|
+
return {
|
|
1862
|
+
onEnd,
|
|
1863
|
+
effectiveModel,
|
|
1864
|
+
effectiveInstructions,
|
|
1865
|
+
effectiveGenerationSettings,
|
|
1866
|
+
effectiveRuntimeContext,
|
|
1867
|
+
effectiveToolsContext,
|
|
1868
|
+
effectiveToolChoiceFromPrepare,
|
|
1869
|
+
effectiveStopWhenFromPrepare,
|
|
1870
|
+
effectiveActiveToolsFromPrepare,
|
|
1871
|
+
effectiveToolApprovalSecret,
|
|
1872
|
+
effectiveTelemetry,
|
|
1873
|
+
telemetryDispatcher,
|
|
1874
|
+
prompt,
|
|
1875
|
+
download,
|
|
1876
|
+
sandbox,
|
|
1877
|
+
effectiveAbortSignal,
|
|
1878
|
+
timeoutAt,
|
|
1879
|
+
mergedOnToolExecutionStart,
|
|
1880
|
+
mergedOnToolExecutionEnd,
|
|
1881
|
+
};
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
private async execute<
|
|
1885
|
+
TTools extends TBaseTools = TBaseTools,
|
|
1886
|
+
TOutput = DefaultWorkflowAgentOutput<OUTPUT>,
|
|
1887
|
+
TPartialOutput = DefaultWorkflowAgentOutput<PARTIAL_OUTPUT>,
|
|
1888
|
+
>(
|
|
1889
|
+
options: WorkflowAgentStreamOptions<
|
|
1890
|
+
TTools,
|
|
1891
|
+
TRuntimeContext,
|
|
1892
|
+
TOutput,
|
|
1893
|
+
TPartialOutput
|
|
1894
|
+
> & { include?: WorkflowAgentGenerateOptions['include'] },
|
|
1895
|
+
mode: 'generate' | 'stream' = 'stream',
|
|
1896
|
+
): Promise<WorkflowExecutionResult<TTools, TOutput>> {
|
|
1897
|
+
const {
|
|
1898
|
+
onEnd,
|
|
1899
|
+
effectiveModel,
|
|
1900
|
+
effectiveInstructions,
|
|
1901
|
+
effectiveGenerationSettings,
|
|
1902
|
+
effectiveRuntimeContext,
|
|
1903
|
+
effectiveToolsContext,
|
|
1904
|
+
effectiveToolChoiceFromPrepare,
|
|
1905
|
+
effectiveStopWhenFromPrepare,
|
|
1906
|
+
effectiveActiveToolsFromPrepare,
|
|
1907
|
+
effectiveToolApprovalSecret,
|
|
1908
|
+
effectiveTelemetry,
|
|
1909
|
+
telemetryDispatcher,
|
|
1910
|
+
prompt,
|
|
1911
|
+
download,
|
|
1912
|
+
sandbox,
|
|
1913
|
+
effectiveAbortSignal,
|
|
1914
|
+
timeoutAt,
|
|
1915
|
+
mergedOnToolExecutionStart,
|
|
1916
|
+
mergedOnToolExecutionEnd,
|
|
1917
|
+
} = await this.prepareInvocation(options, mode);
|
|
1918
|
+
const initialResponseMessages: WorkflowAgentGenerateResult<TTools>['responseMessages'] =
|
|
1919
|
+
[];
|
|
1920
|
+
const abortReason = () =>
|
|
1921
|
+
effectiveAbortSignal?.reason === undefined
|
|
1922
|
+
? undefined
|
|
1923
|
+
: { value: effectiveAbortSignal.reason };
|
|
1924
|
+
|
|
1723
1925
|
// Process tool approval responses before starting the agent loop.
|
|
1724
1926
|
// This mirrors how stream-text.ts handles tool-approval-response parts:
|
|
1725
1927
|
// approved tools are executed, denied tools get denial results, and
|
|
@@ -1939,6 +2141,13 @@ export class WorkflowAgent<
|
|
|
1939
2141
|
}
|
|
1940
2142
|
}
|
|
1941
2143
|
|
|
2144
|
+
if (mode === 'generate' && toolResultContent.length > 0) {
|
|
2145
|
+
initialResponseMessages.push({
|
|
2146
|
+
role: 'tool',
|
|
2147
|
+
content: toolResultContent,
|
|
2148
|
+
} as unknown as WorkflowAgentGenerateResult<TTools>['responseMessages'][number]);
|
|
2149
|
+
}
|
|
2150
|
+
|
|
1942
2151
|
// Add tool results as a new tool message
|
|
1943
2152
|
if (toolResultContent.length > 0) {
|
|
1944
2153
|
cleanedMessages.push({
|
|
@@ -2287,17 +2496,23 @@ export class WorkflowAgent<
|
|
|
2287
2496
|
await options.onAbort({ steps });
|
|
2288
2497
|
}
|
|
2289
2498
|
return {
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2499
|
+
outcome: { status: 'aborted' },
|
|
2500
|
+
abortReason: abortReason(),
|
|
2501
|
+
data: {
|
|
2502
|
+
messages: prompt.messages,
|
|
2503
|
+
steps,
|
|
2504
|
+
toolCalls: [],
|
|
2505
|
+
toolResults: [],
|
|
2506
|
+
finishReason: 'other',
|
|
2507
|
+
totalUsage: aggregateExecutionUsage(steps, mode),
|
|
2508
|
+
output: undefined as TOutput,
|
|
2509
|
+
},
|
|
2297
2510
|
};
|
|
2298
2511
|
}
|
|
2299
2512
|
|
|
2300
|
-
const iterator =
|
|
2513
|
+
const iterator = modelCallIterator({
|
|
2514
|
+
mode,
|
|
2515
|
+
include: options.include,
|
|
2301
2516
|
model: effectiveModel,
|
|
2302
2517
|
tools: effectiveTools as ToolSet,
|
|
2303
2518
|
writable: options.writable,
|
|
@@ -2363,6 +2578,19 @@ export class WorkflowAgent<
|
|
|
2363
2578
|
providerExecutedToolResults,
|
|
2364
2579
|
providerExecutedToolResultPositions,
|
|
2365
2580
|
} = result.value;
|
|
2581
|
+
if (mode === 'generate' && step != null) {
|
|
2582
|
+
for (const call of toolCalls) {
|
|
2583
|
+
if (call.invalid && !call.providerExecuted)
|
|
2584
|
+
step.content.push({
|
|
2585
|
+
type: 'tool-error',
|
|
2586
|
+
toolCallId: call.toolCallId,
|
|
2587
|
+
toolName: call.toolName,
|
|
2588
|
+
input: call.input,
|
|
2589
|
+
error: call.error,
|
|
2590
|
+
dynamic: true,
|
|
2591
|
+
});
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2366
2594
|
const capturedProviderToolResults =
|
|
2367
2595
|
providerExecutedToolResults ??
|
|
2368
2596
|
new Map<string, ProviderExecutedToolResult>();
|
|
@@ -2461,7 +2689,14 @@ export class WorkflowAgent<
|
|
|
2461
2689
|
// stop the loop and return them.
|
|
2462
2690
|
// This matches AI SDK behavior: tools without execute or needing
|
|
2463
2691
|
// approval pause the agent loop.
|
|
2464
|
-
|
|
2692
|
+
const providerApprovalRequests =
|
|
2693
|
+
step?.content
|
|
2694
|
+
.filter(part => part.type === 'tool-approval-request')
|
|
2695
|
+
.filter(part => part.toolCall.providerExecuted) ?? [];
|
|
2696
|
+
if (
|
|
2697
|
+
pausedToolCalls.length > 0 ||
|
|
2698
|
+
providerApprovalRequests.length > 0
|
|
2699
|
+
) {
|
|
2465
2700
|
// Execute any executable tools that were also called in this step
|
|
2466
2701
|
const executableResults = await Promise.all(
|
|
2467
2702
|
executableToolCalls.map(
|
|
@@ -2479,15 +2714,23 @@ export class WorkflowAgent<
|
|
|
2479
2714
|
|
|
2480
2715
|
// Collect provider tool results
|
|
2481
2716
|
const providerResultEntries = await Promise.all(
|
|
2482
|
-
providerToolCallsForResults
|
|
2483
|
-
|
|
2484
|
-
|
|
2717
|
+
providerToolCallsForResults
|
|
2718
|
+
.filter(
|
|
2719
|
+
call =>
|
|
2720
|
+
!providerApprovalRequests.some(
|
|
2721
|
+
request =>
|
|
2722
|
+
request.toolCall.toolCallId === call.toolCallId,
|
|
2723
|
+
),
|
|
2724
|
+
)
|
|
2725
|
+
.map(async toolCall => ({
|
|
2485
2726
|
toolCall,
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2727
|
+
result: await resolveProviderToolResult(
|
|
2728
|
+
toolCall,
|
|
2729
|
+
capturedProviderToolResults,
|
|
2730
|
+
stepTools,
|
|
2731
|
+
download,
|
|
2732
|
+
),
|
|
2733
|
+
})),
|
|
2491
2734
|
);
|
|
2492
2735
|
await Promise.all(
|
|
2493
2736
|
providerResultEntries.flatMap(({ toolCall, result }) =>
|
|
@@ -2534,7 +2777,45 @@ export class WorkflowAgent<
|
|
|
2534
2777
|
output: r.rawOutput,
|
|
2535
2778
|
}));
|
|
2536
2779
|
|
|
2537
|
-
addToolResultsToStep(step, executedResults);
|
|
2780
|
+
addToolResultsToStep(step, executedResults, mode);
|
|
2781
|
+
|
|
2782
|
+
// Approval data belongs to the execution, whether or not it has a
|
|
2783
|
+
// writable. Only the environment-variable reference enters the
|
|
2784
|
+
// durable signing step; the resolved secret stays inside it.
|
|
2785
|
+
const approvalToolCalls = pausedToolCalls.filter(
|
|
2786
|
+
tc => approvalNeeded[nonProviderToolCalls.indexOf(tc)],
|
|
2787
|
+
);
|
|
2788
|
+
const approvalRequests = await Promise.all(
|
|
2789
|
+
approvalToolCalls.map(async tc => {
|
|
2790
|
+
const approvalId = `approval-${tc.toolCallId}`;
|
|
2791
|
+
const signature =
|
|
2792
|
+
effectiveToolApprovalSecret == null
|
|
2793
|
+
? undefined
|
|
2794
|
+
: await signWorkflowToolApproval({
|
|
2795
|
+
secret: effectiveToolApprovalSecret,
|
|
2796
|
+
approvalId,
|
|
2797
|
+
toolCallId: tc.toolCallId,
|
|
2798
|
+
toolName: tc.toolName,
|
|
2799
|
+
input: tc.input,
|
|
2800
|
+
});
|
|
2801
|
+
return {
|
|
2802
|
+
type: 'tool-approval-request' as const,
|
|
2803
|
+
approvalId,
|
|
2804
|
+
toolCall: {
|
|
2805
|
+
...tc,
|
|
2806
|
+
type: 'tool-call' as const,
|
|
2807
|
+
} as StepResult<ToolSet>['toolCalls'][number],
|
|
2808
|
+
...(signature != null ? { signature } : {}),
|
|
2809
|
+
};
|
|
2810
|
+
}),
|
|
2811
|
+
);
|
|
2812
|
+
step?.content.push(...approvalRequests);
|
|
2813
|
+
const approvalMessages = approvalRequests.map(
|
|
2814
|
+
({ toolCall, ...request }) => ({
|
|
2815
|
+
...request,
|
|
2816
|
+
toolCallId: toolCall.toolCallId,
|
|
2817
|
+
}),
|
|
2818
|
+
);
|
|
2538
2819
|
|
|
2539
2820
|
const responseMessages = addToolResultsToConversation({
|
|
2540
2821
|
messages: iterMessages,
|
|
@@ -2546,42 +2827,59 @@ export class WorkflowAgent<
|
|
|
2546
2827
|
),
|
|
2547
2828
|
providerExecutedToolResultPositions,
|
|
2548
2829
|
});
|
|
2830
|
+
const publicResponseMessages = addApprovalRequestsToMessages(
|
|
2831
|
+
mode === 'generate'
|
|
2832
|
+
? toModelResponseMessages(responseMessages)
|
|
2833
|
+
: (responseMessages as unknown as ModelMessage[]),
|
|
2834
|
+
[
|
|
2835
|
+
...approvalMessages,
|
|
2836
|
+
...providerApprovalRequests.map(({ toolCall, ...request }) => ({
|
|
2837
|
+
...request,
|
|
2838
|
+
toolCallId: toolCall.toolCallId,
|
|
2839
|
+
})),
|
|
2840
|
+
],
|
|
2841
|
+
);
|
|
2549
2842
|
step?.response.messages.push(
|
|
2550
|
-
...(
|
|
2843
|
+
...(publicResponseMessages as NonNullable<
|
|
2551
2844
|
typeof step
|
|
2552
2845
|
>['response']['messages']),
|
|
2553
2846
|
);
|
|
2554
2847
|
|
|
2555
|
-
|
|
2848
|
+
// Approval requests are public conversation data, not provider
|
|
2849
|
+
// prompt parts. The next invocation consumes them before conversion.
|
|
2850
|
+
const messages = (
|
|
2851
|
+
publicResponseMessages === responseMessages
|
|
2852
|
+
? iterMessages
|
|
2853
|
+
: [
|
|
2854
|
+
...iterMessages.slice(
|
|
2855
|
+
0,
|
|
2856
|
+
iterMessages.length - responseMessages.length,
|
|
2857
|
+
),
|
|
2858
|
+
...publicResponseMessages,
|
|
2859
|
+
]
|
|
2860
|
+
) as ModelMessage[];
|
|
2556
2861
|
const lastStep = steps[steps.length - 1];
|
|
2557
|
-
const totalUsage =
|
|
2862
|
+
const totalUsage = aggregateExecutionUsage(steps, mode);
|
|
2558
2863
|
const finishReason = lastStep?.finishReason ?? 'other';
|
|
2559
2864
|
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
const lastTelemetryStep =
|
|
2577
|
-
telemetrySteps[telemetrySteps.length - 1];
|
|
2578
|
-
await telemetryDispatcher.onEnd?.({
|
|
2579
|
-
...lastTelemetryStep,
|
|
2580
|
-
steps: telemetrySteps,
|
|
2581
|
-
usage: totalUsage,
|
|
2582
|
-
totalUsage,
|
|
2583
|
-
});
|
|
2865
|
+
const data: WorkflowExecutionData<TTools, TOutput> = {
|
|
2866
|
+
messages,
|
|
2867
|
+
steps,
|
|
2868
|
+
toolCalls: allToolCalls,
|
|
2869
|
+
toolResults: allToolResults,
|
|
2870
|
+
finishReason,
|
|
2871
|
+
totalUsage,
|
|
2872
|
+
output: undefined as TOutput,
|
|
2873
|
+
};
|
|
2874
|
+
if (mode === 'generate' && step != null) {
|
|
2875
|
+
await mergedOnStepEnd?.(
|
|
2876
|
+
step as StepResult<TTools, TRuntimeContext>,
|
|
2877
|
+
);
|
|
2878
|
+
await telemetryDispatcher.onStepEnd?.(
|
|
2879
|
+
normalizeStepForTelemetry(step),
|
|
2880
|
+
);
|
|
2584
2881
|
}
|
|
2882
|
+
await notifyEnd(data, wasAborted);
|
|
2585
2883
|
|
|
2586
2884
|
// Emit tool-approval-request chunks for tools that need approval
|
|
2587
2885
|
// so useChat can show the approval UI
|
|
@@ -2601,58 +2899,16 @@ export class WorkflowAgent<
|
|
|
2601
2899
|
);
|
|
2602
2900
|
}
|
|
2603
2901
|
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
pausedToolCalls[i],
|
|
2607
|
-
);
|
|
2608
|
-
return approvalNeeded[tcIndex];
|
|
2609
|
-
});
|
|
2610
|
-
if (approvalToolCalls.length > 0) {
|
|
2611
|
-
// The signing step receives only a non-secret environment
|
|
2612
|
-
// variable reference. It resolves the secret inside the step,
|
|
2613
|
-
// and only the resulting signature is persisted in the stream.
|
|
2614
|
-
const approvalRequests = await Promise.all(
|
|
2615
|
-
approvalToolCalls.map(async tc => {
|
|
2616
|
-
const approvalId = `approval-${tc.toolCallId}`;
|
|
2617
|
-
const signature =
|
|
2618
|
-
effectiveToolApprovalSecret == null
|
|
2619
|
-
? undefined
|
|
2620
|
-
: await signWorkflowToolApproval({
|
|
2621
|
-
secret: effectiveToolApprovalSecret,
|
|
2622
|
-
approvalId,
|
|
2623
|
-
toolCallId: tc.toolCallId,
|
|
2624
|
-
toolName: tc.toolName,
|
|
2625
|
-
input: tc.input,
|
|
2626
|
-
});
|
|
2627
|
-
|
|
2628
|
-
return {
|
|
2629
|
-
approvalId,
|
|
2630
|
-
toolCallId: tc.toolCallId,
|
|
2631
|
-
...(signature != null ? { signature } : {}),
|
|
2632
|
-
};
|
|
2633
|
-
}),
|
|
2634
|
-
);
|
|
2635
|
-
await writeApprovalRequests(options.writable, approvalRequests);
|
|
2636
|
-
}
|
|
2637
|
-
}
|
|
2638
|
-
|
|
2639
|
-
// Close the stream before returning for paused tools
|
|
2640
|
-
if (options.writable) {
|
|
2641
|
-
const sendFinish = options.sendFinish ?? true;
|
|
2642
|
-
const preventClose = options.preventClose ?? false;
|
|
2643
|
-
if (sendFinish || !preventClose) {
|
|
2644
|
-
await closeStream(options.writable, preventClose, sendFinish);
|
|
2902
|
+
if (approvalMessages.length > 0) {
|
|
2903
|
+
await writeApprovalRequests(options.writable, approvalMessages);
|
|
2645
2904
|
}
|
|
2646
2905
|
}
|
|
2647
2906
|
|
|
2907
|
+
await closeOutput();
|
|
2648
2908
|
return {
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
toolResults: allToolResults,
|
|
2653
|
-
finishReason,
|
|
2654
|
-
totalUsage,
|
|
2655
|
-
output: undefined as TOutput,
|
|
2909
|
+
data,
|
|
2910
|
+
outcome: { status: 'completed' },
|
|
2911
|
+
initialResponseMessages,
|
|
2656
2912
|
};
|
|
2657
2913
|
}
|
|
2658
2914
|
|
|
@@ -2781,7 +3037,7 @@ export class WorkflowAgent<
|
|
|
2781
3037
|
output: r.rawOutput,
|
|
2782
3038
|
}));
|
|
2783
3039
|
|
|
2784
|
-
addToolResultsToStep(step, executedToolResults);
|
|
3040
|
+
addToolResultsToStep(step, executedToolResults, mode);
|
|
2785
3041
|
|
|
2786
3042
|
result = await iterator.next(continuationToolResults);
|
|
2787
3043
|
} else {
|
|
@@ -2843,7 +3099,7 @@ export class WorkflowAgent<
|
|
|
2843
3099
|
| OutputSpecification<TOutput, TPartialOutput>
|
|
2844
3100
|
| undefined;
|
|
2845
3101
|
let experimentalOutput: TOutput = undefined as TOutput;
|
|
2846
|
-
if (effectiveOutput && steps.length > 0) {
|
|
3102
|
+
if (mode === 'stream' && effectiveOutput && steps.length > 0) {
|
|
2847
3103
|
const lastStep = steps[steps.length - 1];
|
|
2848
3104
|
const text = lastStep.text;
|
|
2849
3105
|
if (text) {
|
|
@@ -2868,37 +3124,68 @@ export class WorkflowAgent<
|
|
|
2868
3124
|
}
|
|
2869
3125
|
|
|
2870
3126
|
const lastStep = steps[steps.length - 1];
|
|
2871
|
-
const totalUsage =
|
|
3127
|
+
const totalUsage = aggregateExecutionUsage(steps, mode);
|
|
2872
3128
|
const finishReason = lastStep?.finishReason ?? 'other';
|
|
2873
3129
|
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
3130
|
+
const data: WorkflowExecutionData<TTools, TOutput> = {
|
|
3131
|
+
messages,
|
|
3132
|
+
steps,
|
|
3133
|
+
toolCalls: lastStepToolCalls,
|
|
3134
|
+
toolResults: lastStepToolResults,
|
|
3135
|
+
finishReason,
|
|
3136
|
+
totalUsage,
|
|
3137
|
+
output: experimentalOutput,
|
|
3138
|
+
};
|
|
3139
|
+
if (mode === 'stream' || (!hasEncounteredError && !hasTerminalError))
|
|
3140
|
+
await notifyEnd(data, wasAborted);
|
|
3141
|
+
await closeOutput();
|
|
3142
|
+
|
|
3143
|
+
const outcome: WorkflowExecutionOutcome = hasEncounteredError
|
|
3144
|
+
? wasAborted
|
|
3145
|
+
? { status: 'aborted', rejection: { value: encounteredError } }
|
|
3146
|
+
: { status: 'failed', source: 'execution', error: encounteredError }
|
|
3147
|
+
: hasTerminalError
|
|
3148
|
+
? { status: 'failed', source: 'model-stream', error: terminalError }
|
|
3149
|
+
: wasAborted
|
|
3150
|
+
? { status: 'aborted' }
|
|
3151
|
+
: { status: 'completed' };
|
|
3152
|
+
return {
|
|
3153
|
+
data,
|
|
3154
|
+
outcome,
|
|
3155
|
+
initialResponseMessages,
|
|
3156
|
+
...(outcome.status === 'aborted' ? { abortReason: abortReason() } : {}),
|
|
3157
|
+
};
|
|
3158
|
+
|
|
3159
|
+
async function notifyEnd(
|
|
3160
|
+
data: WorkflowExecutionData<TTools, TOutput>,
|
|
3161
|
+
aborted: boolean,
|
|
3162
|
+
) {
|
|
3163
|
+
if (aborted) return;
|
|
3164
|
+
if (mergedOnEnd) {
|
|
3165
|
+
await mergedOnEnd({
|
|
3166
|
+
steps: data.steps,
|
|
3167
|
+
messages: data.messages,
|
|
3168
|
+
text: data.steps.at(-1)?.text ?? '',
|
|
3169
|
+
finishReason: data.finishReason,
|
|
3170
|
+
usage: data.totalUsage,
|
|
3171
|
+
totalUsage: data.totalUsage,
|
|
3172
|
+
runtimeContext,
|
|
3173
|
+
toolsContext: toolsContext as unknown as InferToolSetContext<TTools>,
|
|
3174
|
+
output: data.output,
|
|
3175
|
+
});
|
|
3176
|
+
}
|
|
3177
|
+
if (data.steps.length > 0) {
|
|
3178
|
+
const telemetrySteps = data.steps.map(normalizeStepForTelemetry);
|
|
3179
|
+
await telemetryDispatcher.onEnd?.({
|
|
3180
|
+
...telemetrySteps[telemetrySteps.length - 1],
|
|
3181
|
+
steps: telemetrySteps,
|
|
3182
|
+
usage: data.totalUsage,
|
|
3183
|
+
totalUsage: data.totalUsage,
|
|
3184
|
+
});
|
|
3185
|
+
}
|
|
2897
3186
|
}
|
|
2898
3187
|
|
|
2899
|
-
|
|
2900
|
-
if (hasEncounteredError) {
|
|
2901
|
-
// Close the stream before throwing
|
|
3188
|
+
async function closeOutput() {
|
|
2902
3189
|
if (options.writable) {
|
|
2903
3190
|
const sendFinish = options.sendFinish ?? true;
|
|
2904
3191
|
const preventClose = options.preventClose ?? false;
|
|
@@ -2906,28 +3193,7 @@ export class WorkflowAgent<
|
|
|
2906
3193
|
await closeStream(options.writable, preventClose, sendFinish);
|
|
2907
3194
|
}
|
|
2908
3195
|
}
|
|
2909
|
-
throw encounteredError;
|
|
2910
3196
|
}
|
|
2911
|
-
|
|
2912
|
-
// Close the writable stream
|
|
2913
|
-
if (options.writable) {
|
|
2914
|
-
const sendFinish = options.sendFinish ?? true;
|
|
2915
|
-
const preventClose = options.preventClose ?? false;
|
|
2916
|
-
if (sendFinish || !preventClose) {
|
|
2917
|
-
await closeStream(options.writable, preventClose, sendFinish);
|
|
2918
|
-
}
|
|
2919
|
-
}
|
|
2920
|
-
|
|
2921
|
-
return {
|
|
2922
|
-
messages: messages as ModelMessage[],
|
|
2923
|
-
steps,
|
|
2924
|
-
toolCalls: lastStepToolCalls,
|
|
2925
|
-
toolResults: lastStepToolResults,
|
|
2926
|
-
finishReason,
|
|
2927
|
-
totalUsage,
|
|
2928
|
-
output: experimentalOutput,
|
|
2929
|
-
...(hasTerminalError ? { error: terminalError } : {}),
|
|
2930
|
-
};
|
|
2931
3197
|
}
|
|
2932
3198
|
}
|
|
2933
3199
|
|
|
@@ -3393,3 +3659,45 @@ async function executeTool(
|
|
|
3393
3659
|
isError: false,
|
|
3394
3660
|
};
|
|
3395
3661
|
}
|
|
3662
|
+
|
|
3663
|
+
function aggregateExecutionUsage(
|
|
3664
|
+
steps: StepResult<any, any>[],
|
|
3665
|
+
mode: 'generate' | 'stream',
|
|
3666
|
+
): LanguageModelUsage {
|
|
3667
|
+
return mode === 'stream'
|
|
3668
|
+
? aggregateUsage(steps)
|
|
3669
|
+
: steps.reduce(
|
|
3670
|
+
(usage, step) => addLanguageModelUsage(usage, step.usage),
|
|
3671
|
+
createNullLanguageModelUsage(),
|
|
3672
|
+
);
|
|
3673
|
+
}
|
|
3674
|
+
|
|
3675
|
+
function addApprovalRequestsToMessages(
|
|
3676
|
+
messages: ModelMessage[],
|
|
3677
|
+
requests: Array<{
|
|
3678
|
+
type: 'tool-approval-request';
|
|
3679
|
+
approvalId: string;
|
|
3680
|
+
toolCallId: string;
|
|
3681
|
+
signature?: string;
|
|
3682
|
+
}>,
|
|
3683
|
+
): ModelMessage[] {
|
|
3684
|
+
if (requests.length === 0) return messages;
|
|
3685
|
+
const result = [...messages];
|
|
3686
|
+
for (let index = result.length - 1; index >= 0; index--) {
|
|
3687
|
+
const message = result[index];
|
|
3688
|
+
if (message.role === 'assistant') {
|
|
3689
|
+
result[index] = {
|
|
3690
|
+
...message,
|
|
3691
|
+
content: [
|
|
3692
|
+
...(typeof message.content === 'string'
|
|
3693
|
+
? [{ type: 'text' as const, text: message.content }]
|
|
3694
|
+
: message.content),
|
|
3695
|
+
...requests,
|
|
3696
|
+
],
|
|
3697
|
+
};
|
|
3698
|
+
return result;
|
|
3699
|
+
}
|
|
3700
|
+
}
|
|
3701
|
+
result.unshift({ role: 'assistant', content: requests });
|
|
3702
|
+
return result;
|
|
3703
|
+
}
|