Skip to main content

vox_rtc_server/
types.rs

1use serde::{Deserialize, Serialize};
2use serde_json::{Map, Value};
3
4pub const EVENT_CLIENT_EVENT: &str = "client.event";
5pub const EVENT_BROWSER_EVENT: &str = "browser.event";
6pub const EVENT_RTC_CLIENT_DISCONNECTED: &str = "rtc.client.disconnected";
7pub const EVENT_ERROR: &str = "error";
8pub const EVENT_INTERRUPTION_DETECTED: &str = "interruption.detected";
9pub const EVENT_INTERRUPTION_FALSE_POSITIVE: &str = "interruption.false_positive";
10pub const EVENT_RESPONSE_AUDIO_CLEAR: &str = "response.audio.clear";
11pub const EVENT_RESPONSE_CANCELLED: &str = "response.cancelled";
12pub const EVENT_RESPONSE_COMMITTED: &str = "response.committed";
13pub const EVENT_RESPONSE_CREATED: &str = "response.created";
14pub const EVENT_RESPONSE_DONE: &str = "response.done";
15pub const EVENT_RTC_SESSION_ATTACHED: &str = "rtc.session.attached";
16pub const EVENT_SESSION_CREATED: &str = "session.created";
17pub const EVENT_TRANSCRIPT_COMPLETED: &str =
18    "conversation.item.input_audio_transcription.completed";
19pub const EVENT_TURN_STATE_CHANGED: &str = "turn.state_changed";
20pub const EVENT_SPEECH_STARTED: &str = "input_audio_buffer.speech_started";
21pub const EVENT_SPEECH_STOPPED: &str = "input_audio_buffer.speech_stopped";
22pub const EVENT_TRANSCRIPT_DELTA: &str = "conversation.item.input_audio_transcription.delta";
23pub const EVENT_TURN_EOU_PREDICTED: &str = "turn.eou.predicted";
24
25pub const ERROR_CODE_RESPONSE_REJECTED_TURN_STATE: &str = "response_rejected_turn_state";
26pub const ERROR_CODE_RESPONSE_REJECTED_USER_SPEECH: &str = "response_rejected_user_speech";
27pub const ERROR_CODE_RESPONSE_STALE_GENERATION: &str = "response_stale_generation";
28pub const ERROR_CODE_RESPONSE_ALREADY_ACTIVE: &str = "response_already_active";
29pub const ERROR_CODE_RESPONSE_FAILED: &str = "response_failed";
30pub const ERROR_CODE_COMMAND_INVALID: &str = "command_invalid";
31pub const ERROR_CODE_SESSION_FAILED: &str = "session_failed";
32
33pub type EventData = Map<String, Value>;
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum ConnectionState {
37    Disconnected,
38    Connecting,
39    Connected,
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43pub enum ChannelState {
44    Idle,
45    Joining,
46    Joined,
47    Closed,
48    Declined,
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
52pub struct RtcIceServer {
53    pub urls: Value,
54    #[serde(default, skip_serializing_if = "Option::is_none")]
55    pub username: Option<String>,
56    #[serde(default, skip_serializing_if = "Option::is_none")]
57    pub credential: Option<String>,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
61pub struct SessionBootstrap {
62    pub session_id: String,
63    pub expires_at: String,
64    #[serde(default)]
65    pub attach_ttl_seconds: u64,
66    #[serde(default)]
67    pub ice_servers: Vec<RtcIceServer>,
68}
69
70#[derive(Debug, Clone, PartialEq, Eq)]
71pub struct RtcSessionDescription {
72    pub r#type: String,
73    pub sdp: String,
74}
75
76#[derive(Debug, Clone, PartialEq, Eq)]
77pub struct RtcIceCandidate {
78    pub candidate: String,
79    pub sdp_mid: Option<String>,
80    pub sdp_m_line_index: Option<u32>,
81    pub username_fragment: Option<String>,
82}
83
84#[derive(Debug, Clone, Default)]
85pub struct SessionConfig {
86    pub stt_model: Option<String>,
87    pub tts_model: Option<String>,
88    pub voice: Option<String>,
89    pub turn_profile: Option<String>,
90    pub vad_backend: Option<String>,
91    pub turn_detector: Option<String>,
92    pub extra: EventData,
93}
94
95#[derive(Debug, Clone, Default)]
96pub struct ResponseOptions {
97    pub allow_interruptions: Option<bool>,
98    pub generation_id: Option<String>,
99}
100
101#[derive(Debug, Clone)]
102pub struct ClientEventEnvelope {
103    pub event: String,
104    pub payload: Value,
105}
106
107#[derive(Debug, Clone)]
108pub struct WireEvent {
109    pub r#type: String,
110    pub data: EventData,
111    pub session_id: String,
112    pub channel_name: String,
113}
114
115#[derive(Debug, Clone)]
116pub struct SessionAttachedEvent {
117    pub session_id: String,
118    pub channel_name: String,
119    pub data: EventData,
120}
121
122#[derive(Debug, Clone)]
123pub struct SessionCreatedEvent {
124    pub session_id: String,
125    pub channel_name: String,
126    pub data: EventData,
127    pub session: Option<EventData>,
128}
129
130#[derive(Debug, Clone)]
131pub struct TranscriptEvent {
132    pub session_id: String,
133    pub channel_name: String,
134    pub data: EventData,
135    pub transcript: String,
136    pub language: Option<String>,
137    pub start_ms: Option<f64>,
138    pub end_ms: Option<f64>,
139    pub eou_probability: Option<f64>,
140    pub topics: Option<Vec<String>>,
141}
142
143#[derive(Debug, Clone)]
144pub struct TurnStateEvent {
145    pub session_id: String,
146    pub channel_name: String,
147    pub data: EventData,
148    pub state: String,
149    pub previous_state: Option<String>,
150}
151
152#[derive(Debug, Clone)]
153pub struct SpeechStartedEvent {
154    pub session_id: String,
155    pub channel_name: String,
156    pub data: EventData,
157    pub timestamp_ms: Option<f64>,
158}
159
160#[derive(Debug, Clone)]
161pub struct SpeechStoppedEvent {
162    pub session_id: String,
163    pub channel_name: String,
164    pub data: EventData,
165    pub timestamp_ms: Option<f64>,
166}
167
168#[derive(Debug, Clone)]
169pub struct TranscriptDeltaEvent {
170    pub session_id: String,
171    pub channel_name: String,
172    pub data: EventData,
173    pub delta: String,
174    pub start_ms: Option<f64>,
175    pub end_ms: Option<f64>,
176}
177
178#[derive(Debug, Clone)]
179pub struct TurnEouPredictedEvent {
180    pub session_id: String,
181    pub channel_name: String,
182    pub data: EventData,
183    pub probability: Option<f64>,
184    pub threshold: Option<f64>,
185    pub delay_ms: Option<f64>,
186    pub start_ms: Option<f64>,
187    pub end_ms: Option<f64>,
188    pub decision: Option<String>,
189    pub action: Option<String>,
190    pub turn_detector: Option<String>,
191}
192
193#[derive(Debug, Clone)]
194pub struct ResponseEvent {
195    pub session_id: String,
196    pub channel_name: String,
197    pub data: EventData,
198    pub response_id: Option<String>,
199    pub generation_id: Option<String>,
200}
201
202#[derive(Debug, Clone)]
203pub struct InterruptionEvent {
204    pub response: ResponseEvent,
205    pub vad_active_ms: Option<f64>,
206    pub partial_transcript: Option<String>,
207}
208
209#[derive(Debug, Clone)]
210pub struct BrowserEvent {
211    pub session_id: String,
212    pub channel_name: String,
213    pub data: EventData,
214    pub event: String,
215    pub payload: Value,
216}
217
218#[derive(Debug, Clone)]
219pub struct CloseEvent {
220    pub session_id: String,
221    pub channel_name: String,
222    pub data: EventData,
223    pub reason: String,
224    pub connection_state: Option<String>,
225    pub ice_connection_state: Option<String>,
226    pub data_channel_state: Option<String>,
227}
228
229#[derive(Debug, Clone)]
230pub struct ErrorEvent {
231    pub session_id: String,
232    pub channel_name: String,
233    pub data: EventData,
234    pub message: Option<String>,
235    pub code: Option<String>,
236    pub recoverable: bool,
237    pub generation_id: Option<String>,
238}
239
240#[derive(Debug, Clone)]
241pub struct StartAck {
242    pub accepted: bool,
243    pub generation_id: String,
244    pub response_id: Option<String>,
245    pub error_code: Option<String>,
246    pub error_message: Option<String>,
247    pub recoverable: bool,
248}
249
250pub(crate) fn optional_string(data: &EventData, key: &str) -> Option<String> {
251    data.get(key).and_then(Value::as_str).map(ToOwned::to_owned)
252}
253
254pub(crate) fn required_string(data: &EventData, key: &str, fallback: &str) -> String {
255    optional_string(data, key)
256        .filter(|s| !s.is_empty())
257        .unwrap_or_else(|| fallback.to_owned())
258}
259
260pub(crate) fn optional_number(data: &EventData, key: &str) -> Option<f64> {
261    data.get(key).and_then(Value::as_f64)
262}
263
264pub(crate) fn optional_nonempty_string(data: &EventData, key: &str) -> Option<String> {
265    optional_string(data, key).filter(|s| !s.is_empty())
266}
267
268pub(crate) fn recoverable_flag(data: &EventData) -> bool {
269    data.get("recoverable").and_then(Value::as_bool).unwrap_or(true)
270}
271
272pub(crate) fn optional_string_vec(data: &EventData, key: &str) -> Option<Vec<String>> {
273    data.get(key).and_then(Value::as_array).and_then(|items| {
274        items
275            .iter()
276            .map(|item| item.as_str().map(ToOwned::to_owned))
277            .collect()
278    })
279}