@ai-sdk/google 4.0.21 → 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.
- package/CHANGELOG.md +7 -0
- package/dist/index.js +36 -3
- package/dist/index.js.map +1 -1
- package/dist/internal/index.js +14 -2
- package/dist/internal/index.js.map +1 -1
- package/docs/15-google.mdx +5 -0
- package/package.json +1 -1
- package/src/convert-to-google-messages.ts +18 -2
- package/src/realtime/google-realtime-event-mapper.ts +34 -0
package/docs/15-google.mdx
CHANGED
|
@@ -1079,6 +1079,11 @@ Google realtime models may require provider-specific audio formats, depending
|
|
|
1079
1079
|
on the model and modality. See [Realtime](/docs/ai-sdk-core/realtime) for the
|
|
1080
1080
|
complete setup and tool calling pattern.
|
|
1081
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
|
+
|
|
1082
1087
|
### Live Translation
|
|
1083
1088
|
|
|
1084
1089
|
Use `gemini-3.5-live-translate-preview` for low-latency speech-to-speech
|
package/package.json
CHANGED
|
@@ -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 (
|
|
480
|
+
if (!isServerToolCall && thoughtSignature != null) {
|
|
481
|
+
modelResponseHasSignedFunctionCall = true;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
if (isServerToolCall) {
|
|
469
485
|
return {
|
|
470
486
|
toolCall: {
|
|
471
487
|
toolType: serverToolType,
|
|
@@ -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({
|