@llmrtc/llmrtc-core 1.2.0 → 1.3.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/CHANGELOG.md CHANGED
@@ -1,5 +1,63 @@
1
1
  # @llmrtc/llmrtc-core
2
2
 
3
+ ## 1.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 6a74624: Experimental realtime speech-to-speech relay mode (RFC 0001, M1).
8
+ - New opt-in realtimeSpeech server mode: sessions connect to a native
9
+ speech-to-speech model over the provider's WebSocket instead of the
10
+ STT-LLM-TTS pipeline, for ~300-500ms voice-to-voice latency.
11
+ - OpenAIRealtimeSpeechProvider (gpt-realtime-2.1 family): bidirectional
12
+ 24kHz PCM, user/assistant transcripts, provider-side turn detection,
13
+ barge-in with response cancellation and history truncation, per-
14
+ response usage, session-expiry warning ahead of the 60-minute cap.
15
+ - Relay orchestrator with a decoupled control loop, epoch-tagged
16
+ playback queue, and independent pacer, so interruption reaction is
17
+ bounded regardless of response length; final transcripts are mirrored
18
+ into session history.
19
+ - Protocol (additive): assistant-transcript and usage messages,
20
+ ready.mode, REALTIME_ERROR/BUDGET_EXCEEDED error codes.
21
+ - M1 scope: tool bridging, budgets, session renewal, playbooks, client
22
+ reconnect grace, and the Gemini adapter land in subsequent milestones.
23
+
24
+ - 2cc97a0: Realtime relay milestone 2 (RFC 0001): tools, budgets, session renewal.
25
+ - Tool bridging: the same ToolRegistry definitions drive provider-native
26
+ function calling; calls execute through ToolExecutor (timeouts, abort
27
+ support) without blocking the relay control loop, with results
28
+ returned to the live session and tool-call-start/end relayed to
29
+ clients. Provider-side cancellations abort in-flight executions.
30
+ - Session budgets: maxSessionMs (default 120 minutes) and maxTokens
31
+ guardrails with warn or end-session behavior; ending emits
32
+ BUDGET_EXCEEDED and closes the provider session.
33
+ - OpenAI 60-minute session renewal: near expiry (session-expiring
34
+ events now carry a renewable flag in core) the orchestrator opens a
35
+ fresh provider session seeded from the mirrored transcript history
36
+ and swaps it in at a quiet moment; in-flight work is aborted cleanly
37
+ if quiescence cannot be reached. The onSessionRenewed hook is
38
+ deferred to a later milestone.
39
+ - Budget end-session reports exactly one BUDGET_EXCEEDED error to the
40
+ client; tool bridge failures (unserializable results) are contained
41
+ per call instead of crashing the process.
42
+
43
+ - 3db8818: Realtime relay milestone 3 (RFC 0001): playbooks, client events,
44
+ reconnect grace.
45
+ - Playbooks work in relay mode (llm_decision transitions;
46
+ clearHistory and per-stage llmConfig are not applied in relay mode): playbook_transition tool calls
47
+ reconfigure the live session's instructions and tools via the shared
48
+ PlaybookEngine, emit stage-change to clients, and nudge the model to
49
+ speak the new stage.
50
+ - Web client: new assistantTranscript and usage events, plus a
51
+ reserved modeChanged event (mid-session pipeline fallback ships in a
52
+ later milestone);
53
+ new mode-changed protocol message; session interface gains optional
54
+ requestResponse (OpenAI adapter implements it).
55
+ - Client reconnect grace: a dropped client has clientReconnectGraceMs
56
+ (default 30s) to reconnect and adopt its still-live provider session
57
+ (honest historyRecovered semantics); playback re-targets the new
58
+ peer.
59
+ - New docs page: Realtime Speech-to-Speech (experimental).
60
+
3
61
  ## 1.2.0
4
62
 
5
63
  ### Minor Changes
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from './types.js';
2
2
  export * from './async-event-queue.js';
3
+ export * from './realtime-speech.js';
3
4
  export * from './tools.js';
4
5
  export * from './tool-executor.js';
5
6
  export * from './playbook.js';
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from './types.js';
2
2
  export * from './async-event-queue.js';
3
+ export * from './realtime-speech.js';
3
4
  export * from './tools.js';
4
5
  export * from './tool-executor.js';
5
6
  export * from './playbook.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,wBAAwB,CAAC;AACvC,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC"}
