@ai-sdk/google 4.0.24 → 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 +17 -0
- package/dist/index.d.ts +50 -3
- package/dist/index.js +482 -15
- package/dist/index.js.map +1 -1
- package/docs/15-google.mdx +51 -0
- package/package.json +3 -3
- package/src/get-realtime-base-url.ts +31 -0
- package/src/google-provider.ts +32 -0
- package/src/index.ts +6 -0
- package/src/realtime/google-realtime-model.ts +5 -17
- package/src/translation/google-translation-model-options.ts +26 -0
- package/src/translation/google-translation-model.ts +612 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
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
|
+
|
|
12
|
+
## 4.0.25
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- Updated dependencies [1e2f324]
|
|
17
|
+
- @ai-sdk/provider@4.0.4
|
|
18
|
+
- @ai-sdk/provider-utils@5.0.13
|
|
19
|
+
|
|
3
20
|
## 4.0.24
|
|
4
21
|
|
|
5
22
|
### 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
|
|
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.
|
|
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
|
-
|
|
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,
|