@ai-sdk/workflow 2.0.58 → 2.0.60
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 +27 -0
- package/dist/index.d.ts +61 -43
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +686 -305
- 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 +521 -206
- 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
|
|
@@ -1748,6 +1950,7 @@ export class WorkflowAgent<
|
|
|
1748
1950
|
input: collected.toolCall.input,
|
|
1749
1951
|
reason: collected.approvalResponse.reason,
|
|
1750
1952
|
providerExecuted: collected.toolCall.providerExecuted === true,
|
|
1953
|
+
existingToolResult: collected.existingToolResult,
|
|
1751
1954
|
}),
|
|
1752
1955
|
);
|
|
1753
1956
|
|
|
@@ -1890,7 +2093,10 @@ export class WorkflowAgent<
|
|
|
1890
2093
|
for (const denial of deniedToolApprovals) {
|
|
1891
2094
|
// Provider-executed denials are forwarded to the provider via the
|
|
1892
2095
|
// preserved approval response below, not turned into a local result.
|
|
1893
|
-
if (
|
|
2096
|
+
if (
|
|
2097
|
+
denial.providerExecuted ||
|
|
2098
|
+
denial.existingToolResult !== undefined
|
|
2099
|
+
) {
|
|
1894
2100
|
continue;
|
|
1895
2101
|
}
|
|
1896
2102
|
toolResultContent.push({
|
|
@@ -1939,6 +2145,13 @@ export class WorkflowAgent<
|
|
|
1939
2145
|
}
|
|
1940
2146
|
}
|
|
1941
2147
|
|
|
2148
|
+
if (mode === 'generate' && toolResultContent.length > 0) {
|
|
2149
|
+
initialResponseMessages.push({
|
|
2150
|
+
role: 'tool',
|
|
2151
|
+
content: toolResultContent,
|
|
2152
|
+
} as unknown as WorkflowAgentGenerateResult<TTools>['responseMessages'][number]);
|
|
2153
|
+
}
|
|
2154
|
+
|
|
1942
2155
|
// Add tool results as a new tool message
|
|
1943
2156
|
if (toolResultContent.length > 0) {
|
|
1944
2157
|
cleanedMessages.push({
|
|
@@ -1952,10 +2165,13 @@ export class WorkflowAgent<
|
|
|
1952
2165
|
// Write tool results and step boundaries to the stream so the UI
|
|
1953
2166
|
// can transition approved/denied tool parts to the correct state
|
|
1954
2167
|
// and properly separate them from the subsequent model step.
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
2168
|
+
const deniedResults = deniedToolApprovals
|
|
2169
|
+
.filter(denial => !denial.providerExecuted)
|
|
2170
|
+
.map(denial => ({ toolCallId: denial.toolCallId }));
|
|
2171
|
+
if (
|
|
2172
|
+
options.writable &&
|
|
2173
|
+
(toolResultContent.length > 0 || deniedResults.length > 0)
|
|
2174
|
+
) {
|
|
1959
2175
|
await writeApprovalToolResults(
|
|
1960
2176
|
options.writable,
|
|
1961
2177
|
approvedRawResults,
|
|
@@ -2287,17 +2503,23 @@ export class WorkflowAgent<
|
|
|
2287
2503
|
await options.onAbort({ steps });
|
|
2288
2504
|
}
|
|
2289
2505
|
return {
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2506
|
+
outcome: { status: 'aborted' },
|
|
2507
|
+
abortReason: abortReason(),
|
|
2508
|
+
data: {
|
|
2509
|
+
messages: prompt.messages,
|
|
2510
|
+
steps,
|
|
2511
|
+
toolCalls: [],
|
|
2512
|
+
toolResults: [],
|
|
2513
|
+
finishReason: 'other',
|
|
2514
|
+
totalUsage: aggregateExecutionUsage(steps, mode),
|
|
2515
|
+
output: undefined as TOutput,
|
|
2516
|
+
},
|
|
2297
2517
|
};
|
|
2298
2518
|
}
|
|
2299
2519
|
|
|
2300
|
-
const iterator =
|
|
2520
|
+
const iterator = modelCallIterator({
|
|
2521
|
+
mode,
|
|
2522
|
+
include: options.include,
|
|
2301
2523
|
model: effectiveModel,
|
|
2302
2524
|
tools: effectiveTools as ToolSet,
|
|
2303
2525
|
writable: options.writable,
|
|
@@ -2363,6 +2585,19 @@ export class WorkflowAgent<
|
|
|
2363
2585
|
providerExecutedToolResults,
|
|
2364
2586
|
providerExecutedToolResultPositions,
|
|
2365
2587
|
} = result.value;
|
|
2588
|
+
if (mode === 'generate' && step != null) {
|
|
2589
|
+
for (const call of toolCalls) {
|
|
2590
|
+
if (call.invalid && !call.providerExecuted)
|
|
2591
|
+
step.content.push({
|
|
2592
|
+
type: 'tool-error',
|
|
2593
|
+
toolCallId: call.toolCallId,
|
|
2594
|
+
toolName: call.toolName,
|
|
2595
|
+
input: call.input,
|
|
2596
|
+
error: call.error,
|
|
2597
|
+
dynamic: true,
|
|
2598
|
+
});
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2366
2601
|
const capturedProviderToolResults =
|
|
2367
2602
|
providerExecutedToolResults ??
|
|
2368
2603
|
new Map<string, ProviderExecutedToolResult>();
|
|
@@ -2461,7 +2696,14 @@ export class WorkflowAgent<
|
|
|
2461
2696
|
// stop the loop and return them.
|
|
2462
2697
|
// This matches AI SDK behavior: tools without execute or needing
|
|
2463
2698
|
// approval pause the agent loop.
|
|
2464
|
-
|
|
2699
|
+
const providerApprovalRequests =
|
|
2700
|
+
step?.content
|
|
2701
|
+
.filter(part => part.type === 'tool-approval-request')
|
|
2702
|
+
.filter(part => part.toolCall.providerExecuted) ?? [];
|
|
2703
|
+
if (
|
|
2704
|
+
pausedToolCalls.length > 0 ||
|
|
2705
|
+
providerApprovalRequests.length > 0
|
|
2706
|
+
) {
|
|
2465
2707
|
// Execute any executable tools that were also called in this step
|
|
2466
2708
|
const executableResults = await Promise.all(
|
|
2467
2709
|
executableToolCalls.map(
|
|
@@ -2479,15 +2721,23 @@ export class WorkflowAgent<
|
|
|
2479
2721
|
|
|
2480
2722
|
// Collect provider tool results
|
|
2481
2723
|
const providerResultEntries = await Promise.all(
|
|
2482
|
-
providerToolCallsForResults
|
|
2483
|
-
|
|
2484
|
-
|
|
2724
|
+
providerToolCallsForResults
|
|
2725
|
+
.filter(
|
|
2726
|
+
call =>
|
|
2727
|
+
!providerApprovalRequests.some(
|
|
2728
|
+
request =>
|
|
2729
|
+
request.toolCall.toolCallId === call.toolCallId,
|
|
2730
|
+
),
|
|
2731
|
+
)
|
|
2732
|
+
.map(async toolCall => ({
|
|
2485
2733
|
toolCall,
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2734
|
+
result: await resolveProviderToolResult(
|
|
2735
|
+
toolCall,
|
|
2736
|
+
capturedProviderToolResults,
|
|
2737
|
+
stepTools,
|
|
2738
|
+
download,
|
|
2739
|
+
),
|
|
2740
|
+
})),
|
|
2491
2741
|
);
|
|
2492
2742
|
await Promise.all(
|
|
2493
2743
|
providerResultEntries.flatMap(({ toolCall, result }) =>
|
|
@@ -2534,7 +2784,45 @@ export class WorkflowAgent<
|
|
|
2534
2784
|
output: r.rawOutput,
|
|
2535
2785
|
}));
|
|
2536
2786
|
|
|
2537
|
-
addToolResultsToStep(step, executedResults);
|
|
2787
|
+
addToolResultsToStep(step, executedResults, mode);
|
|
2788
|
+
|
|
2789
|
+
// Approval data belongs to the execution, whether or not it has a
|
|
2790
|
+
// writable. Only the environment-variable reference enters the
|
|
2791
|
+
// durable signing step; the resolved secret stays inside it.
|
|
2792
|
+
const approvalToolCalls = pausedToolCalls.filter(
|
|
2793
|
+
tc => approvalNeeded[nonProviderToolCalls.indexOf(tc)],
|
|
2794
|
+
);
|
|
2795
|
+
const approvalRequests = await Promise.all(
|
|
2796
|
+
approvalToolCalls.map(async tc => {
|
|
2797
|
+
const approvalId = `approval-${tc.toolCallId}`;
|
|
2798
|
+
const signature =
|
|
2799
|
+
effectiveToolApprovalSecret == null
|
|
2800
|
+
? undefined
|
|
2801
|
+
: await signWorkflowToolApproval({
|
|
2802
|
+
secret: effectiveToolApprovalSecret,
|
|
2803
|
+
approvalId,
|
|
2804
|
+
toolCallId: tc.toolCallId,
|
|
2805
|
+
toolName: tc.toolName,
|
|
2806
|
+
input: tc.input,
|
|
2807
|
+
});
|
|
2808
|
+
return {
|
|
2809
|
+
type: 'tool-approval-request' as const,
|
|
2810
|
+
approvalId,
|
|
2811
|
+
toolCall: {
|
|
2812
|
+
...tc,
|
|
2813
|
+
type: 'tool-call' as const,
|
|
2814
|
+
} as StepResult<ToolSet>['toolCalls'][number],
|
|
2815
|
+
...(signature != null ? { signature } : {}),
|
|
2816
|
+
};
|
|
2817
|
+
}),
|
|
2818
|
+
);
|
|
2819
|
+
step?.content.push(...approvalRequests);
|
|
2820
|
+
const approvalMessages = approvalRequests.map(
|
|
2821
|
+
({ toolCall, ...request }) => ({
|
|
2822
|
+
...request,
|
|
2823
|
+
toolCallId: toolCall.toolCallId,
|
|
2824
|
+
}),
|
|
2825
|
+
);
|
|
2538
2826
|
|
|
2539
2827
|
const responseMessages = addToolResultsToConversation({
|
|
2540
2828
|
messages: iterMessages,
|
|
@@ -2546,42 +2834,59 @@ export class WorkflowAgent<
|
|
|
2546
2834
|
),
|
|
2547
2835
|
providerExecutedToolResultPositions,
|
|
2548
2836
|
});
|
|
2837
|
+
const publicResponseMessages = addApprovalRequestsToMessages(
|
|
2838
|
+
mode === 'generate'
|
|
2839
|
+
? toModelResponseMessages(responseMessages)
|
|
2840
|
+
: (responseMessages as unknown as ModelMessage[]),
|
|
2841
|
+
[
|
|
2842
|
+
...approvalMessages,
|
|
2843
|
+
...providerApprovalRequests.map(({ toolCall, ...request }) => ({
|
|
2844
|
+
...request,
|
|
2845
|
+
toolCallId: toolCall.toolCallId,
|
|
2846
|
+
})),
|
|
2847
|
+
],
|
|
2848
|
+
);
|
|
2549
2849
|
step?.response.messages.push(
|
|
2550
|
-
...(
|
|
2850
|
+
...(publicResponseMessages as NonNullable<
|
|
2551
2851
|
typeof step
|
|
2552
2852
|
>['response']['messages']),
|
|
2553
2853
|
);
|
|
2554
2854
|
|
|
2555
|
-
|
|
2855
|
+
// Approval requests are public conversation data, not provider
|
|
2856
|
+
// prompt parts. The next invocation consumes them before conversion.
|
|
2857
|
+
const messages = (
|
|
2858
|
+
publicResponseMessages === responseMessages
|
|
2859
|
+
? iterMessages
|
|
2860
|
+
: [
|
|
2861
|
+
...iterMessages.slice(
|
|
2862
|
+
0,
|
|
2863
|
+
iterMessages.length - responseMessages.length,
|
|
2864
|
+
),
|
|
2865
|
+
...publicResponseMessages,
|
|
2866
|
+
]
|
|
2867
|
+
) as ModelMessage[];
|
|
2556
2868
|
const lastStep = steps[steps.length - 1];
|
|
2557
|
-
const totalUsage =
|
|
2869
|
+
const totalUsage = aggregateExecutionUsage(steps, mode);
|
|
2558
2870
|
const finishReason = lastStep?.finishReason ?? 'other';
|
|
2559
2871
|
|
|
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
|
-
});
|
|
2872
|
+
const data: WorkflowExecutionData<TTools, TOutput> = {
|
|
2873
|
+
messages,
|
|
2874
|
+
steps,
|
|
2875
|
+
toolCalls: allToolCalls,
|
|
2876
|
+
toolResults: allToolResults,
|
|
2877
|
+
finishReason,
|
|
2878
|
+
totalUsage,
|
|
2879
|
+
output: undefined as TOutput,
|
|
2880
|
+
};
|
|
2881
|
+
if (mode === 'generate' && step != null) {
|
|
2882
|
+
await mergedOnStepEnd?.(
|
|
2883
|
+
step as StepResult<TTools, TRuntimeContext>,
|
|
2884
|
+
);
|
|
2885
|
+
await telemetryDispatcher.onStepEnd?.(
|
|
2886
|
+
normalizeStepForTelemetry(step),
|
|
2887
|
+
);
|
|
2584
2888
|
}
|
|
2889
|
+
await notifyEnd(data, wasAborted);
|
|
2585
2890
|
|
|
2586
2891
|
// Emit tool-approval-request chunks for tools that need approval
|
|
2587
2892
|
// so useChat can show the approval UI
|
|
@@ -2601,58 +2906,16 @@ export class WorkflowAgent<
|
|
|
2601
2906
|
);
|
|
2602
2907
|
}
|
|
2603
2908
|
|
|
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);
|
|
2909
|
+
if (approvalMessages.length > 0) {
|
|
2910
|
+
await writeApprovalRequests(options.writable, approvalMessages);
|
|
2645
2911
|
}
|
|
2646
2912
|
}
|
|
2647
2913
|
|
|
2914
|
+
await closeOutput();
|
|
2648
2915
|
return {
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
toolResults: allToolResults,
|
|
2653
|
-
finishReason,
|
|
2654
|
-
totalUsage,
|
|
2655
|
-
output: undefined as TOutput,
|
|
2916
|
+
data,
|
|
2917
|
+
outcome: { status: 'completed' },
|
|
2918
|
+
initialResponseMessages,
|
|
2656
2919
|
};
|
|
2657
2920
|
}
|
|
2658
2921
|
|
|
@@ -2781,7 +3044,7 @@ export class WorkflowAgent<
|
|
|
2781
3044
|
output: r.rawOutput,
|
|
2782
3045
|
}));
|
|
2783
3046
|
|
|
2784
|
-
addToolResultsToStep(step, executedToolResults);
|
|
3047
|
+
addToolResultsToStep(step, executedToolResults, mode);
|
|
2785
3048
|
|
|
2786
3049
|
result = await iterator.next(continuationToolResults);
|
|
2787
3050
|
} else {
|
|
@@ -2843,7 +3106,7 @@ export class WorkflowAgent<
|
|
|
2843
3106
|
| OutputSpecification<TOutput, TPartialOutput>
|
|
2844
3107
|
| undefined;
|
|
2845
3108
|
let experimentalOutput: TOutput = undefined as TOutput;
|
|
2846
|
-
if (effectiveOutput && steps.length > 0) {
|
|
3109
|
+
if (mode === 'stream' && effectiveOutput && steps.length > 0) {
|
|
2847
3110
|
const lastStep = steps[steps.length - 1];
|
|
2848
3111
|
const text = lastStep.text;
|
|
2849
3112
|
if (text) {
|
|
@@ -2868,37 +3131,68 @@ export class WorkflowAgent<
|
|
|
2868
3131
|
}
|
|
2869
3132
|
|
|
2870
3133
|
const lastStep = steps[steps.length - 1];
|
|
2871
|
-
const totalUsage =
|
|
3134
|
+
const totalUsage = aggregateExecutionUsage(steps, mode);
|
|
2872
3135
|
const finishReason = lastStep?.finishReason ?? 'other';
|
|
2873
3136
|
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
3137
|
+
const data: WorkflowExecutionData<TTools, TOutput> = {
|
|
3138
|
+
messages,
|
|
3139
|
+
steps,
|
|
3140
|
+
toolCalls: lastStepToolCalls,
|
|
3141
|
+
toolResults: lastStepToolResults,
|
|
3142
|
+
finishReason,
|
|
3143
|
+
totalUsage,
|
|
3144
|
+
output: experimentalOutput,
|
|
3145
|
+
};
|
|
3146
|
+
if (mode === 'stream' || (!hasEncounteredError && !hasTerminalError))
|
|
3147
|
+
await notifyEnd(data, wasAborted);
|
|
3148
|
+
await closeOutput();
|
|
3149
|
+
|
|
3150
|
+
const outcome: WorkflowExecutionOutcome = hasEncounteredError
|
|
3151
|
+
? wasAborted
|
|
3152
|
+
? { status: 'aborted', rejection: { value: encounteredError } }
|
|
3153
|
+
: { status: 'failed', source: 'execution', error: encounteredError }
|
|
3154
|
+
: hasTerminalError
|
|
3155
|
+
? { status: 'failed', source: 'model-stream', error: terminalError }
|
|
3156
|
+
: wasAborted
|
|
3157
|
+
? { status: 'aborted' }
|
|
3158
|
+
: { status: 'completed' };
|
|
3159
|
+
return {
|
|
3160
|
+
data,
|
|
3161
|
+
outcome,
|
|
3162
|
+
initialResponseMessages,
|
|
3163
|
+
...(outcome.status === 'aborted' ? { abortReason: abortReason() } : {}),
|
|
3164
|
+
};
|
|
3165
|
+
|
|
3166
|
+
async function notifyEnd(
|
|
3167
|
+
data: WorkflowExecutionData<TTools, TOutput>,
|
|
3168
|
+
aborted: boolean,
|
|
3169
|
+
) {
|
|
3170
|
+
if (aborted) return;
|
|
3171
|
+
if (mergedOnEnd) {
|
|
3172
|
+
await mergedOnEnd({
|
|
3173
|
+
steps: data.steps,
|
|
3174
|
+
messages: data.messages,
|
|
3175
|
+
text: data.steps.at(-1)?.text ?? '',
|
|
3176
|
+
finishReason: data.finishReason,
|
|
3177
|
+
usage: data.totalUsage,
|
|
3178
|
+
totalUsage: data.totalUsage,
|
|
3179
|
+
runtimeContext,
|
|
3180
|
+
toolsContext: toolsContext as unknown as InferToolSetContext<TTools>,
|
|
3181
|
+
output: data.output,
|
|
3182
|
+
});
|
|
3183
|
+
}
|
|
3184
|
+
if (data.steps.length > 0) {
|
|
3185
|
+
const telemetrySteps = data.steps.map(normalizeStepForTelemetry);
|
|
3186
|
+
await telemetryDispatcher.onEnd?.({
|
|
3187
|
+
...telemetrySteps[telemetrySteps.length - 1],
|
|
3188
|
+
steps: telemetrySteps,
|
|
3189
|
+
usage: data.totalUsage,
|
|
3190
|
+
totalUsage: data.totalUsage,
|
|
3191
|
+
});
|
|
3192
|
+
}
|
|
2897
3193
|
}
|
|
2898
3194
|
|
|
2899
|
-
|
|
2900
|
-
if (hasEncounteredError) {
|
|
2901
|
-
// Close the stream before throwing
|
|
3195
|
+
async function closeOutput() {
|
|
2902
3196
|
if (options.writable) {
|
|
2903
3197
|
const sendFinish = options.sendFinish ?? true;
|
|
2904
3198
|
const preventClose = options.preventClose ?? false;
|
|
@@ -2906,28 +3200,7 @@ export class WorkflowAgent<
|
|
|
2906
3200
|
await closeStream(options.writable, preventClose, sendFinish);
|
|
2907
3201
|
}
|
|
2908
3202
|
}
|
|
2909
|
-
throw encounteredError;
|
|
2910
3203
|
}
|
|
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
3204
|
}
|
|
2932
3205
|
}
|
|
2933
3206
|
|
|
@@ -3393,3 +3666,45 @@ async function executeTool(
|
|
|
3393
3666
|
isError: false,
|
|
3394
3667
|
};
|
|
3395
3668
|
}
|
|
3669
|
+
|
|
3670
|
+
function aggregateExecutionUsage(
|
|
3671
|
+
steps: StepResult<any, any>[],
|
|
3672
|
+
mode: 'generate' | 'stream',
|
|
3673
|
+
): LanguageModelUsage {
|
|
3674
|
+
return mode === 'stream'
|
|
3675
|
+
? aggregateUsage(steps)
|
|
3676
|
+
: steps.reduce(
|
|
3677
|
+
(usage, step) => addLanguageModelUsage(usage, step.usage),
|
|
3678
|
+
createNullLanguageModelUsage(),
|
|
3679
|
+
);
|
|
3680
|
+
}
|
|
3681
|
+
|
|
3682
|
+
function addApprovalRequestsToMessages(
|
|
3683
|
+
messages: ModelMessage[],
|
|
3684
|
+
requests: Array<{
|
|
3685
|
+
type: 'tool-approval-request';
|
|
3686
|
+
approvalId: string;
|
|
3687
|
+
toolCallId: string;
|
|
3688
|
+
signature?: string;
|
|
3689
|
+
}>,
|
|
3690
|
+
): ModelMessage[] {
|
|
3691
|
+
if (requests.length === 0) return messages;
|
|
3692
|
+
const result = [...messages];
|
|
3693
|
+
for (let index = result.length - 1; index >= 0; index--) {
|
|
3694
|
+
const message = result[index];
|
|
3695
|
+
if (message.role === 'assistant') {
|
|
3696
|
+
result[index] = {
|
|
3697
|
+
...message,
|
|
3698
|
+
content: [
|
|
3699
|
+
...(typeof message.content === 'string'
|
|
3700
|
+
? [{ type: 'text' as const, text: message.content }]
|
|
3701
|
+
: message.content),
|
|
3702
|
+
...requests,
|
|
3703
|
+
],
|
|
3704
|
+
};
|
|
3705
|
+
return result;
|
|
3706
|
+
}
|
|
3707
|
+
}
|
|
3708
|
+
result.unshift({ role: 'assistant', content: requests });
|
|
3709
|
+
return result;
|
|
3710
|
+
}
|