@@ -81,6 +81,12 @@ export interface ReadyMessage extends BaseMessage {
81
81
  protocolVersion: number;
82
82
  /** ICE servers for WebRTC (STUN/TURN) - provided by server for client use */
83
83
  iceServers?: RTCIceServer[];
84
+ /**
85
+ * Session mode: 'pipeline' (STT-LLM-TTS, the default) or 'realtime'
86
+ * (native speech-to-speech relay). Absent from older servers, which
87
+ * are always pipeline.
88
+ */
89
+ mode?: 'pipeline' | 'realtime';
84
90
  }
85
91
  /**
86
92
  * Heartbeat pong response
@@ -122,6 +128,35 @@ export interface TranscriptMessage extends BaseMessage {
122
128
  /** Whether this is the final transcription */
123
129
  isFinal: boolean;
124
130
  }
131
+ /**
132
+ * Assistant-side transcript (realtime relay mode): what the assistant
133
+ * is saying, streamed as it speaks.
134
+ */
135
+ export interface AssistantTranscriptMessage extends BaseMessage {
136
+ type: 'assistant-transcript';
137
+ /** Transcript text */
138
+ text: string;
139
+ /** Whether this is the final transcript for the response */
140
+ isFinal: boolean;
141
+ }
142
+ /**
143
+ * Per-response token usage (realtime relay mode).
144
+ */
145
+ export interface UsageMessage extends BaseMessage {
146
+ type: 'usage';
147
+ inputTokens: number;
148
+ outputTokens: number;
149
+ audioInputTokens?: number;
150
+ audioOutputTokens?: number;
151
+ cachedTokens?: number;
152
+ }
153
+ /**
154
+ * Mid-session mode change (realtime relay fell back to pipeline).
155
+ */
156
+ export interface ModeChangedMessage extends BaseMessage {
157
+ type: 'mode-changed';
158
+ mode: 'pipeline' | 'realtime';
159
+ }
125
160
  /**
126
161
  * LLM response chunk (streaming)
127
162
  */
