ai 6.0.178 → 6.0.182

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai",
3
- "version": "6.0.178",
3
+ "version": "6.0.182",
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,
@@ -45,7 +45,7 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "@opentelemetry/api": "^1.9.0",
48
- "@ai-sdk/gateway": "3.0.112",
48
+ "@ai-sdk/gateway": "3.0.114",
49
49
  "@ai-sdk/provider": "3.0.10",
50
50
  "@ai-sdk/provider-utils": "4.0.27"
51
51
  },
@@ -25,7 +25,10 @@ import {
25
25
  createDefaultDownloadFunction,
26
26
  type DownloadFunction,
27
27
  } from '../util/download/download-function';
28
- import { convertToLanguageModelV3DataContent } from './data-content';
28
+ import {
29
+ convertDataContentToBase64String,
30
+ convertToLanguageModelV3DataContent,
31
+ } from './data-content';
29
32
  import { InvalidMessageRoleError } from './invalid-message-role-error';
30
33
  import type { StandardizedPrompt } from './standardize-prompt';
31
34
  import { asArray } from '../util/as-array';
@@ -283,7 +286,10 @@ export function convertToLanguageModelMessage({
283
286
  type: 'tool-result' as const,
284
287
  toolCallId: part.toolCallId,
285
288
  toolName: part.toolName,
286
- output: mapToolResultOutput(part.output),
289
+ output: mapToolResultOutput({
290
+ output: part.output,
291
+ downloadedAssets,
292
+ }),
287
293
  providerOptions,
288
294
  };
289
295
  }
@@ -309,7 +315,10 @@ export function convertToLanguageModelMessage({
309
315
  type: 'tool-result' as const,
310
316
  toolCallId: part.toolCallId,
311
317
  toolName: part.toolName,
312
- output: mapToolResultOutput(part.output),
318
+ output: mapToolResultOutput({
319
+ output: part.output,
320
+ downloadedAssets,
321
+ }),
313
322
  providerOptions: part.providerOptions,
314
323
  };
315
324
  }
@@ -344,28 +353,59 @@ async function downloadAssets(
344
353
  ): Promise<
345
354
  Record<string, { mediaType: string | undefined; data: Uint8Array }>
346
355
  > {
347
- const plannedDownloads = messages
348
- .filter(message => message.role === 'user')
349
- .map(message => message.content)
350
- .filter((content): content is Array<TextPart | ImagePart | FilePart> =>
351
- Array.isArray(content),
352
- )
353
- .flat()
354
- .filter(
355
- (part): part is ImagePart | FilePart =>
356
- part.type === 'image' || part.type === 'file',
357
- )
358
- .map(part => {
359
- const mediaType =
360
- part.mediaType ?? (part.type === 'image' ? 'image/*' : undefined);
361
-
362
- let data = part.type === 'image' ? part.image : part.data;
363
- if (typeof data === 'string') {
364
- try {
365
- data = new URL(data);
366
- } catch (ignored) {}
356
+ type DownloadableFile = {
357
+ mediaType: string | undefined;
358
+ data: DataContent | URL;
359
+ };
360
+
361
+ const downloadableFiles: DownloadableFile[] = [];
362
+ for (const message of messages) {
363
+ if (message.role === 'user' && Array.isArray(message.content)) {
364
+ for (const part of message.content) {
365
+ if (part.type === 'image' || part.type === 'file') {
366
+ downloadableFiles.push({
367
+ data: part.type === 'image' ? part.image : part.data,
368
+ mediaType:
369
+ part.mediaType ?? (part.type === 'image' ? 'image/*' : undefined),
370
+ });
371
+ }
367
372
  }
373
+ }
374
+
375
+ if (message.role === 'tool' || message.role === 'assistant') {
376
+ if (!Array.isArray(message.content)) {
377
+ continue;
378
+ }
379
+
380
+ for (const part of message.content) {
381
+ if (part.type !== 'tool-result') {
382
+ continue;
383
+ }
368
384
 
385
+ if (part.output.type !== 'content') {
386
+ continue;
387
+ }
388
+
389
+ for (const contentPart of part.output.value) {
390
+ if (
391
+ contentPart.type === 'image-url' ||
392
+ contentPart.type === 'file-url'
393
+ ) {
394
+ downloadableFiles.push({
395
+ data: new URL(contentPart.url),
396
+ mediaType:
397
+ contentPart.type === 'image-url' ? 'image/*' : undefined,
398
+ });
399
+ }
400
+ }
401
+ }
402
+ }
403
+ }
404
+
405
+ const plannedDownloads = downloadableFiles
406
+ .map(part => {
407
+ const mediaType = part.mediaType;
408
+ const { data } = convertToLanguageModelV3DataContent(part.data);
369
409
  return { mediaType, data };
370
410
  })
371
411
 
@@ -383,7 +423,6 @@ async function downloadAssets(
383
423
  supportedUrls,
384
424
  }),
385
425
  }));
