@ai-sdk/google 4.0.0-canary.81 → 4.0.1

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.
@@ -1091,6 +1091,45 @@ Google realtime models may require provider-specific audio formats, depending
1091
1091
  on the model and modality. See [Realtime](/docs/ai-sdk-core/realtime) for the
1092
1092
  complete setup and tool calling pattern.
1093
1093
 
1094
+ ### Live Translation
1095
+
1096
+ Use `gemini-3.5-live-translate-preview` for low-latency speech-to-speech
1097
+ translation. Configure the target language with
1098
+ `providerOptions.google.translationConfig` in the realtime session config you
1099
+ use when creating the token:
1100
+
1101
+ ```ts
1102
+ import {
1103
+ google,
1104
+ type Experimental_GoogleRealtimeModelOptions as GoogleRealtimeModelOptions,
1105
+ } from '@ai-sdk/google';
1106
+
1107
+ const token = await google.experimental_realtime.getToken({
1108
+ model: 'gemini-3.5-live-translate-preview',
1109
+ sessionConfig: {
1110
+ outputModalities: ['audio'],
1111
+ inputAudioTranscription: {},
1112
+ outputAudioTranscription: {},
1113
+ providerOptions: {
1114
+ google: {
1115
+ translationConfig: {
1116
+ targetLanguageCode: 'pl',
1117
+ echoTargetLanguage: true,
1118
+ },
1119
+ } satisfies GoogleRealtimeModelOptions,
1120
+ },
1121
+ },
1122
+ });
1123
+ ```
1124
+
1125
+ Set `targetLanguageCode` to a BCP-47 language code (defaults to `en`). When
1126
+ `echoTargetLanguage` is `true`, input audio already in the target language is
1127
+ echoed instead of producing silence.
1128
+
1129
+ Gemini Live Translation accepts audio input and produces translated audio
1130
+ output. Text input, tools, and custom instructions are not supported by this
1131
+ model.
1132
+
1094
1133
  ## Interactions API
1095
1134
 
1096
1135
  The [Gemini Interactions API](https://ai.google.dev/gemini-api/docs/interactions)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/google",
3
- "version": "4.0.0-canary.81",
3
+ "version": "4.0.1",
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.0-canary.18",
39
- "@ai-sdk/provider-utils": "5.0.0-canary.48"
38
+ "@ai-sdk/provider": "4.0.0",
39
+ "@ai-sdk/provider-utils": "5.0.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/node": "22.19.19",
43
43
  "tsup": "^8.5.1",
44
44
  "typescript": "5.8.3",
45
45
  "zod": "3.25.76",
46
- "@ai-sdk/test-server": "2.0.0-canary.6",
46
+ "@ai-sdk/test-server": "2.0.0",
47
47
  "@vercel/ai-tsconfig": "0.0.0"
48
48
  },
