@ai-sdk/google 4.0.22 → 4.0.24

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.
@@ -91,6 +91,16 @@ Google language models can also be used in the `streamText` function
91
91
  and support structured data generation with [`Output`](/docs/reference/ai-sdk-core/output)
92
92
  (see [AI SDK Core](/docs/ai-sdk-core)).
93
93
 
94
+ <Note>
95
+ To remain forward-compatible, the provider treats unrecognized `gemini-*`
96
+ model IDs and `-latest` aliases like the newest supported Gemini generation.
97
+ Currently, this means Gemini 3 request behavior for provider-defined and mixed
98
+ tools, `thinkingLevel` reasoning, multimodal function responses, and thought
99
+ signatures. Known legacy Gemini model IDs keep their generation-specific
100
+ request behavior. This SDK default controls request serialization; the
101
+ selected model must still support each feature in the Google API.
102
+ </Note>
103
+
94
104
  Google also supports some model specific settings that are not part of the [standard call settings](/docs/ai-sdk-core/settings).
95
105
  You can pass them as an options argument:
96
106
 
@@ -163,7 +173,7 @@ The following optional provider options are available for Google models:
163
173
  Optional. Configuration for the model's thinking process. Only supported by specific [Google models](https://ai.google.dev/gemini-api/docs/thinking).
164
174
  - **thinkingLevel** _'minimal' | 'low' | 'medium' | 'high'_
165
175
 
166
- Optional. Controls the thinking depth for Gemini 3 models. Gemini 3.1 Pro supports 'low', 'medium', and 'high', Gemini 3 Pro supports 'low' and 'high', while Gemini 3 Flash supports all four levels: 'minimal', 'low', 'medium', and 'high'. Only supported by Gemini 3 models.
176
+ Optional. Controls the thinking depth for Gemini 3 and later models. Gemini 3.1 Pro supports 'low', 'medium', and 'high', Gemini 3 Pro supports 'low' and 'high', while Gemini 3 Flash supports all four levels: 'minimal', 'low', 'medium', and 'high'.
167
177
 
168
178
  - **thinkingBudget** _number_
169
179
 
