@ai-sdk/google 4.0.70 → 4.0.72

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.
@@ -1186,6 +1186,50 @@ Gemini Live Translation accepts audio input and produces translated audio
1186
1186
  output. Text input, tools, and custom instructions are not supported by this
1187
1187
  model.
1188
1188
 
1189
+ ### Gemini 3.8 Live
1190
+
1191
+ `gemini-3.8-live` and `gemini-3.8-live-extended-thinking` are audio-only
1192
+ realtime models: request `outputModalities: ['audio']`, and add
1193
+ `outputAudioTranscription: {}` if you want the assistant's transcript as
1194
+ `audio-transcript-delta` events. Server-side voice activity detection and
1195
+ proactive audio are always on.
1196
+
1197
+ `gemini-3.8-live-extended-thinking` reasons in the background while streaming
1198
+ audio. Google requires exactly one of `thinkingLevel` (`low`, `medium`, `high`)
1199
+ or `thinkingBudget` for it; the provider sends `thinkingLevel: 'low'` when you
1200
+ set neither. Override it through `providerOptions.google.thinkingConfig`:
1201
+
1202
+ ```ts
1203
+ import {
1204
+ google,
1205
+ type Experimental_GoogleRealtimeModelOptions as GoogleRealtimeModelOptions,
1206
+ } from '@ai-sdk/google';
1207
+
1208
+ const token = await google.experimental_realtime.getToken({
1209
+ model: 'gemini-3.8-live-extended-thinking',
1210
+ sessionConfig: {
1211
+ outputModalities: ['audio'],
1212
+ outputAudioTranscription: {},
1213
+ providerOptions: {
1214
+ google: {
1215
+ thinkingConfig: { thinkingLevel: 'high' },
1216
+ } satisfies GoogleRealtimeModelOptions,
1217
+ },
1218
+ },
1219
+ });
1220
+ ```
1221
+
1222
+ Tools declared in the session config run asynchronously (`NON_BLOCKING`) by
1223
+ default on these models, so the model keeps talking while a tool call is in
1224
+ flight. Set `providerOptions.google.defaultToolBehavior: 'BLOCKING'` on
1225
+ `gemini-3.8-live` to make it wait for the result instead.
1226
+
1227
+ On the extended thinking model, `turnComplete` (the `response-done` event) does
1228
+ not mean the model is idle. Watch the `interactionStatus` custom event: its
1229
+ `raw.interactionStatus` is `IN_PROGRESS`, `IDLE`, or `WAITING_FOR_INPUT`. A
1230
+ `waitingForInput` custom event is emitted when the model has stopped and needs
1231
+ user input to continue.
1232
+
1189
1233
  ## Translation Models
1190
1234
 
1191
1235
  <Note type="warning">Speech translation is an experimental feature.</Note>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/google",
