@ai-sdk/google 4.0.83 → 4.0.85

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 CHANGED
@@ -1,5 +1,19 @@
1
1
  # @ai-sdk/google
2
2
 
3
+ ## 4.0.85
4
+
5
+ ### Patch Changes
6
+
7
+ - c2511c1: fix: use standards-compliant User-Agent header
8
+ - Updated dependencies [c2511c1]
9
+ - @ai-sdk/provider-utils@5.0.51
10
+
11
+ ## 4.0.84
12
+
13
+ ### Patch Changes
14
+
15
+ - 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
16
+
3
17
  ## 4.0.83
4
18
 
5
19
  ### 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.83" : "0.0.0-test";
11
+ var VERSION = true ? "4.0.85" : "0.0.0-test";
12
12
 
13
13
  // src/google-embedding-model.ts
14
14
  import {
@@ -8091,6 +8091,10 @@ import { isRecord, safeParseJSON } from "@ai-sdk/provider-utils";
8091
8091
  var GoogleRealtimeEventMapper = class {
8092
8092
  constructor() {
8093
8093
  this.turnCounter = 0;
8094
+ this.inputTranscriptionCounter = 0;
8095
+ this.inputTranscriptionBuffer = "";
8096
+ this.inputTranscriptionBoundary = "none";
8097
+ this.preserveInputTranscriptionAcrossTurnComplete = false;
8094
8098
  this.hasAudio = false;
8095
8099
  this.hasText = false;
8096
8100
  this.hasTranscript = false;
@@ -8104,12 +8108,11 @@ var GoogleRealtimeEventMapper = class {
8104
8108
  return `google-item-${this.turnCounter}`;
8105
8109
  }
8106
8110
  /**
8107
- * Rolls over to the next turn lazily, only once new model content actually
8108
- * arrives. `turnComplete` merely marks the current turn closed; the counter
8109
- * is not advanced until the next response begins. This keeps a transcript
8110
- * that arrives shortly after `turnComplete` attached to the turn it belongs
8111
- * to, since Google delivers transcription independently with no guaranteed
8112
- * 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`.
8113
8116
  */
8114
8117
  beginTurnIfClosed() {
8115
8118
  if (!this.turnClosed) return;
@@ -8118,6 +8121,48 @@ var GoogleRealtimeEventMapper = class {
8118
8121
  this.hasText = false;
8119
8122
  this.hasTranscript = false;
8120
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 = "";
8121
8166
  }
8122
8167
  parseServerEvent(raw) {
8123
8168
  const data = raw;
@@ -8174,19 +8219,26 @@ var GoogleRealtimeEventMapper = class {
8174
8219
  if (data.serverContent != null) {
8175
8220
  return this.parseServerContent(data.serverContent, raw);
8176
8221
  }
8177
- if (data.inputTranscription?.text != null) {
8178
- return {
8179
- type: "input-transcription-completed",
8180
- itemId: `google-input-${this.turnCounter}`,
8181
- transcript: data.inputTranscription.text,
8182
- raw
8183
- };
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
+ }
8184
8233
  }
8185
8234
  return { type: "custom", rawType: String(Object.keys(data)[0]), raw };
8186
8235
  }
8187
8236
  parseServerContent(serverContent, raw) {
8188
8237
  const events = [];
8189
8238
  if (serverContent.interrupted) {
8239
+ this.inputTranscriptionBoundary = "interrupted";
8240
+ this.preserveInputTranscriptionAcrossTurnComplete = true;
8241
+ this.turnClosed = true;
8190
8242
  events.push({
8191
8243
  type: "speech-started",
8192
8244
  raw
@@ -8227,13 +8279,17 @@ var GoogleRealtimeEventMapper = class {
8227
8279
  raw
8228
8280
  });
8229
8281
  }
8230
- if (serverContent.inputTranscription?.text) {
8231
- events.push({
8232
- type: "input-transcription-completed",
8233
- itemId: `google-input-${this.turnCounter}`,
8234
- transcript: serverContent.inputTranscription.text,
8235
- raw
8236
- });
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
+ }
8237
8293
  }
8238
8294
  if (serverContent.generationComplete) {
8239
8295
  events.push({
@@ -8288,6 +8344,13 @@ var GoogleRealtimeEventMapper = class {
8288
8344
  raw
8289
8345
  });
8290
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
+ }
8291
8354
  }
8292
8355
  if (events.length === 0) {
8293
8356
  return { type: "custom", rawType: "serverContent", raw };
@@ -9528,7 +9591,7 @@ function createGoogle(options = {}) {
9528
9591
  }),
9529
9592
  ...options.headers
9530
9593
  },
9531
- `ai-sdk/google/${VERSION}`
9594
+ `ai-sdk-google/${VERSION}`
9532
9595
  );
9533
9596
  const getSupportedUrls = (modelId, includeExternalUrls = modelId == null || supportsExternalFileUrls(modelId)) => ({
9534
9597
  "*": [