@ai-sdk/google 3.0.54 → 3.0.56

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-sdk/google",
3
- "version": "3.0.54",
3
+ "version": "3.0.56",
4
4
  "license": "Apache-2.0",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
@@ -36,16 +36,16 @@
36
36
  }
37
37
  },
38
38
  "dependencies": {
39
- "@ai-sdk/provider": "3.0.8",
40
- "@ai-sdk/provider-utils": "4.0.21"
39
+ "@ai-sdk/provider-utils": "4.0.22",
40
+ "@ai-sdk/provider": "3.0.8"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/node": "20.17.24",
44
44
  "tsup": "^8",
45
45
  "typescript": "5.8.3",
46
46
  "zod": "3.25.76",
47
- "@vercel/ai-tsconfig": "0.0.0",
48
- "@ai-sdk/test-server": "1.0.3"
47
+ "@ai-sdk/test-server": "1.0.3",
48
+ "@vercel/ai-tsconfig": "0.0.0"
49
49
  },
50
50
  "peerDependencies": {
51
51
  "zod": "^3.25.76 || ^4.1.8"
@@ -290,6 +290,29 @@ export function convertToGoogleGenerativeAIMessages(
290
290
  }
291
291
 
292
292
  case 'tool-call': {
293
+ const serverToolCallId =
294
+ providerOpts?.serverToolCallId != null
295
+ ? String(providerOpts.serverToolCallId)
296
+ : undefined;
297
+ const serverToolType =
298
+ providerOpts?.serverToolType != null
299
+ ? String(providerOpts.serverToolType)
300
+ : undefined;
301
+
302
+ if (serverToolCallId && serverToolType) {
303
+ return {
304
+ toolCall: {
305
+ toolType: serverToolType,
306
+ args:
307
+ typeof part.input === 'string'
308
+ ? JSON.parse(part.input)
309
+ : part.input,
310
+ id: serverToolCallId,
311
+ },
312
+ thoughtSignature,
313
+ };
314
+ }
315
+
293
316
  return {
294
317
  functionCall: {
295
318
  name: part.toolName,
@@ -298,10 +321,36 @@ export function convertToGoogleGenerativeAIMessages(
298
321
  thoughtSignature,
299
322
  };
300
323
  }
324
+
325
+ case 'tool-result': {
326
+ const serverToolCallId =
327
+ providerOpts?.serverToolCallId != null
328
+ ? String(providerOpts.serverToolCallId)
329
+ : undefined;
330
+ const serverToolType =
331
+ providerOpts?.serverToolType != null
332
+ ? String(providerOpts.serverToolType)
333
+ : undefined;
334
+
335
+ if (serverToolCallId && serverToolType) {
336
+ return {
337
+ toolResponse: {
338
+ toolType: serverToolType,
339
+ response:
340
+ part.output.type === 'json' ? part.output.value : {},
341
+ id: serverToolCallId,
342
+ },
343
+ thoughtSignature,
344
+ };
345
+ }
346
+
347
+ return undefined;
348
+ }
301
349
  }
302
350
  })
303
351
  .filter(part => part !== undefined),
304
352
  });
353
+
305
354
  break;
306
355
  }
307
356
 
