ai 6.0.259 → 6.0.260
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 +8 -0
- package/dist/index.js +118 -49
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +118 -49
- package/dist/index.mjs.map +1 -1
- package/dist/internal/index.js +1 -1
- package/dist/internal/index.mjs +1 -1
- package/package.json +1 -1
- package/src/generate-object/stream-object.ts +61 -3
- package/src/generate-text/generate-text.ts +7 -1
- package/src/generate-text/is-tool-execution-allowed-finish-reason.ts +7 -0
- package/src/generate-text/run-tools-transformation.ts +48 -36
- package/src/ui/convert-to-model-messages.ts +1 -1
- package/src/util/create-stitchable-stream.ts +32 -15
package/dist/internal/index.js
CHANGED
|
@@ -164,7 +164,7 @@ function detectMediaType({
|
|
|
164
164
|
var import_provider_utils2 = require("@ai-sdk/provider-utils");
|
|
165
165
|
|
|
166
166
|
// src/version.ts
|
|
167
|
-
var VERSION = true ? "6.0.
|
|
167
|
+
var VERSION = true ? "6.0.260" : "0.0.0-test";
|
|
168
168
|
|
|
169
169
|
// src/util/download/download.ts
|
|
170
170
|
var download = async ({
|
package/dist/internal/index.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.260",
|
|
4
4
|
"description": "AI SDK by Vercel - build apps like ChatGPT, Claude, Gemini, and more with a single interface for any model using the Vercel AI Gateway or go direct to OpenAI, Anthropic, Google, or any other model provider.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -64,6 +64,12 @@ import { validateObjectGenerationInput } from './validate-object-generation-inpu
|
|
|
64
64
|
|
|
65
65
|
const originalGenerateId = createIdGenerator({ prefix: 'aiobj', size: 24 });
|
|
66
66
|
|
|
67
|
+
async function markPromiseAsHandled<T>(promise: Promise<T>): Promise<void> {
|
|
68
|
+
try {
|
|
69
|
+
await promise;
|
|
70
|
+
} catch {}
|
|
71
|
+
}
|
|
72
|
+
|
|
67
73
|
/**
|
|
68
74
|
* Callback that is set using the `onError` option.
|
|
69
75
|
*
|
|
@@ -586,6 +592,7 @@ class DefaultStreamObjectResult<
|
|
|
586
592
|
let providerMetadata: ProviderMetadata | undefined;
|
|
587
593
|
let object: RESULT | undefined;
|
|
588
594
|
let error: unknown | undefined;
|
|
595
|
+
let terminalError: { error: unknown } | undefined;
|
|
589
596
|
|
|
590
597
|
// pipe chunks through a transformation stream that extracts metadata:
|
|
591
598
|
let accumulatedText = '';
|
|
@@ -698,6 +705,19 @@ class DefaultStreamObjectResult<
|
|
|
698
705
|
break;
|
|
699
706
|
}
|
|
700
707
|
|
|
708
|
+
case 'error': {
|
|
709
|
+
if (terminalError === undefined) {
|
|
710
|
+
const wrappedError = wrapGatewayError(chunk.error);
|
|
711
|
+
terminalError = { error: wrappedError };
|
|
712
|
+
error = wrappedError;
|
|
713
|
+
finishReason = 'error';
|
|
714
|
+
self.rejectResultPromises(wrappedError);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
controller.enqueue(chunk);
|
|
718
|
+
break;
|
|
719
|
+
}
|
|
720
|
+
|
|
701
721
|
case 'finish': {
|
|
702
722
|
// send final text delta:
|
|
703
723
|
if (textDelta !== '') {
|
|
@@ -705,7 +725,10 @@ class DefaultStreamObjectResult<
|
|
|
705
725
|
}
|
|
706
726
|
|
|
707
727
|
// store finish reason for telemetry:
|
|
708
|
-
finishReason =
|
|
728
|
+
finishReason =
|
|
729
|
+
terminalError === undefined
|
|
730
|
+
? chunk.finishReason.unified
|
|
731
|
+
: 'error';
|
|
709
732
|
|
|
710
733
|
// store usage and metadata for promises and onFinish callback:
|
|
711
734
|
usage = asLanguageModelUsage(chunk.usage);
|
|
@@ -713,7 +736,7 @@ class DefaultStreamObjectResult<
|
|
|
713
736
|
|
|
714
737
|
controller.enqueue({
|
|
715
738
|
...chunk,
|
|
716
|
-
finishReason
|
|
739
|
+
finishReason,
|
|
717
740
|
usage,
|
|
718
741
|
response: fullResponse,
|
|
719
742
|
});
|
|
@@ -725,6 +748,10 @@ class DefaultStreamObjectResult<
|
|
|
725
748
|
model: model.modelId,
|
|
726
749
|
});
|
|
727
750
|
|
|
751
|
+
if (terminalError !== undefined) {
|
|
752
|
+
break;
|
|
753
|
+
}
|
|
754
|
+
|
|
728
755
|
// resolve promises that can be resolved now:
|
|
729
756
|
self._usage.resolve(usage);
|
|
730
757
|
self._providerMetadata.resolve(providerMetadata);
|
|
@@ -846,10 +873,18 @@ class DefaultStreamObjectResult<
|
|
|
846
873
|
}),
|
|
847
874
|
);
|
|
848
875
|
|
|
849
|
-
stitchableStream.addStream(transformedStream
|
|
876
|
+
stitchableStream.addStream(transformedStream, {
|
|
877
|
+
onError(error) {
|
|
878
|
+
const wrappedError = wrapGatewayError(error);
|
|
879
|
+
self.rejectResultPromises(wrappedError);
|
|
880
|
+
void onError({ error: wrappedError });
|
|
881
|
+
},
|
|
882
|
+
});
|
|
850
883
|
},
|
|
851
884
|
})
|
|
852
885
|
.catch(error => {
|
|
886
|
+
self.rejectResultPromises(error);
|
|
887
|
+
|
|
853
888
|
// add an empty stream with an error to break the stream:
|
|
854
889
|
stitchableStream.addStream(
|
|
855
890
|
new ReadableStream({
|
|
@@ -867,6 +902,29 @@ class DefaultStreamObjectResult<
|
|
|
867
902
|
this.outputStrategy = outputStrategy;
|
|
868
903
|
}
|
|
869
904
|
|
|
905
|
+
private rejectResultPromises(error: unknown) {
|
|
906
|
+
this.rejectResultPromise({ delayedPromise: this._object, error });
|
|
907
|
+
this.rejectResultPromise({ delayedPromise: this._usage, error });
|
|
908
|
+
this.rejectResultPromise({ delayedPromise: this._providerMetadata, error });
|
|
909
|
+
this.rejectResultPromise({ delayedPromise: this._warnings, error });
|
|
910
|
+
this.rejectResultPromise({ delayedPromise: this._request, error });
|
|
911
|
+
this.rejectResultPromise({ delayedPromise: this._response, error });
|
|
912
|
+
this.rejectResultPromise({ delayedPromise: this._finishReason, error });
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
private rejectResultPromise<T>({
|
|
916
|
+
delayedPromise,
|
|
917
|
+
error,
|
|
918
|
+
}: {
|
|
919
|
+
delayedPromise: DelayedPromise<T>;
|
|
920
|
+
error: unknown;
|
|
921
|
+
}) {
|
|
922
|
+
if (delayedPromise.isPending()) {
|
|
923
|
+
delayedPromise.reject(error);
|
|
924
|
+
markPromiseAsHandled(delayedPromise.promise);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
|
|
870
928
|
get object() {
|
|
871
929
|
return this._object.promise;
|
|
872
930
|
}
|
|
@@ -71,6 +71,7 @@ import { filterActiveTools } from './filter-active-tools';
|
|
|
71
71
|
import type { GenerateTextResult } from './generate-text-result';
|
|
72
72
|
import { DefaultGeneratedFile } from './generated-file';
|
|
73
73
|
import { isApprovalNeeded } from './is-approval-needed';
|
|
74
|
+
import { isToolExecutionAllowedFinishReason } from './is-tool-execution-allowed-finish-reason';
|
|
74
75
|
import { maybeSignApproval } from './tool-approval-signature';
|
|
75
76
|
import { validateApprovedToolApprovals } from './validate-tool-approvals';
|
|
76
77
|
import { text, type Output } from './output';
|
|
@@ -1031,7 +1032,12 @@ export async function generateText<
|
|
|
1031
1032
|
toolCall => !toolCall.providerExecuted,
|
|
1032
1033
|
);
|
|
1033
1034
|
|
|
1034
|
-
if (
|
|
1035
|
+
if (
|
|
1036
|
+
stepToolSet != null &&
|
|
1037
|
+
isToolExecutionAllowedFinishReason(
|
|
1038
|
+
currentModelResponse.finishReason.unified,
|
|
1039
|
+
)
|
|
1040
|
+
) {
|
|
1035
1041
|
clientToolOutputs.push(
|
|
1036
1042
|
...(await executeTools({
|
|
1037
1043
|
toolCalls: clientToolCalls.filter(
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
type GeneratedFile,
|
|
29
29
|
} from './generated-file';
|
|
30
30
|
import { isApprovalNeeded } from './is-approval-needed';
|
|
31
|
+
import { isToolExecutionAllowedFinishReason } from './is-tool-execution-allowed-finish-reason';
|
|
31
32
|
import { maybeSignApproval } from './tool-approval-signature';
|
|
32
33
|
import { parseToolCall } from './parse-tool-call';
|
|
33
34
|
import type { ToolApprovalRequestOutput } from './tool-approval-request-output';
|
|
@@ -200,6 +201,7 @@ export function runToolsTransformation<TOOLS extends ToolSet>({
|
|
|
200
201
|
|
|
201
202
|
// keep track of outstanding tool results for stream closing:
|
|
202
203
|
const outstandingToolResults = new Set<string>();
|
|
204
|
+
const toolCallsToExecute: Array<TypedToolCall<TOOLS>> = [];
|
|
203
205
|
|
|
204
206
|
// keep track of parsed tool calls so provider-emitted approval requests can reference them
|
|
205
207
|
// keep track of tool inputs for provider-side tool results
|
|
@@ -224,6 +226,41 @@ export function runToolsTransformation<TOOLS extends ToolSet>({
|
|
|
224
226
|
}
|
|
225
227
|
}
|
|
226
228
|
|
|
229
|
+
function executeToolCallAfterFinish(toolCall: TypedToolCall<TOOLS>) {
|
|
230
|
+
const toolExecutionId = generateId(); // use our own id to guarantee uniqueness
|
|
231
|
+
outstandingToolResults.add(toolExecutionId);
|
|
232
|
+
|
|
233
|
+
executeToolCall({
|
|
234
|
+
toolCall,
|
|
235
|
+
tools,
|
|
236
|
+
tracer,
|
|
237
|
+
telemetry,
|
|
238
|
+
messages,
|
|
239
|
+
abortSignal,
|
|
240
|
+
experimental_context,
|
|
241
|
+
stepNumber,
|
|
242
|
+
model,
|
|
243
|
+
onToolCallStart,
|
|
244
|
+
onToolCallFinish,
|
|
245
|
+
onPreliminaryToolResult: result => {
|
|
246
|
+
enqueueToolResult(result);
|
|
247
|
+
},
|
|
248
|
+
})
|
|
249
|
+
.then(result => {
|
|
250
|
+
enqueueToolResult(result);
|
|
251
|
+
})
|
|
252
|
+
.catch(error => {
|
|
253
|
+
enqueueToolResult({
|
|
254
|
+
type: 'error',
|
|
255
|
+
error,
|
|
256
|
+
});
|
|
257
|
+
})
|
|
258
|
+
.finally(() => {
|
|
259
|
+
outstandingToolResults.delete(toolExecutionId);
|
|
260
|
+
attemptClose();
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
227
264
|
// forward stream
|
|
228
265
|
const forwardStream = new TransformStream<
|
|
229
266
|
LanguageModelV3StreamPart,
|
|
@@ -279,6 +316,15 @@ export function runToolsTransformation<TOOLS extends ToolSet>({
|
|
|
279
316
|
usage: asLanguageModelUsage(chunk.usage),
|
|
280
317
|
providerMetadata: chunk.providerMetadata,
|
|
281
318
|
};
|
|
319
|
+
|
|
320
|
+
if (isToolExecutionAllowedFinishReason(chunk.finishReason.unified)) {
|
|
321
|
+
for (const toolCall of toolCallsToExecute.splice(0)) {
|
|
322
|
+
executeToolCallAfterFinish(toolCall);
|
|
323
|
+
}
|
|
324
|
+
} else {
|
|
325
|
+
toolCallsToExecute.length = 0;
|
|
326
|
+
}
|
|
327
|
+
|
|
282
328
|
break;
|
|
283
329
|
}
|
|
284
330
|
|
|
@@ -315,7 +361,7 @@ export function runToolsTransformation<TOOLS extends ToolSet>({
|
|
|
315
361
|
});
|
|
316
362
|
|
|
317
363
|
toolCallsByToolCallId.set(toolCall.toolCallId, toolCall);
|
|
318
|
-
controller.enqueue(toolCall);
|
|
364
|
+
controller.enqueue({ ...toolCall });
|
|
319
365
|
|
|
320
366
|
if (toolCall.invalid) {
|
|
321
367
|
if (!toolCall.providerExecuted) {
|
|
@@ -381,41 +427,7 @@ export function runToolsTransformation<TOOLS extends ToolSet>({
|
|
|
381
427
|
|
|
382
428
|
// Only execute tools that are not provider-executed:
|
|
383
429
|
if (tool.execute != null && toolCall.providerExecuted !== true) {
|
|
384
|
-
|
|
385
|
-
outstandingToolResults.add(toolExecutionId);
|
|
386
|
-
|
|
387
|
-
// Note: we don't await the tool execution here (by leaving out 'await' on recordSpan),
|
|
388
|
-
// because we want to process the next chunk as soon as possible.
|
|
389
|
-
// This is important for the case where the tool execution takes a long time.
|
|
390
|
-
executeToolCall({
|
|
391
|
-
toolCall,
|
|
392
|
-
tools,
|
|
393
|
-
tracer,
|
|
394
|
-
telemetry,
|
|
395
|
-
messages,
|
|
396
|
-
abortSignal,
|
|
397
|
-
experimental_context,
|
|
398
|
-
stepNumber,
|
|
399
|
-
model,
|
|
400
|
-
onToolCallStart,
|
|
401
|
-
onToolCallFinish,
|
|
402
|
-
onPreliminaryToolResult: result => {
|
|
403
|
-
enqueueToolResult(result);
|
|
404
|
-
},
|
|
405
|
-
})
|
|
406
|
-
.then(result => {
|
|
407
|
-
enqueueToolResult(result);
|
|
408
|
-
})
|
|
409
|
-
.catch(error => {
|
|
410
|
-
enqueueToolResult({
|
|
411
|
-
type: 'error',
|
|
412
|
-
error,
|
|
413
|
-
});
|
|
414
|
-
})
|
|
415
|
-
.finally(() => {
|
|
416
|
-
outstandingToolResults.delete(toolExecutionId);
|
|
417
|
-
attemptClose();
|
|
418
|
-
});
|
|
430
|
+
toolCallsToExecute.push(toolCall);
|
|
419
431
|
}
|
|
420
432
|
} catch (error) {
|
|
421
433
|
enqueueToolResult({ type: 'error', error });
|
|
@@ -58,7 +58,7 @@ export async function convertToModelMessages<UI_MESSAGE extends UIMessage>(
|
|
|
58
58
|
part =>
|
|
59
59
|
!isToolUIPart(part) ||
|
|
60
60
|
part.state === 'approval-responded' ||
|
|
61
|
-
part.state === 'output-available' ||
|
|
61
|
+
(part.state === 'output-available' && part.preliminary !== true) ||
|
|
62
62
|
part.state === 'output-error' ||
|
|
63
63
|
part.state === 'output-denied',
|
|
64
64
|
),
|
|
@@ -8,11 +8,19 @@ import { createResolvablePromise } from './create-resolvable-promise';
|
|
|
8
8
|
*/
|
|
9
9
|
export function createStitchableStream<T>(): {
|
|
10
10
|
stream: ReadableStream<T>;
|
|
11
|
-
addStream: (
|
|
11
|
+
addStream: (
|
|
12
|
+
innerStream: ReadableStream<T>,
|
|
13
|
+
callbacks?: {
|
|
14
|
+
onError?: (error: unknown) => void;
|
|
15
|
+
},
|
|
16
|
+
) => void;
|
|
12
17
|
close: () => void;
|
|
13
18
|
terminate: () => void;
|
|
14
19
|
} {
|
|
15
|
-
let
|
|
20
|
+
let innerStreams: Array<{
|
|
21
|
+
reader: ReadableStreamDefaultReader<T>;
|
|
22
|
+
onError?: (error: unknown) => void;
|
|
23
|
+
}> = [];
|
|
16
24
|
let controller: ReadableStreamDefaultController<T> | null = null;
|
|
17
25
|
let isClosed = false;
|
|
18
26
|
let waitForNewStream = createResolvablePromise<void>();
|
|
@@ -21,34 +29,34 @@ export function createStitchableStream<T>(): {
|
|
|
21
29
|
isClosed = true;
|
|
22
30
|
waitForNewStream.resolve();
|
|
23
31
|
|
|
24
|
-
|
|
25
|
-
|
|
32
|
+
innerStreams.forEach(({ reader }) => reader.cancel());
|
|
33
|
+
innerStreams = [];
|
|
26
34
|
controller?.close();
|
|
27
35
|
};
|
|
28
36
|
|
|
29
37
|
const processPull = async () => {
|
|
30
38
|
// Case 1: Outer stream is closed and no more inner streams
|
|
31
|
-
if (isClosed &&
|
|
39
|
+
if (isClosed && innerStreams.length === 0) {
|
|
32
40
|
controller?.close();
|
|
33
41
|
return;
|
|
34
42
|
}
|
|
35
43
|
|
|
36
44
|
// Case 2: No inner streams available, but outer stream is open
|
|
37
45
|
// wait for a new inner stream to be added or the outer stream to close
|
|
38
|
-
if (
|
|
46
|
+
if (innerStreams.length === 0) {
|
|
39
47
|
waitForNewStream = createResolvablePromise<void>();
|
|
40
48
|
await waitForNewStream.promise;
|
|
41
49
|
return processPull();
|
|
42
50
|
}
|
|
43
51
|
|
|
44
52
|
try {
|
|
45
|
-
const { value, done } = await
|
|
53
|
+
const { value, done } = await innerStreams[0].reader.read();
|
|
46
54
|
|
|
47
55
|
if (done) {
|
|
48
56
|
// Case 3: Current inner stream is done
|
|
49
|
-
|
|
57
|
+
innerStreams.shift(); // Remove the finished stream
|
|
50
58
|
|
|
51
|
-
if (
|
|
59
|
+
if (innerStreams.length === 0 && isClosed) {
|
|
52
60
|
// when closed and no more inner streams, stop pulling
|
|
53
61
|
controller?.close();
|
|
54
62
|
} else {
|
|
@@ -61,8 +69,9 @@ export function createStitchableStream<T>(): {
|
|
|
61
69
|
}
|
|
62
70
|
} catch (error) {
|
|
63
71
|
// Case 5: Current inner stream throws an error
|
|
72
|
+
innerStreams[0].onError?.(error);
|
|
64
73
|
controller?.error(error);
|
|
65
|
-
|
|
74
|
+
innerStreams.shift(); // Remove the errored stream
|
|
66
75
|
terminate(); // we have errored, terminate all streams
|
|
67
76
|
}
|
|
68
77
|
};
|
|
@@ -74,19 +83,27 @@ export function createStitchableStream<T>(): {
|
|
|
74
83
|
},
|
|
75
84
|
pull: processPull,
|
|
76
85
|
async cancel() {
|
|
77
|
-
for (const reader of
|
|
86
|
+
for (const { reader } of innerStreams) {
|
|
78
87
|
await reader.cancel();
|
|
79
88
|
}
|
|
80
|
-
|
|
89
|
+
innerStreams = [];
|
|
81
90
|
isClosed = true;
|
|
82
91
|
},
|
|
83
92
|
}),
|
|
84
|
-
addStream: (
|
|
93
|
+
addStream: (
|
|
94
|
+
innerStream: ReadableStream<T>,
|
|
95
|
+
callbacks?: {
|
|
96
|
+
onError?: (error: unknown) => void;
|
|
97
|
+
},
|
|
98
|
+
) => {
|
|
85
99
|
if (isClosed) {
|
|
86
100
|
throw new Error('Cannot add inner stream: outer stream is closed');
|
|
87
101
|
}
|
|
88
102
|
|
|
89
|
-
|
|
103
|
+
innerStreams.push({
|
|
104
|
+
reader: innerStream.getReader(),
|
|
105
|
+
...callbacks,
|
|
106
|
+
});
|
|
90
107
|
waitForNewStream.resolve();
|
|
91
108
|
},
|
|
92
109
|
|
|
@@ -98,7 +115,7 @@ export function createStitchableStream<T>(): {
|
|
|
98
115
|
isClosed = true;
|
|
99
116
|
waitForNewStream.resolve();
|
|
100
117
|
|
|
101
|
-
if (
|
|
118
|
+
if (innerStreams.length === 0) {
|
|
102
119
|
controller?.close();
|
|
103
120
|
}
|
|
104
121
|
},
|