@memberjunction/ai-gemini 5.40.2 → 5.41.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.
@@ -0,0 +1,651 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var GeminiRealtime_1;
11
+ // Google Gemini Live API imports
12
+ import { GoogleGenAI, Modality, } from '@google/genai';
13
+ // MemberJunction AI core contract
14
+ import { BaseRealtimeModel, } from '@memberjunction/ai';
15
+ import { RegisterClass } from '@memberjunction/global';
16
+ /**
17
+ * MIME type Gemini Live expects for client-streamed audio: 16-bit signed PCM at 16 kHz, mono.
18
+ * Output audio from the model is 24 kHz PCM; the driver passes output frames through untouched as
19
+ * raw `ArrayBuffer` and leaves resampling/playback to the consumer.
20
+ */
21
+ const GEMINI_INPUT_AUDIO_MIME_TYPE = 'audio/pcm;rate=16000';
22
+ /**
23
+ * Window (ms) within which a client-direct browser must OPEN its Live session using a minted
24
+ * ephemeral token. After this the token can no longer start a NEW session (an already-open
25
+ * session continues until {@link GEMINI_CLIENT_TOKEN_EXPIRY_MS}).
26
+ */
27
+ const GEMINI_CLIENT_TOKEN_NEW_SESSION_WINDOW_MS = 10 * 60 * 1000;
28
+ /**
29
+ * Total lifetime (ms) of a minted client-direct ephemeral token: messages on a Live session
30
+ * authenticated with the token are rejected after this point.
31
+ */
32
+ const GEMINI_CLIENT_TOKEN_EXPIRY_MS = 30 * 60 * 1000;
33
+ /**
34
+ * Real-time, full-duplex driver for Google's **Gemini Live API**, implementing the Core
35
+ * {@link BaseRealtimeModel} primitive.
36
+ *
37
+ * The driver opens a bidirectional Gemini Live session, streams client audio in, and translates the
38
+ * provider's {@link LiveServerMessage} frames into the modality-agnostic Core events
39
+ * ({@link RealtimeTranscript}, {@link RealtimeToolCall}, {@link RealtimeUsage}, output media, and
40
+ * interruption). It registers via the MemberJunction class factory as `GeminiRealtime` and is
41
+ * resolved for `MJ: AI Models` typed `Realtime`.
42
+ *
43
+ * **Testability:** the live-session creation is isolated behind the overridable
44
+ * {@link connectLiveSession} seam, so unit tests inject a fake {@link GeminiLiveSession} and exercise
45
+ * the full message→event translation with no network.
46
+ */
47
+ let GeminiRealtime = GeminiRealtime_1 = class GeminiRealtime extends BaseRealtimeModel {
48
+ constructor(apiKey) {
49
+ super(apiKey);
50
+ this.geminiClient = null;
51
+ this.geminiTokenClient = null;
52
+ // Client is created lazily in connectLiveSession so subclasses (and tests overriding the
53
+ // seam) never trigger an unused live client construction.
54
+ }
55
+ /**
56
+ * Opens a Gemini Live session and returns the Core session handle that translates between the
57
+ * provider's frames and the MemberJunction realtime contract.
58
+ */
59
+ async StartSession(params) {
60
+ const session = new GeminiRealtimeSession();
61
+ session.SetConnectTimeTools(params.Tools ?? []);
62
+ const config = this.buildConnectConfig(params);
63
+ const live = await this.connectLiveSession({
64
+ Model: params.Model,
65
+ Config: config,
66
+ OnMessage: (message) => session.HandleServerMessage(message),
67
+ OnError: (event) => session.HandleTransportError(event?.message ?? 'Gemini Live websocket error'),
68
+ OnClose: (event) => session.HandleTransportClose(event?.code, event?.reason),
69
+ });
70
+ session.AttachLiveSession(live);
71
+ // If the caller provided initial context, seed it as client content (without completing the
72
+ // turn) so the model starts with the same history a loop agent would assemble.
73
+ if (params.InitialContext && params.InitialContext.trim().length > 0) {
74
+ session.SeedInitialContext(params.InitialContext);
75
+ }
76
+ return session;
77
+ }
78
+ /**
79
+ * Gemini Live supports the client-direct topology: the server mints a short-lived ephemeral
80
+ * auth token (`v1alpha` `auth_tokens` API) that the browser uses to open its OWN Live
81
+ * websocket, while the server keeps prompt/tool authority by LOCKING the connect config into
82
+ * the token via `liveConnectConstraints`.
83
+ */
84
+ get SupportsClientDirect() {
85
+ return true;
86
+ }
87
+ /**
88
+ * Mints an ephemeral, server-scoped Live credential for a **client-direct** session.
89
+ *
90
+ * The connect config is built EXACTLY as {@link StartSession} builds it (same
91
+ * {@link buildConnectConfig}: audio modality, input+output transcription, system instruction,
92
+ * mapped tools) and is **locked into the token** via `liveConnectConstraints` +
93
+ * `lockAdditionalFields: []` — so the API ignores any attempt by the browser to change the
94
+ * locked fields. The same config is ALSO carried in `SessionConfig` (as `{ model, config }`)
95
+ * because the SDK still expects the client to pass a model/config at `live.connect` time; the
96
+ * token-side lock is what makes the server's prompt and tool set authoritative.
97
+ *
98
+ * Expiry: the browser must open its session within
99
+ * {@link GEMINI_CLIENT_TOKEN_NEW_SESSION_WINDOW_MS}; the token (and thus the session's
100
+ * ability to send messages) dies at {@link GEMINI_CLIENT_TOKEN_EXPIRY_MS}.
101
+ *
102
+ * @param params Session configuration (model, system prompt, tools, config bag).
103
+ * @returns The minted {@link ClientRealtimeSessionConfig} the browser authenticates + applies.
104
+ */
105
+ async CreateClientSession(params) {
106
+ const config = this.buildConnectConfig(params);
107
+ const now = Date.now();
108
+ const expireTime = new Date(now + GEMINI_CLIENT_TOKEN_EXPIRY_MS).toISOString();
109
+ const newSessionExpireTime = new Date(now + GEMINI_CLIENT_TOKEN_NEW_SESSION_WINDOW_MS).toISOString();
110
+ const token = await this.mintAuthToken({
111
+ config: {
112
+ uses: 1,
113
+ expireTime,
114
+ newSessionExpireTime,
115
+ // Lock the model + the MASK-SAFE config subset into the token
116
+ // (lockAdditionalFields: [] means "lock exactly the fields set in
117
+ // liveConnectConstraints.config"). The token API only accepts a SUBSET of
118
+ // LiveConnectConfig as constraints — systemInstruction / tools /
119
+ // transcription keys make the generated field_mask invalid
120
+ // ("field_mask is invalid for BidiGenerateContentSetup", 400), so those
121
+ // travel ONLY via SessionConfig below. They remain server-authored: the
122
+ // browser applies the server-built config verbatim at live.connect; the
123
+ // token simply can't cryptographically pin them on Gemini today.
124
+ liveConnectConstraints: { model: params.Model, config: GeminiRealtime_1.BuildConstraintConfig(config) },
125
+ lockAdditionalFields: [],
126
+ },
127
+ });
128
+ if (!token.name) {
129
+ throw new Error('Gemini auth-token mint returned no token name');
130
+ }
131
+ return {
132
+ Provider: 'gemini',
133
+ Model: params.Model,
134
+ EphemeralToken: token.name,
135
+ ExpiresAt: expireTime,
136
+ // Plain-JSON copy of what the browser passes to live.connect (model + config). The
137
+ // token lock above makes these values authoritative even if a client tampers.
138
+ SessionConfig: JSON.parse(JSON.stringify({ model: params.Model, config })),
139
+ };
140
+ }
141
+ /**
142
+ * Mint seam for the ephemeral auth token. Production routes through the SDK's
143
+ * `authTokens.create` on a `v1alpha` client (ephemeral tokens are v1alpha-only); unit tests
144
+ * override this to return a fake token with no network.
145
+ *
146
+ * @param params The auth-token create parameters (expiry, uses, live-connect constraints).
147
+ * @returns The created {@link AuthToken} (its `name` is the credential the browser presents).
148
+ */
149
+ async mintAuthToken(params) {
150
+ return this.ensureTokenClient().authTokens.create(params);
151
+ }
152
+ /**
153
+ * Lazily constructs the `v1alpha` `GoogleGenAI` client used ONLY for auth-token minting
154
+ * (the ephemeral-token API is exposed on `v1alpha`; the regular live client stays default).
155
+ */
156
+ ensureTokenClient() {
157
+ if (!this.geminiTokenClient) {
158
+ this.geminiTokenClient = new GoogleGenAI({ apiKey: this.apiKey, httpOptions: { apiVersion: 'v1alpha' } });
159
+ }
160
+ return this.geminiTokenClient;
161
+ }
162
+ /**
163
+ * Creation seam for the underlying Gemini Live session.
164
+ *
165
+ * Production code routes through `ai.live.connect`; unit tests override this method to inject a
166
+ * fake {@link GeminiLiveSession}. Kept as a thin, single-responsibility method so the network
167
+ * boundary is the *only* thing tests need to replace.
168
+ *
169
+ * @param args Resolved model, connect config, and the server-message callback.
170
+ * @returns A promise resolving to the live session handle.
171
+ */
172
+ async connectLiveSession(args) {
173
+ const client = this.ensureClient();
174
+ return client.live.connect({
175
+ model: args.Model,
176
+ config: args.Config,
177
+ callbacks: {
178
+ onmessage: args.OnMessage,
179
+ onerror: args.OnError,
180
+ onclose: args.OnClose,
181
+ },
182
+ });
183
+ }
184
+ /**
185
+ * Lazily constructs the `GoogleGenAI` client from the driver's API key.
186
+ */
187
+ ensureClient() {
188
+ if (!this.geminiClient) {
189
+ this.geminiClient = new GoogleGenAI({ apiKey: this.apiKey });
190
+ }
191
+ return this.geminiClient;
192
+ }
193
+ /**
194
+ * Builds the {@link LiveConnectConfig} from the Core session params: audio response modality,
195
+ * input/output transcription, system instruction, mapped tools, plus any provider-specific
196
+ * overrides from the open config bag.
197
+ */
198
+ /**
199
+ * Projects the full connect config down to the fields Gemini's ephemeral-token API accepts
200
+ * as `liveConnectConstraints.config`. The token mint converts the provided keys into a
201
+ * field mask over `BidiGenerateContentSetup`, and only generation-level fields are valid
202
+ * there — `systemInstruction`, `tools`, and the transcription configs are NOT, and their
203
+ * presence 400s the entire mint. Only defined fields are copied (an absent key must stay
204
+ * absent so it doesn't enter the mask).
205
+ */
206
+ static BuildConstraintConfig(config) {
207
+ const constraint = {};
208
+ if (config.responseModalities) {
209
+ constraint.responseModalities = config.responseModalities;
210
+ }
211
+ if (config.speechConfig) {
212
+ constraint.speechConfig = config.speechConfig;
213
+ }
214
+ if (config.temperature != null) {
215
+ constraint.temperature = config.temperature;
216
+ }
217
+ if (config.topP != null) {
218
+ constraint.topP = config.topP;
219
+ }
220
+ if (config.maxOutputTokens != null) {
221
+ constraint.maxOutputTokens = config.maxOutputTokens;
222
+ }
223
+ if (config.sessionResumption) {
224
+ constraint.sessionResumption = config.sessionResumption;
225
+ }
226
+ return constraint;
227
+ }
228
+ buildConnectConfig(params) {
229
+ const config = {
230
+ responseModalities: [Modality.AUDIO],
231
+ inputAudioTranscription: {},
232
+ outputAudioTranscription: {},
233
+ systemInstruction: params.SystemPrompt,
234
+ };
235
+ if (params.Tools && params.Tools.length > 0) {
236
+ config.tools = [{ functionDeclarations: GeminiRealtime_1.MapToolsToFunctionDeclarations(params.Tools) }];
237
+ }
238
+ // The open config bag is merged last so per-conversation overrides (voice, language,
239
+ // turn-taking) win over the defaults above. Cast through the shared JSON object shape.
240
+ if (params.Config) {
241
+ Object.assign(config, params.Config);
242
+ }
243
+ return config;
244
+ }
245
+ /**
246
+ * Maps Core {@link RealtimeToolDefinition}s up to Gemini {@link FunctionDeclaration}s.
247
+ *
248
+ * The Core `ParametersSchema` is a JSON-schema object, so it rides in `parametersJsonSchema`
249
+ * (the SDK's JSON-schema slot) rather than the OpenAPI-style `parameters` slot.
250
+ */
251
+ static MapToolsToFunctionDeclarations(tools) {
252
+ return tools.map((tool) => ({
253
+ name: tool.Name,
254
+ description: tool.Description,
255
+ parametersJsonSchema: tool.ParametersSchema,
256
+ }));
257
+ }
258
+ };
259
+ GeminiRealtime = GeminiRealtime_1 = __decorate([
260
+ RegisterClass(BaseRealtimeModel, 'GeminiRealtime'),
261
+ __metadata("design:paramtypes", [String])
262
+ ], GeminiRealtime);
263
+ export { GeminiRealtime };
264
+ /**
265
+ * Concrete {@link IRealtimeSession} backed by a Gemini Live {@link GeminiLiveSession}.
266
+ *
267
+ * Owns the inbound translation (Gemini {@link LiveServerMessage} → Core events) and the outbound
268
+ * translation (Core calls → Gemini send methods). It is created by {@link GeminiRealtime.StartSession}
269
+ * and never instantiated directly by consumers.
270
+ */
271
+ class GeminiRealtimeSession {
272
+ constructor() {
273
+ this.live = null;
274
+ this.outputHandler = null;
275
+ this.transcriptHandler = null;
276
+ this.toolCallHandler = null;
277
+ this.interruptionHandler = null;
278
+ this.usageHandler = null;
279
+ this.errorHandler = null;
280
+ /** True once Close() ran — an expected close must not surface as a fatal error. */
281
+ this.closedByConsumer = false;
282
+ /**
283
+ * Maps each pending tool call's `CallID` to its function name. Gemini's `sendToolResponse`
284
+ * requires the function name, but the Core {@link IRealtimeSession.SendToolResult} contract only
285
+ * carries `callID` + `output`. We cache the name when a tool call arrives and clear the entry
286
+ * once the result is sent.
287
+ */
288
+ this.pendingToolCallNames = new Map();
289
+ /**
290
+ * Fingerprint of the tool set bound at connect time (set via {@link SetConnectTimeTools});
291
+ * {@link RegisterTools} compares against it to no-op identical re-registrations.
292
+ * Defaults to the empty set's fingerprint for sessions started without tools.
293
+ */
294
+ this.connectTimeToolsFingerprint = GeminiRealtimeSession.ToolSetFingerprint([]);
295
+ /**
296
+ * Whether a model turn is currently being generated. Minimal turn tracking mirroring the
297
+ * client driver: set when model output arrives (Gemini has no `response.created`-style frame,
298
+ * so the first `modelTurn` content is the signal) and eagerly when this session itself sends a
299
+ * turn-triggering client content; cleared on `turnComplete`, `interrupted`, and a tool-call
300
+ * frame (the model yields the floor pending the result). Consumed by {@link enqueueOrRun}:
301
+ * on Gemini Live ANY client content sent mid-turn INTERRUPTS the in-flight generation, so
302
+ * interim-update sends are deferred rather than sent into an active turn.
303
+ */
304
+ this.responseActive = false;
305
+ /**
306
+ * Client-content sends deferred while a turn is in flight; drained in order when the turn
307
+ * completes (the drain stops at the first send that itself starts a new turn). Cleared on
308
+ * {@link Close} so a closed session never replays stale sends.
309
+ */
310
+ this.queuedSends = [];
311
+ }
312
+ /**
313
+ * Binds the underlying live session. Called by the driver once `connect` resolves.
314
+ */
315
+ AttachLiveSession(live) {
316
+ this.live = live;
317
+ }
318
+ /**
319
+ * Seeds the conversation with prior context as a client-content turn (not turn-complete), so the
320
+ * model has the same starting history a loop agent would assemble.
321
+ */
322
+ SeedInitialContext(context) {
323
+ const turns = [{ role: 'user', parts: [{ text: context }] }];
324
+ this.requireLive().sendClientContent({ turns, turnComplete: false });
325
+ }
326
+ /** @inheritdoc */
327
+ SendInput(chunk) {
328
+ const audio = {
329
+ data: GeminiRealtimeSession.ArrayBufferToBase64(chunk),
330
+ mimeType: GEMINI_INPUT_AUDIO_MIME_TYPE,
331
+ };
332
+ this.requireLive().sendRealtimeInput({ audio });
333
+ }
334
+ /**
335
+ * @inheritdoc
336
+ *
337
+ * Gemini Live binds its tool set at connect time via {@link LiveConnectConfig.tools}; the
338
+ * provider does not support re-declaring tool schemas on an already-open session. Per the
339
+ * contract's idempotency rule:
340
+ * - A post-start set IDENTICAL to the connect-time set (`params.Tools` at `StartSession`,
341
+ * compared order-insensitively) is a **silent no-op**.
342
+ * - A DIFFERENT set is unsupported: it logs a clear warning and does **nothing** — it is
343
+ * never injected into the conversation as content (that would degrade the conversation
344
+ * without making the tools callable).
345
+ */
346
+ async RegisterTools(tools) {
347
+ if (GeminiRealtimeSession.ToolSetFingerprint(tools) === this.connectTimeToolsFingerprint) {
348
+ return; // identical to the connect-time set — silent no-op
349
+ }
350
+ console.warn('GeminiRealtimeSession.RegisterTools: Gemini Live binds its tool set at connect time and cannot ' +
351
+ 're-declare schemas on an open session. The requested set differs from the connect-time set and is ' +
352
+ 'IGNORED — pass the full tool set via RealtimeSessionParams.Tools at StartSession.');
353
+ }
354
+ /**
355
+ * Records the tool set the driver bound at connect time so {@link RegisterTools} can
356
+ * apply the contract's idempotency rule. Called by {@link GeminiRealtime.StartSession}.
357
+ */
358
+ SetConnectTimeTools(tools) {
359
+ this.connectTimeToolsFingerprint = GeminiRealtimeSession.ToolSetFingerprint(tools);
360
+ }
361
+ /** Canonical, order-insensitive fingerprint of a tool set for identity comparison. */
362
+ static ToolSetFingerprint(tools) {
363
+ return JSON.stringify([...tools]
364
+ .sort((a, b) => a.Name.localeCompare(b.Name))
365
+ .map((t) => ({ Name: t.Name, Description: t.Description, ParametersSchema: t.ParametersSchema })));
366
+ }
367
+ /** @inheritdoc */
368
+ OnOutput(handler) {
369
+ this.outputHandler = handler;
370
+ }
371
+ /** @inheritdoc */
372
+ OnTranscript(handler) {
373
+ this.transcriptHandler = handler;
374
+ }
375
+ /** @inheritdoc */
376
+ OnToolCall(handler) {
377
+ this.toolCallHandler = handler;
378
+ }
379
+ /** @inheritdoc */
380
+ OnInterruption(handler) {
381
+ this.interruptionHandler = handler;
382
+ }
383
+ /** @inheritdoc */
384
+ OnUsage(handler) {
385
+ this.usageHandler = handler;
386
+ }
387
+ OnError(handler) {
388
+ this.errorHandler = handler;
389
+ }
390
+ /**
391
+ * Surfaces a websocket-level failure as a FATAL session error — the transport is gone,
392
+ * so the consumer (e.g. the session runner) should finalize cleanly instead of idling.
393
+ */
394
+ HandleTransportError(message) {
395
+ this.errorHandler?.({ Message: message, Fatal: true });
396
+ }
397
+ /**
398
+ * Surfaces an UNEXPECTED socket close as a fatal error (expected closes — the consumer
399
+ * called {@link Close} — are silent). Gemini hard-closes at token expiry, so this is
400
+ * also how credential death reaches the consumer.
401
+ */
402
+ HandleTransportClose(code, reason) {
403
+ if (this.closedByConsumer) {
404
+ return;
405
+ }
406
+ const detail = [code != null ? `code ${code}` : null, reason || null].filter(Boolean).join(' — ');
407
+ this.errorHandler?.({ Message: `Gemini Live session closed unexpectedly${detail ? ` (${detail})` : ''}`, Fatal: true });
408
+ }
409
+ /**
410
+ * @inheritdoc
411
+ *
412
+ * Completes the tool-call loop for Gemini. Gemini's `sendToolResponse` requires the function
413
+ * *name*, which the Core contract does not pass — so the name is looked up from the
414
+ * {@link pendingToolCallNames} cache populated when the originating tool call arrived. The
415
+ * `output` JSON string is parsed into the structured response object Gemini expects, and the
416
+ * cache entry is cleared once the response is sent.
417
+ *
418
+ * @param callID The originating tool call's id.
419
+ * @param output The tool's result as a JSON-stringified string.
420
+ */
421
+ async SendToolResult(callID, output) {
422
+ const name = this.pendingToolCallNames.get(callID) ?? '';
423
+ const functionResponse = {
424
+ id: callID,
425
+ name,
426
+ response: this.parseToolOutput(output),
427
+ };
428
+ this.requireLive().sendToolResponse({ functionResponses: [functionResponse] });
429
+ this.pendingToolCallNames.delete(callID);
430
+ }
431
+ /**
432
+ * Parses a JSON-stringified tool result into the structured object Gemini's function-response
433
+ * slot expects. Falls back to wrapping non-JSON output as `{ result: <text> }` so a free-text
434
+ * result still round-trips without throwing.
435
+ *
436
+ * @param output The tool's result as a JSON-stringified string.
437
+ * @returns The parsed response object.
438
+ */
439
+ parseToolOutput(output) {
440
+ try {
441
+ const parsed = JSON.parse(output);
442
+ if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) {
443
+ return parsed;
444
+ }
445
+ return { result: parsed };
446
+ }
447
+ catch {
448
+ return { result: output };
449
+ }
450
+ }
451
+ /**
452
+ * @inheritdoc
453
+ *
454
+ * Injects background context as a **user turn with `turnComplete: false`** — appended to the
455
+ * conversation WITHOUT starting generation, so the model draws on it the next time it speaks.
456
+ * Gemini Live turns have no system role, so the user role carries it (the same mapping the
457
+ * client-direct Gemini driver uses; the caller owns any prefixing policy).
458
+ *
459
+ * **Collision behavior: queue.** Any client content sent mid-turn interrupts in-flight
460
+ * generation on Gemini Live, so the send is deferred until the active turn completes.
461
+ *
462
+ * @param text The context note to append to the conversation.
463
+ */
464
+ SendContextNote(text) {
465
+ this.enqueueOrRun(() => {
466
+ this.requireLive().sendClientContent({ turns: [{ role: 'user', parts: [{ text }] }], turnComplete: false });
467
+ });
468
+ }
469
+ /**
470
+ * @inheritdoc
471
+ *
472
+ * Triggers ONE short spoken update. Gemini Live has no per-response-instructions slot
473
+ * (OpenAI's `response.create.instructions`), so this is **emulated**: the instructions ride
474
+ * as a realtime-text user turn (`sendRealtimeInput({ text })`) — the path that triggers
475
+ * generation on every Live model generation, exactly as the client-direct Gemini driver
476
+ * emulates it. (A mid-call `sendClientContent` with `turnComplete: true` lands in history
477
+ * WITHOUT starting generation on native-audio models — the model would stay silent until
478
+ * the user next speaks.)
479
+ *
480
+ * **Collision behavior: queue.** Deferred behind any in-flight turn (sending it mid-turn would
481
+ * interrupt the pending reply); when it does send, the turn it triggers is marked active so
482
+ * later queued sends wait for its completion.
483
+ *
484
+ * @param instructions Instructions for the single spoken update.
485
+ */
486
+ RequestSpokenUpdate(instructions) {
487
+ this.enqueueOrRun(() => {
488
+ this.responseActive = true;
489
+ this.requireLive().sendRealtimeInput({ text: instructions });
490
+ });
491
+ }
492
+ /**
493
+ * Runs a client-content send immediately when no turn is in flight; otherwise queues it for
494
+ * the next turn boundary. Gemini-specific collision rule: ANY client content interrupts
495
+ * in-flight generation on the Live API, so deferral (not skipping) is the safe default.
496
+ */
497
+ enqueueOrRun(send) {
498
+ if (this.responseActive) {
499
+ this.queuedSends.push(send);
500
+ return;
501
+ }
502
+ send();
503
+ }
504
+ /**
505
+ * Turn boundary (`turnComplete` or `interrupted`): releases the busy flag and drains queued
506
+ * sends in order, stopping at the first send that itself starts a new turn (a queued
507
+ * {@link RequestSpokenUpdate} re-sets {@link responseActive}).
508
+ */
509
+ completeTurn() {
510
+ this.responseActive = false;
511
+ while (!this.responseActive && this.queuedSends.length > 0) {
512
+ const send = this.queuedSends.shift();
513
+ send?.();
514
+ }
515
+ }
516
+ /** @inheritdoc */
517
+ async Close() {
518
+ this.closedByConsumer = true;
519
+ this.live?.close();
520
+ this.live = null;
521
+ this.clearHandlers();
522
+ }
523
+ /**
524
+ * Entry point for an inbound {@link LiveServerMessage}. Fans the message out to the focused
525
+ * per-concern handlers so each translation unit stays small and testable.
526
+ */
527
+ HandleServerMessage(message) {
528
+ if (message.serverContent) {
529
+ this.handleServerContent(message.serverContent);
530
+ }
531
+ if (message.toolCall) {
532
+ this.handleToolCall(message.toolCall.functionCalls);
533
+ }
534
+ if (message.usageMetadata) {
535
+ this.handleUsage(message.usageMetadata.promptTokenCount, message.usageMetadata.responseTokenCount);
536
+ }
537
+ }
538
+ /**
539
+ * Translates a {@link LiveServerContent} frame: model audio output, input/output transcription,
540
+ * and interruption.
541
+ */
542
+ handleServerContent(content) {
543
+ if (content.interrupted) {
544
+ this.interruptionHandler?.();
545
+ this.completeTurn();
546
+ }
547
+ if (content.modelTurn) {
548
+ // First model output of a turn marks generation in flight (Gemini emits no explicit
549
+ // "response started" frame), so interim-update sends defer instead of interrupting.
550
+ this.responseActive = true;
551
+ this.emitAudioOutput(content.modelTurn);
552
+ }
553
+ if (content.turnComplete) {
554
+ this.completeTurn();
555
+ }
556
+ if (content.inputTranscription) {
557
+ this.emitTranscript('user', content.inputTranscription.text, content.inputTranscription.finished);
558
+ }
559
+ if (content.outputTranscription) {
560
+ this.emitTranscript('assistant', content.outputTranscription.text, content.outputTranscription.finished);
561
+ }
562
+ }
563
+ /**
564
+ * Extracts inline audio parts from the model turn and forwards each as a raw `ArrayBuffer`.
565
+ */
566
+ emitAudioOutput(modelTurn) {
567
+ if (!this.outputHandler || !modelTurn.parts) {
568
+ return;
569
+ }
570
+ for (const part of modelTurn.parts) {
571
+ const data = part.inlineData?.data;
572
+ if (data) {
573
+ this.outputHandler(GeminiRealtimeSession.Base64ToArrayBuffer(data));
574
+ }
575
+ }
576
+ }
577
+ /**
578
+ * Emits a transcript event, defaulting missing text to empty and `finished` to a partial update.
579
+ */
580
+ emitTranscript(role, text, finished) {
581
+ if (!this.transcriptHandler) {
582
+ return;
583
+ }
584
+ this.transcriptHandler({ Role: role, Text: text ?? '', IsFinal: finished ?? false });
585
+ }
586
+ /**
587
+ * Translates Gemini {@link FunctionCall}s into Core {@link RealtimeToolCall}s, serializing the
588
+ * arguments object to the JSON-string form the contract specifies.
589
+ */
590
+ handleToolCall(functionCalls) {
591
+ if (!functionCalls) {
592
+ return;
593
+ }
594
+ // The model has yielded the floor pending the tool result — clear the busy flag (without
595
+ // draining the queue; queued sends flush at the next real turn boundary) so the eventual
596
+ // SendToolResult and any fresh context note are not deferred behind a turn that will not
597
+ // complete until after the result is sent. Mirrors the client driver's deadlock guard.
598
+ this.responseActive = false;
599
+ for (const call of functionCalls) {
600
+ const callID = call.id ?? '';
601
+ const toolName = call.name ?? '';
602
+ // Cache the call's name (regardless of whether a tool-call handler is registered) so
603
+ // SendToolResult can supply it to Gemini's sendToolResponse, which requires the function
604
+ // name the Core contract does not carry.
605
+ this.pendingToolCallNames.set(callID, toolName);
606
+ this.toolCallHandler?.({
607
+ CallID: callID,
608
+ ToolName: toolName,
609
+ Arguments: JSON.stringify(call.args ?? {}),
610
+ });
611
+ }
612
+ }
613
+ /**
614
+ * Emits an incremental usage update, defaulting missing token counts to zero.
615
+ */
616
+ handleUsage(promptTokens, responseTokens) {
617
+ this.usageHandler?.({ InputTokens: promptTokens ?? 0, OutputTokens: responseTokens ?? 0 });
618
+ }
619
+ /** Drops all registered handlers so a closed session can't fire stale callbacks. */
620
+ clearHandlers() {
621
+ this.outputHandler = null;
622
+ this.transcriptHandler = null;
623
+ this.toolCallHandler = null;
624
+ this.interruptionHandler = null;
625
+ this.usageHandler = null;
626
+ this.pendingToolCallNames.clear();
627
+ this.queuedSends = [];
628
+ this.responseActive = false;
629
+ }
630
+ /** Returns the bound live session or throws if it was never attached / already closed. */
631
+ requireLive() {
632
+ if (!this.live) {
633
+ throw new Error('Gemini realtime session is not open (no live session attached or it was closed).');
634
+ }
635
+ return this.live;
636
+ }
637
+ /** Encodes an `ArrayBuffer` of raw bytes to a base64 string for Gemini's `Blob.data`. */
638
+ static ArrayBufferToBase64(buffer) {
639
+ return Buffer.from(new Uint8Array(buffer)).toString('base64');
640
+ }
641
+ /** Decodes a base64 `Blob.data` string from Gemini into a raw `ArrayBuffer`. */
642
+ static Base64ToArrayBuffer(base64) {
643
+ const bytes = Buffer.from(base64, 'base64');
644
+ // Copy into a freshly-allocated ArrayBuffer so we never leak the surrounding Node pool buffer
645
+ // (and so the result is a plain ArrayBuffer, not ArrayBufferLike).
646
+ const out = new ArrayBuffer(bytes.byteLength);
647
+ new Uint8Array(out).set(bytes);
648
+ return out;
649
+ }
650
+ }
651
+ //# sourceMappingURL=geminiRealtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geminiRealtime.js","sourceRoot":"","sources":["../src/geminiRealtime.ts"],"names":[],"mappings":"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"}