@ai-sdk/google 3.0.53 → 3.0.55

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.
@@ -245,6 +245,12 @@ The following optional provider options are available for Google Generative AI m
245
245
  Optional. Defines labels used in billing reports. Available on Vertex AI only.
246
246
  See [Google Cloud labels documentation](https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/add-labels-to-api-calls).
247
247
 
248
+ - **serviceTier** _'SERVICE_TIER_STANDARD' | 'SERVICE_TIER_FLEX' | 'SERVICE_TIER_PRIORITY'_
249
+
250
+ Optional. The service tier to use for the request.
251
+ Set to 'SERVICE_TIER_FLEX' for 50% cheaper processing at the cost of increased latency.
252
+ Set to 'SERVICE_TIER_PRIORITY' for ultra-low latency at a 75-100% price premium over 'SERVICE_TIER_STANDARD'.
253
+
248
254
  - **threshold** _string_
249
255
 
250
256
  Optional. Standalone threshold setting that can be used independently of `safetySettings`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/google",
3
- "version": "3.0.53",
3
+ "version": "3.0.55",
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-utils": "4.0.21",
40
- "@ai-sdk/provider": "3.0.8"
39
+ "@ai-sdk/provider": "3.0.8",
40
+ "@ai-sdk/provider-utils": "4.0.21"
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
- "@ai-sdk/test-server": "1.0.3",
48
- "@vercel/ai-tsconfig": "0.0.0"
47
+ "@vercel/ai-tsconfig": "0.0.0",
48
+ "@ai-sdk/test-server": "1.0.3"
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';
@@ -207,6 +208,7 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
207
208
  : googleToolConfig,
208
209
  cachedContent: googleOptions?.cachedContent,
209
210
  labels: googleOptions?.labels,
211
+ serviceTier: googleOptions?.serviceTier,
210
212
  },
211
213
  warnings: [...warnings, ...toolWarnings],
212
214
  providerOptionsName,
@@ -249,6 +251,8 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
249
251
 
250
252
  // Associates a code execution result with its preceding call.
251
253
  let lastCodeExecutionToolCallId: string | undefined;
254
+ // Associates a server-side tool response with its preceding call (tool combination).
255
+ let lastServerToolCallId: string | undefined;
252
256
 
253
257
  // Build content array from all parts
254
258
  for (const part of parts) {
@@ -330,6 +334,57 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
330
334
  }
331
335
  : undefined,
332
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;
333
388
  }
334
389
  }
335
390
 
@@ -364,6 +419,7 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
364
419
  safetyRatings: candidate.safetyRatings ?? null,
365
420
  usageMetadata: usageMetadata ?? null,
366
421
  finishMessage: candidate.finishMessage ?? null,
422
+ serviceTier: response.serviceTier ?? null,
367
423
  } satisfies GoogleGenerativeAIProviderMetadata,
368
424
  },
369
425
  request: { body: args },
@@ -405,6 +461,7 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
405
461
  let providerMetadata: SharedV3ProviderMetadata | undefined = undefined;
406
462
  let lastGroundingMetadata: GroundingMetadataSchema | null = null;
407
463
  let lastUrlContextMetadata: UrlContextMetadataSchema | null = null;
464
+ let serviceTier: string | null = null;
408
465
 
409
466
  const generateId = this.config.generateId;
410
467
  let hasToolCalls = false;
@@ -418,6 +475,8 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
418
475
  const emittedSourceUrls = new Set<string>();
419
476
  // Associates a code execution result with its preceding call.
420
477
  let lastCodeExecutionToolCallId: string | undefined;
478
+ // Associates a server-side tool response with its preceding call (tool combination).
479
+ let lastServerToolCallId: string | undefined;
421
480
 
422
481
  return {
423
482
  stream: response.pipeThrough(
@@ -447,6 +506,10 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
447
506
  usage = usageMetadata;
448
507
  }
449
508
 
509
+ if (value.serviceTier != null) {
510
+ serviceTier = value.serviceTier;
511
+ }
512
+
450
513
  const candidate = value.candidates?.[0];
451
514
 
452
515
  // sometimes the API returns an empty candidates array
@@ -624,6 +687,51 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
624
687
  data: part.inlineData.data,
625
688
  providerMetadata: fileMeta,
626
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;
627
735
  }
628
736
  }
629
737
 
@@ -685,6 +793,7 @@ export class GoogleGenerativeAILanguageModel implements LanguageModelV3 {
685
793
  safetyRatings: candidate.safetyRatings ?? null,
686
794
  usageMetadata: usageMetadata ?? null,
687
795
  finishMessage: candidate.finishMessage ?? null,
796
+ serviceTier,
688
797
  } satisfies GoogleGenerativeAIProviderMetadata,
689
798
  };
690
799
  }
@@ -952,6 +1061,22 @@ const getContentSchema = () =>
952
1061
  thought: z.boolean().nullish(),
953
1062
  thoughtSignature: z.string().nullish(),
954
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
+ }),
955
1080
  z.object({
956
1081
  executableCode: z
957
1082
  .object({
@@ -1028,6 +1153,7 @@ const responseSchema = lazySchema(() =>
1028
1153
  safetyRatings: z.array(getSafetyRatingSchema()).nullish(),
1029
1154
  })
1030
1155
  .nullish(),
1156
+ serviceTier: z.string().nullish(),
1031
1157
  }),
1032
1158
  ),
1033
1159
  );
@@ -1083,6 +1209,7 @@ const chunkSchema = lazySchema(() =>
1083
1209
  safetyRatings: z.array(getSafetyRatingSchema()).nullish(),
1084
1210
  })
1085
1211
  .nullish(),
1212
+ serviceTier: z.string().nullish(),
1086
1213
  }),
1087
1214
  ),
1088
1215
  );
@@ -188,6 +188,17 @@ export const googleLanguageModelOptions = lazySchema(() =>
188
188
  .optional(),
189
189
  })
190
190
  .optional(),
191
+
192
+ /**
193
+ * Optional. The service tier to use for the request.
194
+ */
195
+ serviceTier: z
196
+ .enum([
197
+ 'SERVICE_TIER_STANDARD',
198
+ 'SERVICE_TIER_FLEX',
199
+ 'SERVICE_TIER_PRIORITY',
200
+ ])
201
+ .optional(),
191
202
  }),
192
203
  ),
193
204
  );
@@ -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 };
@@ -54,4 +70,5 @@ export interface GoogleGenerativeAIProviderMetadata {
54
70
  safetyRatings: GoogleGenerativeAISafetyRating[] | null;
55
71
  usageMetadata: GoogleGenerativeAIUsageMetadata | null;
56
72
  finishMessage: string | null;
73
+ serviceTier: string | null;
57
74
  }
@@ -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,