@@ -249,7 +284,7 @@ export interface StageChangeMessage extends BaseMessage {
249
284
  * - Processing: AUDIO_*, VAD_*, INVALID_*
250
285
  * - Generic: INTERNAL_*, RATE_LIMITED
251
286
  */
252
- export type ErrorCode = 'WEBRTC_UNAVAILABLE' | 'CONNECTION_FAILED' | 'SESSION_NOT_FOUND' | 'SESSION_EXPIRED' | 'STT_ERROR' | 'STT_TIMEOUT' | 'LLM_ERROR' | 'LLM_TIMEOUT' | 'TTS_ERROR' | 'TTS_TIMEOUT' | 'AUDIO_PROCESSING_ERROR' | 'VAD_ERROR' | 'INVALID_MESSAGE' | 'INVALID_AUDIO_FORMAT' | 'TOOL_ERROR' | 'PLAYBOOK_ERROR' | 'INTERNAL_ERROR' | 'RATE_LIMITED';
287
+ export type ErrorCode = 'WEBRTC_UNAVAILABLE' | 'CONNECTION_FAILED' | 'SESSION_NOT_FOUND' | 'SESSION_EXPIRED' | 'STT_ERROR' | 'STT_TIMEOUT' | 'LLM_ERROR' | 'LLM_TIMEOUT' | 'TTS_ERROR' | 'TTS_TIMEOUT' | 'AUDIO_PROCESSING_ERROR' | 'VAD_ERROR' | 'INVALID_MESSAGE' | 'INVALID_AUDIO_FORMAT' | 'TOOL_ERROR' | 'PLAYBOOK_ERROR' | 'REALTIME_ERROR' | 'BUDGET_EXCEEDED' | 'INTERNAL_ERROR' | 'RATE_LIMITED';
253
288
  /**
254
289
  * Error message from server
255
290
  */
@@ -263,7 +298,7 @@ export interface ErrorMessage extends BaseMessage {
263
298
  /**
264
299
  * Union of all server-to-client message types
265
300
  */
266
- export type ServerMessage = ReadyMessage | PongMessage | SignalMessage | ReconnectAckMessage | TranscriptMessage | LLMChunkMessage | LLMMessage | TTSStartMessage | TTSChunkMessage | TTSMessage | TTSCompleteMessage | TTSCancelledMessage | SpeechStartMessage | SpeechEndMessage | ToolCallStartMessage | ToolCallEndMessage | StageChangeMessage | ErrorMessage;
301
+ export type ServerMessage = ReadyMessage | PongMessage | SignalMessage | ReconnectAckMessage | TranscriptMessage | AssistantTranscriptMessage | UsageMessage | ModeChangedMessage | LLMChunkMessage | LLMMessage | TTSStartMessage | TTSChunkMessage | TTSMessage | TTSCompleteMessage | TTSCancelledMessage | SpeechStartMessage | SpeechEndMessage | ToolCallStartMessage | ToolCallEndMessage | StageChangeMessage | ErrorMessage;
267
302
  /**
268
303
  * Union of all protocol messages
269
304
  */
@@ -288,7 +323,7 @@ export declare function parseMessage(json: string): ProtocolMessage | null;
288
323
  /**
289
324
  * Create a ready message
290
325
  */
291
- export declare function createReadyMessage(id: string, iceServers?: RTCIceServer[]): ReadyMessage;
326
+ export declare function createReadyMessage(id: string, iceServers?: RTCIceServer[], mode?: 'pipeline' | 'realtime'): ReadyMessage;
292
327
  /**
293
328
  * Create an error message
294
329
  */
package/dist/protocol.js CHANGED
@@ -32,6 +32,9 @@ const SERVER_MESSAGE_TYPES = new Set([
32
32
  'signal',
33
33
  'reconnect-ack',
34
34
  'transcript',
35
+ 'assistant-transcript',
36
+ 'usage',
37
+ 'mode-changed',
35
38
  'llm-chunk',
36
39
  'llm',
37
40
  'tts-start',
@@ -89,8 +92,8 @@ export function parseMessage(json) {
89
92
  /**
90
93
  * Create a ready message
91
94
  */
92
- export function createReadyMessage(id, iceServers) {
93
- return { type: 'ready', id, protocolVersion: PROTOCOL_VERSION, iceServers };
95
+ export function createReadyMessage(id, iceServers, mode) {
96
+ return { type: 'ready', id, protocolVersion: PROTOCOL_VERSION, iceServers, ...(mode && { mode }) };
94
97
  }
95
98
  /**
96
99
  * Create an error message
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Realtime speech-to-speech provider abstraction (RFC 0001).
3
+ *
4
+ * A RealtimeSpeechProvider connects a voice session to a provider's
5
+ * native speech-to-speech model over a bidirectional stream: mic PCM
6
+ * goes up, assistant PCM comes down, with transcripts, tool calls, and
7
+ * turn events normalized into RealtimeSpeechEvent. Turn detection,
8
+ * comprehension, and voice synthesis are provider-side; the orchestrator
9
+ * relays audio and reacts to events.
10
+ *
11
+ * Experimental: shipped behind the opt-in `realtimeSpeech` server mode.
12
+ */
13
+ import type { ToolDefinition } from './tools.js';
14
+ export interface RealtimeSpeechConfig {
15
+ /** System prompt for the realtime model. */
16
+ instructions?: string;
17
+ /** Provider voice id (pass-through, e.g. OpenAI 'marin'). */
18
+ voice?: string;
19
+ /** Tool definitions - same shape as pipeline mode. */
20
+ tools?: ToolDefinition[];
21
+ /**
22
+ * Emit user-transcript events (default true). Input transcription is
23
+ * a separate transcription-model pass billed on top of realtime audio
24
+ * tokens.
25
+ */
26
+ inputTranscription?: boolean;
27
+ /**
28
+ * Transcription model for user transcripts
29
+ * (default: 'gpt-4o-mini-transcribe' on OpenAI; Gemini transcribes
30
+ * natively).
31
+ */
32
+ transcriptionModel?: string;
33
+ /** Provider-native VAD tuning. */
34
+ turnDetection?: {
35
+ type: 'server_vad';
36
+ silenceDurationMs?: number;
37
+ thresholdOverride?: number;
38
+ } | {
39
+ type: 'semantic';
40
+ eagerness?: 'low' | 'medium' | 'high' | 'auto';
41
+ };
42
+ /** Per-response output token cap (the primary runaway-cost bound). */
43
+ maxOutputTokens?: number;
44
+ /** Context-size cost lever, provider-mapped. */
45
+ contextManagement?: {
46
+ strategy: 'truncate' | 'compress';
47
+ /** OpenAI truncation retention ratio. */
48
+ retentionRatio?: number;
49
+ /** Gemini compression trigger tokens. */
50
+ triggerTokens?: number;
51
+ };
52
+ }
53
+ export interface RealtimeUsage {
54
+ inputTokens: number;
55
+ outputTokens: number;
56
+ audioInputTokens?: number;
57
+ audioOutputTokens?: number;
58
+ cachedTokens?: number;
59
+ }
60
+ /** Events pushed by the provider session, normalized across providers. */
61
+ export type RealtimeSpeechEvent =
62
+ /** Assistant audio delta, tagged so stale deltas of a cancelled response can be dropped. */
63
+ {
64
+ type: 'audio';
65
+ pcm: Buffer;
66
+ sampleRate: number;
67
+ responseId: string;
68
+ itemId?: string;
69
+ } | {
70
+ type: 'user-transcript';
71
+ text: string;
72
+ isFinal: boolean;
73
+ } | {
74
+ type: 'assistant-transcript';
75
+ text: string;
76
+ isFinal: boolean;
77
+ }
78
+ /** Barge-in signal (OpenAI server VAD; not emitted by Gemini). */
79
+ | {
80
+ type: 'user-speech-started';
81
+ } | {
82
+ type: 'user-speech-stopped';
83
+ } | {
84
+ type: 'response-started';
85
+ responseId: string;
86
+ } | {
87
+ type: 'response-done';
88
+ responseId: string;
89
+ usage?: RealtimeUsage;
90
+ } | {
91
+ type: 'tool-call';
92
+ callId: string;
93
+ name: string;
94
+ arguments: Record<string, unknown>;
95
+ }
96
+ /** Provider cancelled in-flight tool calls (Gemini interruption). */
97
+ | {
98
+ type: 'tool-call-cancelled';
99
+ callIds: string[];
100
+ }
101
+ /** Provider-driven barge-in (Gemini). */
102
+ | {
103
+ type: 'interrupted';
104
+ }
105
+ /**
106
+ * Session nearing its lifetime cap. renewable: true means the adapter
107
+ * has no internal recovery (OpenAI expires_at) and the orchestrator
108
+ * should renew by reseeding; adapters that recover internally (Gemini
109
+ * resumption) emit it as informational only.
110
+ */
111
+ | {
112
+ type: 'session-expiring';
113
+ inMs?: number;
114
+ renewable?: boolean;
115
+ } | {
116
+ type: 'error';
117
+ error: Error;
118
+ recoverable: boolean;
119
+ };
120
+ export interface RealtimeSpeechSession {
121
+ /** PCM rate the session expects from sendAudio (16k Gemini, 24k OpenAI). */
122
+ readonly inputSampleRate: number;
123
+ /** PCM rate of emitted 'audio' events (24k for both current targets). */
124
+ readonly outputSampleRate: number;
125
+ /**
126
+ * Ordered control stream. Consumers must never block this stream on
127
+ * realtime-paced playback: move 'audio' events into a playback queue
128
+ * synchronously and handle everything else immediately.
129
+ */
130
+ events(): AsyncIterable<RealtimeSpeechEvent>;
131
+ /**
132
+ * Fire-and-forget mic PCM (16-bit signed LE mono at inputSampleRate).
133
+ * During an adapter-internal reconnect, frames are buffered (bounded)
134
+ * and replayed once the session is re-established; frames beyond the
135
+ * bound are dropped.
136
+ */
137
+ sendAudio(frame: Buffer): void;
138
+ /**
139
+ * Cancel the in-progress response, if any; a safe no-op when nothing
140
+ * is active. playedMs (from the playback pacer) trims provider-side
141
+ * history to the audio the user actually heard, where the provider
142
+ * supports truncation.
143
+ */
144
+ cancelResponse(playedMs?: number): void;
145
+ sendToolResult(callId: string, output: unknown): void;
146
+ /**
147
+ * Ask the model to produce a response now (e.g. a stage onEnter
148
+ * announcement after a playbook transition). Optional: providers
149
+ * whose responses are purely VAD-driven may omit it.
150
+ */
151
+ requestResponse?(): void;
152
+ /** Live re-configuration (playbook stage changes swap instructions/tools). */
153
+ update(config: Partial<RealtimeSpeechConfig>): Promise<void>;
154
+ close(): Promise<void>;
155
+ }
156
+ export interface RealtimeSpeechProvider {
157
+ name: string;
158
+ connect(config: RealtimeSpeechConfig): Promise<RealtimeSpeechSession>;
159
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Realtime speech-to-speech provider abstraction (RFC 0001).
3
+ *
4
+ * A RealtimeSpeechProvider connects a voice session to a provider's
5
+ * native speech-to-speech model over a bidirectional stream: mic PCM
6
+ * goes up, assistant PCM comes down, with transcripts, tool calls, and
7
+ * turn events normalized into RealtimeSpeechEvent. Turn detection,
8
+ * comprehension, and voice synthesis are provider-side; the orchestrator
9
+ * relays audio and reacts to events.
10
+ *
11
+ * Experimental: shipped behind the opt-in `realtimeSpeech` server mode.
12
+ */
13
+ export {};
14
+ //# sourceMappingURL=realtime-speech.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime-speech.js","sourceRoot":"","sources":["../src/realtime-speech.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llmrtc/llmrtc-core",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Core contracts and orchestrators for @llmrtc/LLMRTC",
5
5
  "homepage": "https://www.llmrtc.org",
6
6
  "license": "Apache-2.0",