@ai-sdk/google 4.0.82 → 4.0.84
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 +26 -0
- package/dist/index.js +98 -22
- package/dist/index.js.map +1 -1
- package/docs/15-google.mdx +12 -0
- package/package.json +3 -3
- package/src/google-image-model.ts +16 -0
- package/src/realtime/google-realtime-event-mapper.ts +125 -29
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# @ai-sdk/google
|
|
2
2
|
|
|
3
|
+
## 4.0.84
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- d0290cf: fix(google): preserve realtime user utterances with independent IDs and cumulative transcripts, honor text-free transcription completion markers, and advance response IDs after interruption
|
|
8
|
+
|
|
9
|
+
## 4.0.83
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 32cf2f6: fix(google): send the default Gemini Live `thinkingLevel` when `thinkingConfig` sets neither `thinkingLevel` nor `thinkingBudget`, and stop overwriting a raw `generationConfig.thinkingConfig`
|
|
14
|
+
- 525efc5: feat(provider): advertise image model file and mask input support
|
|
15
|
+
|
|
16
|
+
Use confirmed model IDs for capability declarations so unrecognized model names
|
|
17
|
+
remain unknown. Include Together AI FLUX.2 Pro and Flex single-image editing, and
|
|
18
|
+
allow asynchronous capability lookups and middleware overrides to resolve to
|
|
19
|
+
unknown.
|
|
20
|
+
|
|
21
|
+
Advertise QuiverAI Arrow 2 and Arrow 2 Telos file-input support, and mark Together
|
|
22
|
+
AI Gemini image inputs unsupported by the current single-image request mapping.
|
|
23
|
+
|
|
24
|
+
- Updated dependencies [e3605f6]
|
|
25
|
+
- Updated dependencies [525efc5]
|
|
26
|
+
- @ai-sdk/provider-utils@5.0.50
|
|
27
|
+
- @ai-sdk/provider@4.0.19
|
|
28
|
+
|
|
3
29
|
## 4.0.82
|
|
4
30
|
|
|
5
31
|
### Patch Changes
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import { Experimental_EvaluationLanguageModel as EvaluationLanguageModel } from "@ai-sdk/provider-utils/experimental-evaluation";
|
|
9
9
|
|
|
10
10
|
// src/version.ts
|
|
11
|
-
var VERSION = true ? "4.0.
|
|
11
|
+
var VERSION = true ? "4.0.84" : "0.0.0-test";
|
|
12
12
|
|
|
13
13
|
// src/google-embedding-model.ts
|
|
14
14
|
import {
|
|
@@ -3912,6 +3912,11 @@ import {
|
|
|
3912
3912
|
WORKFLOW_SERIALIZE as WORKFLOW_SERIALIZE3,
|
|
3913
3913
|
WORKFLOW_DESERIALIZE as WORKFLOW_DESERIALIZE3
|
|
3914
3914
|
} from "@ai-sdk/provider-utils";
|
|
3915
|
+
var googleImageModelsWithFileInputSupport = /* @__PURE__ */ new Set([
|
|
3916
|
+
"gemini-2.5-flash-image",
|
|
3917
|
+
"gemini-3-pro-image-preview",
|
|
3918
|
+
"gemini-3.1-flash-image-preview"
|
|
3919
|
+
]);
|
|
3915
3920
|
var GoogleImageModel = class _GoogleImageModel {
|
|
3916
3921
|
constructor(modelId, settings, config) {
|
|
3917
3922
|
this.modelId = modelId;
|
|
@@ -3934,6 +3939,12 @@ var GoogleImageModel = class _GoogleImageModel {
|
|
|
3934
3939
|
}
|
|
3935
3940
|
return 1;
|
|
3936
3941
|
}
|
|
3942
|
+
get supportsFileInputs() {
|
|
3943
|
+
return googleImageModelsWithFileInputSupport.has(this.modelId) ? true : void 0;
|
|
3944
|
+
}
|
|
3945
|
+
get supportsMaskInputs() {
|
|
3946
|
+
return this.supportsFileInputs === true ? false : void 0;
|
|
3947
|
+
}
|
|
3937
3948
|
get provider() {
|
|
3938
3949
|
return this.config.provider;
|
|
3939
3950
|
}
|
|
@@ -8080,6 +8091,10 @@ import { isRecord, safeParseJSON } from "@ai-sdk/provider-utils";
|
|
|
8080
8091
|
var GoogleRealtimeEventMapper = class {
|
|
8081
8092
|
constructor() {
|
|
8082
8093
|
this.turnCounter = 0;
|
|
8094
|
+
this.inputTranscriptionCounter = 0;
|
|
8095
|
+
this.inputTranscriptionBuffer = "";
|
|
8096
|
+
this.inputTranscriptionBoundary = "none";
|
|
8097
|
+
this.preserveInputTranscriptionAcrossTurnComplete = false;
|
|
8083
8098
|
this.hasAudio = false;
|
|
8084
8099
|
this.hasText = false;
|
|
8085
8100
|
this.hasTranscript = false;
|
|
@@ -8093,12 +8108,11 @@ var GoogleRealtimeEventMapper = class {
|
|
|
8093
8108
|
return `google-item-${this.turnCounter}`;
|
|
8094
8109
|
}
|
|
8095
8110
|
/**
|
|
8096
|
-
* Rolls over to the next turn lazily,
|
|
8097
|
-
* arrives. `turnComplete` merely marks the current turn closed
|
|
8098
|
-
*
|
|
8099
|
-
*
|
|
8100
|
-
*
|
|
8101
|
-
* ordering relative to `turnComplete`.
|
|
8111
|
+
* Rolls over to the next turn lazily, once model content for the next turn
|
|
8112
|
+
* arrives. `turnComplete` merely marks the current turn closed.
|
|
8113
|
+
* This keeps an output transcript that arrives shortly after `turnComplete`
|
|
8114
|
+
* attached to the turn it belongs to, since Google delivers transcription
|
|
8115
|
+
* independently with no guaranteed ordering relative to `turnComplete`.
|
|
8102
8116
|
*/
|
|
8103
8117
|
beginTurnIfClosed() {
|
|
8104
8118
|
if (!this.turnClosed) return;
|
|
@@ -8107,6 +8121,48 @@ var GoogleRealtimeEventMapper = class {
|
|
|
8107
8121
|
this.hasText = false;
|
|
8108
8122
|
this.hasTranscript = false;
|
|
8109
8123
|
this.turnClosed = false;
|
|
8124
|
+
this.preserveInputTranscriptionAcrossTurnComplete = false;
|
|
8125
|
+
}
|
|
8126
|
+
// Google streams input transcription as a sequence of non-accumulating
|
|
8127
|
+
// fragments for a single user utterance (e.g. "The quick brown fox",
|
|
8128
|
+
// " jumps over the", " lazy dog."). Concatenate consecutive fragments into
|
|
8129
|
+
// one running transcript under a stable synthetic id so the realtime reducer
|
|
8130
|
+
// (which overwrites the message for a given id) surfaces them as a single
|
|
8131
|
+
// coherent user message rather than one message per fragment. Google's
|
|
8132
|
+
// `finished` signal is the authoritative utterance boundary. For older
|
|
8133
|
+
// payloads without that signal, response boundaries remain a fallback.
|
|
8134
|
+
//
|
|
8135
|
+
// Input transcriptions can arrive independently of response turn events, so
|
|
8136
|
+
// their synthetic IDs use a sequence independent of `turnCounter`.
|
|
8137
|
+
accumulateInputTranscription({
|
|
8138
|
+
text,
|
|
8139
|
+
finished
|
|
8140
|
+
}) {
|
|
8141
|
+
if (!text) {
|
|
8142
|
+
if (finished === true) {
|
|
8143
|
+
this.finishInputTranscription();
|
|
8144
|
+
}
|
|
8145
|
+
return void 0;
|
|
8146
|
+
}
|
|
8147
|
+
const hasFinishedSignal = finished != null;
|
|
8148
|
+
if (this.inputTranscriptionBuffer !== "" && (this.inputTranscriptionBoundary === "interrupted" || this.inputTranscriptionBoundary === "turn-complete" && !hasFinishedSignal)) {
|
|
8149
|
+
this.finishInputTranscription();
|
|
8150
|
+
}
|
|
8151
|
+
this.inputTranscriptionBoundary = "none";
|
|
8152
|
+
this.inputTranscriptionBuffer += text;
|
|
8153
|
+
const result = {
|
|
8154
|
+
itemId: `google-input-${this.inputTranscriptionCounter}`,
|
|
8155
|
+
transcript: this.inputTranscriptionBuffer
|
|
8156
|
+
};
|
|
8157
|
+
if (finished === true) {
|
|
8158
|
+
this.finishInputTranscription();
|
|
8159
|
+
}
|
|
8160
|
+
return result;
|
|
8161
|
+
}
|
|
8162
|
+
finishInputTranscription() {
|
|
8163
|
+
if (this.inputTranscriptionBuffer === "") return;
|
|
8164
|
+
this.inputTranscriptionCounter++;
|
|
8165
|
+
this.inputTranscriptionBuffer = "";
|
|
8110
8166
|
}
|
|
8111
8167
|
parseServerEvent(raw) {
|
|
8112
8168
|
const data = raw;
|
|
@@ -8163,19 +8219,26 @@ var GoogleRealtimeEventMapper = class {
|
|
|
8163
8219
|
if (data.serverContent != null) {
|
|
8164
8220
|
return this.parseServerContent(data.serverContent, raw);
|
|
8165
8221
|
}
|
|
8166
|
-
if (data.inputTranscription
|
|
8167
|
-
|
|
8168
|
-
|
|
8169
|
-
|
|
8170
|
-
|
|
8171
|
-
|
|
8172
|
-
|
|
8222
|
+
if (data.inputTranscription != null) {
|
|
8223
|
+
const transcription = this.accumulateInputTranscription(
|
|
8224
|
+
data.inputTranscription
|
|
8225
|
+
);
|
|
8226
|
+
if (transcription != null) {
|
|
8227
|
+
return {
|
|
8228
|
+
type: "input-transcription-completed",
|
|
8229
|
+
...transcription,
|
|
8230
|
+
raw
|
|
8231
|
+
};
|
|
8232
|
+
}
|
|
8173
8233
|
}
|
|
8174
8234
|
return { type: "custom", rawType: String(Object.keys(data)[0]), raw };
|
|
8175
8235
|
}
|
|
8176
8236
|
parseServerContent(serverContent, raw) {
|
|
8177
8237
|
const events = [];
|
|
8178
8238
|
if (serverContent.interrupted) {
|
|
8239
|
+
this.inputTranscriptionBoundary = "interrupted";
|
|
8240
|
+
this.preserveInputTranscriptionAcrossTurnComplete = true;
|
|
8241
|
+
this.turnClosed = true;
|
|
8179
8242
|
events.push({
|
|
8180
8243
|
type: "speech-started",
|
|
8181
8244
|
raw
|
|
@@ -8216,13 +8279,17 @@ var GoogleRealtimeEventMapper = class {
|
|
|
8216
8279
|
raw
|
|
8217
8280
|
});
|
|
8218
8281
|
}
|
|
8219
|
-
if (serverContent.inputTranscription
|
|
8220
|
-
|
|
8221
|
-
|
|
8222
|
-
|
|
8223
|
-
|
|
8224
|
-
|
|
8225
|
-
|
|
8282
|
+
if (serverContent.inputTranscription != null) {
|
|
8283
|
+
const transcription = this.accumulateInputTranscription(
|
|
8284
|
+
serverContent.inputTranscription
|
|
8285
|
+
);
|
|
8286
|
+
if (transcription != null) {
|
|
8287
|
+
events.push({
|
|
8288
|
+
type: "input-transcription-completed",
|
|
8289
|
+
...transcription,
|
|
8290
|
+
raw
|
|
8291
|
+
});
|
|
8292
|
+
}
|
|
8226
8293
|
}
|
|
8227
8294
|
if (serverContent.generationComplete) {
|
|
8228
8295
|
events.push({
|
|
@@ -8277,6 +8344,13 @@ var GoogleRealtimeEventMapper = class {
|
|
|
8277
8344
|
raw
|
|
8278
8345
|
});
|
|
8279
8346
|
this.turnClosed = true;
|
|
8347
|
+
if (this.preserveInputTranscriptionAcrossTurnComplete) {
|
|
8348
|
+
this.preserveInputTranscriptionAcrossTurnComplete = false;
|
|
8349
|
+
} else {
|
|
8350
|
+
if (this.inputTranscriptionBoundary !== "interrupted") {
|
|
8351
|
+
this.inputTranscriptionBoundary = "turn-complete";
|
|
8352
|
+
}
|
|
8353
|
+
}
|
|
8280
8354
|
}
|
|
8281
8355
|
if (events.length === 0) {
|
|
8282
8356
|
return { type: "custom", rawType: "serverContent", raw };
|
|
@@ -8405,7 +8479,9 @@ function buildGoogleSessionConfig(config, modelId) {
|
|
|
8405
8479
|
if (config?.outputAudioTranscription != null) {
|
|
8406
8480
|
setup.outputAudioTranscription = {};
|
|
8407
8481
|
}
|
|
8408
|
-
const
|
|
8482
|
+
const rawGenerationConfig = restProviderOptions.generationConfig;
|
|
8483
|
+
const explicitThinkingConfig = googleOptions?.thinkingConfig ?? (isRecord(rawGenerationConfig) && isRecord(rawGenerationConfig.thinkingConfig) ? rawGenerationConfig.thinkingConfig : void 0);
|
|
8484
|
+
const thinkingConfig = isThinkingLiveModel(modelId) && explicitThinkingConfig?.thinkingLevel == null && explicitThinkingConfig?.thinkingBudget == null ? { ...explicitThinkingConfig, thinkingLevel: "low" } : googleOptions?.thinkingConfig;
|
|
8409
8485
|
const applyThinkingConfig = () => {
|
|
8410
8486
|
if (thinkingConfig == null) return;
|
|
8411
8487
|
const target = isRecord(setup.generationConfig) ? setup.generationConfig : generationConfig;
|