@@ -314,6 +363,44 @@ export function convertToGoogleGenerativeAIMessages(
314
363
  if (part.type === 'tool-approval-response') {
315
364
  continue;
316
365
  }
366
+
367
+ const partProviderOpts =
368
+ part.providerOptions?.[providerOptionsName] ??
369
+ (providerOptionsName !== 'google'
370
+ ? part.providerOptions?.google
371
+ : part.providerOptions?.vertex);
372
+ const serverToolCallId =
373
+ partProviderOpts?.serverToolCallId != null
374
+ ? String(partProviderOpts.serverToolCallId)
375
+ : undefined;
376
+ const serverToolType =
377
+ partProviderOpts?.serverToolType != null
378
+ ? String(partProviderOpts.serverToolType)
379
+ : undefined;
380
+
381
+ if (serverToolCallId && serverToolType) {
382
+ const serverThoughtSignature =
383
+ partProviderOpts?.thoughtSignature != null
384
+ ? String(partProviderOpts.thoughtSignature)
385
+ : undefined;
386
+
387
+ if (contents.length > 0) {
388
+ const lastContent = contents[contents.length - 1];
389
+ if (lastContent.role === 'model') {
390
+ lastContent.parts.push({
391
+ toolResponse: {
392
+ toolType: serverToolType,
393
+ response:
394
+ part.output.type === 'json' ? part.output.value : {},
395
+ id: serverToolCallId,
396
+ },
397
+ thoughtSignature: serverThoughtSignature,
398
+ });
399
+ continue;
400
+ }
401
+ }
402
+ }
403
+
317
404
  const output = part.output;
318
405
 
319
406
  if (output.type === 'content') {
@@ -7,6 +7,7 @@ import {
7
7
  LanguageModelV3Source,
8
8
  LanguageModelV3StreamPart,
9
9
  LanguageModelV3StreamResult,
10
+ JSONObject,
10
11
  SharedV3ProviderMetadata,
11
12
  SharedV3Warning,
12
13
  } from '@ai-sdk/provider';
@@ -250,6 +251,8 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
250
251
 
251
252
  // Associates a code execution result with its preceding call.
252
253
  let lastCodeExecutionToolCallId: string | undefined;
254
+ // Associates a server-side tool response with its preceding call (tool combination).
255
+ let lastServerToolCallId: string | undefined;
253
256
 
254
257
  // Build content array from all parts
255
258
  for (const part of parts) {
@@ -331,6 +334,57 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
331
334
  }
332
335
  : undefined,
333
336
  });
337
+ } else if ('toolCall' in part && part.toolCall) {
338
+ const toolCallId = part.toolCall.id ?? this.config.generateId();
339
+ lastServerToolCallId = toolCallId;
340
+ content.push({
341
+ type: 'tool-call',
342
+ toolCallId,
343
+ toolName: `server:${part.toolCall.toolType}`,
344
+ input: JSON.stringify(part.toolCall.args ?? {}),
345
+ providerExecuted: true,
346
+ dynamic: true,
347
+ providerMetadata: part.thoughtSignature
348
+ ? {
349
+ [providerOptionsName]: {
350
+ thoughtSignature: part.thoughtSignature,
351
+ serverToolCallId: toolCallId,
352
+ serverToolType: part.toolCall.toolType,
353
+ },
354
+ }
355
+ : {
356
+ [providerOptionsName]: {
357
+ serverToolCallId: toolCallId,
358
+ serverToolType: part.toolCall.toolType,
359
+ },
360
+ },
361
+ });
362
+ } else if ('toolResponse' in part && part.toolResponse) {
363
+ const responseToolCallId =
364
+ lastServerToolCallId ??
365
+ part.toolResponse.id ??
366
+ this.config.generateId();
367
+ content.push({
368
+ type: 'tool-result',
369
+ toolCallId: responseToolCallId,
370
+ toolName: `server:${part.toolResponse.toolType}`,
371
+ result: (part.toolResponse.response ?? {}) as JSONObject,
372
+ providerMetadata: part.thoughtSignature
373
+ ? {
374
+ [providerOptionsName]: {
375
+ thoughtSignature: part.thoughtSignature,
376
+ serverToolCallId: responseToolCallId,
377
+ serverToolType: part.toolResponse.toolType,
378
+ },
379
+ }
380
+ : {
381
+ [providerOptionsName]: {
382
+ serverToolCallId: responseToolCallId,
383
+ serverToolType: part.toolResponse.toolType,
384
+ },
385
+ },
386
+ });
387
+ lastServerToolCallId = undefined;
334
388
  }
335
389
  }
336
390
 
