@memberjunction/ai 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.
- package/dist/generic/baseEmbeddings.d.ts +27 -2
- package/dist/generic/baseEmbeddings.d.ts.map +1 -1
- package/dist/generic/baseEmbeddings.js +65 -1
- package/dist/generic/baseEmbeddings.js.map +1 -1
- package/dist/generic/baseLLM.d.ts.map +1 -1
- package/dist/generic/baseLLM.js +5 -0
- package/dist/generic/baseLLM.js.map +1 -1
- package/dist/generic/baseRealtime.d.ts +468 -0
- package/dist/generic/baseRealtime.d.ts.map +1 -0
- package/dist/generic/baseRealtime.js +103 -0
- package/dist/generic/baseRealtime.js.map +1 -0
- package/dist/generic/baseRealtimeChannelServer.d.ts +235 -0
- package/dist/generic/baseRealtimeChannelServer.d.ts.map +1 -0
- package/dist/generic/baseRealtimeChannelServer.js +193 -0
- package/dist/generic/baseRealtimeChannelServer.js.map +1 -0
- package/dist/generic/embed.types.d.ts +5 -0
- package/dist/generic/embed.types.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/readme.md +7 -2
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
import { BaseModel } from "./baseModel.js";
|
|
2
|
+
/**
|
|
3
|
+
* A JSON-serializable value. Used to type open configuration bags and JSON-schema
|
|
4
|
+
* objects at the Core layer without resorting to `any`.
|
|
5
|
+
*/
|
|
6
|
+
export type JSONValue = string | number | boolean | null | JSONValue[] | {
|
|
7
|
+
[key: string]: JSONValue;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* A JSON object — the common shape for a JSON-schema document or an open,
|
|
11
|
+
* provider-specific configuration bag.
|
|
12
|
+
*/
|
|
13
|
+
export type JSONObject = {
|
|
14
|
+
[key: string]: JSONValue;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Base class for real-time, full-duplex, tool-calling models.
|
|
18
|
+
*
|
|
19
|
+
* `BaseRealtimeModel` is the lowest-level primitive for streaming, bidirectional models
|
|
20
|
+
* (e.g. Google Gemini Live, OpenAI GPT Realtime, and — as a fast-follow — the Eleven Labs
|
|
21
|
+
* stack). It is a sibling of {@link BaseModel}-derived capability classes such as `BaseLLM`
|
|
22
|
+
* and `BaseAudioGenerator`, and is resolved through the MemberJunction `ClassFactory` by
|
|
23
|
+
* `AIModelType` + `DriverClass`.
|
|
24
|
+
*
|
|
25
|
+
* The contract is deliberately **modality-agnostic** — *streaming, full-duplex, tool-calling* —
|
|
26
|
+
* so the same primitive covers voice now and video later. It is distinct from
|
|
27
|
+
* `BaseAudioGenerator`, which is request/response STT/TTS (a different shape, not the
|
|
28
|
+
* real-time path).
|
|
29
|
+
*
|
|
30
|
+
* **Driver registration:** Concrete drivers ship in the respective `@memberjunction/ai-*`
|
|
31
|
+
* provider packages and self-register via the class factory, e.g.
|
|
32
|
+
* `@RegisterClass(BaseRealtimeModel, 'GeminiRealtime')`. The associated `MJ: AI Models`
|
|
33
|
+
* are typed with the `AIModelType` value **`Realtime`**.
|
|
34
|
+
*
|
|
35
|
+
* ## DRIVER AUTHOR OBLIGATIONS
|
|
36
|
+
*
|
|
37
|
+
* Hard-won, provider-independent rules every server-side realtime driver (and its
|
|
38
|
+
* client-direct twin — see `BaseRealtimeClient` in `@memberjunction/ai-realtime-client`)
|
|
39
|
+
* MUST honor. Most were paid for in live debugging; do not relearn them:
|
|
40
|
+
*
|
|
41
|
+
* 1. **Silent exit from "speaking" after a tool call.** When the model emits a tool call,
|
|
42
|
+
* the host typically shows its own busy ("thinking") indicator while the tool executes.
|
|
43
|
+
* A driver that surfaces UI state must leave any "speaking" state *silently* (no state
|
|
44
|
+
* emission) at tool-call time so the turn's trailing frames don't clobber the host's
|
|
45
|
+
* indicator.
|
|
46
|
+
* 2. **Busy-flag release on tool-call emission (deadlock guard).** A tool-call frame means
|
|
47
|
+
* the model has yielded the floor pending the result. Any internal "response active" /
|
|
48
|
+
* busy flag MUST be cleared at that point — otherwise the eventual `SendToolResult` (or
|
|
49
|
+
* a queued send) deadlocks waiting for a turn boundary that will never arrive until
|
|
50
|
+
* after the result is sent.
|
|
51
|
+
* 3. **Playback flush + honest playback reporting on interruption.** On a true barge-in the
|
|
52
|
+
* driver (or its client twin) must flush any locally-owned audio playback and report
|
|
53
|
+
* "audio playing = false" promptly — stale queued audio after an interruption is a
|
|
54
|
+
* product bug, not a nicety.
|
|
55
|
+
* 4. **Text injection must NOT echo a user transcript.** A "send typed text" capability
|
|
56
|
+
* must not synthesize a user-role transcript event for the injected text — the host owns
|
|
57
|
+
* the local echo and would render the message twice.
|
|
58
|
+
* 5. **Tool-result delivery invariant.** Every tool result fed back via
|
|
59
|
+
* {@link IRealtimeSession.SendToolResult} must EVENTUALLY be voiced/processed by the
|
|
60
|
+
* model and must never be dropped. If the provider rejects overlapping generation
|
|
61
|
+
* triggers, the driver queues the result's trigger behind the in-flight response and
|
|
62
|
+
* flushes it at the next turn boundary.
|
|
63
|
+
* 6. **Token/credential expiry surfaces as a FATAL error.** When the session's credential
|
|
64
|
+
* dies (ephemeral token expiry, auth revocation), the driver must surface it through
|
|
65
|
+
* {@link IRealtimeSession.OnError} with `Fatal: true` so the consumer finalizes cleanly
|
|
66
|
+
* instead of idling forever on a dead socket.
|
|
67
|
+
* 7. **"Ready" only after the session config is applied.** A driver (or client twin) must
|
|
68
|
+
* not report the session as live/listening until the server-built session config
|
|
69
|
+
* (system prompt + tools) has actually been applied to the provider socket — otherwise
|
|
70
|
+
* early turns run against an unconfigured model.
|
|
71
|
+
* 8. **`SessionConfig` is a private pact.** The {@link ClientRealtimeSessionConfig.SessionConfig}
|
|
72
|
+
* payload a server driver mints is consumed ONLY by the same-keyed client driver. Hosts
|
|
73
|
+
* and intermediaries treat it as an opaque blob; its shape may change between the two
|
|
74
|
+
* driver halves without notice.
|
|
75
|
+
*
|
|
76
|
+
* @abstract
|
|
77
|
+
*/
|
|
78
|
+
export declare abstract class BaseRealtimeModel extends BaseModel {
|
|
79
|
+
/**
|
|
80
|
+
* Opens a stateful duplex session with the provider.
|
|
81
|
+
*
|
|
82
|
+
* The returned {@link IRealtimeSession} is the long-lived handle that streams media and
|
|
83
|
+
* transcripts in both directions, surfaces tool calls and usage telemetry, and reports
|
|
84
|
+
* provider-detected interruptions (barge-in). The caller is responsible for closing the
|
|
85
|
+
* session via {@link IRealtimeSession.Close}.
|
|
86
|
+
*
|
|
87
|
+
* @param params Configuration for the session (system prompt, tools, initial context, model, and an open config bag).
|
|
88
|
+
* @returns A promise resolving to the live session handle.
|
|
89
|
+
*/
|
|
90
|
+
abstract StartSession(params: RealtimeSessionParams): Promise<IRealtimeSession>;
|
|
91
|
+
/**
|
|
92
|
+
* Whether this driver can mint an ephemeral, server-scoped client credential for a
|
|
93
|
+
* **client-direct** realtime session (the browser opens its own provider socket using a
|
|
94
|
+
* short-lived token the server minted).
|
|
95
|
+
*
|
|
96
|
+
* Defaults to `false`. Providers that support browser-direct sessions override this to `true`
|
|
97
|
+
* and implement {@link CreateClientSession}. The server-bridged topology (where the provider
|
|
98
|
+
* socket lives on the server, via {@link StartSession}) is supported by every driver regardless
|
|
99
|
+
* of this flag.
|
|
100
|
+
*
|
|
101
|
+
* @returns `true` if {@link CreateClientSession} is supported; `false` otherwise.
|
|
102
|
+
*/
|
|
103
|
+
get SupportsClientDirect(): boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Mints an ephemeral, server-scoped client credential plus a provider-native session config for
|
|
106
|
+
* a **client-direct** realtime session.
|
|
107
|
+
*
|
|
108
|
+
* In the client-direct topology the browser owns the provider socket (e.g. WebRTC), but the
|
|
109
|
+
* **server** still controls the prompt and tool set: it mints a short-lived token and hands back
|
|
110
|
+
* a {@link ClientRealtimeSessionConfig} whose `SessionConfig` the matching client driver applies
|
|
111
|
+
* when it opens its socket. This keeps prompt/tool authority server-side even though the media
|
|
112
|
+
* plane is client-direct.
|
|
113
|
+
*
|
|
114
|
+
* Not every provider supports this (some only expose a server-bridged socket), so this is a
|
|
115
|
+
* concrete method that throws by default rather than an abstract one — that would force every
|
|
116
|
+
* existing and future driver to implement it. Providers that support it override
|
|
117
|
+
* {@link SupportsClientDirect} to `true` and override this method.
|
|
118
|
+
*
|
|
119
|
+
* @param _params The session parameters (system prompt, tools, model, config bag).
|
|
120
|
+
* @returns A promise resolving to the minted {@link ClientRealtimeSessionConfig}.
|
|
121
|
+
* @throws Always, unless overridden by a provider that supports client-direct sessions.
|
|
122
|
+
*/
|
|
123
|
+
CreateClientSession(_params: RealtimeSessionParams): Promise<ClientRealtimeSessionConfig>;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* The server-minted configuration a browser needs to open a **client-direct** realtime session.
|
|
127
|
+
*
|
|
128
|
+
* Returned by {@link BaseRealtimeModel.CreateClientSession}. The browser authenticates to the
|
|
129
|
+
* provider with {@link ClientRealtimeSessionConfig.EphemeralToken} and hands
|
|
130
|
+
* {@link ClientRealtimeSessionConfig.SessionConfig} to the matching client driver — so the server
|
|
131
|
+
* retains control of the prompt and tool set even though the browser owns the socket.
|
|
132
|
+
*
|
|
133
|
+
* **`SessionConfig` is a private pact between same-keyed driver halves.** The server driver that
|
|
134
|
+
* minted it (selected by {@link ClientRealtimeSessionConfig.Provider}) and the client driver
|
|
135
|
+
* registered under the same key are the ONLY parties that understand its shape. Hosts and any
|
|
136
|
+
* transport in between must treat it as an opaque, serializable blob — never inspect, edit, or
|
|
137
|
+
* depend on its fields.
|
|
138
|
+
*/
|
|
139
|
+
export interface ClientRealtimeSessionConfig {
|
|
140
|
+
/**
|
|
141
|
+
* The provider that minted the credential (e.g. `'openai'`). Lets the browser select the
|
|
142
|
+
* correct provider-direct client implementation.
|
|
143
|
+
*/
|
|
144
|
+
Provider: string;
|
|
145
|
+
/**
|
|
146
|
+
* The provider realtime model id the session is scoped to (e.g. `gpt-realtime`).
|
|
147
|
+
*/
|
|
148
|
+
Model: string;
|
|
149
|
+
/**
|
|
150
|
+
* The short-lived client secret the browser presents to the provider to authenticate its
|
|
151
|
+
* direct session. Server-scoped and expiring (see {@link ClientRealtimeSessionConfig.ExpiresAt}).
|
|
152
|
+
*/
|
|
153
|
+
EphemeralToken: string;
|
|
154
|
+
/**
|
|
155
|
+
* ISO-8601 timestamp at which {@link ClientRealtimeSessionConfig.EphemeralToken} expires.
|
|
156
|
+
*/
|
|
157
|
+
ExpiresAt: string;
|
|
158
|
+
/**
|
|
159
|
+
* The provider-native session config the matching client driver applies when it opens its
|
|
160
|
+
* socket (instructions/system prompt, tools, audio formats, turn detection). Because the server
|
|
161
|
+
* builds this, prompt and tool authority stay server-side even in the client-direct topology.
|
|
162
|
+
* Typed as a JSON object so it stays serializable across the server→client boundary — but its
|
|
163
|
+
* SHAPE is a private pact between the same-keyed server and client drivers; hosts must treat it
|
|
164
|
+
* opaquely and never read or rewrite its fields.
|
|
165
|
+
*/
|
|
166
|
+
SessionConfig: JSONObject;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* A long-lived, full-duplex session handle returned by {@link BaseRealtimeModel.StartSession}.
|
|
170
|
+
*
|
|
171
|
+
* All `On*` methods register a single handler invoked as the corresponding provider events
|
|
172
|
+
* arrive. Drivers must be written against this interface so a mock provider socket can be
|
|
173
|
+
* substituted for deterministic, network-free testing.
|
|
174
|
+
*/
|
|
175
|
+
export interface IRealtimeSession {
|
|
176
|
+
/**
|
|
177
|
+
* Sends a client media frame to the model (audio now, video later).
|
|
178
|
+
*
|
|
179
|
+
* Fire-and-forget: frames are streamed straight to the provider with no JSON intermediation.
|
|
180
|
+
*
|
|
181
|
+
* @param chunk A raw media frame as an `ArrayBuffer`.
|
|
182
|
+
*/
|
|
183
|
+
SendInput(chunk: ArrayBuffer): void;
|
|
184
|
+
/**
|
|
185
|
+
* Registers the set of tools the model may call, translating them into the provider's
|
|
186
|
+
* native function-calling format.
|
|
187
|
+
*
|
|
188
|
+
* The Core-level {@link RealtimeToolDefinition} is intentionally minimal: `BaseRealtimeModel`
|
|
189
|
+
* lives in the lowest AI layer and cannot depend on the richer tool metadata defined in
|
|
190
|
+
* higher packages (the agent layer) — doing so would create an illegal upward/circular
|
|
191
|
+
* dependency. The agent layer is responsible for **mapping its richer tool metadata down**
|
|
192
|
+
* to this Core type before calling `RegisterTools`, and the concrete driver maps this Core
|
|
193
|
+
* type **up** to the provider's native function-calling schema.
|
|
194
|
+
*
|
|
195
|
+
* Note that some providers (e.g. Eleven Labs) bind to a pre-declared tool set on a
|
|
196
|
+
* server-side agent configuration; for those, the driver maps these definitions onto the
|
|
197
|
+
* pre-declared tool names rather than registering arbitrary schemas at session start.
|
|
198
|
+
*
|
|
199
|
+
* **Idempotency rule:** a post-start registration of a set IDENTICAL to the set supplied at
|
|
200
|
+
* connect time (via {@link RealtimeSessionParams.Tools}) MUST be a no-op. Providers that bind
|
|
201
|
+
* their tool set at connect time and cannot re-declare schemas on an open session MUST no-op
|
|
202
|
+
* (and may log) rather than degrade the conversation — e.g. by injecting schema text into the
|
|
203
|
+
* conversation as content. A genuinely DIFFERENT post-start set on such a provider is
|
|
204
|
+
* unsupported and should be surfaced as a warning, not silently mangled.
|
|
205
|
+
*
|
|
206
|
+
* @param tools The tools to expose to the model.
|
|
207
|
+
*/
|
|
208
|
+
RegisterTools(tools: RealtimeToolDefinition[]): Promise<void>;
|
|
209
|
+
/**
|
|
210
|
+
* Registers a handler for model media output frames (the media plane).
|
|
211
|
+
*
|
|
212
|
+
* @param handler Invoked with each output media frame as an `ArrayBuffer`.
|
|
213
|
+
*/
|
|
214
|
+
OnOutput(handler: (chunk: ArrayBuffer) => void): void;
|
|
215
|
+
/**
|
|
216
|
+
* Registers a handler for transcript events (the text stream).
|
|
217
|
+
*
|
|
218
|
+
* Consumers typically forward these to the control plane and persist them as
|
|
219
|
+
* `ConversationDetail` records.
|
|
220
|
+
*
|
|
221
|
+
* @param handler Invoked with each {@link RealtimeTranscript} (partial or final).
|
|
222
|
+
*/
|
|
223
|
+
OnTranscript(handler: (t: RealtimeTranscript) => void): void;
|
|
224
|
+
/**
|
|
225
|
+
* Registers a handler for model tool-call requests.
|
|
226
|
+
*
|
|
227
|
+
* Consumers execute the requested tool (under the session's context user) and feed the
|
|
228
|
+
* result back to the model.
|
|
229
|
+
*
|
|
230
|
+
* @param handler Invoked with each {@link RealtimeToolCall}.
|
|
231
|
+
*/
|
|
232
|
+
OnToolCall(handler: (call: RealtimeToolCall) => void): void;
|
|
233
|
+
/**
|
|
234
|
+
* Send the result of an executed tool/function call back to the model so it can continue the
|
|
235
|
+
* turn. `output` is the JSON-stringified tool result. Called by the agent layer after it handles
|
|
236
|
+
* an {@link IRealtimeSession.OnToolCall}.
|
|
237
|
+
*
|
|
238
|
+
* @param callID The `CallID` from the originating {@link RealtimeToolCall}, used to correlate the result.
|
|
239
|
+
* @param output The tool's result as a JSON-stringified string.
|
|
240
|
+
* @returns A promise that resolves once the result has been sent to the provider.
|
|
241
|
+
*/
|
|
242
|
+
SendToolResult(callID: string, output: string): Promise<void>;
|
|
243
|
+
/**
|
|
244
|
+
* **Optional capability** — injects background context (e.g. delegated-run progress, freshly
|
|
245
|
+
* retrieved data, or a state change the model should be aware of) into the model's
|
|
246
|
+
* conversation **without** forcing a spoken reply. The model simply has the note available
|
|
247
|
+
* the next time it speaks.
|
|
248
|
+
*
|
|
249
|
+
* This is the server-side counterpart of the client-direct `BaseRealtimeClient.SendContextNote`
|
|
250
|
+
* capability: in the server-bridged topology, the agent layer (e.g. a session runner observing
|
|
251
|
+
* a delegated agent run) calls this to keep the realtime model informed of long-running work
|
|
252
|
+
* so it can narrate naturally when asked or when it next takes the floor.
|
|
253
|
+
*
|
|
254
|
+
* Optionality models **capability**, not laziness: not every provider supports injecting
|
|
255
|
+
* conversation items into an already-open session (some only accept media frames and tool
|
|
256
|
+
* results mid-session). Drivers that cannot inject mid-session omit the member entirely, and
|
|
257
|
+
* callers must feature-detect (`if (session.SendContextNote) { ... }`) rather than assume it.
|
|
258
|
+
*
|
|
259
|
+
* @param text The context note to append to the conversation (plain text; the caller owns any
|
|
260
|
+
* prefixing/framing policy such as "[progress]" markers).
|
|
261
|
+
*/
|
|
262
|
+
SendContextNote?(text: string): void;
|
|
263
|
+
/**
|
|
264
|
+
* **Optional capability** — asks the model to voice **one brief interim update** following the
|
|
265
|
+
* given instructions (e.g. "In one short sentence, tell the user the report agent has finished
|
|
266
|
+
* gathering data and is now drafting"). Used by the agent layer to narrate delegated-run
|
|
267
|
+
* progress while a long-running tool/agent call is still in flight.
|
|
268
|
+
*
|
|
269
|
+
* Implementations **must not collide with an in-flight model response**: providers reject or
|
|
270
|
+
* garble overlapping generation requests. A driver must either queue the request until the
|
|
271
|
+
* current response completes or skip it outright — skipping is explicitly acceptable because
|
|
272
|
+
* interim updates are disposable by contract (a stale "still working…" line has no value once
|
|
273
|
+
* the real result lands; the next update or the final result supersedes it).
|
|
274
|
+
*
|
|
275
|
+
* Like {@link IRealtimeSession.SendContextNote}, this is optional because it models provider
|
|
276
|
+
* capability: drivers whose provider cannot trigger an instructed, one-off spoken response
|
|
277
|
+
* mid-session omit the member, and callers must feature-detect before invoking.
|
|
278
|
+
*
|
|
279
|
+
* @param instructions Instructions for the single spoken update (tone, brevity, content).
|
|
280
|
+
*/
|
|
281
|
+
RequestSpokenUpdate?(instructions: string): void;
|
|
282
|
+
/**
|
|
283
|
+
* Registers a handler for provider-detected interruptions (barge-in).
|
|
284
|
+
*
|
|
285
|
+
* **True barge-in only:** the handler fires ONLY when user speech interrupts ACTIVE model
|
|
286
|
+
* output — NOT on every user utterance. A user simply taking their normal turn while the
|
|
287
|
+
* model is idle is not an interruption, and drivers must not report it as one (e.g. a raw
|
|
288
|
+
* "speech started" frame must be gated on whether a model response is actually in flight).
|
|
289
|
+
*
|
|
290
|
+
* Turn detection / VAD is owned by the provider. The agent layer uses this hook to cancel
|
|
291
|
+
* the model's current turn **and** to fire the `cancellationToken` of any in-flight
|
|
292
|
+
* delegated agent run — a stale delegated result must never be narrated into a conversation
|
|
293
|
+
* that has moved on.
|
|
294
|
+
*
|
|
295
|
+
* @param handler Invoked when the provider reports a true barge-in interruption.
|
|
296
|
+
*/
|
|
297
|
+
OnInterruption(handler: () => void): void;
|
|
298
|
+
/**
|
|
299
|
+
* Registers a handler for session errors.
|
|
300
|
+
*
|
|
301
|
+
* Fatality semantics mirror the client-side `BaseRealtimeClient` contract:
|
|
302
|
+
* - `Fatal: true` — the session is unusable (transport/socket failure, credential/token
|
|
303
|
+
* expiry, unexpected connection loss). The consumer should finalize the session (e.g.
|
|
304
|
+
* `RealtimeSessionRunner` calls `Stop()`) instead of idling forever on a dead socket.
|
|
305
|
+
* - `Fatal: false` — a provider-reported, recoverable error frame; the session stays open
|
|
306
|
+
* and the consumer should log and continue.
|
|
307
|
+
*
|
|
308
|
+
* @param handler Invoked with each {@link RealtimeSessionError}.
|
|
309
|
+
*/
|
|
310
|
+
OnError(handler: (error: RealtimeSessionError) => void): void;
|
|
311
|
+
/**
|
|
312
|
+
* **Optional capability** — registers a handler invoked when the underlying provider
|
|
313
|
+
* connection closes WITHOUT the consumer having called {@link IRealtimeSession.Close}
|
|
314
|
+
* (provider-side hangup, network drop). Not fired for a consumer-initiated `Close()` —
|
|
315
|
+
* the caller already knows about that one.
|
|
316
|
+
*
|
|
317
|
+
* Optional because not every provider surface exposes a close signal cheaply; callers
|
|
318
|
+
* must feature-detect (`if (session.OnClose) { ... }`). An unexpected close is typically
|
|
319
|
+
* ALSO surfaced as a `Fatal` {@link IRealtimeSession.OnError}, which is the signal
|
|
320
|
+
* consumers should drive finalization from.
|
|
321
|
+
*
|
|
322
|
+
* @param handler Invoked when the provider connection closes unexpectedly.
|
|
323
|
+
*/
|
|
324
|
+
OnClose?(handler: () => void): void;
|
|
325
|
+
/**
|
|
326
|
+
* Registers a handler for usage/telemetry updates.
|
|
327
|
+
*
|
|
328
|
+
* Usage is checkpointed incrementally by the agent layer (debounced onto the prompt run) so
|
|
329
|
+
* partial usage is never lost if the session is force-closed after a crash.
|
|
330
|
+
*
|
|
331
|
+
* @param handler Invoked with each {@link RealtimeUsage} update.
|
|
332
|
+
*/
|
|
333
|
+
OnUsage(handler: (u: RealtimeUsage) => void): void;
|
|
334
|
+
/**
|
|
335
|
+
* Closes the session and releases the underlying provider connection.
|
|
336
|
+
*
|
|
337
|
+
* @returns A promise that resolves once the session is fully closed.
|
|
338
|
+
*/
|
|
339
|
+
Close(): Promise<void>;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Parameters used to open a {@link IRealtimeSession} via {@link BaseRealtimeModel.StartSession}.
|
|
343
|
+
*/
|
|
344
|
+
export interface RealtimeSessionParams {
|
|
345
|
+
/**
|
|
346
|
+
* The API name of the realtime model to use (the `MJ: AI Model Vendors` API name for the
|
|
347
|
+
* driver's provider).
|
|
348
|
+
*/
|
|
349
|
+
Model: string;
|
|
350
|
+
/**
|
|
351
|
+
* The system prompt that establishes the model's persona and behavior for the session.
|
|
352
|
+
*/
|
|
353
|
+
SystemPrompt: string;
|
|
354
|
+
/**
|
|
355
|
+
* Optional set of tools to register at session start. Equivalent to calling
|
|
356
|
+
* {@link IRealtimeSession.RegisterTools} immediately after the session opens; drivers may
|
|
357
|
+
* register these eagerly when the provider accepts tool schemas at session start.
|
|
358
|
+
*/
|
|
359
|
+
Tools?: RealtimeToolDefinition[];
|
|
360
|
+
/**
|
|
361
|
+
* Optional initial context to seed the conversation (e.g. the prior conversation history
|
|
362
|
+
* and retrieved memory) so the model starts with the same context a loop agent assembles.
|
|
363
|
+
*/
|
|
364
|
+
InitialContext?: string;
|
|
365
|
+
/**
|
|
366
|
+
* Optional open, provider-specific configuration bag (e.g. voice, language, turn-taking
|
|
367
|
+
* settings, or per-conversation override fields). Typed as a JSON object rather than `any`
|
|
368
|
+
* so it stays serializable and inspectable.
|
|
369
|
+
*/
|
|
370
|
+
Config?: JSONObject;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* A transcript event emitted by the model for either the user's speech or the assistant's
|
|
374
|
+
* response.
|
|
375
|
+
*/
|
|
376
|
+
export interface RealtimeTranscript {
|
|
377
|
+
/**
|
|
378
|
+
* Whose turn this transcript belongs to.
|
|
379
|
+
*/
|
|
380
|
+
Role: 'user' | 'assistant';
|
|
381
|
+
/**
|
|
382
|
+
* The transcribed text. For `IsFinal: false` events this is the incremental **DELTA** for
|
|
383
|
+
* the in-flight turn (drivers emit each new fragment, not a re-send of the accumulated
|
|
384
|
+
* text); for `IsFinal: true` it is the complete turn text.
|
|
385
|
+
*/
|
|
386
|
+
Text: string;
|
|
387
|
+
/**
|
|
388
|
+
* Whether this is the final transcript for the turn (`true`) or an interim delta (`false`).
|
|
389
|
+
*/
|
|
390
|
+
IsFinal: boolean;
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* An error surfaced by a realtime session via {@link IRealtimeSession.OnError}.
|
|
394
|
+
*
|
|
395
|
+
* Mirrors the client-side `RealtimeClientError` shape: `Fatal: true` means the session is
|
|
396
|
+
* unusable (transport failure, credential expiry, unexpected close) and the consumer should
|
|
397
|
+
* finalize; `Fatal: false` is a provider-reported, recoverable error frame.
|
|
398
|
+
*/
|
|
399
|
+
export interface RealtimeSessionError {
|
|
400
|
+
/** Human-readable error message. */
|
|
401
|
+
Message: string;
|
|
402
|
+
/** Optional provider-specific error code. */
|
|
403
|
+
Code?: string;
|
|
404
|
+
/** Whether the error terminated the session. */
|
|
405
|
+
Fatal: boolean;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* A tool-call request emitted by the model.
|
|
409
|
+
*/
|
|
410
|
+
export interface RealtimeToolCall {
|
|
411
|
+
/**
|
|
412
|
+
* Provider-assigned identifier for this call, used to correlate the eventual tool result.
|
|
413
|
+
*/
|
|
414
|
+
CallID: string;
|
|
415
|
+
/**
|
|
416
|
+
* The name of the tool the model is requesting to invoke.
|
|
417
|
+
*/
|
|
418
|
+
ToolName: string;
|
|
419
|
+
/**
|
|
420
|
+
* The arguments for the call as a JSON string, exactly as the provider emitted them.
|
|
421
|
+
* Consumers parse this into the tool's expected parameter shape.
|
|
422
|
+
*/
|
|
423
|
+
Arguments: string;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Incremental usage/telemetry reported during a realtime session.
|
|
427
|
+
*
|
|
428
|
+
* This is the realtime-specific counterpart to Core's request/response `ModelUsage`. Because a
|
|
429
|
+
* realtime session is long-lived and usage is reported in increments (and checkpointed
|
|
430
|
+
* incrementally), this type carries the token deltas a provider emits over the life of the
|
|
431
|
+
* session rather than a single final tally.
|
|
432
|
+
*/
|
|
433
|
+
export interface RealtimeUsage {
|
|
434
|
+
/**
|
|
435
|
+
* Number of input tokens reported in this usage update.
|
|
436
|
+
*/
|
|
437
|
+
InputTokens: number;
|
|
438
|
+
/**
|
|
439
|
+
* Number of output tokens reported in this usage update.
|
|
440
|
+
*/
|
|
441
|
+
OutputTokens: number;
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Minimal, Core-level definition of a tool exposed to a realtime model.
|
|
445
|
+
*
|
|
446
|
+
* This type lives in the lowest AI layer and is intentionally provider- and agent-agnostic.
|
|
447
|
+
* The agent layer maps its richer tool metadata **down** to this type before registering tools,
|
|
448
|
+
* and each concrete driver maps this type **up** to the provider's native function-calling
|
|
449
|
+
* schema. Keeping the Core type minimal avoids an illegal upward dependency from Core onto the
|
|
450
|
+
* agent packages.
|
|
451
|
+
*/
|
|
452
|
+
export interface RealtimeToolDefinition {
|
|
453
|
+
/**
|
|
454
|
+
* The tool's name, used to match provider tool-call frames back to MJ tool execution.
|
|
455
|
+
*/
|
|
456
|
+
Name: string;
|
|
457
|
+
/**
|
|
458
|
+
* A human-readable description of what the tool does. Surfaced to the model so it can decide
|
|
459
|
+
* when to call the tool.
|
|
460
|
+
*/
|
|
461
|
+
Description: string;
|
|
462
|
+
/**
|
|
463
|
+
* A JSON-schema object describing the tool's parameters. Drivers translate this into the
|
|
464
|
+
* provider's native function-parameter schema.
|
|
465
|
+
*/
|
|
466
|
+
ParametersSchema: JSONObject;
|
|
467
|
+
}
|
|
468
|
+
//# sourceMappingURL=baseRealtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"baseRealtime.d.ts","sourceRoot":"","sources":["../../src/generic/baseRealtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { BaseModel } from "./baseModel.js";
|
|
2
|
+
/**
|
|
3
|
+
* Base class for real-time, full-duplex, tool-calling models.
|
|
4
|
+
*
|
|
5
|
+
* `BaseRealtimeModel` is the lowest-level primitive for streaming, bidirectional models
|
|
6
|
+
* (e.g. Google Gemini Live, OpenAI GPT Realtime, and — as a fast-follow — the Eleven Labs
|
|
7
|
+
* stack). It is a sibling of {@link BaseModel}-derived capability classes such as `BaseLLM`
|
|
8
|
+
* and `BaseAudioGenerator`, and is resolved through the MemberJunction `ClassFactory` by
|
|
9
|
+
* `AIModelType` + `DriverClass`.
|
|
10
|
+
*
|
|
11
|
+
* The contract is deliberately **modality-agnostic** — *streaming, full-duplex, tool-calling* —
|
|
12
|
+
* so the same primitive covers voice now and video later. It is distinct from
|
|
13
|
+
* `BaseAudioGenerator`, which is request/response STT/TTS (a different shape, not the
|
|
14
|
+
* real-time path).
|
|
15
|
+
*
|
|
16
|
+
* **Driver registration:** Concrete drivers ship in the respective `@memberjunction/ai-*`
|
|
17
|
+
* provider packages and self-register via the class factory, e.g.
|
|
18
|
+
* `@RegisterClass(BaseRealtimeModel, 'GeminiRealtime')`. The associated `MJ: AI Models`
|
|
19
|
+
* are typed with the `AIModelType` value **`Realtime`**.
|
|
20
|
+
*
|
|
21
|
+
* ## DRIVER AUTHOR OBLIGATIONS
|
|
22
|
+
*
|
|
23
|
+
* Hard-won, provider-independent rules every server-side realtime driver (and its
|
|
24
|
+
* client-direct twin — see `BaseRealtimeClient` in `@memberjunction/ai-realtime-client`)
|
|
25
|
+
* MUST honor. Most were paid for in live debugging; do not relearn them:
|
|
26
|
+
*
|
|
27
|
+
* 1. **Silent exit from "speaking" after a tool call.** When the model emits a tool call,
|
|
28
|
+
* the host typically shows its own busy ("thinking") indicator while the tool executes.
|
|
29
|
+
* A driver that surfaces UI state must leave any "speaking" state *silently* (no state
|
|
30
|
+
* emission) at tool-call time so the turn's trailing frames don't clobber the host's
|
|
31
|
+
* indicator.
|
|
32
|
+
* 2. **Busy-flag release on tool-call emission (deadlock guard).** A tool-call frame means
|
|
33
|
+
* the model has yielded the floor pending the result. Any internal "response active" /
|
|
34
|
+
* busy flag MUST be cleared at that point — otherwise the eventual `SendToolResult` (or
|
|
35
|
+
* a queued send) deadlocks waiting for a turn boundary that will never arrive until
|
|
36
|
+
* after the result is sent.
|
|
37
|
+
* 3. **Playback flush + honest playback reporting on interruption.** On a true barge-in the
|
|
38
|
+
* driver (or its client twin) must flush any locally-owned audio playback and report
|
|
39
|
+
* "audio playing = false" promptly — stale queued audio after an interruption is a
|
|
40
|
+
* product bug, not a nicety.
|
|
41
|
+
* 4. **Text injection must NOT echo a user transcript.** A "send typed text" capability
|
|
42
|
+
* must not synthesize a user-role transcript event for the injected text — the host owns
|
|
43
|
+
* the local echo and would render the message twice.
|
|
44
|
+
* 5. **Tool-result delivery invariant.** Every tool result fed back via
|
|
45
|
+
* {@link IRealtimeSession.SendToolResult} must EVENTUALLY be voiced/processed by the
|
|
46
|
+
* model and must never be dropped. If the provider rejects overlapping generation
|
|
47
|
+
* triggers, the driver queues the result's trigger behind the in-flight response and
|
|
48
|
+
* flushes it at the next turn boundary.
|
|
49
|
+
* 6. **Token/credential expiry surfaces as a FATAL error.** When the session's credential
|
|
50
|
+
* dies (ephemeral token expiry, auth revocation), the driver must surface it through
|
|
51
|
+
* {@link IRealtimeSession.OnError} with `Fatal: true` so the consumer finalizes cleanly
|
|
52
|
+
* instead of idling forever on a dead socket.
|
|
53
|
+
* 7. **"Ready" only after the session config is applied.** A driver (or client twin) must
|
|
54
|
+
* not report the session as live/listening until the server-built session config
|
|
55
|
+
* (system prompt + tools) has actually been applied to the provider socket — otherwise
|
|
56
|
+
* early turns run against an unconfigured model.
|
|
57
|
+
* 8. **`SessionConfig` is a private pact.** The {@link ClientRealtimeSessionConfig.SessionConfig}
|
|
58
|
+
* payload a server driver mints is consumed ONLY by the same-keyed client driver. Hosts
|
|
59
|
+
* and intermediaries treat it as an opaque blob; its shape may change between the two
|
|
60
|
+
* driver halves without notice.
|
|
61
|
+
*
|
|
62
|
+
* @abstract
|
|
63
|
+
*/
|
|
64
|
+
export class BaseRealtimeModel extends BaseModel {
|
|
65
|
+
/**
|
|
66
|
+
* Whether this driver can mint an ephemeral, server-scoped client credential for a
|
|
67
|
+
* **client-direct** realtime session (the browser opens its own provider socket using a
|
|
68
|
+
* short-lived token the server minted).
|
|
69
|
+
*
|
|
70
|
+
* Defaults to `false`. Providers that support browser-direct sessions override this to `true`
|
|
71
|
+
* and implement {@link CreateClientSession}. The server-bridged topology (where the provider
|
|
72
|
+
* socket lives on the server, via {@link StartSession}) is supported by every driver regardless
|
|
73
|
+
* of this flag.
|
|
74
|
+
*
|
|
75
|
+
* @returns `true` if {@link CreateClientSession} is supported; `false` otherwise.
|
|
76
|
+
*/
|
|
77
|
+
get SupportsClientDirect() {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Mints an ephemeral, server-scoped client credential plus a provider-native session config for
|
|
82
|
+
* a **client-direct** realtime session.
|
|
83
|
+
*
|
|
84
|
+
* In the client-direct topology the browser owns the provider socket (e.g. WebRTC), but the
|
|
85
|
+
* **server** still controls the prompt and tool set: it mints a short-lived token and hands back
|
|
86
|
+
* a {@link ClientRealtimeSessionConfig} whose `SessionConfig` the matching client driver applies
|
|
87
|
+
* when it opens its socket. This keeps prompt/tool authority server-side even though the media
|
|
88
|
+
* plane is client-direct.
|
|
89
|
+
*
|
|
90
|
+
* Not every provider supports this (some only expose a server-bridged socket), so this is a
|
|
91
|
+
* concrete method that throws by default rather than an abstract one — that would force every
|
|
92
|
+
* existing and future driver to implement it. Providers that support it override
|
|
93
|
+
* {@link SupportsClientDirect} to `true` and override this method.
|
|
94
|
+
*
|
|
95
|
+
* @param _params The session parameters (system prompt, tools, model, config bag).
|
|
96
|
+
* @returns A promise resolving to the minted {@link ClientRealtimeSessionConfig}.
|
|
97
|
+
* @throws Always, unless overridden by a provider that supports client-direct sessions.
|
|
98
|
+
*/
|
|
99
|
+
async CreateClientSession(_params) {
|
|
100
|
+
throw new Error(`${this.constructor.name} does not support client-direct realtime sessions`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=baseRealtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"baseRealtime.js","sourceRoot":"","sources":["../../src/generic/baseRealtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAoBxC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AACH,MAAM,OAAgB,iBAAkB,SAAQ,SAAS;IAcrD;;;;;;;;;;;OAWG;IACH,IAAW,oBAAoB;QAC3B,OAAO,KAAK,CAAC;IACjB,CAAC;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACI,KAAK,CAAC,mBAAmB,CAAC,OAA8B;QAC3D,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,mDAAmD,CAAC,CAAC;IACjG,CAAC;CACJ"}
|