@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 +58 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/protocol.d.ts +38 -3
- package/dist/protocol.js +5 -2
- package/dist/protocol.js.map +1 -1
- package/dist/realtime-speech.d.ts +159 -0
- package/dist/realtime-speech.js +14 -0
- package/dist/realtime-speech.js.map +1 -0
- package/package.json +1 -1
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
package/dist/index.js
CHANGED
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"}
|
package/dist/protocol.d.ts
CHANGED
|
@@ -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
|
package/dist/protocol.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,gFAAgF;AAChF,mBAAmB;AACnB,gFAAgF;AAEhF;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC;
|
|
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"}
|