ai 7.0.89 → 7.0.91
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 +19 -0
- package/dist/index.d.ts +42 -4
- package/dist/index.js +401 -81
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +3 -1
- package/dist/internal/index.js +9 -7
- package/dist/internal/index.js.map +1 -1
- package/docs/02-foundations/02-providers-and-models.mdx +6 -0
- package/docs/03-agents/06-policy-tool-approvals.mdx +2 -1
- package/docs/03-agents/06-tool-approvals.mdx +7 -4
- package/docs/03-agents/07-workflow-agent.mdx +15 -0
- package/docs/03-ai-sdk-core/50-error-handling.mdx +83 -6
- package/docs/06-advanced/11-secure-url-fetching.mdx +7 -1
- package/docs/07-reference/01-ai-sdk-core/02-stream-text.mdx +9 -2
- package/package.json +11 -11
- package/src/generate-text/execute-tools-from-stream.ts +18 -4
- package/src/generate-text/index.ts +2 -0
- package/src/generate-text/invoke-tool-callbacks-from-stream.ts +14 -2
- package/src/generate-text/stream-retry-attempt-boundary.ts +29 -0
- package/src/generate-text/stream-text.ts +635 -156
- package/src/util/prepare-retries.ts +9 -5
|
@@ -127,7 +127,15 @@ import {
|
|
|
127
127
|
isStopConditionMet,
|
|
128
128
|
type StopCondition,
|
|
129
129
|
} from './stop-condition';
|
|
130
|
-
import {
|
|
130
|
+
import {
|
|
131
|
+
streamLanguageModelCall,
|
|
132
|
+
type LanguageModelStreamPart,
|
|
133
|
+
} from './stream-language-model-call';
|
|
134
|
+
import {
|
|
135
|
+
createStreamRetryAttemptBoundaryPart,
|
|
136
|
+
isStreamRetryAttemptBoundaryPart,
|
|
137
|
+
type StreamRetryAttemptBoundaryPart,
|
|
138
|
+
} from './stream-retry-attempt-boundary';
|
|
131
139
|
import type {
|
|
132
140
|
ConsumeStreamOptions,
|
|
133
141
|
StreamTextResult,
|
|
@@ -193,9 +201,14 @@ const isOutputChunkType = {
|
|
|
193
201
|
'model-call-end': false,
|
|
194
202
|
error: false,
|
|
195
203
|
raw: false,
|
|
196
|
-
} as const satisfies Record<
|
|
197
|
-
|
|
198
|
-
|
|
204
|
+
} as const satisfies Record<
|
|
205
|
+
Exclude<ExecuteToolsStreamPart, StreamRetryAttemptBoundaryPart>['type'],
|
|
206
|
+
boolean
|
|
207
|
+
>;
|
|
208
|
+
|
|
209
|
+
function isOutputChunk(
|
|
210
|
+
chunk: Exclude<ExecuteToolsStreamPart, StreamRetryAttemptBoundaryPart>,
|
|
211
|
+
): boolean {
|
|
199
212
|
if (!isOutputChunkType[chunk.type]) {
|
|
200
213
|
return false;
|
|
201
214
|
}
|
|
@@ -257,14 +270,29 @@ export type StreamTextTransform<TOOLS extends ToolSet> = (options: {
|
|
|
257
270
|
stopStream: () => void;
|
|
258
271
|
}) => TransformStream<TextStreamPart<TOOLS>, TextStreamPart<TOOLS>>;
|
|
259
272
|
|
|
273
|
+
/** A result that requests recovery from a streamed provider error. */
|
|
274
|
+
export type StreamTextOnErrorResult = { retry: true };
|
|
275
|
+
|
|
260
276
|
/**
|
|
261
|
-
*
|
|
277
|
+
* Existing observer callback that is set using the `onError` option.
|
|
262
278
|
*
|
|
263
279
|
* @param event - The event that is passed to the callback.
|
|
264
280
|
*/
|
|
265
|
-
export type StreamTextOnErrorCallback = Callback<{
|
|
281
|
+
export type StreamTextOnErrorCallback = Callback<{ error: unknown }>;
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Retry-capable callback that is set using the `onError` option.
|
|
285
|
+
*
|
|
286
|
+
* @param event - The event that is passed to the callback.
|
|
287
|
+
*/
|
|
288
|
+
export type StreamTextOnErrorRetryCallback = (event: {
|
|
266
289
|
error: unknown;
|
|
267
|
-
}
|
|
290
|
+
}) =>
|
|
291
|
+
| PromiseLike<void | StreamTextOnErrorResult>
|
|
292
|
+
| void
|
|
293
|
+
| StreamTextOnErrorResult;
|
|
294
|
+
|
|
295
|
+
type StreamTextOnErrorHandler = (event: { error: unknown }) => void;
|
|
268
296
|
|
|
269
297
|
/**
|
|
270
298
|
* Callback that is set using the `onChunk` option.
|
|
@@ -344,6 +372,7 @@ export type StreamTextOnAbortCallback<
|
|
|
344
372
|
* If set and supported by the model, calls will generate deterministic results.
|
|
345
373
|
*
|
|
346
374
|
* @param maxRetries - Maximum number of retries. Set to 0 to disable retries. Default: 2.
|
|
375
|
+
* @param streamRetries - Maximum number of retries for provider errors received after streaming starts. Set to 0 to disable automatic stream retries while allowing `onError` to request retries. Omit to disable all stream retry behavior. Default: 0.
|
|
347
376
|
* @param abortSignal - An optional abort signal that can be used to cancel the call.
|
|
348
377
|
* @param timeout - An optional timeout in milliseconds. The call will be aborted if it takes longer than the specified timeout.
|
|
349
378
|
* @param headers - Additional HTTP headers to be sent with the request. Only applicable for HTTP-based providers.
|
|
@@ -388,6 +417,7 @@ export function streamText<
|
|
|
388
417
|
messages,
|
|
389
418
|
allowSystemInMessages,
|
|
390
419
|
maxRetries,
|
|
420
|
+
streamRetries,
|
|
391
421
|
abortSignal,
|
|
392
422
|
timeout,
|
|
393
423
|
headers,
|
|
@@ -410,9 +440,7 @@ export function streamText<
|
|
|
410
440
|
experimental_download: download,
|
|
411
441
|
includeRawChunks,
|
|
412
442
|
onChunk,
|
|
413
|
-
onError
|
|
414
|
-
console.error(error);
|
|
415
|
-
},
|
|
443
|
+
onError: onErrorArg,
|
|
416
444
|
onFinish,
|
|
417
445
|
onEnd = onFinish,
|
|
418
446
|
onAbort,
|
|
@@ -597,9 +625,35 @@ export function streamText<
|
|
|
597
625
|
/**
|
|
598
626
|
* Callback that is invoked when an error occurs during streaming.
|
|
599
627
|
* You can use it to log errors.
|
|
628
|
+
* Return `{ retry: true }` to retry the current model step after a provider
|
|
629
|
+
* error is received from the response stream when `streamRetries` is
|
|
630
|
+
* explicitly configured.
|
|
600
631
|
* The stream processing will pause until the callback promise is resolved.
|
|
601
632
|
*/
|
|
602
|
-
onError?:
|
|
633
|
+
onError?:
|
|
634
|
+
| StreamTextOnErrorCallback
|
|
635
|
+
| StreamTextOnErrorRetryCallback
|
|
636
|
+
| StreamTextOnErrorHandler;
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* Maximum number of automatic retries for provider errors received after
|
|
640
|
+
* response streaming has started. Each retry reruns only the current model
|
|
641
|
+
* step. Completed earlier steps and their tool results are preserved.
|
|
642
|
+
*
|
|
643
|
+
* Partial output from a failed attempt that was already emitted cannot be
|
|
644
|
+
* retracted and remains in consumer-facing streams. It is excluded from
|
|
645
|
+
* the recovered step result, structured output parsing, response messages,
|
|
646
|
+
* and subsequent model steps.
|
|
647
|
+
*
|
|
648
|
+
* Set to `0` to disable automatic retries while allowing `onError` to
|
|
649
|
+
* request one retry. When automatic retries are configured, `onError` can
|
|
650
|
+
* request at most one additional retry after they are exhausted. Omit this
|
|
651
|
+
* option to disable all stream retry behavior and preserve incremental tool
|
|
652
|
+
* streaming for existing `onError` observers.
|
|
653
|
+
*
|
|
654
|
+
* @default 0 (stream retry behavior disabled when omitted)
|
|
655
|
+
*/
|
|
656
|
+
streamRetries?: number;
|
|
603
657
|
|
|
604
658
|
/**
|
|
605
659
|
* Callback that is called when the LLM response and all request tool executions
|
|
@@ -781,6 +835,11 @@ export function streamText<
|
|
|
781
835
|
firstChunkTimeoutMs != null ? new AbortController() : undefined;
|
|
782
836
|
const chunkAbortController =
|
|
783
837
|
chunkTimeoutMs != null ? new AbortController() : undefined;
|
|
838
|
+
const onError: StreamTextOnErrorHandler =
|
|
839
|
+
onErrorArg ??
|
|
840
|
+
(({ error }) => {
|
|
841
|
+
console.error(error);
|
|
842
|
+
});
|
|
784
843
|
const resolvedOnStart = onStart ?? experimental_onStart;
|
|
785
844
|
const resolvedOnStepStart = onStepStart ?? experimental_onStepStart;
|
|
786
845
|
const resolvedOnLanguageModelCallStart =
|
|
@@ -798,6 +857,7 @@ export function streamText<
|
|
|
798
857
|
headers,
|
|
799
858
|
settings,
|
|
800
859
|
maxRetries,
|
|
860
|
+
streamRetries,
|
|
801
861
|
abortSignal: mergeAbortSignals(
|
|
802
862
|
abortSignal,
|
|
803
863
|
totalTimeoutMs,
|
|
@@ -836,6 +896,7 @@ export function streamText<
|
|
|
836
896
|
timeout,
|
|
837
897
|
onChunk,
|
|
838
898
|
onError,
|
|
899
|
+
canRetryStreamViaOnError: streamRetries !== undefined && onErrorArg != null,
|
|
839
900
|
onEnd,
|
|
840
901
|
onAbort,
|
|
841
902
|
onStepFinish: resolvedOnStepEnd,
|
|
@@ -864,6 +925,31 @@ export type EnrichedStreamPart<TOOLS extends ToolSet, PARTIAL_OUTPUT> = {
|
|
|
864
925
|
partialOutput: PARTIAL_OUTPUT | undefined;
|
|
865
926
|
};
|
|
866
927
|
|
|
928
|
+
type StreamRetryBoundaryMetadata = {
|
|
929
|
+
request: LanguageModelRequestMetadata;
|
|
930
|
+
warnings: Array<CallWarning>;
|
|
931
|
+
};
|
|
932
|
+
|
|
933
|
+
const streamRetryBoundarySymbol = Symbol('streamRetryBoundary');
|
|
934
|
+
|
|
935
|
+
type StreamRetryBoundaryPart = {
|
|
936
|
+
[streamRetryBoundarySymbol]: StreamRetryBoundaryMetadata;
|
|
937
|
+
};
|
|
938
|
+
|
|
939
|
+
type InternalTextStreamPart<TOOLS extends ToolSet> =
|
|
940
|
+
| TextStreamPart<TOOLS>
|
|
941
|
+
| StreamRetryBoundaryPart;
|
|
942
|
+
|
|
943
|
+
type InternalEnrichedStreamPart<TOOLS extends ToolSet, PARTIAL_OUTPUT> =
|
|
944
|
+
| EnrichedStreamPart<TOOLS, PARTIAL_OUTPUT>
|
|
945
|
+
| StreamRetryBoundaryPart;
|
|
946
|
+
|
|
947
|
+
function isStreamRetryBoundaryPart(
|
|
948
|
+
part: object,
|
|
949
|
+
): part is StreamRetryBoundaryPart {
|
|
950
|
+
return streamRetryBoundarySymbol in part;
|
|
951
|
+
}
|
|
952
|
+
|
|
867
953
|
async function markPromiseAsHandled<T>(promise: Promise<T>): Promise<void> {
|
|
868
954
|
try {
|
|
869
955
|
await promise;
|
|
@@ -876,8 +962,8 @@ function createOutputTransformStream<
|
|
|
876
962
|
>(
|
|
877
963
|
output: OUTPUT,
|
|
878
964
|
): TransformStream<
|
|
879
|
-
|
|
880
|
-
|
|
965
|
+
InternalTextStreamPart<TOOLS>,
|
|
966
|
+
InternalEnrichedStreamPart<TOOLS, InferPartialOutput<OUTPUT>>
|
|
881
967
|
> {
|
|
882
968
|
let firstTextChunkId: string | undefined = undefined;
|
|
883
969
|
let text = '';
|
|
@@ -885,32 +971,61 @@ function createOutputTransformStream<
|
|
|
885
971
|
let textProviderMetadata: ProviderMetadata | undefined = undefined;
|
|
886
972
|
let lastPublishedValue = '';
|
|
887
973
|
|
|
974
|
+
function resetAttemptState() {
|
|
975
|
+
firstTextChunkId = undefined;
|
|
976
|
+
text = '';
|
|
977
|
+
textChunk = '';
|
|
978
|
+
textProviderMetadata = undefined;
|
|
979
|
+
lastPublishedValue = '';
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
function enqueueChunk({
|
|
983
|
+
controller,
|
|
984
|
+
chunk,
|
|
985
|
+
}: {
|
|
986
|
+
controller: TransformStreamDefaultController<
|
|
987
|
+
InternalEnrichedStreamPart<TOOLS, InferPartialOutput<OUTPUT>>
|
|
988
|
+
>;
|
|
989
|
+
chunk: EnrichedStreamPart<TOOLS, InferPartialOutput<OUTPUT>>;
|
|
990
|
+
}) {
|
|
991
|
+
controller.enqueue(chunk);
|
|
992
|
+
}
|
|
993
|
+
|
|
888
994
|
function publishTextChunk({
|
|
889
995
|
controller,
|
|
890
996
|
partialOutput = undefined,
|
|
891
997
|
}: {
|
|
892
998
|
controller: TransformStreamDefaultController<
|
|
893
|
-
|
|
999
|
+
InternalEnrichedStreamPart<TOOLS, InferPartialOutput<OUTPUT>>
|
|
894
1000
|
>;
|
|
895
1001
|
partialOutput?: InferPartialOutput<OUTPUT>;
|
|
896
1002
|
}) {
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
1003
|
+
enqueueChunk({
|
|
1004
|
+
controller,
|
|
1005
|
+
chunk: {
|
|
1006
|
+
part: {
|
|
1007
|
+
type: 'text-delta',
|
|
1008
|
+
id: firstTextChunkId!,
|
|
1009
|
+
text: textChunk,
|
|
1010
|
+
providerMetadata: textProviderMetadata,
|
|
1011
|
+
},
|
|
1012
|
+
partialOutput,
|
|
903
1013
|
},
|
|
904
|
-
partialOutput,
|
|
905
1014
|
});
|
|
906
1015
|
textChunk = '';
|
|
907
1016
|
}
|
|
908
1017
|
|
|
909
1018
|
return new TransformStream<
|
|
910
|
-
|
|
911
|
-
|
|
1019
|
+
InternalTextStreamPart<TOOLS>,
|
|
1020
|
+
InternalEnrichedStreamPart<TOOLS, InferPartialOutput<OUTPUT>>
|
|
912
1021
|
>({
|
|
913
1022
|
async transform(chunk, controller) {
|
|
1023
|
+
if (isStreamRetryBoundaryPart(chunk)) {
|
|
1024
|
+
resetAttemptState();
|
|
1025
|
+
controller.enqueue(chunk);
|
|
1026
|
+
return;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
914
1029
|
// ensure that we publish the last text chunk before the step finish:
|
|
915
1030
|
if (chunk.type === 'finish-step' && textChunk.length > 0) {
|
|
916
1031
|
publishTextChunk({ controller });
|
|
@@ -921,7 +1036,10 @@ function createOutputTransformStream<
|
|
|
921
1036
|
chunk.type !== 'text-start' &&
|
|
922
1037
|
chunk.type !== 'text-end'
|
|
923
1038
|
) {
|
|
924
|
-
|
|
1039
|
+
enqueueChunk({
|
|
1040
|
+
controller,
|
|
1041
|
+
chunk: { part: chunk, partialOutput: undefined },
|
|
1042
|
+
});
|
|
925
1043
|
return;
|
|
926
1044
|
}
|
|
927
1045
|
|
|
@@ -930,12 +1048,18 @@ function createOutputTransformStream<
|
|
|
930
1048
|
if (firstTextChunkId == null) {
|
|
931
1049
|
firstTextChunkId = chunk.id;
|
|
932
1050
|
} else if (chunk.id !== firstTextChunkId) {
|
|
933
|
-
|
|
1051
|
+
enqueueChunk({
|
|
1052
|
+
controller,
|
|
1053
|
+
chunk: { part: chunk, partialOutput: undefined },
|
|
1054
|
+
});
|
|
934
1055
|
return;
|
|
935
1056
|
}
|
|
936
1057
|
|
|
937
1058
|
if (chunk.type === 'text-start') {
|
|
938
|
-
|
|
1059
|
+
enqueueChunk({
|
|
1060
|
+
controller,
|
|
1061
|
+
chunk: { part: chunk, partialOutput: undefined },
|
|
1062
|
+
});
|
|
939
1063
|
return;
|
|
940
1064
|
}
|
|
941
1065
|
|
|
@@ -943,7 +1067,10 @@ function createOutputTransformStream<
|
|
|
943
1067
|
if (textChunk.length > 0) {
|
|
944
1068
|
publishTextChunk({ controller });
|
|
945
1069
|
}
|
|
946
|
-
|
|
1070
|
+
enqueueChunk({
|
|
1071
|
+
controller,
|
|
1072
|
+
chunk: { part: chunk, partialOutput: undefined },
|
|
1073
|
+
});
|
|
947
1074
|
return;
|
|
948
1075
|
}
|
|
949
1076
|
|
|
@@ -952,7 +1079,10 @@ function createOutputTransformStream<
|
|
|
952
1079
|
textProviderMetadata = chunk.providerMetadata ?? textProviderMetadata;
|
|
953
1080
|
|
|
954
1081
|
if (chunk.text.length === 0 && chunk.providerMetadata != null) {
|
|
955
|
-
|
|
1082
|
+
enqueueChunk({
|
|
1083
|
+
controller,
|
|
1084
|
+
chunk: { part: chunk, partialOutput: undefined },
|
|
1085
|
+
});
|
|
956
1086
|
return;
|
|
957
1087
|
}
|
|
958
1088
|
|
|
@@ -976,6 +1106,95 @@ function createOutputTransformStream<
|
|
|
976
1106
|
});
|
|
977
1107
|
}
|
|
978
1108
|
|
|
1109
|
+
function applyStreamTextTransforms<TOOLS extends ToolSet>({
|
|
1110
|
+
stream,
|
|
1111
|
+
transforms,
|
|
1112
|
+
tools,
|
|
1113
|
+
stopStream,
|
|
1114
|
+
}: {
|
|
1115
|
+
stream: ReadableStream<InternalTextStreamPart<TOOLS>>;
|
|
1116
|
+
transforms: Array<StreamTextTransform<TOOLS>>;
|
|
1117
|
+
tools: TOOLS;
|
|
1118
|
+
stopStream: () => void;
|
|
1119
|
+
}): ReadableStream<InternalTextStreamPart<TOOLS>> {
|
|
1120
|
+
const sourceReader = stream.getReader();
|
|
1121
|
+
let sourceDone = false;
|
|
1122
|
+
let pendingBoundary: StreamRetryBoundaryPart | undefined;
|
|
1123
|
+
let transformedSegmentReader:
|
|
1124
|
+
| ReadableStreamDefaultReader<TextStreamPart<TOOLS>>
|
|
1125
|
+
| undefined;
|
|
1126
|
+
|
|
1127
|
+
const createTransformedSegmentReader = () => {
|
|
1128
|
+
let segment = new ReadableStream<TextStreamPart<TOOLS>>(
|
|
1129
|
+
{
|
|
1130
|
+
async pull(controller) {
|
|
1131
|
+
const { done, value } = await sourceReader.read();
|
|
1132
|
+
|
|
1133
|
+
if (done) {
|
|
1134
|
+
sourceDone = true;
|
|
1135
|
+
controller.close();
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
if (isStreamRetryBoundaryPart(value)) {
|
|
1140
|
+
pendingBoundary = value;
|
|
1141
|
+
controller.close();
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
controller.enqueue(value);
|
|
1146
|
+
},
|
|
1147
|
+
cancel(reason) {
|
|
1148
|
+
return sourceReader.cancel(reason);
|
|
1149
|
+
},
|
|
1150
|
+
},
|
|
1151
|
+
// Do not prefetch the next source chunk. `stopStream` is invoked from a
|
|
1152
|
+
// user transform and must close the gate before another chunk enters it.
|
|
1153
|
+
{ highWaterMark: 0 },
|
|
1154
|
+
);
|
|
1155
|
+
|
|
1156
|
+
for (const transform of transforms) {
|
|
1157
|
+
segment = segment.pipeThrough(
|
|
1158
|
+
transform({
|
|
1159
|
+
tools,
|
|
1160
|
+
stopStream,
|
|
1161
|
+
}),
|
|
1162
|
+
);
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
return segment.getReader();
|
|
1166
|
+
};
|
|
1167
|
+
|
|
1168
|
+
return new ReadableStream<InternalTextStreamPart<TOOLS>>({
|
|
1169
|
+
async pull(controller) {
|
|
1170
|
+
transformedSegmentReader ??= createTransformedSegmentReader();
|
|
1171
|
+
|
|
1172
|
+
const { done, value } = await transformedSegmentReader.read();
|
|
1173
|
+
|
|
1174
|
+
if (!done) {
|
|
1175
|
+
controller.enqueue(value);
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
transformedSegmentReader = undefined;
|
|
1180
|
+
|
|
1181
|
+
if (pendingBoundary != null) {
|
|
1182
|
+
controller.enqueue(pendingBoundary);
|
|
1183
|
+
pendingBoundary = undefined;
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
if (sourceDone) {
|
|
1188
|
+
controller.close();
|
|
1189
|
+
}
|
|
1190
|
+
},
|
|
1191
|
+
async cancel(reason) {
|
|
1192
|
+
await transformedSegmentReader?.cancel(reason);
|
|
1193
|
+
await sourceReader.cancel(reason);
|
|
1194
|
+
},
|
|
1195
|
+
});
|
|
1196
|
+
}
|
|
1197
|
+
|
|
979
1198
|
class DefaultStreamTextResult<
|
|
980
1199
|
TOOLS extends ToolSet,
|
|
981
1200
|
RUNTIME_CONTEXT extends Context,
|
|
@@ -1000,7 +1219,7 @@ class DefaultStreamTextResult<
|
|
|
1000
1219
|
private outputPromise: Promise<InferCompleteOutput<OUTPUT>> | undefined;
|
|
1001
1220
|
|
|
1002
1221
|
private readonly addStream: (
|
|
1003
|
-
stream: ReadableStream<
|
|
1222
|
+
stream: ReadableStream<InternalTextStreamPart<TOOLS>>,
|
|
1004
1223
|
callbacks?: {
|
|
1005
1224
|
onError?: (error: unknown) => void;
|
|
1006
1225
|
onCancel?: () => void;
|
|
@@ -1023,6 +1242,7 @@ class DefaultStreamTextResult<
|
|
|
1023
1242
|
headers,
|
|
1024
1243
|
settings,
|
|
1025
1244
|
maxRetries: maxRetriesArg,
|
|
1245
|
+
streamRetries: streamRetriesArg,
|
|
1026
1246
|
abortSignal,
|
|
1027
1247
|
stepTimeoutMs,
|
|
1028
1248
|
stepAbortController,
|
|
@@ -1056,6 +1276,7 @@ class DefaultStreamTextResult<
|
|
|
1056
1276
|
timeout,
|
|
1057
1277
|
onChunk,
|
|
1058
1278
|
onError,
|
|
1279
|
+
canRetryStreamViaOnError,
|
|
1059
1280
|
onEnd,
|
|
1060
1281
|
onAbort,
|
|
1061
1282
|
onStepFinish,
|
|
@@ -1075,6 +1296,7 @@ class DefaultStreamTextResult<
|
|
|
1075
1296
|
headers: Record<string, string | undefined> | undefined;
|
|
1076
1297
|
settings: LanguageModelCallOptions;
|
|
1077
1298
|
maxRetries: number | undefined;
|
|
1299
|
+
streamRetries: number | undefined;
|
|
1078
1300
|
abortSignal: AbortSignal | undefined;
|
|
1079
1301
|
stepTimeoutMs: number | undefined;
|
|
1080
1302
|
stepAbortController: AbortController | undefined;
|
|
@@ -1117,7 +1339,8 @@ class DefaultStreamTextResult<
|
|
|
1117
1339
|
|
|
1118
1340
|
// callbacks:
|
|
1119
1341
|
onChunk: undefined | StreamTextOnChunkCallback<TOOLS>;
|
|
1120
|
-
onError:
|
|
1342
|
+
onError: StreamTextOnErrorHandler;
|
|
1343
|
+
canRetryStreamViaOnError: boolean;
|
|
1121
1344
|
onEnd:
|
|
1122
1345
|
| undefined
|
|
1123
1346
|
| StreamTextOnEndCallback<
|
|
@@ -1241,20 +1464,36 @@ class DefaultStreamTextResult<
|
|
|
1241
1464
|
}
|
|
1242
1465
|
> = createIdMap();
|
|
1243
1466
|
let recordedNoOutputError: NoOutputGeneratedError | undefined;
|
|
1467
|
+
const errorsHandledForStreamRetry = new Set<unknown>();
|
|
1244
1468
|
|
|
1245
1469
|
const eventProcessor = new TransformStream<
|
|
1246
|
-
|
|
1470
|
+
InternalEnrichedStreamPart<TOOLS, InferPartialOutput<OUTPUT>>,
|
|
1247
1471
|
EnrichedStreamPart<TOOLS, InferPartialOutput<OUTPUT>>
|
|
1248
1472
|
>({
|
|
1249
1473
|
async transform(chunk, controller) {
|
|
1250
|
-
|
|
1474
|
+
if (isStreamRetryBoundaryPart(chunk)) {
|
|
1475
|
+
const retryBoundary = chunk[streamRetryBoundarySymbol];
|
|
1476
|
+
recordedContent = [];
|
|
1477
|
+
activeReasoningContent = createIdMap();
|
|
1478
|
+
activeTextContent = createIdMap();
|
|
1479
|
+
recordedRequest = retryBoundary.request;
|
|
1480
|
+
recordedRequestMessages = retryBoundary.request.messages ?? [];
|
|
1481
|
+
recordedWarnings = retryBoundary.warnings;
|
|
1482
|
+
return;
|
|
1483
|
+
}
|
|
1251
1484
|
|
|
1252
1485
|
const { part } = chunk;
|
|
1486
|
+
controller.enqueue(chunk); // forward the chunk to the next stream
|
|
1253
1487
|
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1488
|
+
const callbacksHandledForStreamRetry =
|
|
1489
|
+
part.type === 'error' && errorsHandledForStreamRetry.has(part.error);
|
|
1490
|
+
|
|
1491
|
+
if (!callbacksHandledForStreamRetry) {
|
|
1492
|
+
await notify({
|
|
1493
|
+
event: { chunk: part },
|
|
1494
|
+
callbacks: onChunk,
|
|
1495
|
+
});
|
|
1496
|
+
}
|
|
1258
1497
|
|
|
1259
1498
|
if (part.type === 'error') {
|
|
1260
1499
|
const error = wrapGatewayError(part.error);
|
|
@@ -1263,10 +1502,16 @@ class DefaultStreamTextResult<
|
|
|
1263
1502
|
recordedNoOutputError = error;
|
|
1264
1503
|
}
|
|
1265
1504
|
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1505
|
+
if (callbacksHandledForStreamRetry) {
|
|
1506
|
+
errorsHandledForStreamRetry.delete(part.error);
|
|
1507
|
+
} else {
|
|
1508
|
+
await notify({
|
|
1509
|
+
event: { error },
|
|
1510
|
+
callbacks: async event => {
|
|
1511
|
+
await onError(event);
|
|
1512
|
+
},
|
|
1513
|
+
});
|
|
1514
|
+
}
|
|
1270
1515
|
}
|
|
1271
1516
|
|
|
1272
1517
|
if (
|
|
@@ -1581,13 +1826,14 @@ class DefaultStreamTextResult<
|
|
|
1581
1826
|
});
|
|
1582
1827
|
|
|
1583
1828
|
// initialize the stitchable stream and the transformed stream:
|
|
1584
|
-
const stitchableStream =
|
|
1829
|
+
const stitchableStream =
|
|
1830
|
+
createStitchableStream<InternalTextStreamPart<TOOLS>>();
|
|
1585
1831
|
this.addStream = stitchableStream.addStream;
|
|
1586
1832
|
this.closeStream = stitchableStream.close;
|
|
1587
1833
|
|
|
1588
1834
|
// resilient stream that handles abort signals and errors:
|
|
1589
1835
|
const reader = stitchableStream.stream.getReader();
|
|
1590
|
-
let stream = new ReadableStream<
|
|
1836
|
+
let stream = new ReadableStream<InternalTextStreamPart<TOOLS>>({
|
|
1591
1837
|
async start(controller) {
|
|
1592
1838
|
// send start event:
|
|
1593
1839
|
controller.enqueue({ type: 'start' });
|
|
@@ -1659,19 +1905,18 @@ class DefaultStreamTextResult<
|
|
|
1659
1905
|
}),
|
|
1660
1906
|
);
|
|
1661
1907
|
|
|
1662
|
-
//
|
|
1663
|
-
//
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
}
|
|
1908
|
+
// Transform each retry attempt independently. The retry boundary is
|
|
1909
|
+
// intercepted outside user transforms, so transforms may filter or
|
|
1910
|
+
// reconstruct any public stream part without losing logical isolation.
|
|
1911
|
+
stream = applyStreamTextTransforms({
|
|
1912
|
+
stream,
|
|
1913
|
+
transforms,
|
|
1914
|
+
tools: tools as TOOLS,
|
|
1915
|
+
stopStream() {
|
|
1916
|
+
stitchableStream.terminate();
|
|
1917
|
+
isRunning = false;
|
|
1918
|
+
},
|
|
1919
|
+
});
|
|
1675
1920
|
|
|
1676
1921
|
this.baseStream = stream
|
|
1677
1922
|
.pipeThrough(createOutputTransformStream(output ?? text()))
|
|
@@ -1681,6 +1926,12 @@ class DefaultStreamTextResult<
|
|
|
1681
1926
|
maxRetries: maxRetriesArg,
|
|
1682
1927
|
abortSignal,
|
|
1683
1928
|
});
|
|
1929
|
+
const { maxRetries: streamRetries } = prepareRetries({
|
|
1930
|
+
maxRetries: streamRetriesArg,
|
|
1931
|
+
abortSignal,
|
|
1932
|
+
parameter: 'streamRetries',
|
|
1933
|
+
defaultMaxRetries: 0,
|
|
1934
|
+
});
|
|
1684
1935
|
|
|
1685
1936
|
const callSettings = prepareLanguageModelCallOptions(settings);
|
|
1686
1937
|
|
|
@@ -2128,79 +2379,262 @@ class DefaultStreamTextResult<
|
|
|
2128
2379
|
const stepStartTimestampMs = now();
|
|
2129
2380
|
|
|
2130
2381
|
const { retry } = prepareRetries({ maxRetries, abortSignal });
|
|
2382
|
+
let hasNotifiedStepStart = false;
|
|
2383
|
+
|
|
2384
|
+
const callLanguageModel = () =>
|
|
2385
|
+
runInStepTracingChannelContext(() =>
|
|
2386
|
+
retry(async () =>
|
|
2387
|
+
streamLanguageModelCall({
|
|
2388
|
+
model: prepareStepResult?.model ?? model,
|
|
2389
|
+
tools: stepModelTools as TOOLS,
|
|
2390
|
+
toolOrder: stepToolOrder,
|
|
2391
|
+
toolChoice: prepareStepResult?.toolChoice ?? toolChoice,
|
|
2392
|
+
instructions: stepInstructions,
|
|
2393
|
+
messages: stepMessages,
|
|
2394
|
+
allowSystemInMessages,
|
|
2395
|
+
repairToolCall,
|
|
2396
|
+
refineToolInput,
|
|
2397
|
+
abortSignal,
|
|
2398
|
+
headers,
|
|
2399
|
+
includeRawChunks: include.rawChunks,
|
|
2400
|
+
providerOptions: stepProviderOptions,
|
|
2401
|
+
download,
|
|
2402
|
+
output,
|
|
2403
|
+
callId,
|
|
2404
|
+
executeLanguageModelCallInTelemetryContext:
|
|
2405
|
+
telemetryDispatcher.executeLanguageModelCall,
|
|
2406
|
+
toolsContext,
|
|
2407
|
+
experimental_sandbox: stepSandbox,
|
|
2408
|
+
onLanguageModelCallStart: filterNullable(
|
|
2409
|
+
onLanguageModelCallStart,
|
|
2410
|
+
telemetryDispatcher.onLanguageModelCallStart as
|
|
2411
|
+
| undefined
|
|
2412
|
+
| OnLanguageModelCallStartCallback,
|
|
2413
|
+
),
|
|
2414
|
+
onLanguageModelCallEnd: filterNullable(
|
|
2415
|
+
onLanguageModelCallEnd,
|
|
2416
|
+
telemetryDispatcher.onLanguageModelCallEnd as
|
|
2417
|
+
| undefined
|
|
2418
|
+
| OnLanguageModelCallEndCallback<TOOLS>,
|
|
2419
|
+
),
|
|
2420
|
+
onStart: async ({ promptMessages }) => {
|
|
2421
|
+
if (hasNotifiedStepStart) {
|
|
2422
|
+
return;
|
|
2423
|
+
}
|
|
2424
|
+
hasNotifiedStepStart = true;
|
|
2425
|
+
|
|
2426
|
+
await notify({
|
|
2427
|
+
event: {
|
|
2428
|
+
callId,
|
|
2429
|
+
provider: stepModel.provider,
|
|
2430
|
+
modelId: stepModel.modelId,
|
|
2431
|
+
stepNumber: recordedSteps.length,
|
|
2432
|
+
instructions: stepInstructions,
|
|
2433
|
+
messages: stepMessages,
|
|
2434
|
+
tools,
|
|
2435
|
+
toolChoice: prepareStepResult?.toolChoice ?? toolChoice,
|
|
2436
|
+
activeTools:
|
|
2437
|
+
prepareStepResult?.activeTools ?? activeTools,
|
|
2438
|
+
toolOrder: stepToolOrder,
|
|
2439
|
+
steps: [...recordedSteps],
|
|
2440
|
+
providerOptions: stepProviderOptions,
|
|
2441
|
+
runtimeContext,
|
|
2442
|
+
toolsContext,
|
|
2443
|
+
output,
|
|
2444
|
+
promptMessages,
|
|
2445
|
+
stepTools,
|
|
2446
|
+
stepToolChoice,
|
|
2447
|
+
},
|
|
2448
|
+
callbacks: [onStepStart, telemetryDispatcher.onStepStart],
|
|
2449
|
+
});
|
|
2450
|
+
},
|
|
2451
|
+
_internal: {
|
|
2452
|
+
now,
|
|
2453
|
+
},
|
|
2454
|
+
...stepCallSettings,
|
|
2455
|
+
}),
|
|
2456
|
+
),
|
|
2457
|
+
);
|
|
2458
|
+
|
|
2459
|
+
const initialLanguageModelCall = await callLanguageModel();
|
|
2460
|
+
let request = initialLanguageModelCall.request;
|
|
2461
|
+
let response = initialLanguageModelCall.response;
|
|
2462
|
+
let languageModelStreamReader =
|
|
2463
|
+
initialLanguageModelCall.stream.getReader();
|
|
2464
|
+
let automaticStreamRetryCount = 0;
|
|
2465
|
+
let callbackStreamRetryCount = 0;
|
|
2466
|
+
let bufferedAttemptParts: LanguageModelStreamPart<TOOLS>[] = [];
|
|
2467
|
+
const outputChunksHandledBeforeBuffering = new WeakSet<object>();
|
|
2468
|
+
const openTextParts = new Set<string>();
|
|
2469
|
+
const openReasoningParts = new Set<string>();
|
|
2470
|
+
let enqueueStreamRetryAttemptBoundary = false;
|
|
2471
|
+
const shouldBufferToolParts =
|
|
2472
|
+
streamRetries > 0 || canRetryStreamViaOnError;
|
|
2473
|
+
|
|
2474
|
+
const languageModelStream = new ReadableStream<
|
|
2475
|
+
LanguageModelStreamPart<TOOLS> | StreamRetryAttemptBoundaryPart
|
|
2476
|
+
>({
|
|
2477
|
+
async pull(controller) {
|
|
2478
|
+
const enqueueAttemptPart = (
|
|
2479
|
+
part: LanguageModelStreamPart<TOOLS>,
|
|
2480
|
+
) => {
|
|
2481
|
+
switch (part.type) {
|
|
2482
|
+
case 'text-start':
|
|
2483
|
+
openTextParts.add(part.id);
|
|
2484
|
+
break;
|
|
2485
|
+
case 'text-end':
|
|
2486
|
+
openTextParts.delete(part.id);
|
|
2487
|
+
break;
|
|
2488
|
+
case 'reasoning-start':
|
|
2489
|
+
openReasoningParts.add(part.id);
|
|
2490
|
+
break;
|
|
2491
|
+
case 'reasoning-end':
|
|
2492
|
+
openReasoningParts.delete(part.id);
|
|
2493
|
+
break;
|
|
2494
|
+
}
|
|
2495
|
+
|
|
2496
|
+
controller.enqueue(part);
|
|
2497
|
+
};
|
|
2498
|
+
|
|
2499
|
+
const flushBufferedAttemptParts = () => {
|
|
2500
|
+
for (const part of bufferedAttemptParts) {
|
|
2501
|
+
enqueueAttemptPart(part);
|
|
2502
|
+
}
|
|
2503
|
+
bufferedAttemptParts = [];
|
|
2504
|
+
};
|
|
2505
|
+
|
|
2506
|
+
const closeOpenAttemptParts = () => {
|
|
2507
|
+
for (const id of openTextParts) {
|
|
2508
|
+
controller.enqueue({ type: 'text-end', id });
|
|
2509
|
+
}
|
|
2510
|
+
openTextParts.clear();
|
|
2511
|
+
|
|
2512
|
+
for (const id of openReasoningParts) {
|
|
2513
|
+
controller.enqueue({ type: 'reasoning-end', id });
|
|
2514
|
+
}
|
|
2515
|
+
openReasoningParts.clear();
|
|
2516
|
+
};
|
|
2517
|
+
|
|
2518
|
+
while (true) {
|
|
2519
|
+
const { done, value } = await languageModelStreamReader.read();
|
|
2520
|
+
|
|
2521
|
+
if (enqueueStreamRetryAttemptBoundary) {
|
|
2522
|
+
controller.enqueue(
|
|
2523
|
+
createStreamRetryAttemptBoundaryPart({
|
|
2524
|
+
warnings:
|
|
2525
|
+
!done && value.type === 'model-call-start'
|
|
2526
|
+
? value.warnings
|
|
2527
|
+
: [],
|
|
2528
|
+
}),
|
|
2529
|
+
);
|
|
2530
|
+
enqueueStreamRetryAttemptBoundary = false;
|
|
2531
|
+
}
|
|
2532
|
+
|
|
2533
|
+
if (done) {
|
|
2534
|
+
flushBufferedAttemptParts();
|
|
2535
|
+
controller.close();
|
|
2536
|
+
return;
|
|
2537
|
+
}
|
|
2538
|
+
|
|
2539
|
+
const isToolPart =
|
|
2540
|
+
value.type === 'tool-input-start' ||
|
|
2541
|
+
value.type === 'tool-input-delta' ||
|
|
2542
|
+
value.type === 'tool-input-end' ||
|
|
2543
|
+
value.type === 'tool-call' ||
|
|
2544
|
+
value.type === 'tool-approval-request' ||
|
|
2545
|
+
value.type === 'tool-approval-response' ||
|
|
2546
|
+
value.type === 'tool-result' ||
|
|
2547
|
+
value.type === 'tool-error';
|
|
2548
|
+
|
|
2549
|
+
if (value.type === 'model-call-end') {
|
|
2550
|
+
flushBufferedAttemptParts();
|
|
2551
|
+
enqueueAttemptPart(value);
|
|
2552
|
+
return;
|
|
2553
|
+
}
|
|
2554
|
+
|
|
2555
|
+
if (
|
|
2556
|
+
shouldBufferToolParts &&
|
|
2557
|
+
value.type !== 'error' &&
|
|
2558
|
+
(isToolPart || bufferedAttemptParts.length > 0)
|
|
2559
|
+
) {
|
|
2560
|
+
if (isOutputChunk(value)) {
|
|
2561
|
+
clearFirstChunkTimeout();
|
|
2562
|
+
resetChunkTimeout();
|
|
2563
|
+
outputChunksHandledBeforeBuffering.add(value);
|
|
2564
|
+
}
|
|
2131
2565
|
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
)
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
output,
|
|
2190
|
-
promptMessages,
|
|
2191
|
-
stepTools,
|
|
2192
|
-
stepToolChoice,
|
|
2193
|
-
},
|
|
2194
|
-
callbacks: [onStepStart, telemetryDispatcher.onStepStart],
|
|
2566
|
+
bufferedAttemptParts.push(value);
|
|
2567
|
+
continue;
|
|
2568
|
+
}
|
|
2569
|
+
|
|
2570
|
+
if (value.type !== 'error') {
|
|
2571
|
+
enqueueAttemptPart(value);
|
|
2572
|
+
return;
|
|
2573
|
+
}
|
|
2574
|
+
|
|
2575
|
+
await notify({
|
|
2576
|
+
event: { chunk: value },
|
|
2577
|
+
callbacks: onChunk,
|
|
2578
|
+
});
|
|
2579
|
+
const error = wrapGatewayError(value.error);
|
|
2580
|
+
let onErrorResult: unknown;
|
|
2581
|
+
try {
|
|
2582
|
+
onErrorResult = await onError({ error });
|
|
2583
|
+
} catch {}
|
|
2584
|
+
const callbackRequestedRetry =
|
|
2585
|
+
canRetryStreamViaOnError &&
|
|
2586
|
+
typeof onErrorResult === 'object' &&
|
|
2587
|
+
onErrorResult != null &&
|
|
2588
|
+
'retry' in onErrorResult &&
|
|
2589
|
+
onErrorResult.retry === true;
|
|
2590
|
+
const automaticRetry =
|
|
2591
|
+
automaticStreamRetryCount < streamRetries;
|
|
2592
|
+
const callbackRetry =
|
|
2593
|
+
!automaticRetry &&
|
|
2594
|
+
callbackRequestedRetry &&
|
|
2595
|
+
callbackStreamRetryCount < 1;
|
|
2596
|
+
|
|
2597
|
+
if (!automaticRetry && !callbackRetry) {
|
|
2598
|
+
flushBufferedAttemptParts();
|
|
2599
|
+
errorsHandledForStreamRetry.add(value.error);
|
|
2600
|
+
controller.enqueue(value);
|
|
2601
|
+
return;
|
|
2602
|
+
}
|
|
2603
|
+
|
|
2604
|
+
if (automaticRetry) {
|
|
2605
|
+
automaticStreamRetryCount++;
|
|
2606
|
+
} else {
|
|
2607
|
+
callbackStreamRetryCount++;
|
|
2608
|
+
}
|
|
2609
|
+
|
|
2610
|
+
await languageModelStreamReader.cancel(error);
|
|
2611
|
+
bufferedAttemptParts = [];
|
|
2612
|
+
closeOpenAttemptParts();
|
|
2613
|
+
|
|
2614
|
+
let retryLanguageModelCall: Awaited<
|
|
2615
|
+
ReturnType<typeof callLanguageModel>
|
|
2616
|
+
>;
|
|
2617
|
+
try {
|
|
2618
|
+
retryLanguageModelCall = await callLanguageModel();
|
|
2619
|
+
} catch (retryError) {
|
|
2620
|
+
controller.enqueue({
|
|
2621
|
+
type: 'error',
|
|
2622
|
+
error: retryError,
|
|
2195
2623
|
});
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2624
|
+
controller.close();
|
|
2625
|
+
return;
|
|
2626
|
+
}
|
|
2627
|
+
request = retryLanguageModelCall.request;
|
|
2628
|
+
response = retryLanguageModelCall.response;
|
|
2629
|
+
languageModelStreamReader =
|
|
2630
|
+
retryLanguageModelCall.stream.getReader();
|
|
2631
|
+
enqueueStreamRetryAttemptBoundary = true;
|
|
2632
|
+
}
|
|
2633
|
+
},
|
|
2634
|
+
cancel(reason) {
|
|
2635
|
+
return languageModelStreamReader.cancel(reason);
|
|
2636
|
+
},
|
|
2637
|
+
});
|
|
2204
2638
|
|
|
2205
2639
|
startFirstChunkTimeout();
|
|
2206
2640
|
|
|
@@ -2259,14 +2693,14 @@ class DefaultStreamTextResult<
|
|
|
2259
2693
|
|
|
2260
2694
|
// Conditionally include request.body based on include settings.
|
|
2261
2695
|
// Large payloads (e.g., base64-encoded images) can cause memory issues.
|
|
2262
|
-
const
|
|
2696
|
+
const getStepRequest = (): LanguageModelRequestMetadata => ({
|
|
2263
2697
|
...request,
|
|
2264
2698
|
body: include.requestBody ? request?.body : undefined,
|
|
2265
2699
|
messages: include.requestMessages
|
|
2266
2700
|
? cloneModelMessages(stepMessages)
|
|
2267
2701
|
: undefined,
|
|
2268
|
-
};
|
|
2269
|
-
recordedRequestMessages =
|
|
2702
|
+
});
|
|
2703
|
+
recordedRequestMessages = getStepRequest().messages ?? [];
|
|
2270
2704
|
|
|
2271
2705
|
const stepToolCalls: TypedToolCall<TOOLS>[] = [];
|
|
2272
2706
|
const stepToolOutputs: ToolOutput<TOOLS>[] = [];
|
|
@@ -2287,10 +2721,10 @@ class DefaultStreamTextResult<
|
|
|
2287
2721
|
let stepUsage: LanguageModelUsage = createNullLanguageModelUsage();
|
|
2288
2722
|
let stepProviderMetadata: ProviderMetadata | undefined;
|
|
2289
2723
|
let stepFirstChunk = true;
|
|
2290
|
-
|
|
2724
|
+
const createModelCallPerformance = (): Omit<
|
|
2291
2725
|
StepResultPerformance,
|
|
2292
2726
|
'stepTimeMs' | 'toolExecutionMs'
|
|
2293
|
-
>
|
|
2727
|
+
> => ({
|
|
2294
2728
|
responseTimeMs: 0,
|
|
2295
2729
|
effectiveOutputTokensPerSecond: 0,
|
|
2296
2730
|
outputTokensPerSecond: undefined,
|
|
@@ -2298,26 +2732,63 @@ class DefaultStreamTextResult<
|
|
|
2298
2732
|
effectiveTotalTokensPerSecond: 0,
|
|
2299
2733
|
timeToFirstOutputMs: undefined,
|
|
2300
2734
|
timeBetweenOutputChunksMs: undefined,
|
|
2301
|
-
};
|
|
2735
|
+
});
|
|
2736
|
+
let modelCallPerformance = createModelCallPerformance();
|
|
2302
2737
|
const toolExecutionMs: Record<string, number> = {};
|
|
2303
|
-
|
|
2738
|
+
const createStepResponse = () => ({
|
|
2304
2739
|
id: generateId(),
|
|
2305
2740
|
timestamp: new Date(),
|
|
2306
2741
|
modelId: model.modelId,
|
|
2307
|
-
};
|
|
2742
|
+
});
|
|
2743
|
+
let stepResponse: {
|
|
2744
|
+
id: string;
|
|
2745
|
+
timestamp: Date;
|
|
2746
|
+
modelId: string;
|
|
2747
|
+
} = createStepResponse();
|
|
2308
2748
|
|
|
2309
2749
|
// maps provider-assigned IDs to stream-unique IDs for the text and
|
|
2310
2750
|
// reasoning parts that are active in this step
|
|
2311
2751
|
const textPartIds = new Map<string, string>();
|
|
2312
2752
|
const reasoningPartIds = new Map<string, string>();
|
|
2313
2753
|
|
|
2754
|
+
const enqueueStepPart = (
|
|
2755
|
+
controller: TransformStreamDefaultController<
|
|
2756
|
+
InternalTextStreamPart<TOOLS>
|
|
2757
|
+
>,
|
|
2758
|
+
part: TextStreamPart<TOOLS>,
|
|
2759
|
+
) => {
|
|
2760
|
+
controller.enqueue(part);
|
|
2761
|
+
};
|
|
2762
|
+
|
|
2314
2763
|
self.addStream(
|
|
2315
2764
|
streamWithToolResults.pipeThrough(
|
|
2316
2765
|
new TransformStream<
|
|
2317
2766
|
ExecuteToolsStreamPart<TOOLS>,
|
|
2318
|
-
|
|
2767
|
+
InternalTextStreamPart<TOOLS>
|
|
2319
2768
|
>({
|
|
2320
2769
|
async transform(chunk, controller): Promise<void> {
|
|
2770
|
+
if (isStreamRetryAttemptBoundaryPart(chunk)) {
|
|
2771
|
+
warnings = chunk.warnings;
|
|
2772
|
+
stepFinishReason = 'other';
|
|
2773
|
+
stepRawFinishReason = undefined;
|
|
2774
|
+
hasReceivedTerminalChunk = false;
|
|
2775
|
+
hasReceivedOutputChunk = false;
|
|
2776
|
+
stepUsage = createNullLanguageModelUsage();
|
|
2777
|
+
stepProviderMetadata = undefined;
|
|
2778
|
+
modelCallPerformance = createModelCallPerformance();
|
|
2779
|
+
stepResponse = createStepResponse();
|
|
2780
|
+
textPartIds.clear();
|
|
2781
|
+
reasoningPartIds.clear();
|
|
2782
|
+
|
|
2783
|
+
controller.enqueue({
|
|
2784
|
+
[streamRetryBoundarySymbol]: {
|
|
2785
|
+
request: getStepRequest(),
|
|
2786
|
+
warnings,
|
|
2787
|
+
} satisfies StreamRetryBoundaryMetadata,
|
|
2788
|
+
});
|
|
2789
|
+
return;
|
|
2790
|
+
}
|
|
2791
|
+
|
|
2321
2792
|
if (chunk.type === 'model-call-start') {
|
|
2322
2793
|
warnings = chunk.warnings;
|
|
2323
2794
|
return; // stream start chunks are sent immediately and do not count as first chunk
|
|
@@ -2327,9 +2798,9 @@ class DefaultStreamTextResult<
|
|
|
2327
2798
|
stepFirstChunk = false;
|
|
2328
2799
|
|
|
2329
2800
|
// Step start:
|
|
2330
|
-
controller
|
|
2801
|
+
enqueueStepPart(controller, {
|
|
2331
2802
|
type: 'start-step',
|
|
2332
|
-
request:
|
|
2803
|
+
request: getStepRequest(),
|
|
2333
2804
|
warnings: warnings ?? [],
|
|
2334
2805
|
});
|
|
2335
2806
|
}
|
|
@@ -2337,13 +2808,21 @@ class DefaultStreamTextResult<
|
|
|
2337
2808
|
const chunkType = chunk.type;
|
|
2338
2809
|
|
|
2339
2810
|
if (isOutputChunk(chunk)) {
|
|
2340
|
-
|
|
2811
|
+
const timeoutHandledBeforeBuffering =
|
|
2812
|
+
outputChunksHandledBeforeBuffering.has(chunk);
|
|
2813
|
+
|
|
2814
|
+
if (
|
|
2815
|
+
!hasReceivedOutputChunk &&
|
|
2816
|
+
!timeoutHandledBeforeBuffering
|
|
2817
|
+
) {
|
|
2341
2818
|
// Clear before forwarding the first output so a timeout
|
|
2342
2819
|
// cannot race with already-visible generated content.
|
|
2343
2820
|
clearFirstChunkTimeout();
|
|
2344
2821
|
}
|
|
2345
2822
|
hasReceivedOutputChunk = true;
|
|
2346
|
-
|
|
2823
|
+
if (!timeoutHandledBeforeBuffering) {
|
|
2824
|
+
resetChunkTimeout();
|
|
2825
|
+
}
|
|
2347
2826
|
}
|
|
2348
2827
|
|
|
2349
2828
|
switch (chunkType) {
|
|
@@ -2355,14 +2834,14 @@ class DefaultStreamTextResult<
|
|
|
2355
2834
|
case 'tool-input-end':
|
|
2356
2835
|
case 'tool-input-delta':
|
|
2357
2836
|
case 'tool-approval-request': {
|
|
2358
|
-
controller
|
|
2837
|
+
enqueueStepPart(controller, chunk);
|
|
2359
2838
|
break;
|
|
2360
2839
|
}
|
|
2361
2840
|
|
|
2362
2841
|
case 'text-start': {
|
|
2363
2842
|
const id = reserveTextPartId(chunk.id);
|
|
2364
2843
|
textPartIds.set(chunk.id, id);
|
|
2365
|
-
controller
|
|
2844
|
+
enqueueStepPart(controller, { ...chunk, id });
|
|
2366
2845
|
break;
|
|
2367
2846
|
}
|
|
2368
2847
|
|
|
@@ -2371,7 +2850,7 @@ class DefaultStreamTextResult<
|
|
|
2371
2850
|
chunk.text.length > 0 ||
|
|
2372
2851
|
chunk.providerMetadata != null
|
|
2373
2852
|
) {
|
|
2374
|
-
controller
|
|
2853
|
+
enqueueStepPart(controller, {
|
|
2375
2854
|
...chunk,
|
|
2376
2855
|
id: textPartIds.get(chunk.id) ?? chunk.id,
|
|
2377
2856
|
});
|
|
@@ -2380,7 +2859,7 @@ class DefaultStreamTextResult<
|
|
|
2380
2859
|
}
|
|
2381
2860
|
|
|
2382
2861
|
case 'text-end': {
|
|
2383
|
-
controller
|
|
2862
|
+
enqueueStepPart(controller, {
|
|
2384
2863
|
...chunk,
|
|
2385
2864
|
id: textPartIds.get(chunk.id) ?? chunk.id,
|
|
2386
2865
|
});
|
|
@@ -2391,12 +2870,12 @@ class DefaultStreamTextResult<
|
|
|
2391
2870
|
case 'reasoning-start': {
|
|
2392
2871
|
const id = reserveReasoningPartId(chunk.id);
|
|
2393
2872
|
reasoningPartIds.set(chunk.id, id);
|
|
2394
|
-
controller
|
|
2873
|
+
enqueueStepPart(controller, { ...chunk, id });
|
|
2395
2874
|
break;
|
|
2396
2875
|
}
|
|
2397
2876
|
|
|
2398
2877
|
case 'reasoning-delta': {
|
|
2399
|
-
controller
|
|
2878
|
+
enqueueStepPart(controller, {
|
|
2400
2879
|
...chunk,
|
|
2401
2880
|
id: reasoningPartIds.get(chunk.id) ?? chunk.id,
|
|
2402
2881
|
});
|
|
@@ -2404,7 +2883,7 @@ class DefaultStreamTextResult<
|
|
|
2404
2883
|
}
|
|
2405
2884
|
|
|
2406
2885
|
case 'reasoning-end': {
|
|
2407
|
-
controller
|
|
2886
|
+
enqueueStepPart(controller, {
|
|
2408
2887
|
...chunk,
|
|
2409
2888
|
id: reasoningPartIds.get(chunk.id) ?? chunk.id,
|
|
2410
2889
|
});
|
|
@@ -2413,20 +2892,20 @@ class DefaultStreamTextResult<
|
|
|
2413
2892
|
}
|
|
2414
2893
|
|
|
2415
2894
|
case 'tool-call': {
|
|
2416
|
-
controller
|
|
2895
|
+
enqueueStepPart(controller, chunk);
|
|
2417
2896
|
// store tool calls for onEnd callback and toolCalls promise:
|
|
2418
2897
|
stepToolCalls.push(chunk);
|
|
2419
2898
|
break;
|
|
2420
2899
|
}
|
|
2421
2900
|
|
|
2422
2901
|
case 'tool-approval-response': {
|
|
2423
|
-
controller
|
|
2902
|
+
enqueueStepPart(controller, chunk);
|
|
2424
2903
|
stepToolApprovalResponses.push(chunk);
|
|
2425
2904
|
break;
|
|
2426
2905
|
}
|
|
2427
2906
|
|
|
2428
2907
|
case 'tool-result': {
|
|
2429
|
-
controller
|
|
2908
|
+
enqueueStepPart(controller, chunk);
|
|
2430
2909
|
|
|
2431
2910
|
if (!chunk.preliminary) {
|
|
2432
2911
|
stepToolOutputs.push(chunk);
|
|
@@ -2436,7 +2915,7 @@ class DefaultStreamTextResult<
|
|
|
2436
2915
|
}
|
|
2437
2916
|
|
|
2438
2917
|
case 'tool-error': {
|
|
2439
|
-
controller
|
|
2918
|
+
enqueueStepPart(controller, chunk);
|
|
2440
2919
|
stepToolOutputs.push(chunk);
|
|
2441
2920
|
break;
|
|
2442
2921
|
}
|
|
@@ -2471,14 +2950,14 @@ class DefaultStreamTextResult<
|
|
|
2471
2950
|
|
|
2472
2951
|
case 'error': {
|
|
2473
2952
|
hasReceivedTerminalChunk = true;
|
|
2474
|
-
controller
|
|
2953
|
+
enqueueStepPart(controller, chunk);
|
|
2475
2954
|
stepFinishReason = 'error';
|
|
2476
2955
|
break;
|
|
2477
2956
|
}
|
|
2478
2957
|
|
|
2479
2958
|
case 'raw': {
|
|
2480
2959
|
if (include.rawChunks) {
|
|
2481
|
-
controller
|
|
2960
|
+
enqueueStepPart(controller, chunk);
|
|
2482
2961
|
}
|
|
2483
2962
|
break;
|
|
2484
2963
|
}
|
|
@@ -2496,7 +2975,7 @@ class DefaultStreamTextResult<
|
|
|
2496
2975
|
// output instead of recording an empty step. incomplete
|
|
2497
2976
|
// streams with partial output retain the partial result:
|
|
2498
2977
|
if (!hasReceivedTerminalChunk && !hasReceivedOutputChunk) {
|
|
2499
|
-
controller
|
|
2978
|
+
enqueueStepPart(controller, {
|
|
2500
2979
|
type: 'error',
|
|
2501
2980
|
error: new NoOutputGeneratedError({
|
|
2502
2981
|
message:
|
|
@@ -2528,7 +3007,7 @@ class DefaultStreamTextResult<
|
|
|
2528
3007
|
},
|
|
2529
3008
|
};
|
|
2530
3009
|
|
|
2531
|
-
controller
|
|
3010
|
+
enqueueStepPart(controller, finishStepPart);
|
|
2532
3011
|
|
|
2533
3012
|
const combinedUsage = addLanguageModelUsage(usage, stepUsage);
|
|
2534
3013
|
|
|
@@ -2609,7 +3088,7 @@ class DefaultStreamTextResult<
|
|
|
2609
3088
|
}),
|
|
2610
3089
|
);
|
|
2611
3090
|
} catch (error) {
|
|
2612
|
-
controller
|
|
3091
|
+
enqueueStepPart(controller, {
|
|
2613
3092
|
type: 'error',
|
|
2614
3093
|
error,
|
|
2615
3094
|
});
|
|
@@ -2617,7 +3096,7 @@ class DefaultStreamTextResult<
|
|
|
2617
3096
|
self.closeStream();
|
|
2618
3097
|
}
|
|
2619
3098
|
} else {
|
|
2620
|
-
controller
|
|
3099
|
+
enqueueStepPart(controller, {
|
|
2621
3100
|
type: 'finish',
|
|
2622
3101
|
finishReason: stepFinishReason,
|
|
2623
3102
|
rawFinishReason: stepRawFinishReason,
|