@@ -171,7 +181,7 @@ The following optional provider options are available for Google models:
171
181
  For more information about the possible value ranges for each model see [Google thinking documentation](https://ai.google.dev/gemini-api/docs/thinking#set-budget).
172
182
 
173
183
  <Note>
174
- This option is for Gemini 2.5 models. Gemini 3 models should use
184
+ This option is for Gemini 2.5 models. Gemini 3 and later models should use
175
185
  `thinkingLevel` instead.
176
186
  </Note>
177
187
 
@@ -242,11 +252,11 @@ The following optional provider options are available for Google models:
242
252
 
243
253
  ### Thinking
244
254
 
245
- The Gemini 2.5 and Gemini 3 series models use an internal "thinking process" that significantly improves their reasoning and multi-step planning abilities, making them highly effective for complex tasks such as coding, advanced mathematics, and data analysis. For more information see [Google thinking documentation](https://ai.google.dev/gemini-api/docs/thinking).
255
+ The Gemini 2.5 and Gemini 3-and-later series models use an internal "thinking process" that significantly improves their reasoning and multi-step planning abilities, making them highly effective for complex tasks such as coding, advanced mathematics, and data analysis. For more information see [Google thinking documentation](https://ai.google.dev/gemini-api/docs/thinking).
246
256
 
247
- #### Gemini 3 Models
257
+ #### Gemini 3 and Later Models
248
258
 
249
- For Gemini 3 models, use the `thinkingLevel` parameter to control the depth of reasoning:
259
+ For Gemini 3 and later models, use the `thinkingLevel` parameter to control the depth of reasoning:
250
260
 
251
261
  ```ts
252
262
  import { google, GoogleLanguageModelOptions } from '@ai-sdk/google';
@@ -628,7 +638,7 @@ Enterprise Web Search provides the following benefits:
628
638
 
629
639
  ### File Search
630
640
 
631
- The [File Search tool](https://ai.google.dev/gemini-api/docs/file-search) lets Gemini retrieve context from your own documents that you have indexed in File Search stores. Only Gemini 2.5 and Gemini 3 models support this feature.
641
+ The [File Search tool](https://ai.google.dev/gemini-api/docs/file-search) lets Gemini retrieve context from your own documents that you have indexed in File Search stores. The provider enables this tool for Gemini 2.5 and Gemini 3-and-later model IDs, including unrecognized future Gemini IDs.
632
642
 
633
643
  ```ts highlight="9-13"
634
644
  import { google } from '@ai-sdk/google';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/google",
3
- "version": "4.0.22",
3
+ "version": "4.0.24",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -76,6 +76,7 @@ function appendToolResultParts(
76
76
  { type: 'content' }
77
77
  >['value'],
78
78
  toolCallId?: string,
79
+ includeFunctionCallIds = true,
79
80
  ): void {
80
81
  const functionResponseParts: GoogleFunctionResponsePart[] = [];
81
82
  const responseTextParts: string[] = [];
@@ -118,7 +119,9 @@ function appendToolResultParts(
118
119
 
119
120
  parts.push({
120
121
  functionResponse: {
121
- ...(toolCallId != null ? { id: toolCallId } : {}),
122
+ ...(includeFunctionCallIds && toolCallId != null
123
+ ? { id: toolCallId }
124
+ : {}),
122
125
  name: toolName,
123
126
  response: {
124
127
  name: toolName,
@@ -147,13 +150,16 @@ function appendLegacyToolResultParts(
147
150
  { type: 'content' }
148
151
  >['value'],
149
152
  toolCallId?: string,
153
+ includeFunctionCallIds = true,
150
154
  ): void {
151
155
  for (const contentPart of outputValue) {
152
156
  switch (contentPart.type) {
153
157
  case 'text':
154
158
  parts.push({
155
159
  functionResponse: {
156
- ...(toolCallId != null ? { id: toolCallId } : {}),
160
+ ...(includeFunctionCallIds && toolCallId != null
161
+ ? { id: toolCallId }
162
+ : {}),
157
163
  name: toolName,
158
164
  response: {
159
165
  name: toolName,
@@ -206,6 +212,7 @@ export function convertToGoogleMessages(
206
212
  */
207
213
  providerOptionsNames?: readonly string[];
208
214
  supportsFunctionResponseParts?: boolean;
215
+ includeFunctionCallIds?: boolean;
209
216
  },
210
217
  ): GooglePrompt {
211
218
  const systemInstructionParts: Array<{ text: string }> = [];
@@ -218,6 +225,7 @@ export function convertToGoogleMessages(
218
225
  const isVertexLike = !providerOptionsNames.includes('google');
219
226
  const supportsFunctionResponseParts =
220
227
  options?.supportsFunctionResponseParts ?? true;
228
+ const includeFunctionCallIds = options?.includeFunctionCallIds ?? true;
221
229
 
222
230
  let sentinelInjected = false;
223
231
  const missingSignatureToolNames: string[] = [];
@@ -497,7 +505,7 @@ export function convertToGoogleMessages(
497
505
 
498
506
  return {
499
507
  functionCall: {
500
- ...(part.toolCallId != null
508
+ ...(includeFunctionCallIds && part.toolCallId != null
501
509
  ? { id: part.toolCallId }
502
510
  : {}),
503
511
  name: part.toolName,
@@ -591,6 +599,7 @@ export function convertToGoogleMessages(
591
599
  part.toolName,
592
600
  output.value,
593
601
  part.toolCallId,
602
+ includeFunctionCallIds,
594
603
  );
595
604
  } else {
596
605
  appendLegacyToolResultParts(
@@ -598,12 +607,15 @@ export function convertToGoogleMessages(
598
607
  part.toolName,
599
608
  output.value,
600
609
  part.toolCallId,
610
+ includeFunctionCallIds,
601
611
  );
602
612
  }
603
613
  } else {
604
614
  parts.push({
605
615
  functionResponse: {
606
- ...(part.toolCallId != null ? { id: part.toolCallId } : {}),
616
+ ...(includeFunctionCallIds && part.toolCallId != null
617
+ ? { id: part.toolCallId }
618
+ : {}),
607
619
  name: part.toolName,
608
620
  response: {
609
621
  name: part.toolName,
@@ -46,6 +46,7 @@ import {
46
46
  type GoogleLanguageModelOptions,
47
47
  type GoogleModelId,
48
48
  } from './google-language-model-options';
49
+ import { getGoogleModelCapabilities } from './google-model-capabilities';
49
50
  import type { GoogleProviderMetadata } from './google-prompt';
50
51
  import { prepareTools } from './google-prepare-tools';
51
52
  import {
@@ -257,15 +258,15 @@ export class GoogleLanguageModel implements LanguageModelV4 {
257
258
  }
258
259
 
259
260
  const isGemmaModel = this.modelId.toLowerCase().startsWith('gemma-');
260
- const isGemini3Model = /^gemini-3[.-]/.test(this.modelId);
261
- const supportsFunctionResponseParts = isGemini3Model;
261
+ const { usesGemini3Features } = getGoogleModelCapabilities(this.modelId);
262
262
 
263
263
  const { contents, systemInstruction } = convertToGoogleMessages(prompt, {
264
264
  isGemmaModel,
265
- isGemini3Model,
265
+ isGemini3Model: usesGemini3Features,
266
266
  onWarning: warning => warnings.push(warning),
267
267
  providerOptionsNames,
268
- supportsFunctionResponseParts,
268
+ supportsFunctionResponseParts: usesGemini3Features,
269
+ includeFunctionCallIds: !isVertexProvider,
269
270
  });
270
271
 
271
272
  const {
@@ -1136,10 +1137,6 @@ export class GoogleLanguageModel implements LanguageModelV4 {
1136
1137
  }
1137
1138
  }
1138
1139
 
1139
- function isGemini3Model(modelId: string): boolean {
1140
- return /gemini-3[\.\-]/i.test(modelId) || /gemini-3$/i.test(modelId);
1141
- }
1142
-
1143
1140
  function getMaxOutputTokensForGemini25Model(): number {
1144
1141
  return 65536;
1145
1142
  }
@@ -1169,7 +1166,10 @@ function resolveThinkingConfig({
1169
1166
  return undefined;
1170
1167
  }
1171
1168
 
1172
- if (isGemini3Model(modelId) && !modelId.includes('gemini-3-pro-image')) {
1169
+ if (
1170
+ getGoogleModelCapabilities(modelId).usesGemini3Features &&
1171
+ !modelId.includes('gemini-3-pro-image')
1172
+ ) {
1173
1173
  return resolveGemini3ThinkingConfig({ reasoning, warnings });
1174
1174
  }
1175
1175
 
@@ -0,0 +1,44 @@
1
+ type GoogleModelCapabilities = {
2
+ supportsGemini2Tools: boolean;
3
+ supportsFileSearch: boolean;
4
+ usesGemini3Features: boolean;
5
+ };
6
+
7
+ const gemini1ModelPattern = /(^|\/)gemini-1(?:[.-]|$)/i;
8
+ const gemini2ModelPattern = /(^|\/)gemini-2(?:[.-]|$)/i;
9
+ const gemini25ModelPattern = /(^|\/)gemini-2\.5(?:[.-]|$)/i;
10
+ const geminiModelPattern = /(^|\/)gemini-/i;
11
+
12
+ function isKnownPreGemini2Model(modelId: string): boolean {
13
+ return (
14
+ gemini1ModelPattern.test(modelId) ||
15
+ /(^|\/)gemini-pro(?:-vision)?$/i.test(modelId) ||
16
+ /(^|\/)gemini-robotics-er-1\.5(?:[.-]|$)/i.test(modelId)
17
+ );
18
+ }
19
+
20
+ /**
21
+ * Classifies Gemini capabilities by excluding known older generations.
22
+ *
23
+ * Google model IDs are open-ended, so unrecognized Gemini IDs and aliases
24
+ * intentionally inherit the newest supported behavior. Add exceptions here
25
+ * only when a model is known to require a legacy request shape.
26
+ */
27
+ export function getGoogleModelCapabilities(
28
+ modelId: string,
29
+ ): GoogleModelCapabilities {
30
+ const isGeminiModel = geminiModelPattern.test(modelId);
31
+ const isGemini2Model = gemini2ModelPattern.test(modelId);
32
+ const isKnownPreGemini2 = isKnownPreGemini2Model(modelId);
33
+ const isKnownOlderModel = isKnownPreGemini2 || isGemini2Model;
34
+ const usesGemini3Features = isGeminiModel && !isKnownOlderModel;
35
+
36
+ return {
37
+ supportsGemini2Tools:
38
+ (isGeminiModel && !isKnownPreGemini2) ||
39
+ modelId.toLowerCase().includes('nano-banana'),
40
+ supportsFileSearch:
41
+ gemini25ModelPattern.test(modelId) || usesGemini3Features,
42
+ usesGemini3Features,
43
+ };
44
+ }
@@ -5,6 +5,7 @@ import {
5
5
  } from '@ai-sdk/provider';
6
6
  import { convertJSONSchemaToOpenAPISchema } from './convert-json-schema-to-openapi-schema';
7
7
  import type { GoogleModelId } from './google-language-model-options';
8
+ import { getGoogleModelCapabilities } from './google-model-capabilities';
8
9
 
9
10
  export function prepareTools({
10
11
  tools,
@@ -46,21 +47,8 @@ export function prepareTools({
46
47
 
47
48
  const toolWarnings: SharedV4Warning[] = [];
48
49
 
49
- const isLatest = (
50
- [
51
- 'gemini-flash-latest',
52
- 'gemini-flash-lite-latest',
53
- 'gemini-pro-latest',
54
- ] as const satisfies GoogleModelId[]
55
- ).some(id => id === modelId);
56
- const isGemini2orNewer =
57
- modelId.includes('gemini-2') ||
58
- modelId.includes('gemini-3') ||
59
- modelId.includes('nano-banana') ||
60
- isLatest;
61
- const isGemini3orNewer = modelId.includes('gemini-3');
62
- const supportsFileSearch =
63
- modelId.includes('gemini-2.5') || modelId.includes('gemini-3');
50
+ const { supportsGemini2Tools, supportsFileSearch, usesGemini3Features } =
51
+ getGoogleModelCapabilities(modelId);
64
52
 
65
53
  if (tools == null) {
66
54
  return { tools: undefined, toolConfig: undefined, toolWarnings };
@@ -70,7 +58,7 @@ export function prepareTools({
70
58
  const hasFunctionTools = tools.some(tool => tool.type === 'function');
71
59
  const hasProviderTools = tools.some(tool => tool.type === 'provider');
72
60
 
73
- if (hasFunctionTools && hasProviderTools && !isGemini3orNewer) {
61
+ if (hasFunctionTools && hasProviderTools && !usesGemini3Features) {
74
62
  toolWarnings.push({
75
63
  type: 'unsupported',
76
64
  feature: `combination of function and provider-defined tools`,
@@ -84,7 +72,7 @@ export function prepareTools({
84
72
  ProviderTools.forEach(tool => {
85
73
  switch (tool.id) {
86
74
  case 'google.google_search':
87
- if (isGemini2orNewer) {
75
+ if (supportsGemini2Tools) {
88
76
  googleTools.push({ googleSearch: { ...tool.args } });
89
77
  } else {
90
78
  toolWarnings.push({
@@ -95,7 +83,7 @@ export function prepareTools({
95
83
  }
96
84
  break;
97
85
  case 'google.enterprise_web_search':
98
- if (isGemini2orNewer) {
86
+ if (supportsGemini2Tools) {
99
87
  googleTools.push({ enterpriseWebSearch: {} });
100
88
  } else {
101
89
  toolWarnings.push({
@@ -106,7 +94,7 @@ export function prepareTools({
106
94
  }
107
95
  break;
108
96
  case 'google.url_context':
109
- if (isGemini2orNewer) {
97
+ if (supportsGemini2Tools) {
110
98
  googleTools.push({ urlContext: {} });
111
99
  } else {
112
100
  toolWarnings.push({
@@ -118,7 +106,7 @@ export function prepareTools({
118
106
  }
119
107
  break;
120
108
  case 'google.code_execution':
121
- if (isGemini2orNewer) {
109
+ if (supportsGemini2Tools) {
122
110
  googleTools.push({ codeExecution: {} });
123
111
  } else {
124
112
  toolWarnings.push({
@@ -142,7 +130,7 @@ export function prepareTools({
142
130
  }
143
131
  break;
144
132
  case 'google.vertex_rag_store':
145
- if (isGemini2orNewer) {
133
+ if (supportsGemini2Tools) {
146
134
  googleTools.push({
147
135
  retrieval: {
148
136
  vertex_rag_store: {
@@ -163,7 +151,7 @@ export function prepareTools({
163
151
  }
164
152
  break;
165
153
  case 'google.google_maps':
166
- if (isGemini2orNewer) {
154
+ if (supportsGemini2Tools) {
167
155
  googleTools.push({ googleMaps: {} });
168
156
  } else {
169
157
  toolWarnings.push({
@@ -183,7 +171,7 @@ export function prepareTools({
183
171
  }
184
172
  });
185
173
 
186
- if (hasFunctionTools && isGemini3orNewer && googleTools.length > 0) {
174
+ if (hasFunctionTools && usesGemini3Features && googleTools.length > 0) {
187
175
  const functionDeclarations: Array<{
188
176
  name: string;
189
177
  description: string;