3
- "version": "4.0.70",
3
+ "version": "4.0.72",
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.14",
39
- "@ai-sdk/provider-utils": "5.0.40"
38
+ "@ai-sdk/provider": "4.0.15",
39
+ "@ai-sdk/provider-utils": "5.0.41"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@ai-sdk/test-server": "2.0.1",
@@ -17,6 +17,7 @@ type GoogleRealtimeFunctionCall = {
17
17
 
18
18
  type GoogleRealtimeServerContent = {
19
19
  generationComplete?: boolean;
20
+ interactionStatus?: string;
20
21
  interrupted?: boolean;
21
22
  modelTurn?: {
22
23
  parts?: Array<{
@@ -27,6 +28,7 @@ type GoogleRealtimeServerContent = {
27
28
  outputTranscription?: { text?: string };
28
29
  inputTranscription?: { text?: string };
29
30
  turnComplete?: boolean;
31
+ waitingForInput?: boolean;
30
32
  };
31
33
 
32
34
  type GoogleRealtimeWireEvent = {
@@ -232,6 +234,30 @@ export class GoogleRealtimeEventMapper {
232
234
  });
233
235
  }
234
236
 
237
+ // `interactionStatus` (IN_PROGRESS | IDLE | WAITING_FOR_INPUT) is the definitive
238
+ // session-activity signal for background-reasoning models: `turnComplete`
239
+ // no longer implies the model is idle, since asynchronous tool calls and
240
+ // audio may still follow. Surface it as a custom event so clients can
241
+ // coordinate state on it.
242
+ if (serverContent.interactionStatus != null) {
243
+ events.push({
244
+ type: 'custom',
245
+ rawType: 'interactionStatus',
246
+ raw,
247
+ });
248
+ }
249
+
250
+ // `waitingForInput` is the always-on Proactive Audio turn-taking signal:
251
+ // the model has yielded the floor and is not generating because it
252
+ // expects the user to continue.
253
+ if (serverContent.waitingForInput) {
254
+ events.push({
255
+ type: 'custom',
256
+ rawType: 'waitingForInput',
257
+ raw,
258
+ });
259
+ }
260
+
235
261
  if (serverContent.turnComplete) {
236
262
  if (this.hasAudio) {
237
263
  events.push({
@@ -377,6 +403,16 @@ async function serializeFunctionCallOutput(
377
403
  };
378
404
  }
379
405
 
406
+ /**
407
+ * Live models that reason in the background (e.g. `gemini-3.8-live-extended-thinking`).
408
+ * Google requires exactly one of `thinkingLevel` / `thinkingBudget` in their setup and
409
+ * rejects `thinkingConfig` on every other Live model.
410
+ */
411
+ function isThinkingLiveModel(modelId: string): boolean {
412
+ const modelName = modelId.split('/').at(-1)?.toLowerCase() ?? '';
413
+ return /^gemini-\d+\.\d+-live\b.*thinking/.test(modelName);
414
+ }
415
+
380
416
  /**
381
417
  * Builds a Google-specific session configuration from a normalized config.
382
418
  * Used to construct the `bidiGenerateContentSetup` payload for auth token creation.
@@ -389,6 +425,11 @@ export function buildGoogleSessionConfig(
389
425
  model: getModelPath(modelId),
390
426
  };
391
427
 
428
+ const { google, ...restProviderOptions } = config?.providerOptions ?? {};
429
+ const googleOptions = isRecord(google)
430
+ ? (google as GoogleRealtimeModelOptions)
431
+ : undefined;
432
+
392
433
  const generationConfig: Record<string, unknown> = {};
393
434
 
394
435
  if (config?.outputModalities != null) {
@@ -424,6 +465,9 @@ export function buildGoogleSessionConfig(
424
465
  name: tool.name,
425
466
  description: tool.description,
426
467
  parametersJsonSchema: tool.parameters,
468
+ ...(googleOptions?.defaultToolBehavior != null
469
+ ? { behavior: googleOptions.defaultToolBehavior }
470
+ : {}),
427
471
  })),
428
472
  },
429
473
  ];
@@ -437,16 +481,28 @@ export function buildGoogleSessionConfig(
437
481
  setup.outputAudioTranscription = {};
438
482
  }
439
483
 
484
+ // Default to the lowest-latency thinking level so a session on a
485
+ // background-reasoning model works without provider options. Merged last so
486
+ // it survives a raw `providerOptions.generationConfig`.
487
+ const thinkingConfig =
488
+ googleOptions?.thinkingConfig ??
489
+ (isThinkingLiveModel(modelId)
490
+ ? { thinkingLevel: 'low' as const }
491
+ : undefined);
492
+ const applyThinkingConfig = () => {
493
+ if (thinkingConfig == null) return;
494
+ const target = isRecord(setup.generationConfig)
495
+ ? setup.generationConfig
496
+ : generationConfig;
497
+ setup.generationConfig = { ...target, thinkingConfig };
498
+ };
499
+
440
500
  if (config?.providerOptions == null) {
501
+ applyThinkingConfig();
441
502
  return setup;
442
503
  }
443
504
 
444
- const { google, ...providerOptions } = config.providerOptions;
445
- Object.assign(setup, providerOptions);
446
-
447
- const googleOptions = isRecord(google)
448
- ? (google as GoogleRealtimeModelOptions)
449
- : undefined;
505
+ Object.assign(setup, restProviderOptions);
450
506
 
451
507
  if (googleOptions?.translationConfig != null) {
452
508
  const target = isRecord(setup.generationConfig)
@@ -458,5 +514,6 @@ export function buildGoogleSessionConfig(
458
514
  };
459
515
  }
460
516
 
517
+ applyThinkingConfig();
461
518
  return setup;
462
519
  }
@@ -20,4 +20,43 @@ export type GoogleRealtimeModelOptions = {
20
20
  */
21
21
  echoTargetLanguage?: boolean;
22
22
  };
23
+
24
+ /**
25
+ * Gemini Live thinking configuration.
26
+ *
27
+ * Supported by Live models with background reasoning (e.g.
28
+ * `gemini-3.8-live-extended-thinking`), which can process multi-step
29
+ * reasoning and function calls while streaming audio responses. Not
30
+ * supported by latency-optimized models (e.g. `gemini-3.8-live`).
31
+ */
32
+ thinkingConfig?: {
33
+ /**
34
+ * Thinking effort level. Background-reasoning Live models require exactly
35
+ * one of `thinkingLevel` or `thinkingBudget`; when neither is set, the
36
+ * provider sends `thinkingLevel: 'low'` on those models.
37
+ */
38
+ thinkingLevel?: 'low' | 'medium' | 'high';
39
+
40
+ /**
41
+ * Token budget for background thinking. Mutually exclusive with
42
+ * `thinkingLevel`; setting it suppresses the default level.
43
+ */
44
+ thinkingBudget?: number;
45
+
46
+ /**
47
+ * Whether thought summaries should be included in the response.
48
+ */
49
+ includeThoughts?: boolean;
50
+ };
51
+
52
+ /**
53
+ * Default `behavior` stamped onto every function declaration in the
54
+ * session setup.
55
+ *
56
+ * Gemini 3.8 Live models default to `NON_BLOCKING` (asynchronous) function
57
+ * calling. Set to `BLOCKING` for synchronous calls on models that support it
58
+ * (e.g. `gemini-3.8-live`); background-reasoning models accept only
59
+ * `NON_BLOCKING`.
60
+ */
61
+ defaultToolBehavior?: 'BLOCKING' | 'NON_BLOCKING';
23
62
  };