@ai-sdk/google 4.0.25 → 4.0.26

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,14 @@
1
1
  # @ai-sdk/google
2
2
 
3
+ ## 4.0.26
4
+
5
+ ### Patch Changes
6
+
7
+ - c49380c: feat: add experimental streaming speech translation models (`openai.translation('gpt-realtime-translate')` over the OpenAI Realtime translations WebSocket and `google.translation('gemini-3.5-live-translate-preview')` over the Gemini Live API). `connectToWebSocket` in `@ai-sdk/provider-utils` now passes close code and reason to `onClose` (additive, optional parameter).
8
+ - Updated dependencies [0c464d9]
9
+ - Updated dependencies [c49380c]
10
+ - @ai-sdk/provider-utils@5.0.14
11
+
3
12
  ## 4.0.25
4
13
 
5
14
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as _ai_sdk_provider_utils from '@ai-sdk/provider-utils';
2
- import { InferSchema, FetchFunction } from '@ai-sdk/provider-utils';
3
- import { ProviderV4, LanguageModelV4, ImageModelV4, EmbeddingModelV4, Experimental_VideoModelV4, SpeechModelV4, FilesV4, Experimental_RealtimeFactoryV4, Experimental_RealtimeModelV4, Experimental_RealtimeModelV4ClientSecretOptions, Experimental_RealtimeModelV4ClientSecretResult, Experimental_RealtimeModelV4ServerEvent, Experimental_RealtimeModelV4ClientEvent, Experimental_RealtimeModelV4SessionConfig } from '@ai-sdk/provider';
2
+ import { InferSchema, FetchFunction, WebSocketConstructor, WORKFLOW_SERIALIZE, WORKFLOW_DESERIALIZE } from '@ai-sdk/provider-utils';
3
+ import * as _ai_sdk_provider from '@ai-sdk/provider';
4
+ import { ProviderV4, LanguageModelV4, ImageModelV4, EmbeddingModelV4, Experimental_VideoModelV4, Experimental_SpeechTranslationModelV4, SpeechModelV4, FilesV4, Experimental_RealtimeFactoryV4, Experimental_RealtimeModelV4, Experimental_RealtimeModelV4ClientSecretOptions, Experimental_RealtimeModelV4ClientSecretResult, Experimental_RealtimeModelV4ServerEvent, Experimental_RealtimeModelV4ClientEvent, Experimental_RealtimeModelV4SessionConfig, Experimental_SpeechTranslationModelV4StreamOptions } from '@ai-sdk/provider';
4
5
 
