@ai-sdk/google 4.0.87 → 4.0.89

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.
@@ -34,6 +34,15 @@ const google = createGoogle({
34
34
 
35
35
  You can use the following optional settings to customize the Google provider instance:
36
36
 
37
+ - **batchResultDownloads** _object_
38
+
39
+ Settings for downloading file-based JSON Lines batch results.
40
+ - **maxLineBytes** _number_
41
+
42
+ Maximum UTF-8 bytes per row, excluding the LF delimiter. Defaults to 64 MiB
43
+ (67,108,864 bytes). Must be a positive safe integer. Oversized rows throw a
44
+ `DownloadError` and cancel the download.
45
+
37
46
  - **baseURL** _string_
38
47
 
39
48
  Use a different URL prefix for API calls, e.g. to use proxy servers.
@@ -643,7 +652,7 @@ The [File Search tool](https://ai.google.dev/gemini-api/docs/file-search) lets G
643
652
  import { google } from '@ai-sdk/google';
644
653
  import { generateText } from 'ai';
645
654
 
646
- const { text, sources } = await generateText({
655
+ const { text, sources, providerMetadata } = await generateText({
647
656
  model: google('gemini-3.8-flash'),
648
657
  tools: {
649
658
  file_search: google.tools.fileSearch({
@@ -660,6 +669,9 @@ const { text, sources } = await generateText({
660
669
 
661
670
  File Search responses include citations via the normal `sources` field and expose raw [grounding metadata](#google-search) in `providerMetadata.google.groundingMetadata`.
662
671
 
672
+ Custom metadata attached to the indexed documents is available in
673
+ `groundingMetadata.groundingChunks[].retrievedContext.customMetadata`.
674
+
663
675
  ### URL Context
664
676
 
665
677
  Google provides a provider-defined URL context tool.
@@ -1046,6 +1058,7 @@ will still validate the response against the schema in `Output.object`.
1046
1058
 
1047
1059
  | Model | Image Input | Object Generation | Tool Usage | Tool Streaming | Google Search | URL Context |
1048
1060
  | ------------------------------------- | ----------- | ----------------- | ---------- | -------------- | ------------- | ----------- |
1061
+ | `gemini-nano-banana-2.1` | <Check /> | <Cross /> | <Cross /> | <Cross /> | <Check /> | <Cross /> |
1049
1062
  | `gemini-3.8-flash` | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
1050
1063
  | `gemini-3.7-flash` | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
1051
1064
  | `gemini-3.6-flash` | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
@@ -2232,11 +2245,12 @@ console.log(result.providerMetadata?.google?.groundingMetadata);
2232
2245
 
2233
2246
  #### Gemini Image Model Capabilities
2234
2247
 
2235
- | Model | Image Generation | Image Editing | Aspect Ratios |
2236
- | -------------------------------- | ---------------- | ------------- | --------------------------------------------------- |
2237
- | `gemini-2.5-flash-image` | <Check /> | <Check /> | 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9 |
2238
- | `gemini-3-pro-image-preview` | <Check /> | <Check /> | 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9 |
2239
- | `gemini-3.1-flash-image-preview` | <Check /> | <Check /> | 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9 |
2248
+ | Model | Image Generation | Image Editing | Aspect Ratios |
2249
+ | -------------------------------- | ---------------- | ------------- | ----------------------------------------------------------------------- |
2250
+ | `gemini-2.5-flash-image` | <Check /> | <Check /> | 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9 |
2251
+ | `gemini-3-pro-image-preview` | <Check /> | <Check /> | 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9 |
2252
+ | `gemini-3.1-flash-image-preview` | <Check /> | <Check /> | 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9 |
2253
+ | `gemini-nano-banana-2.1` | <Check /> | <Check /> | 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9, 1:8, 8:1, 1:4, 4:1 |
2240
2254
 
2241
2255
  <Note>
2242
2256
  `gemini-3-pro-image-preview` supports additional features including up to 14
@@ -2393,18 +2407,18 @@ Older models continue to use labelled transcripts, such as
2393
2407
  | `gemini-3.8-flash-tts` | <Check /> | <Check /> |
2394
2408
  | `gemini-3.8-flash-lite-tts` | <Check /> | <Check /> |
2395
2409
 
2396
- ## Evaluation Models
2410
+ ## Decision Models
2397
2411
 
2398
- Create an experimental evaluation model with `google.evaluationModel(modelId)`.
2412
+ Create an experimental decision model with `google.decisionModel(modelId)`.
2399
2413
  It uses Gemini structured output for Choice, Score, and Boolean questions. Choose a model
2400
2414
  that supports structured output, such as `gemini-3.5-flash-lite`.
2401
2415
 
2402
2416
  ```ts
2403
2417
  import { google } from '@ai-sdk/google';
2404
- import { experimental_evaluate } from 'ai';
2418
+ import { experimental_decide } from 'ai';
2405
2419
 
2406
- const { answers } = await experimental_evaluate({
2407
- model: google.evaluationModel('gemini-3.5-flash-lite'),
2420
+ const { answers } = await experimental_decide({
2421
+ model: google.decisionModel('gemini-3.5-flash-lite'),
2408
2422
  state: 'I was charged twice.',
2409
2423
  questions: {
2410
2424
  requestsRefund: {
@@ -2436,10 +2450,10 @@ and rejects safety-blocked, truncated, or invalid output. It preserves usage
2436
2450
  Choice and Score answers do not include probability distributions. Boolean
2437
2451
  answers contain prompted estimates of P(true), validated to be finite and in
2438
2452
  `[0, 1]`. These estimates are not guaranteed to be calibrated. Apply thresholds
2439
- in application code, for example `answers.requestsRefund.probability >= 0.5`. See [Evaluation](/docs/ai-sdk-core/evaluation).
2453
+ in application code, for example `answers.requestsRefund.probability >= 0.5`. See [Decisions](/docs/ai-sdk-core/decisions).
2440
2454
 
2441
- Evaluation models can also be accessed through `customProvider` aliases or
2442
- `createProviderRegistry().evaluationModel('provider:model')`. Direct string IDs
2455
+ Decision models can also be accessed through `customProvider` aliases or
2456
+ `createProviderRegistry().decisionModel('provider:model')`. Direct string IDs
2443
2457
  use Gateway by default, or an explicitly configured default provider with an
2444
- `evaluationModel` method. See
2445
- [model aliases and registries](/docs/ai-sdk-core/evaluation#model-aliases-and-registries).
2458
+ `decisionModel` method. See
2459
+ [model aliases and registries](/docs/ai-sdk-core/decisions#model-aliases-and-registries).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/google",
3
- "version": "4.0.87",
3
+ "version": "4.0.89",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -35,15 +35,15 @@
35
35
  }
36
36
  },
37
37
  "dependencies": {
38
- "@ai-sdk/provider": "4.0.21",
39
- "@ai-sdk/provider-utils": "5.0.53"
38
+ "@ai-sdk/provider": "4.0.23",
39
+ "@ai-sdk/provider-utils": "5.0.55"
40
40
  },
41
41
  "devDependencies": {
42
- "@ai-sdk/test-server": "2.0.3",
42
+ "@ai-sdk/test-server": "2.0.4",
43
43
  "@types/node": "22.19.19",
44
44
  "@vercel/ai-tsconfig": "0.0.0",
45
45
  "tsdown": "^0.23.0",
46
- "typescript": "5.8.3",
46
+ "typescript": "6.0.3",
47
47
  "zod": "3.25.76"
48
48
  },
49
49
  "peerDependencies": {
@@ -185,16 +185,19 @@ export class GoogleBatch implements BatchV4<GoogleBatchModelIds> {
185
185
  readonly supportedUrls: Record<string, RegExp[]>;
186
186
  private readonly batchConfig: GoogleLanguageModelConfig;
187
187
  private readonly batchGenerateId: () => string;
188
+ private readonly maxLineBytes?: number;
188
189
 
189
190
  constructor(options: {
190
191
  provider: string;
191
192
  config: GoogleLanguageModelConfig;
192
193
  supportedUrls: Record<string, RegExp[]>;
194
+ maxLineBytes?: number;
193
195
  }) {
194
196
  this.provider = options.provider;
195
197
  this.batchConfig = options.config;
196
198
  this.supportedUrls = options.supportedUrls;
197
199
  this.batchGenerateId = options.config.generateId ?? generateId;
200
+ this.maxLineBytes = options.maxLineBytes;
198
201
  }
199
202
 
200
203
  async doStartBatch(
@@ -511,6 +514,7 @@ export class GoogleBatch implements BatchV4<GoogleBatchModelIds> {
511
514
  failedResponseHandler: googleFailedResponseHandler,
512
515
  successfulResponseHandler: createJsonLinesResponseHandler(
513
516
  googleBatchResultLineSchema,
517
+ { maxLineBytes: this.maxLineBytes },
514
518
  ),
515
519
  abortSignal: options.abortSignal,
516
520
  fetch: this.batchConfig.fetch,
@@ -36,6 +36,7 @@ const googleImageModelsWithFileInputSupport = new Set<string>([
36
36
  'gemini-2.5-flash-image',
37
37
  'gemini-3-pro-image-preview',
38
38
  'gemini-3.1-flash-image-preview',
39
+ 'gemini-nano-banana-2.1',
39
40
  ]);
40
41
 
41
42
  export class GoogleImageModel implements ImageModelV4 {
@@ -3,6 +3,7 @@ export type GoogleImageModelId =
3
3
  | 'gemini-2.5-flash-image'
4
4
  | 'gemini-3-pro-image-preview'
5
5
  | 'gemini-3.1-flash-image-preview'
6
+ | 'gemini-nano-banana-2.1'
6
7
  | (string & {});
7
8
 
8
9
  export interface GoogleImageSettings {
@@ -35,6 +35,7 @@ export type GoogleModelId =
35
35
  | 'gemini-3.6-flash'
36
36
  | 'gemini-3.7-flash'
37
37
  | 'gemini-3.8-flash'
38
+ | 'gemini-nano-banana-2.1'
38
39
  // latest version
39
40
  // https://ai.google.dev/gemini-api/docs/models#latest
40
41
  | 'gemini-pro-latest'
@@ -2,6 +2,7 @@ import type {
2
2
  LanguageModelV4,
3
3
  LanguageModelV4CallOptions,
4
4
  LanguageModelV4Content,
5
+ LanguageModelV4FunctionTool,
5
6
  LanguageModelV4FinishReason,
6
7
  LanguageModelV4GenerateResult,
7
8
  LanguageModelV4Source,
@@ -13,6 +14,7 @@ import type {
13
14
  } from '@ai-sdk/provider';
14
15
  import {
15
16
  combineHeaders,
17
+ convertJsonResponseToolStream,
16
18
  createToolNameMapping,
17
19
  createEventSourceResponseHandler,
18
20
  createJsonResponseHandler,
@@ -448,16 +450,57 @@ export class GoogleLanguageModel implements LanguageModelV4 {
448
450
  };
449
451
  }
450
452
 
451
- private getArgs(
453
+ private async getArgs(
452
454
  options: LanguageModelV4CallOptions,
453
455
  { isStreaming = false }: { isStreaming?: boolean } = {},
454
456
  ) {
455
- return GoogleLanguageModel.prepareRequest({
457
+ const { responseFormat, tools, toolChoice } = options;
458
+ let jsonResponseTool: LanguageModelV4FunctionTool | undefined;
459
+ // Gemini models before Gemini 3 reject JSON response formatting together
460
+ // with function tools. Gemini 3 supports the combination only with automatic
461
+ // tool choice (AUTO). Forced tool choice (ANY) requires a JSON response tool.
462
+ if (
463
+ responseFormat?.type === 'json' &&
464
+ responseFormat.schema != null &&
465
+ tools?.some(tool => tool.type === 'function') &&
466
+ /(^|\/)gemini-/i.test(this.modelId) &&
467
+ (!getGoogleModelCapabilities(this.modelId).usesGemini3Features ||
468
+ toolChoice?.type === 'required' ||
469
+ toolChoice?.type === 'tool')
470
+ ) {
471
+ let name = 'json';
472
+ for (let suffix = 1; tools.some(tool => tool.name === name); suffix++) {
473
+ name = `json_${suffix}`;
474
+ }
475
+ jsonResponseTool = {
476
+ type: 'function',
477
+ name,
478
+ description:
479
+ responseFormat.description ?? 'Respond with a JSON object.',
480
+ inputSchema: responseFormat.schema,
481
+ };
482
+ }
483
+
484
+ const prepared = await GoogleLanguageModel.prepareRequest({
456
485
  modelId: this.modelId,
457
486
  config: this.config,
458
- options,
487
+ options:
488
+ jsonResponseTool == null
489
+ ? options
490
+ : {
491
+ ...options,
492
+ responseFormat: undefined,
493
+ tools: [...(tools ?? []), jsonResponseTool],
494
+ toolChoice:
495
+ toolChoice?.type === 'tool'
496
+ ? toolChoice
497
+ : toolChoice?.type === 'none'
498
+ ? { type: 'tool', toolName: jsonResponseTool.name }
499
+ : { type: 'required' },
500
+ },
459
501
  isStreaming,
460
502
  });
503
+ return { ...prepared, jsonResponseToolName: jsonResponseTool?.name };
461
504
  }
462
505
 
463
506
  static convertGenerateContentResponse({
@@ -466,12 +509,14 @@ export class GoogleLanguageModel implements LanguageModelV4 {
466
509
  warnings,
467
510
  providerOptionsNames,
468
511
  toolNameMapping,
512
+ jsonResponseToolName,
469
513
  }: {
470
514
  config: GoogleLanguageModelConfig;
471
515
  response: InferSchema<typeof responseSchema>;
472
516
  warnings: SharedV4Warning[];
473
517
  providerOptionsNames: readonly string[];
474
518
  toolNameMapping?: ReturnType<typeof createToolNameMapping>;
519
+ jsonResponseToolName?: string;
475
520
  }): LanguageModelV4GenerateResult {
476
521
  const wrapProviderMetadata = (payload: Record<string, unknown>) =>
477
522
  Object.fromEntries(
@@ -529,6 +574,7 @@ export class GoogleLanguageModel implements LanguageModelV4 {
529
574
  },
530
575
  });
531
576
  } else if ('text' in part && part.text != null) {
577
+ if (jsonResponseToolName != null && part.thought !== true) continue;
532
578
  const thoughtSignatureMetadata = part.thoughtSignature
533
579
  ? wrapProviderMetadata({
534
580
  thoughtSignature: part.thoughtSignature,
@@ -548,6 +594,23 @@ export class GoogleLanguageModel implements LanguageModelV4 {
548
594
  });
549
595
  }
550
596
  } else if ('functionCall' in part && part.functionCall.name != null) {
597
+ if (part.functionCall.name === jsonResponseToolName) {
598
+ content.push({
599
+ type: 'text',
600
+ text:
601
+ typeof part.functionCall.args === 'string'
602
+ ? part.functionCall.args
603
+ : JSON.stringify(part.functionCall.args ?? {}),
604
+ ...(part.thoughtSignature
605
+ ? {
606
+ providerMetadata: wrapProviderMetadata({
607
+ thoughtSignature: part.thoughtSignature,
608
+ }),
609
+ }
610
+ : {}),
611
+ });
612
+ continue;
613
+ }
551
614
  content.push({
552
615
  type: 'tool-call' as const,
553
616
  toolCallId: part.functionCall.id || config.generateId(),
@@ -666,6 +729,7 @@ export class GoogleLanguageModel implements LanguageModelV4 {
666
729
  providerOptionsNames,
667
730
  extraHeaders,
668
731
  toolNameMapping,
732
+ jsonResponseToolName,
669
733
  } = await this.getArgs(options);
670
734
 
671
735
  const mergedHeaders = combineHeaders(
@@ -696,6 +760,7 @@ export class GoogleLanguageModel implements LanguageModelV4 {
696
760
  warnings,
697
761
  providerOptionsNames,
698
762
  toolNameMapping,
763
+ jsonResponseToolName,
699
764
  });
700
765
 
701
766
  return {
@@ -718,6 +783,7 @@ export class GoogleLanguageModel implements LanguageModelV4 {
718
783
  providerOptionsNames,
719
784
  extraHeaders,
720
785
  toolNameMapping,
786
+ jsonResponseToolName,
721
787
  } = await this.getArgs(options, { isStreaming: true });
722
788
  const wrapProviderMetadata = (payload: Record<string, unknown>) =>
723
789
  Object.fromEntries(
@@ -813,7 +879,7 @@ export class GoogleLanguageModel implements LanguageModelV4 {
813
879
  hasToolCalls = true;
814
880
  };
815
881
 
816
- return {
882
+ const result = {
817
883
  stream: response.pipeThrough(
818
884
  new TransformStream<
819
885
  ParseResult<ChunkSchema>,
@@ -1301,6 +1367,15 @@ export class GoogleLanguageModel implements LanguageModelV4 {
1301
1367
  response: { headers: responseHeaders },
1302
1368
  request: { body: args },
1303
1369
  };
1370
+ return {
1371
+ ...result,
1372
+ stream:
1373
+ jsonResponseToolName == null
1374
+ ? result.stream
1375
+ : result.stream.pipeThrough(
1376
+ convertJsonResponseToolStream(jsonResponseToolName),
1377
+ ),
1378
+ };
1304
1379
  }
1305
1380
  }
1306
1381
 
@@ -1370,6 +1445,7 @@ function resolveGemini3ThinkingConfig({
1370
1445
  medium: 'medium',
1371
1446
  high: 'high',
1372
1447
  xhigh: 'high',
1448
+ max: 'high',
1373
1449
  },
1374
1450
  warnings,
1375
1451
  });
@@ -1571,6 +1647,18 @@ export const getGroundingMetadataSchema = () =>
1571
1647
  title: z.string().nullish(),
1572
1648
  text: z.string().nullish(),
1573
1649
  fileSearchStore: z.string().nullish(),
1650
+ customMetadata: z
1651
+ .array(
1652
+ z.object({
1653
+ key: z.string(),
1654
+ stringValue: z.string().nullish(),
1655
+ numericValue: z.number().nullish(),
1656
+ stringListValue: z
1657
+ .object({ values: z.array(z.string()).nullish() })
1658
+ .nullish(),
1659
+ }),
1660
+ )
1661
+ .nullish(),
1574
1662
  })
1575
1663
  .nullish(),
1576
1664
  maps: z
@@ -1,7 +1,7 @@
1
1
  import type {
2
2
  EmbeddingModelV4,
3
3
  Experimental_BatchV4 as BatchV4,
4
- Experimental_EvaluationModelV4 as EvaluationModelV4,
4
+ Experimental_DecisionModelV4 as DecisionModelV4,
5
5
  Experimental_VideoModelV4,
6
6
  FilesV4,
7
7
  ImageModelV4,
@@ -21,7 +21,7 @@ import {
21
21
  type FetchFunction,
22
22
  type WebSocketConstructor,
23
23
  } from '@ai-sdk/provider-utils';
24
- import { Experimental_EvaluationLanguageModel as EvaluationLanguageModel } from '@ai-sdk/provider-utils/experimental-evaluation';
24
+ import { Experimental_DecisionLanguageModel as DecisionLanguageModel } from '@ai-sdk/provider-utils/experimental-decision';
25
25
  import { VERSION } from './version';
26
26
  import { GoogleEmbeddingModel } from './google-embedding-model';
27
27
  import type { GoogleEmbeddingModelId } from './google-embedding-model-options';
@@ -63,8 +63,12 @@ export interface GoogleProvider extends ProviderV4 {
63
63
 
64
64
  chat(modelId: GoogleModelId): LanguageModelV4;
65
65
 
66
- /** Creates an experimental Choice/Score/Boolean evaluation model using Gemini. */
67
- evaluationModel(modelId: GoogleModelId): EvaluationModelV4;
66
+ /** Creates an experimental Choice/Score/Boolean decision model using Gemini. */
67
+ decisionModel(modelId: GoogleModelId): DecisionModelV4;
68
+ /** @deprecated Use `decisionModel` instead. */
69
+ evaluationModel(modelId: GoogleModelId): DecisionModelV4 & {
70
+ doEvaluate: DecisionModelV4['doDecide'];
71
+ };
68
72
 
69
73
  experimental_batch(): BatchV4<{
70
74
  text: GoogleModelId;
@@ -174,6 +178,12 @@ export interface GoogleProvider extends ProviderV4 {
174
178
  }
175
179
 
176
180
  export interface GoogleProviderSettings {
181
+ /** Settings for downloading JSON Lines batch results. */
182
+ batchResultDownloads?: {
183
+ /** Maximum UTF-8 bytes per row, excluding LF. Defaults to 64 MiB. */
184
+ maxLineBytes?: number;
185
+ };
186
+
177
187
  /**
178
188
  * Use a different URL prefix for API calls, e.g. to use proxy servers.
179
189
  * The default prefix is `https://generativelanguage.googleapis.com/v1beta`.
@@ -308,6 +318,7 @@ export function createGoogle(
308
318
  const createBatch = () =>
309
319
  new GoogleBatch({
310
320
  provider: `${providerName.replace(/\.generative-ai$/, '')}.batch`,
321
+ maxLineBytes: options.batchResultDownloads?.maxLineBytes,
311
322
  config: languageModelConfig,
312
323
  // Batch prompt conversion happens before the model is available to the
313
324
  // provider. Only advertise URL support shared by every batch model.
@@ -435,11 +446,13 @@ export function createGoogle(
435
446
  provider.languageModel = createChatModel;
436
447
  provider.chat = createChatModel;
437
448
  provider.generativeAI = createChatModel;
438
- provider.evaluationModel = (modelId: GoogleModelId) =>
439
- new EvaluationLanguageModel({
449
+ provider.decisionModel = (modelId: GoogleModelId) =>
450
+ new DecisionLanguageModel({
440
451
  model: createChatModel(modelId),
441
- provider: `${providerName.replace(/\.generative-ai$/, '')}.evaluation`,
452
+ provider: `${providerName.replace(/\.generative-ai$/, '')}.decision`,
442
453
  });
454
+ provider.evaluationModel =
455
+ provider.decisionModel as GoogleProvider['evaluationModel'];
443
456
  provider.experimental_batch = createBatch;
444
457
  provider.embedding = createEmbeddingModel;
445
458
  provider.embeddingModel = createEmbeddingModel;
@@ -37,6 +37,7 @@ export type GoogleInteractionsModelId =
37
37
  | 'gemini-3.6-flash'
38
38
  | 'gemini-3.7-flash'
39
39
  | 'gemini-3.8-flash'
40
+ | 'gemini-nano-banana-2.1'
40
41
  | 'lyria-3-clip-preview'
41
42
  | 'lyria-3-pro-preview'
42
43
  | (string & {});