@ai-sdk/google 4.0.108 → 4.0.109

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.
@@ -97,7 +97,11 @@ You can use the following optional settings to customize the Google provider ins
97
97
  Use a different URL prefix for API calls, e.g. to use proxy servers.
98
98
  The default prefix is `https://generativelanguage.googleapis.com/v1beta`.
99
99
 
100
- - **apiKey** _string_
100
+ - **apiKey** _string | PromiseLike<string> | (() => string | PromiseLike<string>)_
101
+
102
+ Pass a function to resolve credentials before each request, including when keys
103
+ rotate. The function can return a string or a promise. Credential caching and
104
+ refresh are managed by the function.
101
105
 
102
106
  API key that is being sent using the `x-goog-api-key` header.
103
107
  It defaults to the `GOOGLE_GENERATIVE_AI_API_KEY` environment variable.
@@ -1265,6 +1269,8 @@ will still validate the response against the schema in `Output.object`.
1265
1269
 
1266
1270
  ### Model Capabilities
1267
1271
 
1272
+ {/* model-capabilities:google-language:start */}
1273
+
1268
1274
  | Model | Image Input | Object Generation | Tool Usage | Tool Streaming | Google Search | URL Context |
1269
1275
  | ------------------------------------- | ----------- | ----------------- | ---------- | -------------- | ------------- | ----------- |
1270
1276
  | `gemini-nano-banana-2.1` | <Check /> | <Cross /> | <Cross /> | <Cross /> | <Check /> | <Cross /> |
@@ -1286,6 +1292,8 @@ will still validate the response against the schema in `Output.object`.
1286
1292
  | `gemini-2.5-flash-lite-preview-06-17` | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
1287
1293
  | `gemini-2.0-flash` | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
1288
1294
 
1295
+ {/* model-capabilities:google-language:end */}
1296
+
1289
1297
  <Note>
1290
1298
  The table above lists popular models. Please see the [Google Generative AI
1291
1299
  docs](https://ai.google.dev/gemini-api/docs/models/) for a full list of
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/google",
3
- "version": "4.0.108",
3
+ "version": "4.0.109",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -35,8 +35,8 @@
35
35
  }
36
36
  },
37
37
  "dependencies": {
38
- "@ai-sdk/provider": "4.0.34",
39
- "@ai-sdk/provider-utils": "5.0.71"
38
+ "@ai-sdk/provider": "4.0.35",
39
+ "@ai-sdk/provider-utils": "5.0.72"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@ai-sdk/test-server": "2.0.4",
@@ -292,7 +292,6 @@ export class GoogleBatch implements BatchV4<GoogleBatchModelIds> {
292
292
  }
293
293
  }
294
294
 
295
- const headers = await this.getHeaders(options.headers);
296
295
  const createUrl = `${this.batchConfig.baseURL}/${getModelPath(
297
296
  modelId,
298
297
  )}:batchGenerateContent`;
