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 type EventData = Map<String, Value>;
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum ConnectionState {
29 Disconnected,
30 Connecting,
31 Connected,
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum ChannelState {
36 Idle,
37 Joining,
38 Joined,
39 Closed,
40 Declined,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
44pub struct RtcIceServer {
45 pub urls: Value,
46 #[serde(default, skip_serializing_if = "Option::is_none")]
47 pub username: Option<String>,
48 #[serde(default, skip_serializing_if = "Option::is_none")]
49 pub credential: Option<String>,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
53pub struct SessionBootstrap {
54 pub session_id: String,
55 pub client_token: String,
56 pub expires_at: String,
57 #[serde(default)]
58 pub join_token_ttl_seconds: u64,
59 #[serde(default)]
60 pub ice_servers: Vec<RtcIceServer>,
61}
62
63#[derive(Debug, Clone, Default)]
64pub struct SessionConfig {
65 pub stt_model: Option<String>,
66 pub tts_model: Option<String>,
67 pub voice: Option<String>,
68 pub turn_profile: Option<String>,
69 pub vad_backend: Option<String>,
70 pub turn_detector: Option<String>,
71 pub extra: EventData,
72}
73
74#[derive(Debug, Clone, Default)]
75pub struct ResponseOptions {
76 pub allow_interruptions: Option<bool>,
77}
78
79#[derive(Debug, Clone)]
80pub struct ClientEventEnvelope {
81 pub event: String,
82 pub payload: Value,
83}
84
85#[derive(Debug, Clone)]
86pub struct WireEvent {
87 pub r#type: String,
88 pub data: EventData,
89 pub session_id: String,
90 pub channel_name: String,
91}
92
93#[derive(Debug, Clone)]
94pub struct SessionAttachedEvent {
95 pub session_id: String,
96 pub channel_name: String,
97 pub data: EventData,
98}
99
100#[derive(Debug, Clone)]
101pub struct SessionCreatedEvent {
102 pub session_id: String,
103 pub channel_name: String,
104 pub data: EventData,
105 pub session: Option<EventData>,
106}
107
108#[derive(Debug, Clone)]
109pub struct TranscriptEvent {
110 pub session_id: String,
111 pub channel_name: String,
112 pub data: EventData,
113 pub transcript: String,
114 pub language: Option<String>,
115 pub start_ms: Option<f64>,
116 pub end_ms: Option<f64>,
117 pub eou_probability: Option<f64>,
118 pub topics: Option<Vec<String>>,
119}
120
121#[derive(Debug, Clone)]
122pub struct TurnStateEvent {
123 pub session_id: String,
124 pub channel_name: String,
125 pub data: EventData,
126 pub state: String,
127 pub previous_state: Option<String>,
128}
129
130#[derive(Debug, Clone)]
131pub struct SpeechStartedEvent {
132 pub session_id: String,
133 pub channel_name: String,
134 pub data: EventData,
135 pub timestamp_ms: Option<f64>,
136}
137
138#[derive(Debug, Clone)]
139pub struct SpeechStoppedEvent {
140 pub session_id: String,
141 pub channel_name: String,
142 pub data: EventData,
143 pub timestamp_ms: Option<f64>,
144}
145
146#[derive(Debug, Clone)]
147pub struct TranscriptDeltaEvent {
148 pub session_id: String,
149 pub channel_name: String,
150 pub data: EventData,
151 pub delta: String,
152 pub start_ms: Option<f64>,
153 pub end_ms: Option<f64>,
154}
155
156#[derive(Debug, Clone)]
157pub struct TurnEouPredictedEvent {
158 pub session_id: String,
159 pub channel_name: String,
160 pub data: EventData,
161 pub probability: Option<f64>,
162 pub threshold: Option<f64>,
163 pub delay_ms: Option<f64>,
164 pub start_ms: Option<f64>,
165 pub end_ms: Option<f64>,
166 pub decision: Option<String>,
167 pub action: Option<String>,
168 pub turn_detector: Option<String>,
169}
170
171#[derive(Debug, Clone)]
172pub struct ResponseEvent {
173 pub session_id: String,
174 pub channel_name: String,
175 pub data: EventData,
176 pub response_id: Option<String>,
177}
178
179#[derive(Debug, Clone)]
180pub struct InterruptionEvent {
181 pub response: ResponseEvent,
182 pub vad_active_ms: Option<f64>,
183 pub partial_transcript: Option<String>,
184}
185
186#[derive(Debug, Clone)]
187pub struct BrowserEvent {
188 pub session_id: String,
189 pub channel_name: String,
190 pub data: EventData,
191 pub event: String,
192 pub payload: Value,
193}
194
195#[derive(Debug, Clone)]
196pub struct CloseEvent {
197 pub session_id: String,
198 pub channel_name: String,
199 pub data: EventData,
200 pub reason: String,
201 pub connection_state: Option<String>,
202 pub ice_connection_state: Option<String>,
203 pub data_channel_state: Option<String>,
204}
205
206#[derive(Debug, Clone)]
207pub struct ErrorEvent {
208 pub session_id: String,
209 pub channel_name: String,
210 pub data: EventData,
211 pub message: Option<String>,
212 pub code: Option<String>,
213}
214
215pub(crate) fn optional_string(data: &EventData, key: &str) -> Option<String> {
216 data.get(key).and_then(Value::as_str).map(ToOwned::to_owned)
217}
218
219pub(crate) fn required_string(data: &EventData, key: &str, fallback: &str) -> String {
220 optional_string(data, key)
221 .filter(|s| !s.is_empty())
222 .unwrap_or_else(|| fallback.to_owned())
223}
224
225pub(crate) fn optional_number(data: &EventData, key: &str) -> Option<f64> {
226 data.get(key).and_then(Value::as_f64)
227}
228
229pub(crate) fn optional_string_vec(data: &EventData, key: &str) -> Option<Vec<String>> {
230 data.get(key).and_then(Value::as_array).and_then(|items| {
231 items
232 .iter()
233 .map(|item| item.as_str().map(ToOwned::to_owned))
234 .collect()
235 })
236}