@ai-sdk/google 3.0.9 → 3.0.11

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.
Files changed (43) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/index.js +1 -1
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.mjs +1 -1
  5. package/dist/index.mjs.map +1 -1
  6. package/package.json +5 -4
  7. package/src/__snapshots__/google-generative-ai-embedding-model.test.ts.snap +33 -0
  8. package/src/convert-google-generative-ai-usage.ts +51 -0
  9. package/src/convert-json-schema-to-openapi-schema.test.ts +684 -0
  10. package/src/convert-json-schema-to-openapi-schema.ts +158 -0
  11. package/src/convert-to-google-generative-ai-messages.test.ts +495 -0
  12. package/src/convert-to-google-generative-ai-messages.ts +232 -0
  13. package/src/get-model-path.test.ts +16 -0
  14. package/src/get-model-path.ts +3 -0
  15. package/src/google-error.ts +26 -0
  16. package/src/google-generative-ai-embedding-model.test.ts +204 -0
  17. package/src/google-generative-ai-embedding-model.ts +159 -0
  18. package/src/google-generative-ai-embedding-options.ts +52 -0
  19. package/src/google-generative-ai-image-model.test.ts +411 -0
  20. package/src/google-generative-ai-image-model.ts +184 -0
  21. package/src/google-generative-ai-image-settings.ts +12 -0
  22. package/src/google-generative-ai-language-model.test.ts +4616 -0
  23. package/src/google-generative-ai-language-model.ts +1009 -0
  24. package/src/google-generative-ai-options.ts +193 -0
  25. package/src/google-generative-ai-prompt.ts +38 -0
  26. package/src/google-prepare-tools.test.ts +474 -0
  27. package/src/google-prepare-tools.ts +264 -0
  28. package/src/google-provider.test.ts +307 -0
  29. package/src/google-provider.ts +201 -0
  30. package/src/google-supported-file-url.test.ts +57 -0
  31. package/src/google-supported-file-url.ts +20 -0
  32. package/src/google-tools.ts +71 -0
  33. package/src/index.ts +11 -0
  34. package/src/internal/index.ts +3 -0
  35. package/src/map-google-generative-ai-finish-reason.ts +29 -0
  36. package/src/tool/code-execution.ts +35 -0
  37. package/src/tool/enterprise-web-search.ts +18 -0
  38. package/src/tool/file-search.ts +51 -0
  39. package/src/tool/google-maps.ts +14 -0
  40. package/src/tool/google-search.ts +40 -0
  41. package/src/tool/url-context.ts +16 -0
  42. package/src/tool/vertex-rag-store.ts +31 -0
  43. package/src/version.ts +6 -0
