realtime-voice-agents 2.0.0
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/LICENSE +21 -0
- package/README.md +298 -0
- package/assets/elevator-jazz.ulaw +1 -0
- package/assets/keyboard-typing.ulaw +1 -0
- package/assets/lofi.ulaw +1 -0
- package/assets/ringing.ulaw +1 -0
- package/assets/thinking-hum.ulaw +1 -0
- package/dist/BackgroundAudioPlayer-iMcivjis.mjs +282 -0
- package/dist/BackgroundAudioPlayer-jfRULWKC.cjs +313 -0
- package/dist/BaseRealtimeProvider-BQigr5mB.mjs +62 -0
- package/dist/BaseRealtimeProvider-BehPNT1r.d.cts +239 -0
- package/dist/BaseRealtimeProvider-ClP8Wx1X.d.mts +239 -0
- package/dist/BaseRealtimeProvider-DI4pKtOb.cjs +91 -0
- package/dist/GeminiLiveProvider-DvkTgzjG.d.cts +101 -0
- package/dist/GeminiLiveProvider-x2nyx5aO.d.mts +101 -0
- package/dist/InMemorySessionStore-B0_i-DOU.cjs +34 -0
- package/dist/InMemorySessionStore-B5_rq61L.d.cts +83 -0
- package/dist/InMemorySessionStore-B5_rq61L.d.mts +83 -0
- package/dist/InMemorySessionStore-DYParOJO.mjs +29 -0
- package/dist/OpenAICompatibleProvider-Bdtl-UXH.mjs +402 -0
- package/dist/OpenAICompatibleProvider-NS4cKVQj.cjs +409 -0
- package/dist/audio.cjs +21 -0
- package/dist/audio.d.cts +165 -0
- package/dist/audio.d.mts +165 -0
- package/dist/audio.mjs +4 -0
- package/dist/env-DSnGaERV.cjs +19 -0
- package/dist/env-DUwUWTsg.mjs +14 -0
- package/dist/events-BUMYdETO.d.cts +24 -0
- package/dist/events-BUMYdETO.d.mts +24 -0
- package/dist/events-BxDTIKKq.cjs +48 -0
- package/dist/events-BylBSBW-.mjs +43 -0
- package/dist/gemini.cjs +401 -0
- package/dist/gemini.d.cts +41 -0
- package/dist/gemini.d.mts +41 -0
- package/dist/gemini.mjs +395 -0
- package/dist/index.cjs +2142 -0
- package/dist/index.d.cts +920 -0
- package/dist/index.d.mts +920 -0
- package/dist/index.mjs +2113 -0
- package/dist/mulaw--cwU2c9L.mjs +64 -0
- package/dist/mulaw-DLUObjdP.cjs +117 -0
- package/dist/openai.cjs +61 -0
- package/dist/openai.d.cts +121 -0
- package/dist/openai.d.mts +121 -0
- package/dist/openai.mjs +53 -0
- package/dist/presets-Bf75YXs5.d.cts +24 -0
- package/dist/presets-Bf75YXs5.d.mts +24 -0
- package/dist/rest-BYqiVOhe.mjs +265 -0
- package/dist/rest-BvUKut_k.cjs +300 -0
- package/dist/rolldown-runtime-VH7oDXx4.cjs +28 -0
- package/dist/session-config-BVLl7-ha.mjs +74 -0
- package/dist/session-config-c8sOw1XL.cjs +85 -0
- package/dist/store.cjs +3 -0
- package/dist/store.d.cts +2 -0
- package/dist/store.d.mts +2 -0
- package/dist/store.mjs +2 -0
- package/dist/testing.cjs +544 -0
- package/dist/testing.d.cts +191 -0
- package/dist/testing.d.mts +191 -0
- package/dist/testing.mjs +538 -0
- package/dist/transcode-C9aJG7_W.cjs +215 -0
- package/dist/transcode-CsAp97G9.mjs +198 -0
- package/dist/transport-B_PJFIVd.d.cts +155 -0
- package/dist/transport-CEaLFV4E.d.mts +155 -0
- package/dist/twilio.cjs +8 -0
- package/dist/twilio.d.cts +3 -0
- package/dist/twilio.d.mts +3 -0
- package/dist/twilio.mjs +2 -0
- package/dist/twiml-z9LjoF4_.d.cts +67 -0
- package/dist/twiml-z9LjoF4_.d.mts +67 -0
- package/dist/xai.cjs +92 -0
- package/dist/xai.d.cts +35 -0
- package/dist/xai.d.mts +35 -0
- package/dist/xai.mjs +86 -0
- package/package.json +183 -0
package/dist/gemini.cjs
ADDED
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_BaseRealtimeProvider = require("./BaseRealtimeProvider-DI4pKtOb.cjs");
|
|
3
|
+
const require_env = require("./env-DSnGaERV.cjs");
|
|
4
|
+
const require_transcode = require("./transcode-C9aJG7_W.cjs");
|
|
5
|
+
//#region src/providers/gemini/GeminiLiveProvider.ts
|
|
6
|
+
/**
|
|
7
|
+
* Gemini Live provider via the official `@google/genai` SDK (optional peer).
|
|
8
|
+
*
|
|
9
|
+
* Unlike the OpenAI-compatible path, Gemini speaks PCM (16 kHz in, 24 kHz
|
|
10
|
+
* out), so this provider owns a stateful transcoding pair: μ-law↔PCM with
|
|
11
|
+
* inter-chunk resampler state — the engine stays μ-law-only.
|
|
12
|
+
*
|
|
13
|
+
* Gemini specifics handled here:
|
|
14
|
+
* - turn ids are synthesized (`gturn_N`) — the wire has no response ids
|
|
15
|
+
* - `serverContent.interrupted` is the barge-in signal (maps to
|
|
16
|
+
* userSpeechStarted + responseDone; the server already cancelled itself)
|
|
17
|
+
* - session resumption handles are captured continuously and replayed on the
|
|
18
|
+
* next connect, so reconnects restore context server-side
|
|
19
|
+
* - `goAway` (imminent disconnect, e.g. the session time limit) is surfaced
|
|
20
|
+
* so the session can reconnect proactively
|
|
21
|
+
* - tool results go through the proper `toolResponse` message
|
|
22
|
+
*/
|
|
23
|
+
const GEMINI_INPUT_MIME = "audio/pcm;rate=16000";
|
|
24
|
+
var GeminiLiveProvider = class extends require_BaseRealtimeProvider.BaseRealtimeProvider {
|
|
25
|
+
name = "gemini";
|
|
26
|
+
capabilities = {
|
|
27
|
+
truncate: false,
|
|
28
|
+
sessionUpdate: false,
|
|
29
|
+
voiceChangeMidSession: false,
|
|
30
|
+
transcodeRequired: true,
|
|
31
|
+
resumption: true,
|
|
32
|
+
agentTranscriptDeltas: true,
|
|
33
|
+
vadInterruptControl: false
|
|
34
|
+
};
|
|
35
|
+
config;
|
|
36
|
+
logger;
|
|
37
|
+
session = null;
|
|
38
|
+
sessionInit = null;
|
|
39
|
+
ready = false;
|
|
40
|
+
intentionalClose = false;
|
|
41
|
+
inbound = new require_transcode.InboundTranscoder(16e3);
|
|
42
|
+
outbound = new require_transcode.OutboundTranscoder(24e3);
|
|
43
|
+
turnCounter = 0;
|
|
44
|
+
currentTurnId = null;
|
|
45
|
+
userTranscriptBuffer = "";
|
|
46
|
+
agentTranscriptBuffer = "";
|
|
47
|
+
resumptionHandle = null;
|
|
48
|
+
resumed = false;
|
|
49
|
+
constructor(config, logger = require_BaseRealtimeProvider.noopLogger) {
|
|
50
|
+
super();
|
|
51
|
+
this.config = config;
|
|
52
|
+
this.logger = logger;
|
|
53
|
+
}
|
|
54
|
+
get isConnected() {
|
|
55
|
+
return this.ready && this.session !== null;
|
|
56
|
+
}
|
|
57
|
+
/** True when the last connect restored server-side context via a handle. */
|
|
58
|
+
get didResume() {
|
|
59
|
+
return this.resumed;
|
|
60
|
+
}
|
|
61
|
+
async connect(init) {
|
|
62
|
+
if (this.session) await this.close();
|
|
63
|
+
this.sessionInit = init;
|
|
64
|
+
this.intentionalClose = false;
|
|
65
|
+
this.ready = false;
|
|
66
|
+
this.resumed = false;
|
|
67
|
+
this.inbound.reset();
|
|
68
|
+
this.outbound.reset();
|
|
69
|
+
const connector = this.config.connector ?? await this.defaultConnector();
|
|
70
|
+
const handle = init.freshSession ? void 0 : init.resumptionHandle ?? this.resumptionHandle ?? void 0;
|
|
71
|
+
if (init.freshSession) this.resumptionHandle = null;
|
|
72
|
+
let setupResolve;
|
|
73
|
+
let rejectSetup;
|
|
74
|
+
const setupDone = new Promise((resolve, reject) => {
|
|
75
|
+
setupResolve = resolve;
|
|
76
|
+
rejectSetup = reject;
|
|
77
|
+
});
|
|
78
|
+
let setupError = null;
|
|
79
|
+
setupDone.catch(() => {});
|
|
80
|
+
const setupReject = (error) => {
|
|
81
|
+
setupError ??= error;
|
|
82
|
+
rejectSetup(error);
|
|
83
|
+
};
|
|
84
|
+
const timeoutMs = this.config.connectTimeoutMs ?? 1e4;
|
|
85
|
+
const timer = setTimeout(() => setupReject(/* @__PURE__ */ new Error(`gemini setup not complete within ${timeoutMs}ms`)), timeoutMs);
|
|
86
|
+
timer.unref?.();
|
|
87
|
+
try {
|
|
88
|
+
try {
|
|
89
|
+
this.session = await connector({
|
|
90
|
+
model: this.config.model,
|
|
91
|
+
config: this.buildConfig(init, handle),
|
|
92
|
+
callbacks: {
|
|
93
|
+
onopen: () => this.emit("open"),
|
|
94
|
+
onmessage: (message) => {
|
|
95
|
+
if (message?.setupComplete !== void 0) {
|
|
96
|
+
this.ready = true;
|
|
97
|
+
if (handle) this.resumed = true;
|
|
98
|
+
setupResolve();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
this.handleMessage(message);
|
|
102
|
+
},
|
|
103
|
+
onerror: (error) => {
|
|
104
|
+
const err = error instanceof Error ? error : new Error(String(error?.message ?? error));
|
|
105
|
+
if (!this.ready) setupReject(err);
|
|
106
|
+
else this.emit("error", err);
|
|
107
|
+
},
|
|
108
|
+
onclose: (event) => {
|
|
109
|
+
const wasReady = this.ready;
|
|
110
|
+
this.ready = false;
|
|
111
|
+
this.session = null;
|
|
112
|
+
if (!wasReady) {
|
|
113
|
+
setupReject(/* @__PURE__ */ new Error(`gemini closed during setup (${event?.code} ${event?.reason ?? ""})`));
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
this.emit("close", {
|
|
117
|
+
code: event?.code,
|
|
118
|
+
reason: event?.reason,
|
|
119
|
+
retriable: !this.intentionalClose && !isNonRetriableClose(event?.code)
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
} catch (error) {
|
|
125
|
+
throw setupError ?? (error instanceof Error ? error : new Error(String(error)));
|
|
126
|
+
}
|
|
127
|
+
await setupDone;
|
|
128
|
+
} finally {
|
|
129
|
+
clearTimeout(timer);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async defaultConnector() {
|
|
133
|
+
let genai;
|
|
134
|
+
try {
|
|
135
|
+
genai = await import("@google/genai");
|
|
136
|
+
} catch (error) {
|
|
137
|
+
throw new Error("the '@google/genai' package is required for the Gemini provider — npm install @google/genai", { cause: error });
|
|
138
|
+
}
|
|
139
|
+
const { GoogleGenAI } = genai;
|
|
140
|
+
const ai = this.config.vertex ? new GoogleGenAI({
|
|
141
|
+
vertexai: true,
|
|
142
|
+
project: this.config.vertex.project,
|
|
143
|
+
location: this.config.vertex.location
|
|
144
|
+
}) : new GoogleGenAI({ apiKey: this.config.apiKey });
|
|
145
|
+
return (params) => ai.live.connect({
|
|
146
|
+
model: params.model,
|
|
147
|
+
config: params.config,
|
|
148
|
+
callbacks: params.callbacks
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
buildConfig(init, resumptionHandle) {
|
|
152
|
+
const transcription = this.config.transcription;
|
|
153
|
+
const vad = init.vad !== void 0 ? init.vad : this.config.defaultVad;
|
|
154
|
+
const config = {
|
|
155
|
+
responseModalities: ["AUDIO"],
|
|
156
|
+
systemInstruction: init.instructions,
|
|
157
|
+
speechConfig: {
|
|
158
|
+
voiceConfig: { prebuiltVoiceConfig: { voiceName: init.voice ?? this.config.voice ?? "Aoede" } },
|
|
159
|
+
...this.config.languageCode ? { languageCode: this.config.languageCode } : {}
|
|
160
|
+
},
|
|
161
|
+
...init.tools?.length ? { tools: [{ functionDeclarations: init.tools.map((tool) => ({
|
|
162
|
+
name: tool.name,
|
|
163
|
+
description: tool.description ?? "",
|
|
164
|
+
parameters: tool.parameters
|
|
165
|
+
})) }] } : {},
|
|
166
|
+
realtimeInputConfig: { automaticActivityDetection: buildGeminiVad(vad) },
|
|
167
|
+
...transcription === false ? {} : {
|
|
168
|
+
...transcription?.input !== false ? { inputAudioTranscription: {} } : {},
|
|
169
|
+
...transcription?.output !== false ? { outputAudioTranscription: {} } : {}
|
|
170
|
+
},
|
|
171
|
+
...this.config.resumption !== false ? { sessionResumption: { ...resumptionHandle ? { handle: resumptionHandle } : {} } } : {},
|
|
172
|
+
...this.config.contextWindowCompression ? { contextWindowCompression: { slidingWindow: {} } } : {}
|
|
173
|
+
};
|
|
174
|
+
return require_BaseRealtimeProvider.deepMerge(require_BaseRealtimeProvider.deepMerge(config, init.providerOptions), this.config.extraConfig);
|
|
175
|
+
}
|
|
176
|
+
async close() {
|
|
177
|
+
this.intentionalClose = true;
|
|
178
|
+
this.ready = false;
|
|
179
|
+
const session = this.session;
|
|
180
|
+
this.session = null;
|
|
181
|
+
if (!session) return;
|
|
182
|
+
try {
|
|
183
|
+
session.close();
|
|
184
|
+
} catch {}
|
|
185
|
+
}
|
|
186
|
+
sendAudio(base64Mulaw) {
|
|
187
|
+
if (!this.isConnected || !this.session) return;
|
|
188
|
+
const pcm = this.inbound.process(base64Mulaw);
|
|
189
|
+
if (pcm.length === 0) return;
|
|
190
|
+
this.session.sendRealtimeInput({ audio: {
|
|
191
|
+
data: pcm,
|
|
192
|
+
mimeType: GEMINI_INPUT_MIME
|
|
193
|
+
} });
|
|
194
|
+
}
|
|
195
|
+
sendText(text, options = {}) {
|
|
196
|
+
if (!this.session) return;
|
|
197
|
+
const role = options.role === "assistant" ? "model" : "user";
|
|
198
|
+
const content = options.role === "system" ? `(system note) ${text}` : text;
|
|
199
|
+
this.session.sendClientContent({
|
|
200
|
+
turns: [{
|
|
201
|
+
role,
|
|
202
|
+
parts: [{ text: content }]
|
|
203
|
+
}],
|
|
204
|
+
turnComplete: options.triggerResponse !== false
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
sendToolResult(callId, output, options = {}) {
|
|
208
|
+
if (!this.session) return;
|
|
209
|
+
const response = output !== null && typeof output === "object" && !Array.isArray(output) ? output : { result: output };
|
|
210
|
+
this.session.sendToolResponse({ functionResponses: [{
|
|
211
|
+
id: callId,
|
|
212
|
+
name: this.toolNameForCall(callId),
|
|
213
|
+
response
|
|
214
|
+
}] });
|
|
215
|
+
}
|
|
216
|
+
createResponse(options = {}) {
|
|
217
|
+
if (!this.session) return;
|
|
218
|
+
const text = options.instructions ? `(instruction) ${options.instructions}` : "(instruction) Continue the conversation now — speak to the caller.";
|
|
219
|
+
this.session.sendClientContent({
|
|
220
|
+
turns: [{
|
|
221
|
+
role: "user",
|
|
222
|
+
parts: [{ text }]
|
|
223
|
+
}],
|
|
224
|
+
turnComplete: true
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
async updateSession(_patch) {
|
|
228
|
+
this.logger.warn("gemini: updateSession ignored (no mid-session config on Live API)");
|
|
229
|
+
}
|
|
230
|
+
handleMessage(message) {
|
|
231
|
+
const serverContent = message?.serverContent;
|
|
232
|
+
if (message?.toolCall?.functionCalls) {
|
|
233
|
+
for (const call of message.toolCall.functionCalls) {
|
|
234
|
+
const id = call.id ?? `gcall_${++this.turnCounter}`;
|
|
235
|
+
this.callNames.set(id, call.name);
|
|
236
|
+
this.emit("toolCall", {
|
|
237
|
+
id,
|
|
238
|
+
name: call.name,
|
|
239
|
+
argumentsJson: JSON.stringify(call.args ?? {}),
|
|
240
|
+
responseId: this.currentTurnId ?? void 0
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
if (message?.sessionResumptionUpdate) {
|
|
246
|
+
const update = message.sessionResumptionUpdate;
|
|
247
|
+
if (update.resumable && update.newHandle) {
|
|
248
|
+
this.resumptionHandle = update.newHandle;
|
|
249
|
+
this.emit("resumptionUpdate", update.newHandle);
|
|
250
|
+
}
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
if (message?.goAway) {
|
|
254
|
+
const timeLeftMs = parseGoAwayTime(message.goAway.timeLeft);
|
|
255
|
+
this.logger.info("gemini goAway received", { timeLeftMs });
|
|
256
|
+
this.emit("goAway", { timeLeftMs });
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
if (message?.usageMetadata) {
|
|
260
|
+
const usage = message.usageMetadata;
|
|
261
|
+
this.emit("usage", {
|
|
262
|
+
inputTokens: usage.promptTokenCount ?? 0,
|
|
263
|
+
outputTokens: usage.responseTokenCount ?? usage.candidatesTokenCount ?? 0,
|
|
264
|
+
totalTokens: usage.totalTokenCount ?? 0,
|
|
265
|
+
raw: usage
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
if (!serverContent) return;
|
|
269
|
+
if (serverContent.interrupted) {
|
|
270
|
+
const turnId = this.currentTurnId;
|
|
271
|
+
this.emit("userSpeechStarted");
|
|
272
|
+
if (turnId) {
|
|
273
|
+
this.flushAgentTranscript(turnId);
|
|
274
|
+
this.currentTurnId = null;
|
|
275
|
+
this.emit("responseDone", { responseId: turnId });
|
|
276
|
+
}
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
if (serverContent.inputTranscription?.text) this.userTranscriptBuffer += serverContent.inputTranscription.text;
|
|
280
|
+
if (serverContent.outputTranscription?.text) {
|
|
281
|
+
const turnId = this.ensureTurn();
|
|
282
|
+
this.agentTranscriptBuffer += serverContent.outputTranscription.text;
|
|
283
|
+
this.emit("agentTranscriptDelta", {
|
|
284
|
+
responseId: turnId,
|
|
285
|
+
delta: serverContent.outputTranscription.text
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
const parts = serverContent.modelTurn?.parts ?? [];
|
|
289
|
+
for (const part of parts) if (part.inlineData?.data) {
|
|
290
|
+
const turnId = this.ensureTurn();
|
|
291
|
+
const mulaw = this.outbound.process(part.inlineData.data);
|
|
292
|
+
if (mulaw.length > 0) this.emit("audio", {
|
|
293
|
+
base64Mulaw: mulaw,
|
|
294
|
+
responseId: turnId
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
if (serverContent.turnComplete) {
|
|
298
|
+
this.flushUserTranscript();
|
|
299
|
+
const turnId = this.currentTurnId;
|
|
300
|
+
if (turnId) {
|
|
301
|
+
this.flushAgentTranscript(turnId);
|
|
302
|
+
this.currentTurnId = null;
|
|
303
|
+
this.emit("responseDone", { responseId: turnId });
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
callNames = /* @__PURE__ */ new Map();
|
|
308
|
+
toolNameForCall(callId) {
|
|
309
|
+
const name = this.callNames.get(callId) ?? callId;
|
|
310
|
+
this.callNames.delete(callId);
|
|
311
|
+
return name;
|
|
312
|
+
}
|
|
313
|
+
ensureTurn() {
|
|
314
|
+
if (!this.currentTurnId) {
|
|
315
|
+
this.currentTurnId = `gturn_${++this.turnCounter}`;
|
|
316
|
+
this.flushUserTranscript();
|
|
317
|
+
this.emit("responseStarted", { responseId: this.currentTurnId });
|
|
318
|
+
}
|
|
319
|
+
return this.currentTurnId;
|
|
320
|
+
}
|
|
321
|
+
flushUserTranscript() {
|
|
322
|
+
const text = this.userTranscriptBuffer.trim();
|
|
323
|
+
this.userTranscriptBuffer = "";
|
|
324
|
+
if (text) this.emit("userTranscript", { text });
|
|
325
|
+
}
|
|
326
|
+
flushAgentTranscript(turnId) {
|
|
327
|
+
const text = this.agentTranscriptBuffer.trim();
|
|
328
|
+
this.agentTranscriptBuffer = "";
|
|
329
|
+
if (text) this.emit("agentTranscript", {
|
|
330
|
+
responseId: turnId,
|
|
331
|
+
text
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
/** Protocol/auth/policy closes where retrying cannot help. */
|
|
336
|
+
function isNonRetriableClose(code) {
|
|
337
|
+
if (code === void 0) return false;
|
|
338
|
+
return code === 1002 || code === 1003 || code === 1007 || code === 1008 || code >= 4400 && code < 4500;
|
|
339
|
+
}
|
|
340
|
+
function buildGeminiVad(vad) {
|
|
341
|
+
if (vad === null) return { disabled: true };
|
|
342
|
+
return {
|
|
343
|
+
disabled: false,
|
|
344
|
+
...vad?.startSensitivity ? { startOfSpeechSensitivity: `START_SENSITIVITY_${vad.startSensitivity.toUpperCase()}` } : {},
|
|
345
|
+
...vad?.endSensitivity ? { endOfSpeechSensitivity: `END_SENSITIVITY_${vad.endSensitivity.toUpperCase()}` } : {},
|
|
346
|
+
...vad?.prefixPaddingMs !== void 0 ? { prefixPaddingMs: vad.prefixPaddingMs } : {},
|
|
347
|
+
...vad?.silenceDurationMs !== void 0 ? { silenceDurationMs: vad.silenceDurationMs } : {}
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
function parseGoAwayTime(timeLeft) {
|
|
351
|
+
if (typeof timeLeft === "number") return timeLeft;
|
|
352
|
+
if (typeof timeLeft === "string") {
|
|
353
|
+
const seconds = Number.parseFloat(timeLeft.replace(/s$/, ""));
|
|
354
|
+
if (!Number.isNaN(seconds)) return Math.round(seconds * 1e3);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
//#endregion
|
|
358
|
+
//#region src/gemini.ts
|
|
359
|
+
/** Google Gemini Live provider — `realtime-voice-agents/gemini`. */
|
|
360
|
+
/** Env vars checked (in order) when no explicit apiKey is passed. */
|
|
361
|
+
const GEMINI_KEY_ENV_VARS = [
|
|
362
|
+
"GOOGLE_API_KEY",
|
|
363
|
+
"GEMINI_API_KEY",
|
|
364
|
+
"GOOGLE_GENAI_API_KEY"
|
|
365
|
+
];
|
|
366
|
+
const GEMINI_DEFAULT_MODEL = "gemini-2.5-flash-native-audio-preview-12-2025";
|
|
367
|
+
const GEMINI_DEFAULT_VOICE = "Aoede";
|
|
368
|
+
const GEMINI_VOICES = [
|
|
369
|
+
"Aoede",
|
|
370
|
+
"Charon",
|
|
371
|
+
"Fenrir",
|
|
372
|
+
"Kore",
|
|
373
|
+
"Puck"
|
|
374
|
+
];
|
|
375
|
+
/** Create a Gemini Live provider factory for the bridge. */
|
|
376
|
+
function geminiLive(options = {}) {
|
|
377
|
+
const apiKey = require_env.resolveApiKey(options.apiKey, GEMINI_KEY_ENV_VARS);
|
|
378
|
+
if (!apiKey && !options.vertex && !options.connector) throw new Error(`geminiLive: credentials missing (pass apiKey, set one of ${GEMINI_KEY_ENV_VARS.join("/")}, or configure vertex)`);
|
|
379
|
+
const config = {
|
|
380
|
+
apiKey,
|
|
381
|
+
vertex: options.vertex,
|
|
382
|
+
model: options.model ?? "gemini-2.5-flash-native-audio-preview-12-2025",
|
|
383
|
+
voice: options.voice ?? "Aoede",
|
|
384
|
+
languageCode: options.languageCode,
|
|
385
|
+
defaultVad: options.vad,
|
|
386
|
+
transcription: options.transcription,
|
|
387
|
+
resumption: options.resumption,
|
|
388
|
+
contextWindowCompression: options.contextWindowCompression,
|
|
389
|
+
extraConfig: options.config,
|
|
390
|
+
connectTimeoutMs: options.connectTimeoutMs,
|
|
391
|
+
connector: options.connector
|
|
392
|
+
};
|
|
393
|
+
return ({ logger }) => new GeminiLiveProvider(config, logger);
|
|
394
|
+
}
|
|
395
|
+
//#endregion
|
|
396
|
+
exports.GEMINI_DEFAULT_MODEL = GEMINI_DEFAULT_MODEL;
|
|
397
|
+
exports.GEMINI_DEFAULT_VOICE = GEMINI_DEFAULT_VOICE;
|
|
398
|
+
exports.GEMINI_KEY_ENV_VARS = GEMINI_KEY_ENV_VARS;
|
|
399
|
+
exports.GEMINI_VOICES = GEMINI_VOICES;
|
|
400
|
+
exports.GeminiLiveProvider = GeminiLiveProvider;
|
|
401
|
+
exports.geminiLive = geminiLive;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { c as VadConfig, n as ProviderFactory } from "./BaseRealtimeProvider-BehPNT1r.cjs";
|
|
2
|
+
import { a as GeminiLiveSessionLike, i as GeminiLiveProviderConfig, n as GeminiLiveConnector, r as GeminiLiveProvider, t as GeminiConnectParams } from "./GeminiLiveProvider-DvkTgzjG.cjs";
|
|
3
|
+
//#region src/gemini.d.ts
|
|
4
|
+
/** Env vars checked (in order) when no explicit apiKey is passed. */
|
|
5
|
+
declare const GEMINI_KEY_ENV_VARS: readonly ["GOOGLE_API_KEY", "GEMINI_API_KEY", "GOOGLE_GENAI_API_KEY"];
|
|
6
|
+
declare const GEMINI_DEFAULT_MODEL = "gemini-2.5-flash-native-audio-preview-12-2025";
|
|
7
|
+
declare const GEMINI_DEFAULT_VOICE = "Aoede";
|
|
8
|
+
declare const GEMINI_VOICES: readonly ["Aoede", "Charon", "Fenrir", "Kore", "Puck"];
|
|
9
|
+
interface GeminiLiveOptions {
|
|
10
|
+
/** Google AI Studio key. Defaults to the first of GOOGLE_API_KEY / GEMINI_API_KEY / GOOGLE_GENAI_API_KEY set in the env. */
|
|
11
|
+
apiKey?: string;
|
|
12
|
+
/** Vertex AI instead of API-key auth (Application Default Credentials). */
|
|
13
|
+
vertex?: {
|
|
14
|
+
project: string;
|
|
15
|
+
location: string;
|
|
16
|
+
};
|
|
17
|
+
model?: string;
|
|
18
|
+
voice?: string;
|
|
19
|
+
/** BCP-47 speech language (e.g. 'en-US'). */
|
|
20
|
+
languageCode?: string;
|
|
21
|
+
/** Normalized VAD (start/end sensitivity, padding, silence). `null` disables. */
|
|
22
|
+
vad?: VadConfig | null;
|
|
23
|
+
/** Caller/agent transcription toggles. Default both on. */
|
|
24
|
+
transcription?: {
|
|
25
|
+
input?: boolean;
|
|
26
|
+
output?: boolean;
|
|
27
|
+
} | false;
|
|
28
|
+
/** Session resumption across reconnects. Default true. */
|
|
29
|
+
resumption?: boolean;
|
|
30
|
+
/** Sliding-window context compression for long calls. Default false. */
|
|
31
|
+
contextWindowCompression?: boolean;
|
|
32
|
+
/** Provider-native live config, deep-merged last (escape hatch). */
|
|
33
|
+
config?: Record<string, unknown>;
|
|
34
|
+
connectTimeoutMs?: number;
|
|
35
|
+
/** Test seam: replaces the @google/genai live connection. */
|
|
36
|
+
connector?: GeminiLiveConnector;
|
|
37
|
+
}
|
|
38
|
+
/** Create a Gemini Live provider factory for the bridge. */
|
|
39
|
+
declare function geminiLive(options?: GeminiLiveOptions): ProviderFactory;
|
|
40
|
+
//#endregion
|
|
41
|
+
export { GEMINI_DEFAULT_MODEL, GEMINI_DEFAULT_VOICE, GEMINI_KEY_ENV_VARS, GEMINI_VOICES, type GeminiConnectParams, type GeminiLiveConnector, GeminiLiveOptions, GeminiLiveProvider, type GeminiLiveProviderConfig, type GeminiLiveSessionLike, geminiLive };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { c as VadConfig, n as ProviderFactory } from "./BaseRealtimeProvider-ClP8Wx1X.mjs";
|
|
2
|
+
import { a as GeminiLiveSessionLike, i as GeminiLiveProviderConfig, n as GeminiLiveConnector, r as GeminiLiveProvider, t as GeminiConnectParams } from "./GeminiLiveProvider-x2nyx5aO.mjs";
|
|
3
|
+
//#region src/gemini.d.ts
|
|
4
|
+
/** Env vars checked (in order) when no explicit apiKey is passed. */
|
|
5
|
+
declare const GEMINI_KEY_ENV_VARS: readonly ["GOOGLE_API_KEY", "GEMINI_API_KEY", "GOOGLE_GENAI_API_KEY"];
|
|
6
|
+
declare const GEMINI_DEFAULT_MODEL = "gemini-2.5-flash-native-audio-preview-12-2025";
|
|
7
|
+
declare const GEMINI_DEFAULT_VOICE = "Aoede";
|
|
8
|
+
declare const GEMINI_VOICES: readonly ["Aoede", "Charon", "Fenrir", "Kore", "Puck"];
|
|
9
|
+
interface GeminiLiveOptions {
|
|
10
|
+
/** Google AI Studio key. Defaults to the first of GOOGLE_API_KEY / GEMINI_API_KEY / GOOGLE_GENAI_API_KEY set in the env. */
|
|
11
|
+
apiKey?: string;
|
|
12
|
+
/** Vertex AI instead of API-key auth (Application Default Credentials). */
|
|
13
|
+
vertex?: {
|
|
14
|
+
project: string;
|
|
15
|
+
location: string;
|
|
16
|
+
};
|
|
17
|
+
model?: string;
|
|
18
|
+
voice?: string;
|
|
19
|
+
/** BCP-47 speech language (e.g. 'en-US'). */
|
|
20
|
+
languageCode?: string;
|
|
21
|
+
/** Normalized VAD (start/end sensitivity, padding, silence). `null` disables. */
|
|
22
|
+
vad?: VadConfig | null;
|
|
23
|
+
/** Caller/agent transcription toggles. Default both on. */
|
|
24
|
+
transcription?: {
|
|
25
|
+
input?: boolean;
|
|
26
|
+
output?: boolean;
|
|
27
|
+
} | false;
|
|
28
|
+
/** Session resumption across reconnects. Default true. */
|
|
29
|
+
resumption?: boolean;
|
|
30
|
+
/** Sliding-window context compression for long calls. Default false. */
|
|
31
|
+
contextWindowCompression?: boolean;
|
|
32
|
+
/** Provider-native live config, deep-merged last (escape hatch). */
|
|
33
|
+
config?: Record<string, unknown>;
|
|
34
|
+
connectTimeoutMs?: number;
|
|
35
|
+
/** Test seam: replaces the @google/genai live connection. */
|
|
36
|
+
connector?: GeminiLiveConnector;
|
|
37
|
+
}
|
|
38
|
+
/** Create a Gemini Live provider factory for the bridge. */
|
|
39
|
+
declare function geminiLive(options?: GeminiLiveOptions): ProviderFactory;
|
|
40
|
+
//#endregion
|
|
41
|
+
export { GEMINI_DEFAULT_MODEL, GEMINI_DEFAULT_VOICE, GEMINI_KEY_ENV_VARS, GEMINI_VOICES, type GeminiConnectParams, type GeminiLiveConnector, GeminiLiveOptions, GeminiLiveProvider, type GeminiLiveProviderConfig, type GeminiLiveSessionLike, geminiLive };
|