@@ -421,6 +475,8 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
421
475
  const emittedSourceUrls = new Set<string>();
422
476
  // Associates a code execution result with its preceding call.
423
477
  let lastCodeExecutionToolCallId: string | undefined;
478
+ // Associates a server-side tool response with its preceding call (tool combination).
479
+ let lastServerToolCallId: string | undefined;
424
480
 
425
481
  return {
426
482
  stream: response.pipeThrough(
@@ -631,6 +687,51 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
631
687
  data: part.inlineData.data,
632
688
  providerMetadata: fileMeta,
633
689
  });
690
+ } else if ('toolCall' in part && part.toolCall) {
691
+ const toolCallId = part.toolCall.id ?? generateId();
692
+ lastServerToolCallId = toolCallId;
693
+ const serverMeta = {
694
+ [providerOptionsName]: {
695
+ ...(part.thoughtSignature
696
+ ? { thoughtSignature: part.thoughtSignature }
697
+ : {}),
698
+ serverToolCallId: toolCallId,
699
+ serverToolType: part.toolCall.toolType,
700
+ },
701
+ };
702
+
703
+ controller.enqueue({
704
+ type: 'tool-call',
705
+ toolCallId,
706
+ toolName: `server:${part.toolCall.toolType}`,
707
+ input: JSON.stringify(part.toolCall.args ?? {}),
708
+ providerExecuted: true,
709
+ dynamic: true,
710
+ providerMetadata: serverMeta,
711
+ });
712
+ } else if ('toolResponse' in part && part.toolResponse) {
713
+ const responseToolCallId =
714
+ lastServerToolCallId ??
715
+ part.toolResponse.id ??
716
+ generateId();
717
+ const serverMeta = {
718
+ [providerOptionsName]: {
719
+ ...(part.thoughtSignature
720
+ ? { thoughtSignature: part.thoughtSignature }
721
+ : {}),
722
+ serverToolCallId: responseToolCallId,
723
+ serverToolType: part.toolResponse.toolType,
724
+ },
725
+ };
726
+
727
+ controller.enqueue({
728
+ type: 'tool-result',
729
+ toolCallId: responseToolCallId,
730
+ toolName: `server:${part.toolResponse.toolType}`,
731
+ result: (part.toolResponse.response ?? {}) as JSONObject,
732
+ providerMetadata: serverMeta,
733
+ });
734
+ lastServerToolCallId = undefined;
634
735
  }
635
736
  }
636
737
 
@@ -960,6 +1061,22 @@ const getContentSchema = () =>
960
1061
  thought: z.boolean().nullish(),
961
1062
  thoughtSignature: z.string().nullish(),
962
1063
  }),