@@ -0,0 +1,193 @@
1
+ import { InferSchema, lazySchema, zodSchema } from '@ai-sdk/provider-utils';
2
+ import { z } from 'zod/v4';
3
+
4
+ export type GoogleGenerativeAIModelId =
5
+ // Stable models
6
+ // https://ai.google.dev/gemini-api/docs/models/gemini
7
+ | 'gemini-1.5-flash'
8
+ | 'gemini-1.5-flash-latest'
9
+ | 'gemini-1.5-flash-001'
10
+ | 'gemini-1.5-flash-002'
11
+ | 'gemini-1.5-flash-8b'
12
+ | 'gemini-1.5-flash-8b-latest'
13
+ | 'gemini-1.5-flash-8b-001'
14
+ | 'gemini-1.5-pro'
15
+ | 'gemini-1.5-pro-latest'
16
+ | 'gemini-1.5-pro-001'
17
+ | 'gemini-1.5-pro-002'
18
+ | 'gemini-2.0-flash'
19
+ | 'gemini-2.0-flash-001'
20
+ | 'gemini-2.0-flash-live-001'
21
+ | 'gemini-2.0-flash-lite'
22
+ | 'gemini-2.0-pro-exp-02-05'
23
+ | 'gemini-2.0-flash-thinking-exp-01-21'
24
+ | 'gemini-2.0-flash-exp'
25
+ | 'gemini-2.5-pro'
26
+ | 'gemini-2.5-flash'
27
+ | 'gemini-2.5-flash-image-preview'
28
+ | 'gemini-2.5-flash-lite'
29
+ | 'gemini-2.5-flash-lite-preview-09-2025'
30
+ | 'gemini-2.5-flash-preview-04-17'
31
+ | 'gemini-2.5-flash-preview-09-2025'
32
+ | 'gemini-3-pro-preview'
33
+ | 'gemini-3-pro-image-preview'
34
+ | 'gemini-3-flash-preview'
35
+ // latest version
36
+ // https://ai.google.dev/gemini-api/docs/models#latest
37
+ | 'gemini-pro-latest'
38
+ | 'gemini-flash-latest'
39
+ | 'gemini-flash-lite-latest'
40
+ // Experimental models
41
+ // https://ai.google.dev/gemini-api/docs/models/experimental-models
42
+ | 'gemini-2.5-pro-exp-03-25'
43
+ | 'gemini-exp-1206'
44
+ | 'gemma-3-12b-it'
45
+ | 'gemma-3-27b-it'
46
+ | (string & {});
47
+
48
+ export const googleGenerativeAIProviderOptions = lazySchema(() =>
49
+ zodSchema(
50
+ z.object({
51
+ responseModalities: z.array(z.enum(['TEXT', 'IMAGE'])).optional(),
52
+
53
+ thinkingConfig: z
54
+ .object({
55
+ thinkingBudget: z.number().optional(),
56
+ includeThoughts: z.boolean().optional(),
57
+ // https://ai.google.dev/gemini-api/docs/gemini-3?thinking=high#thinking_level
58
+ thinkingLevel: z
59
+ .enum(['minimal', 'low', 'medium', 'high'])
60
+ .optional(),
61
+ })
62
+ .optional(),
63
+
64
+ /**
65
+ * Optional.
66
+ * The name of the cached content used as context to serve the prediction.
67
+ * Format: cachedContents/{cachedContent}
68
+ */
69
+ cachedContent: z.string().optional(),
70
+
71
+ /**
72
+ * Optional. Enable structured output. Default is true.
73
+ *
74
+ * This is useful when the JSON Schema contains elements that are
75
+ * not supported by the OpenAPI schema version that
76
+ * Google Generative AI uses. You can use this to disable
77
+ * structured outputs if you need to.
78
+ */
79
+ structuredOutputs: z.boolean().optional(),
80
+
81
+ /**
82
+ * Optional. A list of unique safety settings for blocking unsafe content.
83
+ */
84
+ safetySettings: z
85
+ .array(
86
+ z.object({
87
+ category: z.enum([
88
+ 'HARM_CATEGORY_UNSPECIFIED',
89
+ 'HARM_CATEGORY_HATE_SPEECH',
90
+ 'HARM_CATEGORY_DANGEROUS_CONTENT',
91
+ 'HARM_CATEGORY_HARASSMENT',
92
+ 'HARM_CATEGORY_SEXUALLY_EXPLICIT',
93
+ 'HARM_CATEGORY_CIVIC_INTEGRITY',
94
+ ]),
95
+ threshold: z.enum([
96
+ 'HARM_BLOCK_THRESHOLD_UNSPECIFIED',
97
+ 'BLOCK_LOW_AND_ABOVE',
98
+ 'BLOCK_MEDIUM_AND_ABOVE',
99
+ 'BLOCK_ONLY_HIGH',
100
+ 'BLOCK_NONE',
101
+ 'OFF',
102
+ ]),
103
+ }),
104
+ )
105
+ .optional(),
106
+
107
+ threshold: z
108
+ .enum([
109
+ 'HARM_BLOCK_THRESHOLD_UNSPECIFIED',
110
+ 'BLOCK_LOW_AND_ABOVE',
111
+ 'BLOCK_MEDIUM_AND_ABOVE',
112
+ 'BLOCK_ONLY_HIGH',
113
+ 'BLOCK_NONE',
114
+ 'OFF',
115
+ ])
116
+ .optional(),
117
+
118
+ /**
119
+ * Optional. Enables timestamp understanding for audio-only files.
120
+ *
121
+ * https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/audio-understanding
122
+ */
123
+ audioTimestamp: z.boolean().optional(),
124
+
125
+ /**
126
+ * Optional. Defines labels used in billing reports. Available on Vertex AI only.
127
+ *
128
+ * https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/add-labels-to-api-calls
129
+ */
130
+ labels: z.record(z.string(), z.string()).optional(),
131
+
132
+ /**
133
+ * Optional. If specified, the media resolution specified will be used.
134
+ *
135
+ * https://ai.google.dev/api/generate-content#MediaResolution
136
+ */
137
+ mediaResolution: z
138
+ .enum([
139
+ 'MEDIA_RESOLUTION_UNSPECIFIED',
140
+ 'MEDIA_RESOLUTION_LOW',
141
+ 'MEDIA_RESOLUTION_MEDIUM',
142
+ 'MEDIA_RESOLUTION_HIGH',
143
+ ])
144
+ .optional(),
145
+
146
+ /**
147
+ * Optional. Configures the image generation aspect ratio for Gemini models.
148
+ *
149
+ * https://ai.google.dev/gemini-api/docs/image-generation#aspect_ratios
150
+ */
151
+ imageConfig: z
152
+ .object({
153
+ aspectRatio: z
154
+ .enum([
155
+ '1:1',
156
+ '2:3',
157
+ '3:2',
158
+ '3:4',
159
+ '4:3',
160
+ '4:5',
161
+ '5:4',
162
+ '9:16',
163
+ '16:9',
164
+ '21:9',
165
+ ])
166
+ .optional(),
167
+ imageSize: z.enum(['1K', '2K', '4K']).optional(),
168
+ })
169
+ .optional(),
170
+
171
+ /**
172
+ * Optional. Configuration for grounding retrieval.
173
+ * Used to provide location context for Google Maps and Google Search grounding.
174
+ *
175
+ * https://cloud.google.com/vertex-ai/generative-ai/docs/grounding/grounding-with-google-maps
176
+ */
177
+ retrievalConfig: z
178
+ .object({
179
+ latLng: z
180
+ .object({
181
+ latitude: z.number(),
182
+ longitude: z.number(),
183
+ })
184
+ .optional(),
185
+ })
186
+ .optional(),
187
+ }),
188
+ ),
189
+ );
190
+
191
+ export type GoogleGenerativeAIProviderOptions = InferSchema<
192
+ typeof googleGenerativeAIProviderOptions
193
+ >;
@@ -0,0 +1,38 @@
1
+ import {
2
+ GroundingMetadataSchema,
3
+ UrlContextMetadataSchema,
4
+ } from './google-generative-ai-language-model';
5
+ import { type SafetyRatingSchema } from './google-generative-ai-language-model';
6
+
7
+ export type GoogleGenerativeAIPrompt = {
8
+ systemInstruction?: GoogleGenerativeAISystemInstruction;
9
+ contents: Array<GoogleGenerativeAIContent>;
10
+ };
11
+
12
+ export type GoogleGenerativeAISystemInstruction = {
13
+ parts: Array<{ text: string }>;
14
+ };
15
+
16
+ export type GoogleGenerativeAIContent = {
17
+ role: 'user' | 'model';
18
+ parts: Array<GoogleGenerativeAIContentPart>;
19
+ };
20
+
21
+ export type GoogleGenerativeAIContentPart =
22
+ | { text: string; thought?: boolean; thoughtSignature?: string }
23
+ | { inlineData: { mimeType: string; data: string } }
24
+ | { functionCall: { name: string; args: unknown }; thoughtSignature?: string }
25
+ | { functionResponse: { name: string; response: unknown } }
26
+ | { fileData: { mimeType: string; fileUri: string } };
27
+
28
+ export type GoogleGenerativeAIGroundingMetadata = GroundingMetadataSchema;
29
+
30
+ export type GoogleGenerativeAIUrlContextMetadata = UrlContextMetadataSchema;
31
+
32
+ export type GoogleGenerativeAISafetyRating = SafetyRatingSchema;
33
+
34
+ export interface GoogleGenerativeAIProviderMetadata {
35
+ groundingMetadata: GoogleGenerativeAIGroundingMetadata | null;
36
+ urlContextMetadata: GoogleGenerativeAIUrlContextMetadata | null;
37
+ safetyRatings: GoogleGenerativeAISafetyRating[] | null;
38
+ }
@@ -0,0 +1,474 @@
1
+ import { LanguageModelV3ProviderTool } from '@ai-sdk/provider';
2
+ import { expect, it } from 'vitest';
3
+ import { prepareTools } from './google-prepare-tools';
4
+
5
+ it('should return undefined tools and tool_choice when tools are null', () => {
6
+ const result = prepareTools({
7
+ tools: undefined,
8
+ modelId: 'gemini-2.5-flash',
9
+ });
10
+ expect(result).toEqual({
11
+ tools: undefined,
12
+ tool_choice: undefined,
13
+ toolWarnings: [],
14
+ });
15
+ });
16
+
17
+ it('should return undefined tools and tool_choice when tools are empty', () => {
18
+ const result = prepareTools({ tools: [], modelId: 'gemini-2.5-flash' });
19
+ expect(result).toEqual({
20
+ tools: undefined,
21
+ tool_choice: undefined,
22
+ toolWarnings: [],
23
+ });
24
+ });
25
+
26
+ it('should correctly prepare function tools', () => {
27
+ const result = prepareTools({
28
+ tools: [
29
+ {
30
+ type: 'function',
31
+ name: 'testFunction',
32
+ description: 'A test function',
33
+ inputSchema: { type: 'object', properties: {} },
34
+ },
35
+ ],
36
+ modelId: 'gemini-2.5-flash',
37
+ });
38
+ expect(result.tools).toEqual([
39
+ {
40
+ functionDeclarations: [
41
+ {
42
+ name: 'testFunction',
43
+ description: 'A test function',
44
+ parameters: undefined,
45
+ },
46
+ ],
47
+ },
48
+ ]);
49
+ expect(result.toolConfig).toBeUndefined();
50
+ expect(result.toolWarnings).toEqual([]);
51
+ });
52
+
53
+ it('should correctly prepare provider-defined tools as array', () => {
54
+ const result = prepareTools({
55
+ tools: [
56
+ {
57
+ type: 'provider',
58
+ id: 'google.google_search',
59
+ name: 'google_search',
60
+ args: {},
61
+ },
62
+ {
63
+ type: 'provider',
64
+ id: 'google.url_context',
65
+ name: 'url_context',
66
+ args: {},
67
+ },
68
+ {
69
+ type: 'provider',
70
+ id: 'google.file_search',
71
+ name: 'file_search',
72
+ args: { fileSearchStoreNames: ['projects/foo/fileSearchStores/bar'] },
73
+ },
74
+ ],
75
+ modelId: 'gemini-2.5-flash',
76
+ });
77
+ expect(result.tools).toEqual([
78
+ { googleSearch: {} },
79
+ { urlContext: {} },
80
+ {
81
+ fileSearch: {
82
+ fileSearchStoreNames: ['projects/foo/fileSearchStores/bar'],
83
+ },
84
+ },
85
+ ]);
86
+ expect(result.toolConfig).toBeUndefined();
87
+ expect(result.toolWarnings).toEqual([]);
88
+ });
89
+
90
+ it('should correctly prepare single provider-defined tool', () => {
91
+ const result = prepareTools({
92
+ tools: [
93
+ {
94
+ type: 'provider',
95
+ id: 'google.google_search',
96
+ name: 'google_search',
97
+ args: {},
98
+ },
99
+ ],
100
+ modelId: 'gemini-2.5-flash',
101
+ });
102
+ expect(result.tools).toEqual([{ googleSearch: {} }]);
103
+ expect(result.toolConfig).toBeUndefined();
104
+ expect(result.toolWarnings).toEqual([]);
105
+ });
106
+
107
+ it('should add warnings for unsupported tools', () => {
108
+ const result = prepareTools({
109
+ tools: [
110
+ {
111
+ type: 'provider',
112
+ id: 'unsupported.tool',
113
+ name: 'unsupported_tool',
114
+ args: {},
115
+ },
116
+ ],
117
+ modelId: 'gemini-2.5-flash',
118
+ });
119
+ expect(result.tools).toBeUndefined();
120
+ expect(result.toolConfig).toBeUndefined();
121
+ expect(result.toolWarnings).toMatchInlineSnapshot(`
122
+ [
123
+ {
124
+ "feature": "provider-defined tool unsupported.tool",
125
+ "type": "unsupported",
126
+ },
127
+ ]
128
+ `);
129
+ });
130
+
131
+ it('should add warnings for file search on unsupported models', () => {
132
+ const tool: LanguageModelV3ProviderTool = {
133
+ type: 'provider' as const,
134
+ id: 'google.file_search',
135
+ name: 'file_search',
136
+ args: { fileSearchStoreNames: ['projects/foo/fileSearchStores/bar'] },
137
+ };
138
+
139
+ const result = prepareTools({
140
+ tools: [tool],
141
+ modelId: 'gemini-1.5-flash-8b',
142
+ });
143
+
144
+ expect(result.tools).toBeUndefined();
145
+ expect(result.toolWarnings).toMatchInlineSnapshot(`
146
+ [
147
+ {
148
+ "details": "The file search tool is only supported with Gemini 2.5 models and Gemini 3 models.",
149
+ "feature": "provider-defined tool google.file_search",
150
+ "type": "unsupported",
151
+ },
152
+ ]
153
+ `);
154
+ });
155
+
156
+ it('should correctly prepare file search tool for gemini-2.5 models', () => {
157
+ const result = prepareTools({
158
+ tools: [
159
+ {
160
+ type: 'provider',
161
+ id: 'google.file_search',
162
+ name: 'file_search',
163
+ args: {
164
+ fileSearchStoreNames: ['projects/foo/fileSearchStores/bar'],
165
+ metadataFilter: 'author=Robert Graves',
166
+ topK: 5,
167
+ },
168
+ },
169
+ ],
170
+ modelId: 'gemini-2.5-pro',
171
+ });
172
+
173
+ expect(result.tools).toEqual([
174
+ {
175
+ fileSearch: {
176
+ fileSearchStoreNames: ['projects/foo/fileSearchStores/bar'],
177
+ metadataFilter: 'author=Robert Graves',
178
+ topK: 5,
179
+ },
180
+ },
181
+ ]);
182
+ expect(result.toolWarnings).toEqual([]);
183
+ });
184
+
185
+ it('should correctly prepare file search tool for gemini-3 models', () => {
186
+ const result = prepareTools({
187
+ tools: [
188
+ {
189
+ type: 'provider',
190
+ id: 'google.file_search',
191
+ name: 'file_search',
192
+ args: {
193
+ fileSearchStoreNames: ['projects/foo/fileSearchStores/bar'],
194
+ metadataFilter: 'author=Robert Graves',
195
+ topK: 5,
196
+ },
197
+ },
198
+ ],
199
+ modelId: 'gemini-3-pro-preview',
200
+ });
201
+
202
+ expect(result.tools).toEqual([
203
+ {
204
+ fileSearch: {
205
+ fileSearchStoreNames: ['projects/foo/fileSearchStores/bar'],
206
+ metadataFilter: 'author=Robert Graves',
207
+ topK: 5,
208
+ },
209
+ },
210
+ ]);
211
+ expect(result.toolWarnings).toEqual([]);
212
+ });
213
+
214
+ it('should handle tool choice "auto"', () => {
215
+ const result = prepareTools({
216
+ tools: [
217
+ {
218
+ type: 'function',
219
+ name: 'testFunction',
220
+ description: 'Test',
221
+ inputSchema: {},
222
+ },
223
+ ],
224
+ toolChoice: { type: 'auto' },
225
+ modelId: 'gemini-2.5-flash',
226
+ });
227
+ expect(result.toolConfig).toEqual({
228
+ functionCallingConfig: { mode: 'AUTO' },
229
+ });
230
+ });
231
+
232
+ it('should handle tool choice "required"', () => {
233
+ const result = prepareTools({
234
+ tools: [
235
+ {
236
+ type: 'function',
237
+ name: 'testFunction',
238
+ description: 'Test',
239
+ inputSchema: {},
240
+ },
241
+ ],
242
+ toolChoice: { type: 'required' },
243
+ modelId: 'gemini-2.5-flash',
244
+ });
245
+ expect(result.toolConfig).toEqual({
246
+ functionCallingConfig: { mode: 'ANY' },
247
+ });
248
+ });
249
+
250
+ it('should handle tool choice "none"', () => {
251
+ const result = prepareTools({
252
+ tools: [
253
+ {
254
+ type: 'function',
255
+ name: 'testFunction',
256
+ description: 'Test',
257
+ inputSchema: {},
258
+ },
259
+ ],
260
+ toolChoice: { type: 'none' },
261
+ modelId: 'gemini-2.5-flash',
262
+ });
263
+ expect(result.tools).toEqual([
264
+ {
265
+ functionDeclarations: [
266
+ {
267
+ name: 'testFunction',
268
+ description: 'Test',
269
+ parameters: {},
270
+ },
271
+ ],
272
+ },
273
+ ]);
274
+ expect(result.toolConfig).toEqual({
275
+ functionCallingConfig: { mode: 'NONE' },
276
+ });
277
+ });
278
+
279
+ it('should handle tool choice "tool"', () => {
280
+ const result = prepareTools({
281
+ tools: [
282
+ {
283
+ type: 'function',
284
+ name: 'testFunction',
285
+ description: 'Test',
286
+ inputSchema: {},
287
+ },
288
+ ],
289
+ toolChoice: { type: 'tool', toolName: 'testFunction' },
290
+ modelId: 'gemini-2.5-flash',
291
+ });
292
+ expect(result.toolConfig).toEqual({
293
+ functionCallingConfig: {
294
+ mode: 'ANY',
295
+ allowedFunctionNames: ['testFunction'],
296
+ },
297
+ });
298
+ });
299
+
300
+ it('should warn when mixing function and provider-defined tools', () => {
301
+ const result = prepareTools({
302
+ tools: [
303
+ {
304
+ type: 'function',
305
+ name: 'testFunction',
306
+ description: 'A test function',
307
+ inputSchema: { type: 'object', properties: {} },
308
+ },
309
+ {
310
+ type: 'provider',
311
+ id: 'google.google_search',
312
+ name: 'google_search',
313
+ args: {},
314
+ },
315
+ ],
316
+ modelId: 'gemini-2.5-flash',
317
+ });
318
+
319
+ expect(result.tools).toEqual([{ googleSearch: {} }]);
320
+
321
+ expect(result.toolWarnings).toMatchInlineSnapshot(`
322
+ [
323
+ {
324
+ "feature": "combination of function and provider-defined tools",
325
+ "type": "unsupported",
326
+ },
327
+ ]
328
+ `);
329
+
330
+ expect(result.toolConfig).toBeUndefined();
331
+ });
332
+
333
+ it('should handle tool choice with mixed tools (provider-defined tools only)', () => {
334
+ const result = prepareTools({
335
+ tools: [
336
+ {
337
+ type: 'function',
338
+ name: 'testFunction',
339
+ description: 'A test function',
340
+ inputSchema: { type: 'object', properties: {} },
341
+ },
342
+ {
343
+ type: 'provider',
344
+ id: 'google.google_search',
345
+ name: 'google_search',
346
+ args: {},
347
+ },
348
+ ],
349
+ toolChoice: { type: 'auto' },
350
+ modelId: 'gemini-2.5-flash',
351
+ });
352
+
353
+ expect(result.tools).toEqual([{ googleSearch: {} }]);
354
+
355
+ expect(result.toolConfig).toEqual(undefined);
356
+
357
+ expect(result.toolWarnings).toMatchInlineSnapshot(`
358
+ [
359
+ {
360
+ "feature": "combination of function and provider-defined tools",
361
+ "type": "unsupported",
362
+ },
363
+ ]
364
+ `);
365
+ });
366
+
367
+ it('should handle latest modelId for provider-defined tools correctly', () => {
368
+ const result = prepareTools({
369
+ tools: [
370
+ {
371
+ type: 'provider',
372
+ id: 'google.google_search',
373
+ name: 'google_search',
374
+ args: {},
375
+ },
376
+ ],
377
+ modelId: 'gemini-flash-latest',
378
+ });
379
+ expect(result.tools).toEqual([{ googleSearch: {} }]);
380
+ expect(result.toolConfig).toBeUndefined();
381
+ expect(result.toolWarnings).toEqual([]);
382
+ });
383
+
384
+ it('should handle gemini-3 modelId for provider-defined tools correctly', () => {
385
+ const result = prepareTools({
386
+ tools: [
387
+ {
388
+ type: 'provider',
389
+ id: 'google.google_search',
390
+ name: 'google_search',
391
+ args: {},
392
+ },
393
+ ],
394
+ modelId: 'gemini-3-pro-preview',
395
+ });
396
+ expect(result.tools).toEqual([{ googleSearch: {} }]);
397
+ expect(result.toolConfig).toBeUndefined();
398
+ expect(result.toolWarnings).toEqual([]);
399
+ });
400
+
401
+ it('should handle code execution tool', () => {
402
+ const result = prepareTools({
403
+ tools: [
404
+ {
405
+ type: 'provider',
406
+ id: 'google.code_execution',
407
+ name: 'code_execution',
408
+ args: {},
409
+ },
410
+ ],
411
+ modelId: 'gemini-2.5-flash',
412
+ });
413
+ expect(result.tools).toEqual([{ codeExecution: {} }]);
414
+ expect(result.toolConfig).toBeUndefined();
415
+ expect(result.toolWarnings).toEqual([]);
416
+ });
417
+
418
+ it('should handle url context tool alone', () => {
419
+ const result = prepareTools({
420
+ tools: [
421
+ {
422
+ type: 'provider',
423
+ id: 'google.url_context',
424
+ name: 'url_context',
425
+ args: {},
426
+ },
427
+ ],
428
+ modelId: 'gemini-2.5-flash',
429
+ });
430
+ expect(result.tools).toEqual([{ urlContext: {} }]);
431
+ expect(result.toolConfig).toBeUndefined();
432
+ expect(result.toolWarnings).toEqual([]);
433
+ });
434
+
435
+ it('should handle google maps tool', () => {
436
+ const result = prepareTools({
437
+ tools: [
438
+ {
439
+ type: 'provider',
440
+ id: 'google.google_maps',
441
+ name: 'google_maps',
442
+ args: {},
443
+ },
444
+ ],
445
+ modelId: 'gemini-2.5-flash',
446
+ });
447
+ expect(result.tools).toEqual([{ googleMaps: {} }]);
448
+ expect(result.toolConfig).toBeUndefined();
449
+ expect(result.toolWarnings).toEqual([]);
450
+ });
451
+
452
+ it('should add warnings for google maps on unsupported models', () => {
453
+ const result = prepareTools({
454
+ tools: [
455
+ {
456
+ type: 'provider',
457
+ id: 'google.google_maps',
458
+ name: 'google_maps',
459
+ args: {},
460
+ },
461
+ ],
462
+ modelId: 'gemini-1.5-flash',
463
+ });
464
+ expect(result.tools).toBeUndefined();
465
+ expect(result.toolWarnings).toMatchInlineSnapshot(`
466
+ [
467
+ {
468
+ "details": "The Google Maps grounding tool is not supported with Gemini models other than Gemini 2 or newer.",
469
+ "feature": "provider-defined tool google.google_maps",
470
+ "type": "unsupported",
471
+ },
472
+ ]
473
+ `);
474
+ });