@memberjunction/ai-gemini 5.40.1 → 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.
- package/dist/geminiEmbedding2.d.ts +27 -0
- package/dist/geminiEmbedding2.d.ts.map +1 -0
- package/dist/geminiEmbedding2.js +146 -0
- package/dist/geminiEmbedding2.js.map +1 -0
- package/dist/geminiRealtime.d.ts +151 -0
- package/dist/geminiRealtime.d.ts.map +1 -0
- package/dist/geminiRealtime.js +651 -0
- package/dist/geminiRealtime.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +86 -41
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/readme.md +26 -0
|
@@ -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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"}
|