1064
+ z.object({
1065
+ toolCall: z.object({
1066
+ toolType: z.string(),
1067
+ args: z.unknown().nullish(),
1068
+ id: z.string(),
1069
+ }),
1070
+ thoughtSignature: z.string().nullish(),
1071
+ }),
1072
+ z.object({
1073
+ toolResponse: z.object({
1074
+ toolType: z.string(),
1075
+ response: z.unknown().nullish(),
1076
+ id: z.string(),
1077
+ }),
1078
+ thoughtSignature: z.string().nullish(),
1079
+ }),
963
1080
  z.object({
964
1081
  executableCode: z
965
1082
  .object({
@@ -31,7 +31,23 @@ export type GoogleGenerativeAIContentPart =
31
31
  parts?: Array<GoogleGenerativeAIFunctionResponsePart>;
32
32
  };
33
33
  }
34
- | { fileData: { mimeType: string; fileUri: string } };
34
+ | { fileData: { mimeType: string; fileUri: string } }
35
+ | {
36
+ toolCall: {
37
+ toolType: string;
38
+ args?: unknown;
39
+ id: string;
40
+ };
41
+ thoughtSignature?: string;
42
+ }
43
+ | {
44
+ toolResponse: {
45
+ toolType: string;
46
+ response?: unknown;
47
+ id: string;
48
+ };
49
+ thoughtSignature?: string;
50
+ };
35
51
 
36
52
  export type GoogleGenerativeAIFunctionResponsePart = {
37
53
  inlineData: { mimeType: string; data: string };
@@ -30,10 +30,11 @@ export function prepareTools({
30
30
  toolConfig:
31
31
  | undefined
32
32
  | {
33
- functionCallingConfig: {
33
+ functionCallingConfig?: {
34
34
  mode: 'AUTO' | 'NONE' | 'ANY' | 'VALIDATED';
35
35
  allowedFunctionNames?: string[];
36
36
  };
37
+ includeServerSideToolInvocations?: boolean;
37
38
  };
38
39
  toolWarnings: SharedV3Warning[];
39
40
  } {
@@ -54,6 +55,7 @@ export function prepareTools({
54
55
  modelId.includes('gemini-3') ||
55
56
  modelId.includes('nano-banana') ||
56
57
  isLatest;
58
+ const isGemini3orNewer = modelId.includes('gemini-3');
57
59
  const supportsFileSearch =
58
60
  modelId.includes('gemini-2.5') || modelId.includes('gemini-3');
59
61
 
@@ -65,7 +67,7 @@ export function prepareTools({
65
67
  const hasFunctionTools = tools.some(tool => tool.type === 'function');
66
68
  const hasProviderTools = tools.some(tool => tool.type === 'provider');
67
69
 
68
- if (hasFunctionTools && hasProviderTools) {
70
+ if (hasFunctionTools && hasProviderTools && !isGemini3orNewer) {
69
71
  toolWarnings.push({
70
72
  type: 'unsupported',
71
73
  feature: `combination of function and provider-defined tools`,
@@ -178,6 +180,59 @@ export function prepareTools({
178
180
  }
179
181
  });
180
182
 
183
+ if (hasFunctionTools && isGemini3orNewer && googleTools.length > 0) {
184
+ const functionDeclarations: Array<{
185
+ name: string;
186
+ description: string;
187
+ parameters: unknown;
188
+ }> = [];
189
+ for (const tool of tools) {
190
+ if (tool.type === 'function') {
191
+ functionDeclarations.push({
192
+ name: tool.name,
193
+ description: tool.description ?? '',
194
+ parameters: convertJSONSchemaToOpenAPISchema(tool.inputSchema),
195
+ });
196
+ }
197
+ }
198
+
199
+ const combinedToolConfig: {
200
+ functionCallingConfig: {
201
+ mode: 'VALIDATED' | 'ANY' | 'NONE';
202
+ allowedFunctionNames?: string[];
203
+ };
204
+ includeServerSideToolInvocations: true;
205
+ } = {
206
+ functionCallingConfig: { mode: 'VALIDATED' },
207
+ includeServerSideToolInvocations: true,
208
+ };
209
+
210
+ if (toolChoice != null) {
211
+ switch (toolChoice.type) {
212
+ case 'auto':
213
+ break;
214
+ case 'none':
215
+ combinedToolConfig.functionCallingConfig = { mode: 'NONE' };
216
+ break;
217
+ case 'required':
218
+ combinedToolConfig.functionCallingConfig = { mode: 'ANY' };
219
+ break;
220
+ case 'tool':
221
+ combinedToolConfig.functionCallingConfig = {
222
+ mode: 'ANY',
223
+ allowedFunctionNames: [toolChoice.toolName],
224
+ };
225
+ break;
226
+ }
227
+ }
228
+
229
+ return {
230
+ tools: [...googleTools, { functionDeclarations }],
231
+ toolConfig: combinedToolConfig,
232
+ toolWarnings,
233
+ };
234
+ }
235
+
181
236
  return {
182
237
  tools: googleTools.length > 0 ? googleTools : undefined,
183
238
  toolConfig: undefined,