@ai-sdk/google 4.0.80 → 4.0.82

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.
@@ -1626,7 +1626,7 @@ These parts are informational and do not require a tool response. They are
1626
1626
  available from both `generateText` and `streamText`.
1627
1627
 
1628
1628
  For static processing, use `'static'` or provide clipping and frame-rate
1629
- options:
1629
+ options. `startOffset` and `endOffset` are measured in seconds:
1630
1630
 
1631
1631
  ```ts
1632
1632
  const video = {
@@ -1637,8 +1637,8 @@ const video = {
1637
1637
  google: {
1638
1638
  processing: {
1639
1639
  type: 'static',
1640
- startOffset: 1200,
1641
- endOffset: 1500,
1640
+ startOffset: 0,
1641
+ endOffset: 10.5,
1642
1642
  fps: 0.5,
1643
1643
  },
1644
1644
  } satisfies GoogleInteractionsVideoOptions,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/google",
3
- "version": "4.0.80",
3
+ "version": "4.0.82",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -36,10 +36,10 @@
36
36
  },
37
37
  "dependencies": {
38
38
  "@ai-sdk/provider": "4.0.18",
39
- "@ai-sdk/provider-utils": "5.0.47"
39
+ "@ai-sdk/provider-utils": "5.0.49"
40
40
  },
41
41
  "devDependencies": {
42
- "@ai-sdk/test-server": "2.0.1",
42
+ "@ai-sdk/test-server": "2.0.2",
43
43
  "@types/node": "22.19.19",
44
44
  "@vercel/ai-tsconfig": "0.0.0",
45
45
  "tsup": "^8.5.1",
@@ -9,6 +9,7 @@ import {
9
9
  convertToBase64,
10
10
  getTopLevelMediaType,
11
11
  isFullMediaType,
12
+ isUrlSupported,
12
13
  resolveFullMediaType,
13
14
  resolveProviderReference,
14
15
  secureJsonParse,
@@ -52,9 +53,6 @@ function parseBase64DataUrl(
52
53
  function convertUrlToolResultPart(
53
54
  url: string,
54
55
  ): GoogleFunctionResponsePart | undefined {
55
- // Per https://ai.google.dev/api/caching#FunctionResponsePart, only inline data is supported.
56
- // https://docs.cloud.google.com/vertex-ai/generative-ai/docs/model-reference/function-calling#functionresponsepart suggests that this
57
- // may be different for Vertex, but this needs to be confirmed and further tested for both APIs.
58
56
  const parsedDataUrl = parseBase64DataUrl(url);
59
57
  if (parsedDataUrl == null) {
60
58
  return undefined;
@@ -106,6 +104,7 @@ function appendToolResultParts(
106
104
  >['value'],
107
105
  toolCallId?: string,
108
106
  includeFunctionCallIds = true,
107
+ supportedUrls: Record<string, RegExp[]> = {},
109
108
  ): void {
110
109
  const functionResponseParts: GoogleFunctionResponsePart[] = [];
111
110
  const responseTextParts: string[] = [];
@@ -125,12 +124,30 @@ function appendToolResultParts(
125
124
  },
126
125
  });
127
126
  } else if (contentPart.data.type === 'url') {
128
- const functionResponsePart = convertUrlToolResultPart(
129
- contentPart.data.url.toString(),
130
- );
131
-
132
- if (functionResponsePart != null) {
133
- functionResponseParts.push(functionResponsePart);
127
+ const url = contentPart.data.url.toString();
128
+ const convertedUrlPart = convertUrlToolResultPart(url);
129
+ const supportedUrl =
130
+ contentPart.data.url.protocol === 'gs:' &&
131
+ contentPart.data.originalUrl != null
132
+ ? contentPart.data.originalUrl
133
+ : url;
134
+
135
+ if (convertedUrlPart != null) {
136
+ functionResponseParts.push(convertedUrlPart);
137
+ } else if (
138
+ isFullMediaType(contentPart.mediaType) &&
139
+ isUrlSupported({
140
+ url: supportedUrl,
141
+ mediaType: contentPart.mediaType,
142
+ supportedUrls,
143
+ })
144
+ ) {
145
+ functionResponseParts.push({
146
+ fileData: {
147
+ mimeType: contentPart.mediaType,
148
+ fileUri: supportedUrl,
149
+ },
150
+ });
134
151
  } else {
135
152
  responseTextParts.push(JSON.stringify(contentPart));
136
153
  }
@@ -242,6 +259,7 @@ export function convertToGoogleMessages(
242
259
  providerOptionsNames?: readonly string[];
243
260
  supportsFunctionResponseParts?: boolean;
244
261
  includeFunctionCallIds?: boolean;
262
+ supportedFunctionResponseUrls?: Record<string, RegExp[]>;
245
263
  },
246
264
  ): GooglePrompt {
247
265
  const systemInstructionParts: Array<{ text: string }> = [];
@@ -255,6 +273,8 @@ export function convertToGoogleMessages(
255
273
  const supportsFunctionResponseParts =
256
274
  options?.supportsFunctionResponseParts ?? true;
257
275
  const includeFunctionCallIds = options?.includeFunctionCallIds ?? true;
276
+ const supportedFunctionResponseUrls =
277
+ options?.supportedFunctionResponseUrls ?? {};
258
278
 
259
279
  let sentinelInjected = false;
260
280
  const missingSignatureToolNames: string[] = [];
@@ -657,6 +677,7 @@ export function convertToGoogleMessages(
657
677
  output.value,
658
678
  part.toolCallId,
659
679
  includeFunctionCallIds,
680
+ supportedFunctionResponseUrls,
660
681
  );
661
682
  } else {
662
683
  appendLegacyToolResultParts(
@@ -6,20 +6,23 @@ import {
6
6
  detectMediaType,
7
7
  downloadBlob,
8
8
  isFullMediaType,
9
+ isUrlSupported,
9
10
  } from '@ai-sdk/provider-utils';
10
11
 
11
12
  /**
12
- * Vertex function responses only accept inline file data. Download remote tool
13
- * result files before converting the prompt to the Google request format.
13
+ * Download tool result file URLs that Vertex cannot reference directly before
14
+ * converting the prompt to the Google request format.
14
15
  */
15
16
  export async function downloadToolResultFiles(
16
17
  prompt: LanguageModelV4Prompt,
17
18
  {
18
19
  abortSignal,
19
20
  maxBytes,
21
+ supportedUrls = {},
20
22
  }: {
21
23
  abortSignal: AbortSignal | undefined;
22
24
  maxBytes: number;
25
+ supportedUrls?: Record<string, RegExp[]>;
23
26
  },
24
27
  ): Promise<LanguageModelV4Prompt> {
25
28
  const result: LanguageModelV4Prompt = [];
@@ -36,6 +39,7 @@ export async function downloadToolResultFiles(
36
39
  output: await downloadToolResultOutput(part.output, {
37
40
  abortSignal,
38
41
  maxBytes,
42
+ supportedUrls,
39
43
  }),
40
44
  }
41
45
  : part,
@@ -60,6 +64,7 @@ export async function downloadToolResultFiles(
60
64
  output: await downloadToolResultOutput(part.output, {
61
65
  abortSignal,
62
66
  maxBytes,
67
+ supportedUrls,
63
68
  }),
64
69
  });
65
70
  }
@@ -79,9 +84,11 @@ async function downloadToolResultOutput(
79
84
  {
80
85
  abortSignal,
81
86
  maxBytes,
87
+ supportedUrls,
82
88
  }: {
83
89
  abortSignal: AbortSignal | undefined;
84
90
  maxBytes: number;
91
+ supportedUrls: Record<string, RegExp[]>;
85
92
  },
86
93
  ): Promise<LanguageModelV4ToolResultOutput> {
87
94
  if (output.type !== 'content') {
@@ -96,6 +103,17 @@ async function downloadToolResultOutput(
96
103
  continue;
97
104
  }
98
105
 
106
+ if (
107
+ isUrlSupported({
108
+ url: part.data.url.toString(),
109
+ mediaType: part.mediaType,
110
+ supportedUrls,
111
+ })
112
+ ) {
113
+ value.push(part);
114
+ continue;
115
+ }
116
+
99
117
  const blob = await downloadBlob(part.data.url.toString(), {
100
118
  abortSignal,
101
119
  maxBytes,
@@ -66,6 +66,14 @@ const configurableSafetySettingCategories = [
66
66
 
67
67
  const gemini25ModelPattern = /(^|\/)gemini-2\.5(?:[.-]|$)/i;
68
68
 
69
+ const googleCloudStorageFunctionResponseUrls = {
70
+ 'image/png': [/^gs:\/\/.*$/],
71
+ 'image/jpeg': [/^gs:\/\/.*$/],
72
+ 'image/webp': [/^gs:\/\/.*$/],
73
+ 'application/pdf': [/^gs:\/\/.*$/],
74
+ 'text/plain': [/^gs:\/\/.*$/],
75
+ } satisfies Record<string, RegExp[]>;
76
+
69
77
  export type GoogleLanguageModelConfig = {
70
78
  provider: string;
71
79
  baseURL: string;
@@ -83,6 +91,7 @@ export type GoogleLanguageModelConfig = {
83
91
  */
84
92
  downloadToolResultFiles?: {
85
93
  maxBytes: number;
94
+ supportsGoogleCloudStorageUrls?: boolean;
86
95
  };
87
96
  };
88
97
 
@@ -295,11 +304,17 @@ export class GoogleLanguageModel implements LanguageModelV4 {
295
304
  }
296
305
 
297
306
  const { usesGemini3Features } = getGoogleModelCapabilities(modelId);
307
+ const supportedFunctionResponseUrls =
308
+ usesGemini3Features &&
309
+ config.downloadToolResultFiles?.supportsGoogleCloudStorageUrls
310
+ ? googleCloudStorageFunctionResponseUrls
311
+ : undefined;
298
312
 
299
313
  const promptWithDownloadedToolResultFiles = config.downloadToolResultFiles
300
314
  ? await downloadToolResultFiles(prompt, {
301
315
  abortSignal,
302
316
  maxBytes: config.downloadToolResultFiles.maxBytes,
317
+ supportedUrls: supportedFunctionResponseUrls,
303
318
  })
304
319
  : prompt;
305
320
 
@@ -312,6 +327,7 @@ export class GoogleLanguageModel implements LanguageModelV4 {
312
327
  providerOptionsNames,
313
328
  supportsFunctionResponseParts: usesGemini3Features,
314
329
  includeFunctionCallIds: !isVertexProvider,
330
+ supportedFunctionResponseUrls,
315
331
  },
316
332
  );
317
333
 
@@ -73,9 +73,13 @@ export type GoogleContentPart =
73
73
  };
74
74
  };
75
75
 
76
- export type GoogleFunctionResponsePart = {
77
- inlineData: { mimeType: string; data: string };
78
- };
76
+ export type GoogleFunctionResponsePart =
77
+ | {
78
+ inlineData: { mimeType: string; data: string };
79
+ }
80
+ | {
81
+ fileData: { mimeType: string; fileUri: string };
82
+ };
79
83
 
80
84
  export type GoogleGroundingMetadata = GroundingMetadataSchema;
81
85
 
@@ -401,10 +401,10 @@ function getVideoProcessingField({
401
401
  processing: {
402
402
  type: 'static',
403
403
  ...(typeof config.startOffset === 'number'
404
- ? { start_offset: config.startOffset }
404
+ ? { start_offset: `${config.startOffset}s` }
405
405
  : {}),
406
406
  ...(typeof config.endOffset === 'number'
407
- ? { end_offset: config.endOffset }
407
+ ? { end_offset: `${config.endOffset}s` }
408
408
  : {}),
409
409
  ...(typeof config.fps === 'number' ? { fps: config.fps } : {}),
410
410
  },
@@ -40,8 +40,8 @@ export type GoogleInteractionsVideoProcessing =
40
40
  | 'static'
41
41
  | {
42
42
  type: 'static';
43
- start_offset?: number;
44
- end_offset?: number;
43
+ start_offset?: string;
44
+ end_offset?: string;
45
45
  fps?: number;
46
46
  };
47
47