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.
@@ -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.259" : "0.0.0-test";
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 ({
@@ -144,7 +144,7 @@ import {
144
144
  } from "@ai-sdk/provider-utils";
145
145
 
146
146
  // src/version.ts
147
- var VERSION = true ? "6.0.259" : "0.0.0-test";
147
+ var VERSION = true ? "6.0.260" : "0.0.0-test";
148
148
 
149
149
  // src/util/download/download.ts
150
150
  var download = async ({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai",
3
- "version": "6.0.259",
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 = chunk.finishReason.unified;
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: chunk.finishReason.unified,
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 (stepToolSet != null) {
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(
@@ -0,0 +1,7 @@
1
+ import type { FinishReason } from '../types';
2
+
3
+ export function isToolExecutionAllowedFinishReason(
4
+ finishReason: FinishReason,
5
+ ): boolean {
6
+ return finishReason === 'stop' || finishReason === 'tool-calls';
7
+ }
@@ -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
- const toolExecutionId = generateId(); // use our own id to guarantee uniqueness
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: (innerStream: ReadableStream<T>) => void;
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 innerStreamReaders: ReadableStreamDefaultReader<T>[] = [];
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
- innerStreamReaders.forEach(reader => reader.cancel());
25
- innerStreamReaders = [];
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 && innerStreamReaders.length === 0) {
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 (innerStreamReaders.length === 0) {
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 innerStreamReaders[0].read();
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
- innerStreamReaders.shift(); // Remove the finished stream
57
+ innerStreams.shift(); // Remove the finished stream
50
58
 
51
- if (innerStreamReaders.length === 0 && isClosed) {
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
- innerStreamReaders.shift(); // Remove the errored stream
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 innerStreamReaders) {
86
+ for (const { reader } of innerStreams) {
78
87
  await reader.cancel();
79
88
  }
80
- innerStreamReaders = [];
89
+ innerStreams = [];
81
90
  isClosed = true;
82
91
  },
83
92
  }),
84
- addStream: (innerStream: ReadableStream<T>) => {
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
- innerStreamReaders.push(innerStream.getReader());
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 (innerStreamReaders.length === 0) {
118
+ if (innerStreams.length === 0) {
102
119
  controller?.close();
103
120
  }
104
121
  },