@@ -300,7 +299,7 @@ export class GoogleBatch implements BatchV4<GoogleBatchModelIds> {
300
299
  if (fileParts == null) {
301
300
  const { value: operation } = await postJsonToApi({
302
301
  url: createUrl,
303
- headers,
302
+ headers: await this.getHeaders(options.headers),
304
303
  body: inlineBatchBody,
305
304
  failedResponseHandler: googleFailedResponseHandler,
306
305
  successfulResponseHandler: createJsonResponseHandler(
@@ -329,7 +328,7 @@ export class GoogleBatch implements BatchV4<GoogleBatchModelIds> {
329
328
 
330
329
  const { value: uploadUrl } = await postJsonToApi({
331
330
  url: `${this.getBaseOrigin()}/upload/v1beta/files`,
332
- headers: combineHeaders(headers, {
331
+ headers: combineHeaders(await this.getHeaders(options.headers), {
333
332
  'X-Goog-Upload-Protocol': 'resumable',
334
333
  'X-Goog-Upload-Command': 'start',
335
334
  'X-Goog-Upload-Header-Content-Length': String(inputFile.size),
@@ -370,7 +369,7 @@ export class GoogleBatch implements BatchV4<GoogleBatchModelIds> {
370
369
 
371
370
  const { value: operation } = await postJsonToApi({
372
371
  url: createUrl,
373
- headers,
372
+ headers: await this.getHeaders(options.headers),
374
373
  body: {
375
374
  batch: {
376
375
  displayName,
@@ -3,6 +3,7 @@ import {
3
3
  type EmbeddingModelV4,
4
4
  } from '@ai-sdk/provider';
5
5
  import {
6
+ type Resolvable,
6
7
  combineHeaders,
7
8
  createJsonResponseHandler,
8
9
  type EmbeddingModelProviderOptionsTransformer,
@@ -26,7 +27,7 @@ import {
26
27
  type GoogleEmbeddingConfig = {
27
28
  provider: string;
28
29
  baseURL: string;
29
- headers?: () => Record<string, string | undefined>;
30
+ headers?: Resolvable<Record<string, string | undefined>>;
30
31
  fetch?: FetchFunction;
31
32
  };
32
33
 
@@ -6,6 +6,8 @@ import {
6
6
  type SharedV4Warning,
7
7
  } from '@ai-sdk/provider';
8
8
  import {
9
+ resolve,
10
+ type Resolvable,
9
11
  combineHeaders,
10
12
  convertInlineFileDataToUint8Array,
11
13
  createJsonResponseHandler,
@@ -31,7 +33,7 @@ export type GoogleFilesUploadOptions = {
31
33
  interface GoogleFilesConfig {
32
34
  provider: string;
33
35
  baseURL: string;
34
- headers: () => Record<string, string | undefined>;
36
+ headers: Resolvable<Record<string, string | undefined>>;
35
37
  fetch?: FetchFunction;
36
38
  }
37
39
 
@@ -53,10 +55,8 @@ export class GoogleFiles implements FilesV4 {
53
55
  schema: googleFilesUploadOptionsSchema,
54
56
  })) as GoogleFilesUploadOptions | undefined;
55
57
 
56
- const resolvedHeaders = combineHeaders(
57
- this.config.headers(),
58
- options.headers,
59
- );
58
+ const getHeaders = async () =>
59
+ combineHeaders(await resolve(this.config.headers), options.headers);
60
60
  const fetchFn = this.config.fetch ?? globalThis.fetch;
61
61
 
62
62
  const warnings: Array<SharedV4Warning> = [];
@@ -74,7 +74,7 @@ export class GoogleFiles implements FilesV4 {
74
74
  const initResponse = await fetchFn(`${baseOrigin}/upload/v1beta/files`, {
75
75
  method: 'POST',
76
76
  headers: {
77
- ...resolvedHeaders,
77
+ ...(await getHeaders()),
78
78
  'X-Goog-Upload-Protocol': 'resumable',
79
79
  'X-Goog-Upload-Command': 'start',
80
80
  'X-Goog-Upload-Header-Content-Length': String(fileBytes.length),
@@ -152,7 +152,7 @@ export class GoogleFiles implements FilesV4 {
152
152
  const { value: fileStatus } = await getFromApi({
153
153
  url: `${this.config.baseURL}/${filePath}`,
154
154
  validateUrl: false,
155
- headers: combineHeaders(resolvedHeaders),
155
+ headers: await getHeaders(),
156
156
  successfulResponseHandler: createJsonResponseHandler(
157
157
  googleFileResponseSchema,
158
158
  ),
@@ -58,7 +58,7 @@ import {
58
58
  type GoogleLanguageModelOptions,
59
59
  type GoogleModelId,
60
60
  } from './google-language-model-options';
61
- import { getGoogleModelCapabilities } from './google-model-capabilities';
61
+ import { getGoogleLanguageModelEntry } from './google-model-catalog';
62
62
  import type { GoogleProviderMetadata } from './google-prompt';
63
63
  import { prepareTools } from './google-prepare-tools';
64
64
  import {
@@ -74,8 +74,6 @@ const configurableSafetySettingCategories = [
74
74
  'HARM_CATEGORY_SEXUALLY_EXPLICIT',
75
75
  ] as const;
76
76
 
77
- const gemini25ModelPattern = /(^|[/.])gemini-2\.5(?:[.-]|$)/i;
78
-
79
77
  const googleCloudStorageFunctionResponseUrls = {
80
78
  'image/png': [/^gs:\/\/.*$/],
81
79
  'image/jpeg': [/^gs:\/\/.*$/],
@@ -296,9 +294,12 @@ export class GoogleLanguageModel implements LanguageModelV4 {
296
294
  }
297
295
  }
298
296
 
299
- const isGemmaModel = modelId.toLowerCase().startsWith('gemma-');
297
+ const entry = getGoogleLanguageModelEntry(modelId);
298
+ const isGemmaModel =
299
+ entry.requestConfig.language.systemInstruction === 'user';
300
300
  const isGemini25DeveloperApiModel =
301
- !isVertexProvider && gemini25ModelPattern.test(modelId);
301
+ !isVertexProvider &&
302
+ entry.capabilities.language.sampling.penalties !== true;
302
303
 
303
304
  if (isGemini25DeveloperApiModel && frequencyPenalty != null) {
304
305
  warnings.push({
@@ -313,7 +314,8 @@ export class GoogleLanguageModel implements LanguageModelV4 {
313
314
  });
314
315
  }
315
316
 
316
- const { usesGemini3Features } = getGoogleModelCapabilities(modelId);
317
+ const usesGemini3Features =
318
+ entry.capabilities.language.tools.functionResponseParts;
317
319
  const supportedFunctionResponseUrls =
318
320
  usesGemini3Features &&
319
321
  config.downloadToolResultFiles?.supportsGoogleCloudStorageUrls
@@ -466,6 +468,8 @@ export class GoogleLanguageModel implements LanguageModelV4 {
466
468
  { isStreaming = false }: { isStreaming?: boolean } = {},
467
469
  ) {
468
470
  const { responseFormat, tools, toolChoice } = options;
471
+ const entry = getGoogleLanguageModelEntry(this.modelId);
472
+ const { structuredOutput } = entry.capabilities.language;
469
473
  let jsonResponseTool: LanguageModelV4FunctionTool | undefined;
470
474
  // Gemini models before Gemini 3 reject JSON response formatting together
471
475
  // with function tools. Gemini 3 supports the combination only with automatic
@@ -474,8 +478,8 @@ export class GoogleLanguageModel implements LanguageModelV4 {
474
478
  responseFormat?.type === 'json' &&
475
479
  responseFormat.schema != null &&
476
480
  tools?.some(tool => tool.type === 'function') &&
477
- /(^|\/)gemini-/i.test(this.modelId) &&
478
- (!getGoogleModelCapabilities(this.modelId).usesGemini3Features ||
481
+ entry.requestConfig.language.jsonResponseTool &&
482
+ (!structuredOutput.withFunctionTools ||
479
483
  toolChoice?.type === 'required' ||
480
484
  toolChoice?.type === 'tool')
481
485
  ) {
@@ -754,18 +758,16 @@ export class GoogleLanguageModel implements LanguageModelV4 {
754
758
  jsonResponseToolName,
755
759
  } = await this.getArgs(options);
756
760
 
757
- const mergedHeaders = combineHeaders(
758
- this.config.headers ? await resolve(this.config.headers) : undefined,
759
- options.headers,
760
- extraHeaders,
761
- );
762
-
763
- const generateContent = (body: typeof args) =>
761
+ const generateContent = async (body: typeof args) =>
764
762
  postJsonToApi({
765
763
  url: `${this.config.baseURL}/${getModelPath(
766
764
  this.modelId,
767
765
  )}:generateContent`,
768
- headers: mergedHeaders,
766
+ headers: combineHeaders(
767
+ await resolve(this.config.headers),
768
+ options.headers,
769
+ extraHeaders,
770
+ ),
769
771
  body,
770
772
  failedResponseHandler: googleFailedResponseHandler,
771
773
  successfulResponseHandler: createJsonResponseHandler(responseSchema),
@@ -875,18 +877,16 @@ export class GoogleLanguageModel implements LanguageModelV4 {
875
877
  providerOptionsNames.map(name => [name, payload]),
876
878
  ) as SharedV4ProviderMetadata;
877
879
 
878
- const headers = combineHeaders(
879
- this.config.headers ? await resolve(this.config.headers) : undefined,
880
- options.headers,
881
- extraHeaders,
882
- );
883
-
884
- const streamGenerateContent = (body: typeof args) =>
880
+ const streamGenerateContent = async (body: typeof args) =>
885
881
  postJsonToApi({
886
882
  url: `${this.config.baseURL}/${getModelPath(
887
883
  this.modelId,
888
884
  )}:streamGenerateContent?alt=sse`,
889
- headers,
885
+ headers: combineHeaders(
886
+ await resolve(this.config.headers),
887
+ options.headers,
888
+ extraHeaders,
889
+ ),
890
890
  body,
891
891
  failedResponseHandler: googleFailedResponseHandler,
892
892
  successfulResponseHandler:
@@ -1571,27 +1571,6 @@ export class GoogleLanguageModel implements LanguageModelV4 {
1571
1571
  }
1572
1572
  }
1573
1573
 
1574
- function getMaxOutputTokensForGemini25Model(): number {
1575
- return 65536;
1576
- }
1577
-
1578
- function getMaxThinkingTokensForGemini25Model(modelId: string): number {
1579
- const id = modelId.toLowerCase();
1580
- if (id.includes('2.5-pro') || id.includes('gemini-3-pro-image')) {
1581
- return 32768;
1582
- }
1583
- return 24576;
1584
- }
1585
-
1586
- /**
1587
- * Gemini 2.5 Pro cannot disable thinking and requires a thinking budget of at
1588
- * least 128 tokens. Other Gemini 2.5 models can disable thinking with a budget
1589
- * of 0.
1590
- */
1591
- function getMinThinkingTokensForGemini25Model(modelId: string): number {
1592
- return modelId.toLowerCase().includes('2.5-pro') ? 128 : 0;
1593
- }
1594
-
1595
1574
  type GoogleThinkingConfig = NonNullable<
1596
1575
  InferSchema<typeof googleLanguageModelOptions>['thinkingConfig']
1597
1576
  >;
@@ -1610,8 +1589,8 @@ function resolveThinkingConfig({
1610
1589
  }
1611
1590
 
1612
1591
  if (
1613
- getGoogleModelCapabilities(modelId).usesGemini3Features &&
1614
- !modelId.includes('gemini-3-pro-image')
1592
+ getGoogleLanguageModelEntry(modelId).requestConfig.language
1593
+ .thinkingFormat === 'level'
1615
1594
  ) {
1616
1595
  return resolveGemini3ThinkingConfig({ reasoning, modelId, warnings });
1617
1596
  }
@@ -1631,7 +1610,9 @@ function resolveGemini3ThinkingConfig({
1631
1610
  modelId: string;
1632
1611
  warnings: SharedV4Warning[];
1633
1612
  }): Pick<GoogleThinkingConfig, 'thinkingLevel'> | undefined {
1634
- const minimumThinkingLevel = getMinimumThinkingLevelForGemini3Model(modelId);
1613
+ const minimumThinkingLevel =
1614
+ getGoogleLanguageModelEntry(modelId).capabilities.language.reasoning
1615
+ .efforts?.[0];
1635
1616
 
1636
1617
  // It's not possible to fully disable thinking with Gemini 3, so reasoning
1637
1618
  // 'none' maps to the lowest supported thinking level.
@@ -1655,41 +1636,6 @@ function resolveGemini3ThinkingConfig({
1655
1636
  return { thinkingLevel };
1656
1637
  }
1657
1638
 
1658
- function getMinimumThinkingLevelForGemini3Model(
1659
- modelId: string,
1660
- ): 'minimal' | 'low' {
1661
- const modelName = /(?:^|[/.])(gemini-[^/]*)$/i
1662
- .exec(modelId)?.[1]
1663
- .toLowerCase();
1664
-
1665
- if (modelName === 'gemini-flash-latest') {
1666
- return 'low';
1667
- }
1668
-
1669
- const proVersionMatch = /^gemini-(\d+)(?:\.\d+)?-pro(?:$|-)/.exec(
1670
- modelName ?? '',
1671
- );
1672
-
1673
- if (Number(proVersionMatch?.[1]) >= 3) {
1674
- return 'low';
1675
- }
1676
-
1677
- const versionMatch = /^gemini-(\d+)\.(\d+)-flash(?:$|-(?!lite(?:-|$)))/.exec(
1678
- modelName ?? '',
1679
- );
1680
-
1681
- if (versionMatch == null) {
1682
- return 'minimal';
1683
- }
1684
-
1685
- const majorVersion = Number(versionMatch[1]);
1686
- const minorVersion = Number(versionMatch[2]);
1687
-
1688
- return majorVersion > 3 || (majorVersion === 3 && minorVersion >= 7)
1689
- ? 'low'
1690
- : 'minimal';
1691
- }
1692
-
1693
1639
  function resolveGemini25ThinkingConfig({
1694
1640
  reasoning,
1695
1641
  modelId,
@@ -1702,7 +1648,8 @@ function resolveGemini25ThinkingConfig({
1702
1648
  modelId: string;
1703
1649
  warnings: SharedV4Warning[];
1704
1650
  }): Pick<GoogleThinkingConfig, 'thinkingBudget'> {
1705
- const minThinkingBudget = getMinThinkingTokensForGemini25Model(modelId);
1651
+ const entry = getGoogleLanguageModelEntry(modelId);
1652
+ const minThinkingBudget = entry.capabilities.language.reasoning.minBudget;
1706
1653
 
1707
1654
  if (reasoning === 'none' && minThinkingBudget === 0) {
1708
1655
  return { thinkingBudget: 0 };
@@ -1710,8 +1657,8 @@ function resolveGemini25ThinkingConfig({
1710
1657
 
1711
1658
  const thinkingBudget = mapReasoningToProviderBudget({
1712
1659
  reasoning,
1713
- maxOutputTokens: getMaxOutputTokensForGemini25Model(),
1714
- maxReasoningBudget: getMaxThinkingTokensForGemini25Model(modelId),
1660
+ maxOutputTokens: entry.requestConfig.language.reasoningMaxOutputTokens,
1661
+ maxReasoningBudget: entry.capabilities.language.reasoning.maxBudget,
1715
1662
  minReasoningBudget: minThinkingBudget,
1716
1663
  warnings,
1717
1664
  });
@@ -1,44 +1,11 @@
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;
1
+ import { getGoogleLanguageModelEntry } from './google-model-catalog';
35
2
 
3
+ /** Compatibility projection of the resolved catalog for existing request code. */
4
+ export function getGoogleModelCapabilities(modelId: string) {
5
+ const { tools } = getGoogleLanguageModelEntry(modelId).capabilities.language;
36
6
  return {
37
- supportsGemini2Tools:
38
- (isGeminiModel && !isKnownPreGemini2) ||
39
- modelId.toLowerCase().includes('nano-banana'),
40
- supportsFileSearch:
41
- gemini25ModelPattern.test(modelId) || usesGemini3Features,
42
- usesGemini3Features,
7
+ supportsGemini2Tools: tools.gemini2,
8
+ supportsFileSearch: tools.fileSearch,
9
+ usesGemini3Features: tools.mixed,
43
10
  };
44
11
  }