@ai-sdk/google 4.0.0-canary.80 → 4.0.0
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.
- package/CHANGELOG.md +159 -0
- package/dist/index.d.ts +23 -1
- package/dist/index.js +45 -13
- package/dist/index.js.map +1 -1
- package/dist/internal/index.js +22 -4
- package/dist/internal/index.js.map +1 -1
- package/docs/15-google.mdx +41 -2
- package/package.json +4 -4
- package/src/google-json-accumulator.ts +39 -4
- package/src/google-video-model.ts +8 -4
- package/src/index.ts +4 -0
- package/src/interactions/google-interactions-language-model.ts +0 -11
- package/src/realtime/google-realtime-event-mapper.ts +30 -2
- package/src/realtime/google-realtime-model-options.ts +21 -1
- package/src/realtime/index.ts +4 -0
package/docs/15-google.mdx
CHANGED
|
@@ -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)
|
|
@@ -1977,7 +2016,7 @@ The `voice` argument can be set to one of Gemini's [30 prebuilt voices](https://
|
|
|
1977
2016
|
e.g. `Kore`, `Puck`, `Zephyr`, or `Charon`. Voice names are case-sensitive. It defaults to `Kore`.
|
|
1978
2017
|
|
|
1979
2018
|
```ts highlight="6"
|
|
1980
|
-
import {
|
|
2019
|
+
import { generateSpeech } from 'ai';
|
|
1981
2020
|
import { google } from '@ai-sdk/google';
|
|
1982
2021
|
|
|
1983
2022
|
const result = await generateSpeech({
|
|
@@ -2000,7 +2039,7 @@ For multi-speaker dialogue, pass a `multiSpeakerVoiceConfig` through `providerOp
|
|
|
2000
2039
|
name must match a name used in the input text. When set, it overrides the top-level `voice`.
|
|
2001
2040
|
|
|
2002
2041
|
```ts highlight="8-23"
|
|
2003
|
-
import {
|
|
2042
|
+
import { generateSpeech } from 'ai';
|
|
2004
2043
|
import { google, type GoogleSpeechModelOptions } from '@ai-sdk/google';
|
|
2005
2044
|
|
|
2006
2045
|
const result = await generateSpeech({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/google",
|
|
3
|
-
"version": "4.0.0
|
|
3
|
+
"version": "4.0.0",
|
|
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
|
|
39
|
-
"@ai-sdk/provider-utils": "5.0.0
|
|
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
|
|
46
|
+
"@ai-sdk/test-server": "2.0.0",
|
|
47
47
|
"@vercel/ai-tsconfig": "0.0.0"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
@@ -276,6 +276,35 @@ function parsePath(rawPath: string): Array<string | number> {
|
|
|
276
276
|
return segments;
|
|
277
277
|
}
|
|
278
278
|
|
|
279
|
+
const hasOwn = Object.prototype.hasOwnProperty;
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Checks only direct properties so path traversal never follows the prototype chain.
|
|
283
|
+
*/
|
|
284
|
+
function hasOwnProperty(
|
|
285
|
+
obj: Record<string | number, unknown>,
|
|
286
|
+
key: string | number,
|
|
287
|
+
): boolean {
|
|
288
|
+
return hasOwn.call(obj, key);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Defines path values as own data properties so special keys like `__proto__`
|
|
293
|
+
* cannot invoke prototype setters while accumulating streamed arguments.
|
|
294
|
+
*/
|
|
295
|
+
function defineOwnProperty(
|
|
296
|
+
obj: Record<string | number, unknown>,
|
|
297
|
+
key: string | number,
|
|
298
|
+
value: unknown,
|
|
299
|
+
): void {
|
|
300
|
+
Object.defineProperty(obj, key, {
|
|
301
|
+
value,
|
|
302
|
+
enumerable: true,
|
|
303
|
+
configurable: true,
|
|
304
|
+
writable: true,
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
279
308
|
/**
|
|
280
309
|
* Traverses a nested object along the given path segments and returns the leaf value.
|
|
281
310
|
*
|
|
@@ -289,7 +318,9 @@ function getNestedValue(
|
|
|
289
318
|
let current: unknown = obj;
|
|
290
319
|
for (const pathSegment of segments) {
|
|
291
320
|
if (current == null || typeof current !== 'object') return undefined;
|
|
292
|
-
|
|
321
|
+
const currentRecord = current as Record<string | number, unknown>;
|
|
322
|
+
if (!hasOwnProperty(currentRecord, pathSegment)) return undefined;
|
|
323
|
+
current = currentRecord[pathSegment];
|
|
293
324
|
}
|
|
294
325
|
return current;
|
|
295
326
|
}
|
|
@@ -309,12 +340,16 @@ function setNestedValue(
|
|
|
309
340
|
for (let i = 0; i < segments.length - 1; i++) {
|
|
310
341
|
const pathSegment = segments[i];
|
|
311
342
|
const nextSeg = segments[i + 1];
|
|
312
|
-
if (current[pathSegment] == null) {
|
|
313
|
-
|
|
343
|
+
if (!hasOwnProperty(current, pathSegment) || current[pathSegment] == null) {
|
|
344
|
+
defineOwnProperty(
|
|
345
|
+
current,
|
|
346
|
+
pathSegment,
|
|
347
|
+
typeof nextSeg === 'number' ? [] : {},
|
|
348
|
+
);
|
|
314
349
|
}
|
|
315
350
|
current = current[pathSegment] as Record<string | number, unknown>;
|
|
316
351
|
}
|
|
317
|
-
current
|
|
352
|
+
defineOwnProperty(current, segments[segments.length - 1], value);
|
|
318
353
|
}
|
|
319
354
|
|
|
320
355
|
/**
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
createJsonResponseHandler,
|
|
10
10
|
delay,
|
|
11
11
|
getFromApi,
|
|
12
|
+
isSameOrigin,
|
|
12
13
|
parseProviderOptions,
|
|
13
14
|
postJsonToApi,
|
|
14
15
|
resolve,
|
|
@@ -263,10 +264,13 @@ export class GoogleVideoModel implements Experimental_VideoModelV4 {
|
|
|
263
264
|
for (const generatedSample of response.generateVideoResponse
|
|
264
265
|
.generatedSamples) {
|
|
265
266
|
if (generatedSample.video?.uri) {
|
|
266
|
-
// Append API key to URL for authentication
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
267
|
+
// Append the API key to the download URL for authentication, but only
|
|
268
|
+
// when the response-supplied URI stays on the provider's own origin —
|
|
269
|
+
// otherwise the key would leak to whatever host the response names.
|
|
270
|
+
const urlWithAuth =
|
|
271
|
+
apiKey && isSameOrigin(generatedSample.video.uri, this.config.baseURL)
|
|
272
|
+
? `${generatedSample.video.uri}${generatedSample.video.uri.includes('?') ? '&' : '?'}key=${apiKey}`
|
|
273
|
+
: generatedSample.video.uri;
|
|
270
274
|
|
|
271
275
|
videos.push({
|
|
272
276
|
type: 'url',
|
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
|
|
379
|
-
|
|
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 =
|
|
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
|
+
};
|
package/src/realtime/index.ts
CHANGED
|
@@ -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';
|