386
-
387
426
  // download in parallel:
388
427
  const downloadedFiles = await download(plannedDownloads);
389
428
 
@@ -492,9 +531,16 @@ function convertPartToLanguageModelPart(
492
531
  }
493
532
  }
494
533
 
495
- function mapToolResultOutput(
496
- output: ToolResultOutput,
497
- ): LanguageModelV3ToolResultOutput {
534
+ function mapToolResultOutput({
535
+ output,
536
+ downloadedAssets,
537
+ }: {
538
+ output: ToolResultOutput;
539
+ downloadedAssets: Record<
540
+ string,
541
+ { mediaType: string | undefined; data: Uint8Array }
542
+ >;
543
+ }): LanguageModelV3ToolResultOutput {
498
544
  if (output.type !== 'content') {
499
545
  return output;
500
546
  }
@@ -502,6 +548,34 @@ function mapToolResultOutput(
502
548
  return {
503
549
  type: 'content',
504
550
  value: output.value.map(item => {
551
+ if (item.type === 'image-url') {
552
+ const downloadedFile = downloadedAssets[new URL(item.url).toString()];
553
+ if (downloadedFile) {
554
+ return {
555
+ type: 'image-data' as const,
556
+ data: convertDataContentToBase64String(downloadedFile.data),
557
+ mediaType: downloadedFile.mediaType ?? 'image/*',
558
+ providerOptions: item.providerOptions,
559
+ };
560
+ }
561
+
562
+ return item;
563
+ }
564
+
565
+ if (item.type === 'file-url') {
566
+ const downloadedFile = downloadedAssets[new URL(item.url).toString()];
567
+ if (downloadedFile) {
568
+ return {
569
+ type: 'file-data' as const,
570
+ data: convertDataContentToBase64String(downloadedFile.data),
571
+ mediaType: downloadedFile.mediaType ?? 'application/octet-stream',
572
+ providerOptions: item.providerOptions,
573
+ };
574
+ }
575
+
576
+ return item;
577
+ }
578
+
505
579
  if (item.type !== 'media') {
506
580
  return item;
507
581
  }
@@ -168,7 +168,7 @@ const uiMessagesSchema = lazySchema(() =>
168
168
  toolCallId: z.string(),
169
169
  toolMetadata: toolMetadataSchema.optional(),
170
170
  state: z.literal('output-error'),
171
- input: z.unknown(),
171
+ input: z.unknown().optional(),
172
172
  rawInput: z.unknown().optional(),
173
173
  providerExecuted: z.boolean().optional(),
174
174
  output: z.never().optional(),
@@ -282,7 +282,7 @@ const uiMessagesSchema = lazySchema(() =>
282
282
  toolMetadata: toolMetadataSchema.optional(),
283
283
  state: z.literal('output-error'),
284
284
  providerExecuted: z.boolean().optional(),
285
- input: z.unknown(),
285
+ input: z.unknown().optional(),
286
286
  rawInput: z.unknown().optional(),
287
287
  output: z.never().optional(),
288
288
  errorText: z.string(),
@@ -1,4 +1,5 @@
1
1
  import { APICallError } from '@ai-sdk/provider';
2
+ import { GatewayError } from '@ai-sdk/gateway';
2
3
  import { delay, getErrorMessage, isAbortError } from '@ai-sdk/provider-utils';
3
4
  import { RetryError } from './retry-error';
4
5
 
@@ -10,10 +11,14 @@ function getRetryDelayInMs({
10
11
  error,
11
12
  exponentialBackoffDelay,
12
13
  }: {
13
- error: APICallError;
14
+ error: APICallError | GatewayError;
14
15
  exponentialBackoffDelay: number;
15
16
  }): number {
16
- const headers = error.responseHeaders;
17
+ const headers = APICallError.isInstance(error)
18
+ ? error.responseHeaders
19
+ : APICallError.isInstance(error.cause)
20
+ ? (error.cause as APICallError).responseHeaders
21
+ : undefined;
17
22
 
18
23
  if (!headers) return exponentialBackoffDelay;
19
24
 
@@ -117,13 +122,13 @@ async function _retryWithExponentialBackoff<OUTPUT>(
117
122
 
118
123
  if (
119
124
  error instanceof Error &&
120
- APICallError.isInstance(error) &&
121
- error.isRetryable === true &&
125
+ ((APICallError.isInstance(error) && error.isRetryable === true) ||
126
+ (GatewayError.isInstance(error) && error.isRetryable === true)) &&
122
127
  tryNumber <= maxRetries
123
128
  ) {
124
129
  await delay(
125
130
  getRetryDelayInMs({
126
- error,
131
+ error: error as APICallError | GatewayError,
127
132
  exponentialBackoffDelay: delayInMs,
128
133
  }),
129
134
  { abortSignal },