@ai-sdk/google 4.0.20 → 4.0.22

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.
@@ -1031,7 +1031,9 @@ The following Zod features are known to not work with Google:
1031
1031
 
1032
1032
  | Model | Image Input | Object Generation | Tool Usage | Tool Streaming | Google Search | URL Context |
1033
1033
  | ------------------------------------- | ----------- | ----------------- | ---------- | -------------- | ------------- | ----------- |
1034
+ | `gemini-3.6-flash` | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
1034
1035
  | `gemini-3.5-flash` | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
1036
+ | `gemini-3.5-flash-lite` | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
1035
1037
  | `gemini-3.1-pro-preview` | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
1036
1038
  | `gemini-3.1-flash-image-preview` | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
1037
1039
  | `gemini-3.1-flash-lite-preview` | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
@@ -1077,6 +1079,11 @@ Google realtime models may require provider-specific audio formats, depending
1077
1079
  on the model and modality. See [Realtime](/docs/ai-sdk-core/realtime) for the
1078
1080
  complete setup and tool calling pattern.
1079
1081
 
1082
+ Gemini lifecycle signals that do not have provider-neutral equivalents are
1083
+ emitted as custom events. Inspect `rawType` for `goAway`,
1084
+ `sessionResumptionUpdate`, and `generationComplete` and use the event's `raw`
1085
+ payload for provider-specific details.
1086
+
1080
1087
  ### Live Translation
1081
1088
 
1082
1089
  Use `gemini-3.5-live-translate-preview` for low-latency speech-to-speech
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/google",
3
- "version": "4.0.20",
3
+ "version": "4.0.22",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -337,6 +337,8 @@ export function convertToGoogleMessages(
337
337
  case 'assistant': {
338
338
  systemMessagesAllowed = false;
339
339
 
340
+ let modelResponseHasSignedFunctionCall = false;
341
+
340
342
  contents.push({
341
343
  role: 'model',
342
344
  parts: content
@@ -459,13 +461,27 @@ export function convertToGoogleMessages(
459
461
  providerOpts?.serverToolType != null
460
462
  ? String(providerOpts.serverToolType)
461
463
  : undefined;
464
+ const isServerToolCall =
465
+ serverToolCallId != null && serverToolType != null;
466
+ const shouldSkipMissingSignatureMitigation =
467
+ // Gemini 3 returns a single signature for a parallel
468
+ // function-call response on the first standard function
469
+ // call. Subsequent standard function calls in the same
470
+ // model response legitimately have no signature.
471
+ !isServerToolCall &&
472
+ thoughtSignature == null &&
473
+ modelResponseHasSignedFunctionCall;
462
474
  const effectiveThoughtSignature =
463
475
  thoughtSignature ??
464
- (isGemini3Model
476
+ (isGemini3Model && !shouldSkipMissingSignatureMitigation
465
477
  ? injectSkipSignature(part.toolName)
466
478
  : undefined);
467
479
 
468
- if (serverToolCallId && serverToolType) {
480
+ if (!isServerToolCall && thoughtSignature != null) {
481
+ modelResponseHasSignedFunctionCall = true;
482
+ }
483
+
484
+ if (isServerToolCall) {
469
485
  return {
470
486
  toolCall: {
471
487
  toolType: serverToolType,
@@ -31,6 +31,8 @@ export type GoogleModelId =
31
31
  | 'gemini-3.1-flash-lite-preview'
32
32
  | 'gemini-3.1-flash-tts-preview'
33
33
  | 'gemini-3.5-flash'
34
+ | 'gemini-3.5-flash-lite'
35
+ | 'gemini-3.6-flash'
34
36
  // latest version
35
37
  // https://ai.google.dev/gemini-api/docs/models#latest
36
38
  | 'gemini-pro-latest'
@@ -33,6 +33,8 @@ export type GoogleInteractionsModelId =
33
33
  | 'gemini-3.1-flash-lite-preview'
34
34
  | 'gemini-3.1-flash-tts-preview'
35
35
  | 'gemini-3.5-flash'
36
+ | 'gemini-3.5-flash-lite'
37
+ | 'gemini-3.6-flash'
36
38
  | 'lyria-3-clip-preview'
37
39
  | 'lyria-3-pro-preview'
38
40
  | (string & {});
@@ -17,6 +17,7 @@ type GoogleRealtimeFunctionCall = {
17
17
  };
18
18
 
19
19
  type GoogleRealtimeServerContent = {
20
+ generationComplete?: boolean;
20
21
  interrupted?: boolean;
21
22
  modelTurn?: {
22
23
  parts?: Array<{
@@ -37,6 +38,12 @@ type GoogleRealtimeWireEvent = {
37
38
  toolCallCancellation?: unknown;
38
39
  serverContent?: GoogleRealtimeServerContent;
39
40
  inputTranscription?: { text?: string };
41
+ goAway?: { timeLeft?: string };
42
+ sessionResumptionUpdate?: {
43
+ newHandle?: string;
44
+ resumable?: boolean;
45
+ lastConsumedClientMessageIndex?: string;
46
+ };
40
47
  };
41
48
 
42
49
  function isRecord(value: unknown): value is Record<string, unknown> {
@@ -127,6 +134,22 @@ export class GoogleRealtimeEventMapper {
127
134
  };
128
135
  }
129
136
 
137
+ if (data.goAway != null) {
138
+ return {
139
+ type: 'custom',
140
+ rawType: 'goAway',
141
+ raw,
142
+ };
143
+ }
144
+
145
+ if (data.sessionResumptionUpdate != null) {
146
+ return {
147
+ type: 'custom',
148
+ rawType: 'sessionResumptionUpdate',
149
+ raw,
150
+ };
151
+ }
152
+
130
153
  if (data.serverContent != null) {
131
154
  return this.parseServerContent(data.serverContent, raw);
132
155
  }
@@ -203,6 +226,17 @@ export class GoogleRealtimeEventMapper {
203
226
  });
204
227
  }
205
228
 
229
+ // `generationComplete` means generation has stopped, but playback and the
230
+ // turn can remain open. Keep it distinct from `response-done`, which is
231
+ // emitted only when Google sends `turnComplete`.
232
+ if (serverContent.generationComplete) {
233
+ events.push({
234
+ type: 'custom',
235
+ rawType: 'generationComplete',
236
+ raw,
237
+ });
238
+ }
239
+
206
240
  if (serverContent.turnComplete) {
207
241
  if (this.hasAudio) {
208
242
  events.push({