49
49
  "peerDependencies": {
@@ -214,12 +214,10 @@ const googleFileResponseSchema = lazySchema(() =>
214
214
 
215
215
  const googleFilesUploadOptionsSchema = lazySchema(() =>
216
216
  zodSchema(
217
- z
218
- .object({
219
- displayName: z.string().nullish(),
220
- pollIntervalMs: z.number().positive().nullish(),
221
- pollTimeoutMs: z.number().positive().nullish(),
222
- })
223
- .passthrough(),
217
+ z.looseObject({
218
+ displayName: z.string().nullish(),
219
+ pollIntervalMs: z.number().positive().nullish(),
220
+ pollTimeoutMs: z.number().positive().nullish(),
221
+ }),
224
222
  ),
225
223
  );
@@ -21,23 +21,21 @@ export type GoogleVideoModelOptions = {
21
21
 
22
22
  export const googleVideoModelOptionsSchema = lazySchema(() =>
23
23
  zodSchema(
24
- z
25
- .object({
26
- pollIntervalMs: z.number().positive().nullish(),
27
- pollTimeoutMs: z.number().positive().nullish(),
28
- personGeneration: z
29
- .enum(['dont_allow', 'allow_adult', 'allow_all'])
30
- .nullish(),
31
- negativePrompt: z.string().nullish(),
32
- referenceImages: z
33
- .array(
34
- z.object({
35
- bytesBase64Encoded: z.string().nullish(),
36
- gcsUri: z.string().nullish(),
37
- }),
38
- )
39
- .nullish(),
40
- })
41
- .passthrough(),
24
+ z.looseObject({
25
+ pollIntervalMs: z.number().positive().nullish(),
26
+ pollTimeoutMs: z.number().positive().nullish(),
27
+ personGeneration: z
28
+ .enum(['dont_allow', 'allow_adult', 'allow_all'])
29
+ .nullish(),
30
+ negativePrompt: z.string().nullish(),
31
+ referenceImages: z
32
+ .array(
33
+ z.object({
34
+ bytesBase64Encoded: z.string().nullish(),
35
+ gcsUri: z.string().nullish(),
36
+ }),
37
+ )
38
+ .nullish(),
39
+ }),
42
40
  ),
43
41
  );
package/src/index.ts CHANGED
@@ -56,5 +56,9 @@ export type {
56
56
  } from './google-provider';
57
57
  export { GoogleRealtimeModel as Experimental_GoogleRealtimeModel } from './realtime/google-realtime-model';
58
58
  export type { GoogleRealtimeModelConfig as Experimental_GoogleRealtimeModelConfig } from './realtime/google-realtime-model';
59
+ export type {
60
+ GoogleRealtimeModelId as Experimental_GoogleRealtimeModelId,
61
+ GoogleRealtimeModelOptions as Experimental_GoogleRealtimeModelOptions,
62
+ } from './realtime/google-realtime-model-options';
59
63
 
60
64
  export { VERSION } from './version';
@@ -459,7 +459,6 @@ export class GoogleInteractionsLanguageModel implements LanguageModelV4 {
459
459
  const url = `${this.config.baseURL}/interactions`;
460
460
 
461
461
  const mergedHeaders = combineHeaders(
462
- INTERACTIONS_API_REVISION_HEADER,
463
462
  this.config.headers ? await resolve(this.config.headers) : undefined,
464
463
  options.headers,
465
464
  );
@@ -588,7 +587,6 @@ export class GoogleInteractionsLanguageModel implements LanguageModelV4 {
588
587
  const url = `${this.config.baseURL}/interactions`;
589
588
 
590
589
  const mergedHeaders = combineHeaders(
591
- INTERACTIONS_API_REVISION_HEADER,
592
590
  this.config.headers ? await resolve(this.config.headers) : undefined,
593
591
  options.headers,
594
592
  );
@@ -757,15 +755,6 @@ export class GoogleInteractionsLanguageModel implements LanguageModelV4 {
757
755
  }
758
756
  }
759
757
 
760
- /*
761
- * Pins the Interactions API revision the SDK targets. Sent on every request
762
- * the model issues so model-id calls, agent calls, polling, SSE reconnects,
763
- * and cancellation all hit the same schema.
764
- */
765
- const INTERACTIONS_API_REVISION_HEADER: Record<string, string> = {
766
- 'Api-Revision': '2026-05-20',
767
- };
768
-
769
758
  function pruneUndefined<T extends Record<string, unknown>>(obj: T): T {
770
759
  const result: Record<string, unknown> = {};
771
760
  for (const [key, value] of Object.entries(obj)) {
@@ -8,6 +8,7 @@ import type {
8
8
  import { safeParseJSON } from '@ai-sdk/provider-utils';
9
9
  import { convertJSONSchemaToOpenAPISchema } from '../convert-json-schema-to-openapi-schema';
10
10
  import { getModelPath } from '../get-model-path';
11
+ import type { GoogleRealtimeModelOptions } from './google-realtime-model-options';
11
12
 
12
13
  type GoogleRealtimeFunctionCall = {
13
14
  id: string;
@@ -38,6 +39,10 @@ type GoogleRealtimeWireEvent = {
38
39
  inputTranscription?: { text?: string };
39
40
  };
40
41
 
42
+ function isRecord(value: unknown): value is Record<string, unknown> {
43
+ return value != null && typeof value === 'object' && !Array.isArray(value);
44
+ }
45
+
41
46
  /**
42
47
  * Stateful event mapper for Google's Gemini Live API.
43
48
  *
@@ -268,6 +273,12 @@ export class GoogleRealtimeEventMapper {
268
273
  };
269
274
 
270
275
  case 'input-audio-commit':
276
+ return {
277
+ realtimeInput: {
278
+ audioStreamEnd: true,
279
+ },
280
+ };
281
+
271
282
  case 'input-audio-clear':
272
283
  case 'response-create':
273
284
  case 'response-cancel':
@@ -375,8 +386,25 @@ export function buildGoogleSessionConfig(
375
386
  setup.outputAudioTranscription = {};
376
387
  }
377
388
 
378
- if (config?.providerOptions != null) {
379
- Object.assign(setup, config.providerOptions);
389
+ if (config?.providerOptions == null) {
390
+ return setup;
391
+ }
392
+
393
+ const { google, ...providerOptions } = config.providerOptions;
394
+ Object.assign(setup, providerOptions);
395
+
396
+ const googleOptions = isRecord(google)
397
+ ? (google as GoogleRealtimeModelOptions)
398
+ : undefined;
399
+
400
+ if (googleOptions?.translationConfig != null) {
401
+ const target = isRecord(setup.generationConfig)
402
+ ? setup.generationConfig
403
+ : generationConfig;
404
+ setup.generationConfig = {
405
+ ...target,
406
+ translationConfig: googleOptions.translationConfig,
407
+ };
380
408
  }
381
409
 
382
410
  return setup;
@@ -1,3 +1,23 @@
1
1
  export type GoogleRealtimeModelId = string;
2
2
 
3
- export type GoogleRealtimeModelOptions = Record<string, never>;
3
+ export type GoogleRealtimeModelOptions = {
4
+ /**
5
+ * Gemini Live Translation configuration.
6
+ *
7
+ * Required for `gemini-3.5-live-translate-preview` when translating speech
8
+ * to a target language.
9
+ */
10
+ translationConfig?: {
11
+ /**
12
+ * BCP-47 language code of the language to translate into.
13
+ * Defaults to `en` in the Gemini API.
14
+ */
15
+ targetLanguageCode?: string;
16
+
17
+ /**
18
+ * Whether input audio already in the target language should be echoed
19
+ * instead of producing silence.
20
+ */
21
+ echoTargetLanguage?: boolean;
22
+ };
23
+ };
@@ -1,2 +1,6 @@
1
1
  export { GoogleRealtimeModel as Experimental_GoogleRealtimeModel } from './google-realtime-model';
2
2
  export type { GoogleRealtimeModelConfig as Experimental_GoogleRealtimeModelConfig } from './google-realtime-model';
3
+ export type {
4
+ GoogleRealtimeModelId as Experimental_GoogleRealtimeModelId,
5
+ GoogleRealtimeModelOptions as Experimental_GoogleRealtimeModelOptions,
6
+ } from './google-realtime-model-options';
@@ -6,35 +6,33 @@ import {
6
6
  import { z } from 'zod/v4';
7
7
 
8
8
  /** Tool to retrieve knowledge from the File Search Stores. */
9
- const fileSearchArgsBaseSchema = z
10
- .object({
11
- /** The names of the file_search_stores to retrieve from.
12
- * Example: `fileSearchStores/my-file-search-store-123`
13
- */
14
- fileSearchStoreNames: z
15
- .array(z.string())
16
- .describe(
17
- 'The names of the file_search_stores to retrieve from. Example: `fileSearchStores/my-file-search-store-123`',
18
- ),
19
- /** The number of file search retrieval chunks to retrieve. */
20
- topK: z
21
- .number()
22
- .int()
23
- .positive()
24
- .describe('The number of file search retrieval chunks to retrieve.')
25
- .optional(),
9
+ const fileSearchArgsBaseSchema = z.looseObject({
10
+ /** The names of the file_search_stores to retrieve from.
11
+ * Example: `fileSearchStores/my-file-search-store-123`
12
+ */
13
+ fileSearchStoreNames: z
14
+ .array(z.string())
15
+ .describe(
16
+ 'The names of the file_search_stores to retrieve from. Example: `fileSearchStores/my-file-search-store-123`',
17
+ ),
18
+ /** The number of file search retrieval chunks to retrieve. */
19
+ topK: z
20
+ .number()
21
+ .int()
22
+ .positive()
23
+ .describe('The number of file search retrieval chunks to retrieve.')
24
+ .optional(),
26
25
 
27
- /** Metadata filter to apply to the file search retrieval documents.
28
- * See https://google.aip.dev/160 for the syntax of the filter expression.
29
- */
30
- metadataFilter: z
31
- .string()
32
- .describe(
33
- 'Metadata filter to apply to the file search retrieval documents. See https://google.aip.dev/160 for the syntax of the filter expression.',
34
- )
35
- .optional(),
36
- })
37
- .passthrough();
26
+ /** Metadata filter to apply to the file search retrieval documents.
27
+ * See https://google.aip.dev/160 for the syntax of the filter expression.
28
+ */
29
+ metadataFilter: z
30
+ .string()
31
+ .describe(
32
+ 'Metadata filter to apply to the file search retrieval documents. See https://google.aip.dev/160 for the syntax of the filter expression.',
33
+ )
34
+ .optional(),
35
+ });
38
36
 
39
37
  export type GoogleFileSearchToolArgs = z.infer<typeof fileSearchArgsBaseSchema>;
40
38
 
@@ -9,23 +9,21 @@ import { z } from 'zod/v4';
9
9
  // https://ai.google.dev/api/generate-content#GroundingSupport
10
10
  // https://cloud.google.com/vertex-ai/generative-ai/docs/grounding/grounding-with-google-search
11
11
 
12
- export const googleSearchToolArgsBaseSchema = z
13
- .object({
14
- searchTypes: z
15
- .object({
16
- webSearch: z.object({}).optional(),
17
- imageSearch: z.object({}).optional(),
18
- })
19
- .optional(),
12
+ export const googleSearchToolArgsBaseSchema = z.looseObject({
13
+ searchTypes: z
14
+ .object({
15
+ webSearch: z.object({}).optional(),
16
+ imageSearch: z.object({}).optional(),
17
+ })
18
+ .optional(),
20
19
 
21
- timeRangeFilter: z
22
- .object({
23
- startTime: z.string(),
24
- endTime: z.string(),
25
- })
26
- .optional(),
27
- })
28
- .passthrough();
20
+ timeRangeFilter: z
21
+ .object({
22
+ startTime: z.string(),
23
+ endTime: z.string(),
24
+ })
25
+ .optional(),
26
+ });
29
27
 
30
28
  export type GoogleSearchToolArgs = z.infer<
31
29
  typeof googleSearchToolArgsBaseSchema