5
6
  declare const googleErrorDataSchema: _ai_sdk_provider_utils.LazySchema<{
6
7
  error: {
@@ -510,6 +511,12 @@ interface GoogleImageSettings {
510
511
  maxImagesPerCall?: number;
511
512
  }
512
513
 
514
+ type GoogleTranslationModelId = 'gemini-3.5-live-translate-preview' | (string & {});
515
+ declare const googleTranslationModelOptions: _ai_sdk_provider_utils.LazySchema<{
516
+ echoTargetLanguage?: boolean | undefined;
517
+ }>;
518
+ type GoogleTranslationModelOptions = InferSchema<typeof googleTranslationModelOptions>;
519
+
513
520
  interface GoogleProvider extends ProviderV4 {
514
521
  (modelId: GoogleModelId): LanguageModelV4;
515
522
  languageModel(modelId: GoogleModelId): LanguageModelV4;
@@ -546,6 +553,14 @@ interface GoogleProvider extends ProviderV4 {
546
553
  * Creates a model for video generation.
547
554
  */
548
555
  videoModel(modelId: GoogleVideoModelId): Experimental_VideoModelV4;
556
+ /**
557
+ * Creates an experimental model for streaming speech translation.
558
+ */
559
+ translation(modelId: GoogleTranslationModelId): Experimental_SpeechTranslationModelV4;
560
+ /**
561
+ * Creates an experimental model for streaming speech translation.
562
+ */
563
+ speechTranslationModel(modelId: GoogleTranslationModelId): Experimental_SpeechTranslationModelV4;
549
564
  /**
550
565
  * Creates a model for speech generation (text-to-speech).
551
566
  */
@@ -595,6 +610,11 @@ interface GoogleProviderSettings {
595
610
  * Optional function to generate a unique ID for each request.
596
611
  */
597
612
  generateId?: () => string;
613
+ /**
614
+ * Custom WebSocket implementation. This is useful for testing or for
615
+ * runtimes that need a WebSocket constructor with header support.
616
+ */
617
+ webSocket?: WebSocketConstructor;
598
618
  /**
599
619
  * Custom provider name
600
620
  * Defaults to 'google.generative-ai'.
@@ -658,6 +678,33 @@ type GoogleRealtimeModelOptions = {
658
678
  };
659
679
  };
660
680
 
681
+ type GoogleTranslationModelConfig = {
682
+ provider: string;
683
+ baseURL: string;
684
+ headers: () => Record<string, string | undefined>;
685
+ webSocket?: WebSocketConstructor;
686
+ _internal?: {
687
+ currentDate?: () => Date;
688
+ finishGraceMs?: number;
689
+ };
690
+ };
691
+ declare class GoogleTranslationModel implements Experimental_SpeechTranslationModelV4 {
692
+ readonly specificationVersion = "v4";
693
+ readonly modelId: GoogleTranslationModelId;
694
+ private readonly config;
695
+ static [WORKFLOW_SERIALIZE](model: GoogleTranslationModel): {
696
+ modelId: string;
697
+ config: _ai_sdk_provider.JSONObject;
698
+ };
699
+ static [WORKFLOW_DESERIALIZE](options: {
700
+ modelId: GoogleTranslationModelId;
701
+ config: GoogleTranslationModelConfig;
702
+ }): GoogleTranslationModel;
703
+ get provider(): string;
704
+ constructor(modelId: GoogleTranslationModelId, config: GoogleTranslationModelConfig);
705
+ doStream(options: Experimental_SpeechTranslationModelV4StreamOptions): Promise<Awaited<ReturnType<Experimental_SpeechTranslationModelV4['doStream']>>>;
706
+ }
707
+
661
708
  declare const VERSION: string;
662
709
 
663
- export { GoogleRealtimeModel as Experimental_GoogleRealtimeModel, type GoogleRealtimeModelConfig as Experimental_GoogleRealtimeModelConfig, type GoogleRealtimeModelId as Experimental_GoogleRealtimeModelId, type GoogleRealtimeModelOptions as Experimental_GoogleRealtimeModelOptions, type GoogleEmbeddingModelOptions, type GoogleErrorData, type GoogleFilesUploadOptions, type GoogleEmbeddingModelOptions as GoogleGenerativeAIEmbeddingProviderOptions, type GoogleImageModelOptions as GoogleGenerativeAIImageProviderOptions, type GoogleProvider as GoogleGenerativeAIProvider, type GoogleProviderMetadata as GoogleGenerativeAIProviderMetadata, type GoogleLanguageModelOptions as GoogleGenerativeAIProviderOptions, type GoogleProviderSettings as GoogleGenerativeAIProviderSettings, type GoogleVideoModelId as GoogleGenerativeAIVideoModelId, type GoogleVideoModelOptions as GoogleGenerativeAIVideoProviderOptions, type GoogleImageModelOptions, type GoogleInteractionsAgentName, type GoogleInteractionsModelId, type GoogleInteractionsProviderMetadata, type GoogleLanguageModelInteractionsOptions, type GoogleLanguageModelOptions, type GoogleProvider, type GoogleProviderMetadata, type GoogleProviderSettings, type GoogleSpeechModelId, type GoogleSpeechModelOptions, type GoogleVideoModelId, type GoogleVideoModelOptions, VERSION, createGoogle, createGoogle as createGoogleGenerativeAI, google };
710
+ export { GoogleRealtimeModel as Experimental_GoogleRealtimeModel, type GoogleRealtimeModelConfig as Experimental_GoogleRealtimeModelConfig, type GoogleRealtimeModelId as Experimental_GoogleRealtimeModelId, type GoogleRealtimeModelOptions as Experimental_GoogleRealtimeModelOptions, GoogleTranslationModel as Experimental_GoogleTranslationModel, type GoogleTranslationModelConfig as Experimental_GoogleTranslationModelConfig, type GoogleTranslationModelId as Experimental_GoogleTranslationModelId, type GoogleTranslationModelOptions as Experimental_GoogleTranslationModelOptions, type GoogleEmbeddingModelOptions, type GoogleErrorData, type GoogleFilesUploadOptions, type GoogleEmbeddingModelOptions as GoogleGenerativeAIEmbeddingProviderOptions, type GoogleImageModelOptions as GoogleGenerativeAIImageProviderOptions, type GoogleProvider as GoogleGenerativeAIProvider, type GoogleProviderMetadata as GoogleGenerativeAIProviderMetadata, type GoogleLanguageModelOptions as GoogleGenerativeAIProviderOptions, type GoogleProviderSettings as GoogleGenerativeAIProviderSettings, type GoogleVideoModelId as GoogleGenerativeAIVideoModelId, type GoogleVideoModelOptions as GoogleGenerativeAIVideoProviderOptions, type GoogleImageModelOptions, type GoogleInteractionsAgentName, type GoogleInteractionsModelId, type GoogleInteractionsProviderMetadata, type GoogleLanguageModelInteractionsOptions, type GoogleLanguageModelOptions, type GoogleProvider, type GoogleProviderMetadata, type GoogleProviderSettings, type GoogleSpeechModelId, type GoogleSpeechModelOptions, type GoogleVideoModelId, type GoogleVideoModelOptions, VERSION, createGoogle, createGoogle as createGoogleGenerativeAI, google };
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  } from "@ai-sdk/provider-utils";
8
8
 
9
9
  // src/version.ts
10
- var VERSION = true ? "4.0.25" : "0.0.0-test";
10
+ var VERSION = true ? "4.0.26" : "0.0.0-test";
11
11
 
12
12
  // src/google-embedding-model.ts
13
13
  import {
@@ -6930,6 +6930,24 @@ function pruneUndefined(obj) {
6930
6930
  return result;
6931
6931
  }
6932
6932
 
6933
+ // src/get-realtime-base-url.ts
6934
+ function getRealtimeBaseURL(baseURL) {
6935
+ const url = new URL(baseURL);
6936
+ const pathSegments = url.pathname.split("/");
6937
+ const version = pathSegments.at(-1);
6938
+ if (version === "v1beta" || version === "v1alpha") {
6939
+ pathSegments.pop();
6940
+ url.pathname = pathSegments.join("/") || "/";
6941
+ }
6942
+ return url;
6943
+ }
6944
+ function getRealtimeWebSocketURL(baseURL, webSocketPath) {
6945
+ const url = getRealtimeBaseURL(baseURL);
6946
+ url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
6947
+ url.pathname = `${url.pathname.replace(/\/$/, "")}/ws/${webSocketPath}`;
6948
+ return url;
6949
+ }
6950
+
6933
6951
  // src/realtime/google-realtime-event-mapper.ts
6934
6952
  import { safeParseJSON } from "@ai-sdk/provider-utils";
6935
6953
  function isRecord(value) {
@@ -7257,26 +7275,13 @@ function buildGoogleSessionConfig(config, modelId) {
7257
7275
 
7258
7276
  // src/realtime/google-realtime-model.ts
7259
7277
  var realtimeWebSocketPath = "google.ai.generativelanguage.v1alpha.GenerativeService.BidiGenerateContentConstrained";
7260
- function getRealtimeBaseURL(baseURL) {
7261
- const url = new URL(baseURL);
7262
- const pathSegments = url.pathname.split("/");
7263
- const version = pathSegments.at(-1);
7264
- if (version === "v1beta" || version === "v1alpha") {
7265
- pathSegments.pop();
7266
- url.pathname = pathSegments.join("/") || "/";
7267
- }
7268
- return url;
7269
- }
7270
7278
  function getAuthTokensURL(baseURL) {
7271
7279
  const url = getRealtimeBaseURL(baseURL);
7272
7280
  url.pathname = `${url.pathname.replace(/\/$/, "")}/v1alpha/auth_tokens`;
7273
7281
  return url.toString();
7274
7282
  }
7275
7283
  function getWebSocketURL(baseURL) {
7276
- const url = getRealtimeBaseURL(baseURL);
7277
- url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
7278
- url.pathname = `${url.pathname.replace(/\/$/, "")}/ws/${realtimeWebSocketPath}`;
7279
- return url.toString();
7284
+ return getRealtimeWebSocketURL(baseURL, realtimeWebSocketPath).toString();
7280
7285
  }
7281
7286
  var GoogleRealtimeModel = class {
7282
7287
  constructor(modelId, config) {
@@ -7351,6 +7356,459 @@ var GoogleRealtimeModel = class {
7351
7356
  }
7352
7357
  };
7353
7358
 
7359
+ // src/translation/google-translation-model.ts
7360
+ import {
7361
+ InvalidArgumentError
7362
+ } from "@ai-sdk/provider";
7363
+ import {
7364
+ connectToWebSocket,
7365
+ combineHeaders as combineHeaders9,
7366
+ convertBase64ToUint8Array as convertBase64ToUint8Array2,
7367
+ convertToBase64 as convertToBase644,
7368
+ parseProviderOptions as parseProviderOptions8,
7369
+ safeParseJSON as safeParseJSON2,
7370
+ serializeModelOptions as serializeModelOptions6,
7371
+ WORKFLOW_DESERIALIZE as WORKFLOW_DESERIALIZE6,
7372
+ WORKFLOW_SERIALIZE as WORKFLOW_SERIALIZE6,
7373
+ waitForWebSocketBufferDrain
7374
+ } from "@ai-sdk/provider-utils";
7375
+
7376
+ // src/translation/google-translation-model-options.ts
7377
+ import {
7378
+ lazySchema as lazySchema20,
7379
+ zodSchema as zodSchema20
7380
+ } from "@ai-sdk/provider-utils";
7381
+ import { z as z22 } from "zod/v4";
7382
+ var googleTranslationModelOptions = lazySchema20(
7383
+ () => zodSchema20(
7384
+ z22.object({
7385
+ /**
7386
+ * Whether input audio already in the target language should be echoed
7387
+ * instead of producing silence.
7388
+ */
7389
+ echoTargetLanguage: z22.boolean().optional()
7390
+ })
7391
+ )
7392
+ );
7393
+
7394
+ // src/translation/google-translation-model.ts
7395
+ var liveWebSocketPath = "google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent";
7396
+ var defaultFinishGraceMs = 1e3;
7397
+ var googleLiveOutputAudioRate = 24e3;
7398
+ var pcm16SilenceAmplitudeThreshold = 128;
7399
+ function getLiveWebSocketURL(baseURL, apiKey) {
7400
+ const url = getRealtimeWebSocketURL(baseURL, liveWebSocketPath);
7401
+ url.searchParams.set("key", apiKey);
7402
+ return url;
7403
+ }
7404
+ var GoogleTranslationModel = class _GoogleTranslationModel {
7405
+ constructor(modelId, config) {
7406
+ this.specificationVersion = "v4";
7407
+ this.modelId = modelId;
7408
+ this.config = config;
7409
+ }
7410
+ static [WORKFLOW_SERIALIZE6](model) {
7411
+ return serializeModelOptions6({
7412
+ modelId: model.modelId,
7413
+ config: model.config
7414
+ });
7415
+ }
7416
+ static [WORKFLOW_DESERIALIZE6](options) {
7417
+ return new _GoogleTranslationModel(options.modelId, options.config);
7418
+ }
7419
+ get provider() {
7420
+ return this.config.provider;
7421
+ }
7422
+ async doStream(options) {
7423
+ var _a, _b, _c, _d, _e, _f;
7424
+ if (options.targetLanguage == null) {
7425
+ throw new InvalidArgumentError({
7426
+ argument: "targetLanguage",
7427
+ message: `targetLanguage is required for translation model '${this.modelId}'.`
7428
+ });
7429
+ }
7430
+ const currentDate = (_c = (_b = (_a = this.config._internal) == null ? void 0 : _a.currentDate) == null ? void 0 : _b.call(_a)) != null ? _c : /* @__PURE__ */ new Date();
7431
+ const googleOptions = await parseProviderOptions8({
7432
+ provider: "google",
7433
+ providerOptions: options.providerOptions,
7434
+ schema: googleTranslationModelOptions
7435
+ });
7436
+ const warnings = [];
7437
+ validateGoogleTranslationInputAudioFormat(options.inputAudioFormat);
7438
+ if (options.sourceLanguage != null) {
7439
+ warnings.push({
7440
+ type: "unsupported",
7441
+ feature: "sourceLanguage",
7442
+ details: "The Gemini Live translation API auto-detects the source language and does not accept a source language."
7443
+ });
7444
+ }
7445
+ if (options.outputAudioFormat != null) {
7446
+ warnings.push({
7447
+ type: "unsupported",
7448
+ feature: "outputAudioFormat",
7449
+ details: "The Gemini Live API always outputs 24kHz 16-bit PCM audio and does not accept an output audio format."
7450
+ });
7451
+ }
7452
+ const headers = combineHeaders9(this.config.headers(), options.headers);
7453
+ let apiKey;
7454
+ for (const [key, value] of Object.entries(headers)) {
7455
+ if (key.toLowerCase() === "x-goog-api-key" && value != null) {
7456
+ apiKey = value;
7457
+ }
7458
+ }
7459
+ if (apiKey == null) {
7460
+ throw new Error(
7461
+ "Google Generative AI API key is required for streaming translation."
7462
+ );
7463
+ }
7464
+ const webSocketHeaders = Object.fromEntries(
7465
+ Object.entries(headers).filter(
7466
+ ([key]) => key.toLowerCase() !== "x-goog-api-key"
7467
+ )
7468
+ );
7469
+ const setup = buildGoogleLiveTranslationSetup({
7470
+ modelId: this.modelId,
7471
+ targetLanguage: options.targetLanguage,
7472
+ providerOptions: googleOptions
7473
+ });
7474
+ return {
7475
+ request: { body: setup },
7476
+ response: {
7477
+ timestamp: currentDate,
7478
+ modelId: this.modelId
7479
+ },
7480
+ stream: createGoogleLiveTranslationStream({
7481
+ webSocket: this.config.webSocket,
7482
+ url: getLiveWebSocketURL(this.config.baseURL, apiKey),
7483
+ headers: webSocketHeaders,
7484
+ setup,
7485
+ inputAudioRate: (_d = options.inputAudioFormat.rate) != null ? _d : 16e3,
7486
+ finishGraceMs: (_f = (_e = this.config._internal) == null ? void 0 : _e.finishGraceMs) != null ? _f : defaultFinishGraceMs,
7487
+ warnings,
7488
+ audio: options.audio,
7489
+ abortSignal: options.abortSignal,
7490
+ includeRawChunks: options.includeRawChunks
7491
+ })
7492
+ };
7493
+ }
7494
+ };
7495
+ function createGoogleLiveTranslationStream({
7496
+ webSocket,
7497
+ url,
7498
+ headers,
7499
+ setup,
7500
+ inputAudioRate,
7501
+ finishGraceMs,
7502
+ warnings,
7503
+ audio,
7504
+ abortSignal,
7505
+ includeRawChunks
7506
+ }) {
7507
+ let finished = false;
7508
+ let cleanup = () => {
7509
+ };
7510
+ return new ReadableStream({
7511
+ start: (controller) => {
7512
+ let audioReader;
7513
+ let connection;
7514
+ let resolveSetupComplete;
7515
+ const setupComplete = new Promise((resolve7) => {
7516
+ resolveSetupComplete = resolve7;
7517
+ });
7518
+ let turnCounter = 0;
7519
+ let sourceText = "";
7520
+ let sourceTurnBuffer = "";
7521
+ let translationText = "";
7522
+ let translationTurnBuffer = "";
7523
+ let audioEnded = false;
7524
+ let usage;
7525
+ let openTurn = false;
7526
+ let sawTurnComplete = false;
7527
+ let trailingSilenceMs = 0;
7528
+ let finishTimer;
7529
+ const itemId = () => `google-item-${turnCounter}`;
7530
+ const cancelPendingFinish = () => {
7531
+ if (finishTimer != null) {
7532
+ clearTimeout(finishTimer);
7533
+ finishTimer = void 0;
7534
+ }
7535
+ };
7536
+ const schedulePendingFinish = () => {
7537
+ if (finished || finishTimer != null) return;
7538
+ finishTimer = setTimeout(() => {
7539
+ finishTimer = void 0;
7540
+ finish();
7541
+ }, finishGraceMs);
7542
+ };
7543
+ const onTurnActivity = () => {
7544
+ openTurn = true;
7545
+ trailingSilenceMs = 0;
7546
+ cancelPendingFinish();
7547
+ };
7548
+ cleanup = (closeCode) => {
7549
+ cancelPendingFinish();
7550
+ if (audioReader != null) {
7551
+ void audioReader.cancel().catch(() => {
7552
+ });
7553
+ } else {
7554
+ void audio.cancel().catch(() => {
7555
+ });
7556
+ }
7557
+ connection == null ? void 0 : connection.close(closeCode);
7558
+ };
7559
+ const finishWithError = (error) => {
7560
+ if (finished) return;
7561
+ finished = true;
7562
+ cleanup();
7563
+ controller.error(error);
7564
+ };
7565
+ const finish = () => {
7566
+ if (finished) return;
7567
+ if (sourceTurnBuffer !== "" || translationTurnBuffer !== "") {
7568
+ completeTurn();
7569
+ }
7570
+ finished = true;
7571
+ controller.enqueue({
7572
+ type: "finish",
7573
+ sourceText,
7574
+ outputText: translationText,
7575
+ usage
7576
+ });
7577
+ controller.close();
7578
+ cleanup(1e3);
7579
+ };
7580
+ const completeTurn = () => {
7581
+ if (sourceTurnBuffer !== "") {
7582
+ controller.enqueue({
7583
+ type: "source-transcript-final",
7584
+ id: itemId(),
7585
+ text: sourceTurnBuffer
7586
+ });
7587
+ sourceText += sourceTurnBuffer;
7588
+ sourceTurnBuffer = "";
7589
+ }
7590
+ if (translationTurnBuffer !== "") {
7591
+ controller.enqueue({
7592
+ type: "output-text-final",
7593
+ id: itemId(),
7594
+ text: translationTurnBuffer
7595
+ });
7596
+ translationText += translationTurnBuffer;
7597
+ translationTurnBuffer = "";
7598
+ }
7599
+ turnCounter++;
7600
+ };
7601
+ const sendAudio = async (socket) => {
7602
+ audioReader = audio.getReader();
7603
+ try {
7604
+ while (true) {
7605
+ const { done, value } = await audioReader.read();
7606
+ if (done || finished) break;
7607
+ socket.send(
7608
+ JSON.stringify({
7609
+ realtimeInput: {
7610
+ audio: {
7611
+ data: convertToBase644(value),
7612
+ mimeType: `audio/pcm;rate=${inputAudioRate}`
7613
+ }
7614
+ }
7615
+ })
7616
+ );
7617
+ await waitForWebSocketBufferDrain(socket);
7618
+ }
7619
+ } finally {
7620
+ audioReader.releaseLock();
7621
+ audioReader = void 0;
7622
+ }
7623
+ if (!finished) {
7624
+ socket.send(
7625
+ JSON.stringify({ realtimeInput: { audioStreamEnd: true } })
7626
+ );
7627
+ audioEnded = true;
7628
+ if (sawTurnComplete && !openTurn) {
7629
+ schedulePendingFinish();
7630
+ }
7631
+ }
7632
+ };
7633
+ connection = connectToWebSocket({
7634
+ url,
7635
+ headers,
7636
+ webSocket,
7637
+ abortSignal,
7638
+ onAbort: finishWithError,
7639
+ onProcessingError: finishWithError,
7640
+ onOpen: (socket) => {
7641
+ controller.enqueue({ type: "stream-start", warnings });
7642
+ socket.send(JSON.stringify({ setup }));
7643
+ void setupComplete.then(() => finished ? void 0 : sendAudio(socket)).catch(finishWithError);
7644
+ },
7645
+ onMessageText: async (text) => {
7646
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
7647
+ if (finished) return;
7648
+ const parsed = await safeParseJSON2({ text });
7649
+ if (!parsed.success) return;
7650
+ const message = parsed.value;
7651
+ if (includeRawChunks) {
7652
+ controller.enqueue({ type: "raw", rawValue: message });
7653
+ }
7654
+ if (message.setupComplete != null) {
7655
+ resolveSetupComplete();
7656
+ }
7657
+ if (message.usageMetadata != null) {
7658
+ usage = accumulateGoogleLiveUsage(usage, message.usageMetadata);
7659
+ }
7660
+ if (message.error != null) {
7661
+ finishWithError(
7662
+ new Error((_a = message.error.message) != null ? _a : "Google Live API error")
7663
+ );
7664
+ return;
7665
+ }
7666
+ const inputTranscriptionText = (_e = (_c = (_b = message.serverContent) == null ? void 0 : _b.inputTranscription) == null ? void 0 : _c.text) != null ? _e : (_d = message.inputTranscription) == null ? void 0 : _d.text;
7667
+ if (inputTranscriptionText) {
7668
+ onTurnActivity();
7669
+ sourceTurnBuffer += inputTranscriptionText;
7670
+ controller.enqueue({
7671
+ type: "source-transcript-delta",
7672
+ id: itemId(),
7673
+ delta: inputTranscriptionText
7674
+ });
7675
+ }
7676
+ const serverContent = message.serverContent;
7677
+ if (serverContent == null) {
7678
+ return;
7679
+ }
7680
+ for (const part of (_g = (_f = serverContent.modelTurn) == null ? void 0 : _f.parts) != null ? _g : []) {
7681
+ if ((_h = part.inlineData) == null ? void 0 : _h.data) {
7682
+ controller.enqueue({
7683
+ type: "audio",
7684
+ id: itemId(),
7685
+ audio: part.inlineData.data
7686
+ });
7687
+ const silenceDurationMs = getPcm16SilenceDurationMs(
7688
+ part.inlineData.data
7689
+ );
7690
+ if (audioEnded && silenceDurationMs != null) {
7691
+ trailingSilenceMs += silenceDurationMs;
7692
+ if (trailingSilenceMs >= finishGraceMs) {
7693
+ finish();
7694
+ return;
7695
+ }
7696
+ } else {
7697
+ onTurnActivity();
7698
+ }
7699
+ }
7700
+ }
7701
+ if ((_i = serverContent.outputTranscription) == null ? void 0 : _i.text) {
7702
+ onTurnActivity();
7703
+ translationTurnBuffer += serverContent.outputTranscription.text;
7704
+ controller.enqueue({
7705
+ type: "output-text-delta",
7706
+ id: itemId(),
7707
+ delta: serverContent.outputTranscription.text
7708
+ });
7709
+ }
7710
+ if (serverContent.turnComplete) {
7711
+ completeTurn();
7712
+ openTurn = false;
7713
+ sawTurnComplete = true;
7714
+ if (audioEnded) {
7715
+ schedulePendingFinish();
7716
+ }
7717
+ }
7718
+ },
7719
+ onSocketError: () => {
7720
+ finishWithError(new Error("Google Live translation error"));
7721
+ },
7722
+ onClose: ({ code, reason }) => {
7723
+ if (finished) return;
7724
+ if (finishTimer != null) {
7725
+ finish();
7726
+ return;
7727
+ }
7728
+ finishWithError(
7729
+ new Error(
7730
+ `Google Live translation WebSocket closed unexpectedly before finishing (code ${code != null ? code : "unknown"}${reason ? `, reason: ${reason}` : ""}).`
7731
+ )
7732
+ );
7733
+ }
7734
+ });
7735
+ },
7736
+ cancel: () => {
7737
+ if (finished) return;
7738
+ finished = true;
7739
+ cleanup();
7740
+ }
7741
+ });
7742
+ }
7743
+ function accumulateGoogleLiveUsage(usage, usageMetadata) {
7744
+ var _a, _b;
7745
+ let inputAudioTokens = usage == null ? void 0 : usage.inputAudioTokens;
7746
+ let outputAudioTokens = usage == null ? void 0 : usage.outputAudioTokens;
7747
+ for (const detail of (_a = usageMetadata.promptTokensDetails) != null ? _a : []) {
7748
+ if (detail.modality === "AUDIO" && detail.tokenCount != null) {
7749
+ inputAudioTokens = (inputAudioTokens != null ? inputAudioTokens : 0) + detail.tokenCount;
7750
+ }
7751
+ }
7752
+ for (const detail of (_b = usageMetadata.responseTokensDetails) != null ? _b : []) {
7753
+ if (detail.modality === "AUDIO" && detail.tokenCount != null) {
7754
+ outputAudioTokens = (outputAudioTokens != null ? outputAudioTokens : 0) + detail.tokenCount;
7755
+ }
7756
+ }
7757
+ if (inputAudioTokens == null && outputAudioTokens == null) {
7758
+ return usage;
7759
+ }
7760
+ return {
7761
+ ...usage,
7762
+ ...inputAudioTokens != null ? { inputAudioTokens } : {},
7763
+ ...outputAudioTokens != null ? { outputAudioTokens } : {}
7764
+ };
7765
+ }
7766
+ function getPcm16SilenceDurationMs(audio) {
7767
+ let bytes;
7768
+ try {
7769
+ bytes = convertBase64ToUint8Array2(audio);
7770
+ } catch (e) {
7771
+ return void 0;
7772
+ }
7773
+ if (bytes.byteLength < 2) {
7774
+ return void 0;
7775
+ }
7776
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
7777
+ const sampleCount = Math.floor(bytes.byteLength / 2);
7778
+ for (let i = 0; i < sampleCount; i++) {
7779
+ if (Math.abs(view.getInt16(i * 2, true)) > pcm16SilenceAmplitudeThreshold) {
7780
+ return void 0;
7781
+ }
7782
+ }
7783
+ return sampleCount / googleLiveOutputAudioRate * 1e3;
7784
+ }
7785
+ function buildGoogleLiveTranslationSetup({
7786
+ modelId,
7787
+ targetLanguage,
7788
+ providerOptions
7789
+ }) {
7790
+ return {
7791
+ model: getModelPath(modelId),
7792
+ generationConfig: {
7793
+ responseModalities: ["AUDIO"],
7794
+ translationConfig: {
7795
+ targetLanguageCode: targetLanguage,
7796
+ ...(providerOptions == null ? void 0 : providerOptions.echoTargetLanguage) != null ? { echoTargetLanguage: providerOptions.echoTargetLanguage } : {}
7797
+ }
7798
+ },
7799
+ inputAudioTranscription: {},
7800
+ outputAudioTranscription: {}
7801
+ };
7802
+ }
7803
+ function validateGoogleTranslationInputAudioFormat(inputAudioFormat) {
7804
+ if (inputAudioFormat.type !== "audio/pcm" || inputAudioFormat.rate != null && inputAudioFormat.rate !== 16e3) {
7805
+ throw new InvalidArgumentError({
7806
+ argument: "inputAudioFormat",
7807
+ message: "The Gemini Live translation API only supports 16kHz 16-bit PCM input audio."
7808
+ });
7809
+ }
7810
+ }
7811
+
7354
7812
  // src/google-provider.ts
7355
7813
  var supportedExternalUrlMediaTypes = [
7356
7814
  "text/html",
@@ -7457,6 +7915,12 @@ function createGoogle(options = {}) {
7457
7915
  headers: getHeaders,
7458
7916
  fetch: options.fetch
7459
7917
  });
7918
+ const createTranslationModel = (modelId) => new GoogleTranslationModel(modelId, {
7919
+ provider: `${providerName}.translation`,
7920
+ baseURL,
7921
+ headers: getHeaders,
7922
+ webSocket: options.webSocket
7923
+ });
7460
7924
  const createSpeechModel = (modelId) => new GoogleSpeechModel(modelId, {
7461
7925
  provider: `${providerName}.speech`,
7462
7926
  baseURL,
@@ -7517,6 +7981,8 @@ function createGoogle(options = {}) {
7517
7981
  provider.files = createFiles;
7518
7982
  provider.speech = createSpeechModel;
7519
7983
  provider.speechModel = createSpeechModel;
7984
+ provider.translation = createTranslationModel;
7985
+ provider.speechTranslationModel = createTranslationModel;
7520
7986
  provider.interactions = createInteractionsModel;
7521
7987
  provider.tools = googleTools;
7522
7988
  return provider;
@@ -7524,6 +7990,7 @@ function createGoogle(options = {}) {
7524
7990
  var google = createGoogle();
7525
7991
  export {
7526
7992
  GoogleRealtimeModel as Experimental_GoogleRealtimeModel,
7993
+ GoogleTranslationModel as Experimental_GoogleTranslationModel,
7527
7994
  VERSION,
7528
7995
  createGoogle,
7529
7996
  createGoogle as createGoogleGenerativeAI,