1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize, de};
4use serde_json::Value;
5
6use super::super::common::*;
7use super::{
8 RealtimeContentPart, RealtimeError, RealtimeItem, RealtimeRateLimit, RealtimeResponse,
9 RealtimeSessionConfig, RealtimeUsage,
10};
11
12#[derive(Debug, Clone, PartialEq, Serialize)]
17#[serde(untagged)]
18#[non_exhaustive]
19pub enum RealtimeServerEvent {
20 Known(Box<KnownRealtimeServerEvent>),
21 Unknown(UnknownRealtimeServerEvent),
22}
23
24impl<'de> Deserialize<'de> for RealtimeServerEvent {
25 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
26 let value = Value::deserialize(deserializer)?;
27 if let Ok(event) = serde_json::from_value(value.clone()) {
28 return Ok(Self::Known(Box::new(event)));
29 }
30 serde_json::from_value(value)
31 .map(Self::Unknown)
32 .map_err(de::Error::custom)
33 }
34}
35
36#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
37#[non_exhaustive]
38pub struct UnknownRealtimeServerEvent {
39 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
40 pub type_: Option<String>,
41 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
42 pub extra: Extra,
43}
44
45#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
46#[serde(tag = "type")]
47#[allow(clippy::large_enum_variant)]
48#[non_exhaustive]
49pub enum KnownRealtimeServerEvent {
50 #[serde(rename = "error")]
51 Error {
52 error: RealtimeError,
53 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
54 extra: Extra,
55 },
56 #[serde(rename = "session.created")]
57 SessionCreated {
58 session: Box<RealtimeSessionConfig>,
59 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
60 extra: Extra,
61 },
62 #[serde(rename = "session.updated")]
63 SessionUpdated {
64 session: Box<RealtimeSessionConfig>,
65 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
66 extra: Extra,
67 },
68 #[serde(rename = "conversation.item.added")]
69 ConversationItemAdded {
70 item: RealtimeItem,
71 #[serde(skip_serializing_if = "Option::is_none")]
72 previous_item_id: Option<String>,
73 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
74 extra: Extra,
75 },
76 #[serde(rename = "conversation.item.created")]
78 ConversationItemCreated {
79 item: RealtimeItem,
80 #[serde(skip_serializing_if = "Option::is_none")]
81 previous_item_id: Option<String>,
82 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
83 extra: Extra,
84 },
85 #[serde(rename = "conversation.item.done")]
86 ConversationItemDone {
87 item: RealtimeItem,
88 #[serde(skip_serializing_if = "Option::is_none")]
89 previous_item_id: Option<String>,
90 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
91 extra: Extra,
92 },
93 #[serde(rename = "conversation.item.retrieved")]
94 ConversationItemRetrieved {
95 item: RealtimeItem,
96 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
97 extra: Extra,
98 },
99 #[serde(rename = "conversation.item.truncated")]
100 ConversationItemTruncated {
101 item_id: String,
102 content_index: u32,
103 audio_end_ms: u64,
104 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
105 extra: Extra,
106 },
107 #[serde(rename = "conversation.item.deleted")]
108 ConversationItemDeleted {
109 item_id: String,
110 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
111 extra: Extra,
112 },
113 #[serde(rename = "conversation.item.input_audio_transcription.delta")]
114 InputAudioTranscriptionDelta {
115 item_id: String,
116 #[serde(skip_serializing_if = "Option::is_none")]
117 content_index: Option<u32>,
118 delta: String,
119 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
120 extra: Extra,
121 },
122 #[serde(rename = "conversation.item.input_audio_transcription.completed")]
123 InputAudioTranscriptionCompleted {
124 item_id: String,
125 #[serde(skip_serializing_if = "Option::is_none")]
126 content_index: Option<u32>,
127 transcript: String,
128 #[serde(skip_serializing_if = "Option::is_none")]
129 usage: Option<RealtimeUsage>,
130 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
131 extra: Extra,
132 },
133 #[serde(rename = "conversation.item.input_audio_transcription.failed")]
134 InputAudioTranscriptionFailed {
135 item_id: String,
136 #[serde(skip_serializing_if = "Option::is_none")]
137 content_index: Option<u32>,
138 error: RealtimeError,
139 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
140 extra: Extra,
141 },
142 #[serde(rename = "conversation.item.input_audio_transcription.segment")]
143 InputAudioTranscriptionSegment {
144 item_id: String,
145 #[serde(skip_serializing_if = "Option::is_none")]
146 id: Option<String>,
147 #[serde(skip_serializing_if = "Option::is_none")]
148 speaker: Option<String>,
149 #[serde(skip_serializing_if = "Option::is_none")]
150 text: Option<String>,
151 #[serde(skip_serializing_if = "Option::is_none")]
152 start: Option<f64>,
153 #[serde(skip_serializing_if = "Option::is_none")]
154 end: Option<f64>,
155 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
156 extra: Extra,
157 },
158 #[serde(rename = "input_audio_buffer.committed")]
159 InputAudioBufferCommitted {
160 item_id: String,
161 #[serde(skip_serializing_if = "Option::is_none")]
162 previous_item_id: Option<String>,
163 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
164 extra: Extra,
165 },
166 #[serde(rename = "input_audio_buffer.cleared")]
167 InputAudioBufferCleared {
168 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
169 extra: Extra,
170 },
171 #[serde(rename = "input_audio_buffer.speech_started")]
172 InputAudioBufferSpeechStarted {
173 item_id: String,
174 audio_start_ms: u64,
175 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
176 extra: Extra,
177 },
178 #[serde(rename = "input_audio_buffer.speech_stopped")]
179 InputAudioBufferSpeechStopped {
180 item_id: String,
181 audio_end_ms: u64,
182 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
183 extra: Extra,
184 },
185 #[serde(rename = "input_audio_buffer.timeout_triggered")]
186 InputAudioBufferTimeoutTriggered {
187 #[serde(skip_serializing_if = "Option::is_none")]
188 item_id: Option<String>,
189 #[serde(skip_serializing_if = "Option::is_none")]
190 audio_start_ms: Option<u64>,
191 #[serde(skip_serializing_if = "Option::is_none")]
192 audio_end_ms: Option<u64>,
193 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
194 extra: Extra,
195 },
196 #[serde(rename = "output_audio_buffer.started")]
197 OutputAudioBufferStarted {
198 response_id: String,
199 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
200 extra: Extra,
201 },
202 #[serde(rename = "output_audio_buffer.stopped")]
203 OutputAudioBufferStopped {
204 response_id: String,
205 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
206 extra: Extra,
207 },
208 #[serde(rename = "output_audio_buffer.cleared")]
209 OutputAudioBufferCleared {
210 response_id: String,
211 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
212 extra: Extra,
213 },
214 #[serde(rename = "rate_limits.updated")]
215 RateLimitsUpdated {
216 rate_limits: Vec<RealtimeRateLimit>,
217 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
218 extra: Extra,
219 },
220 #[serde(rename = "response.created")]
221 ResponseCreated {
222 response: Box<RealtimeResponse>,
223 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
224 extra: Extra,
225 },
226 #[serde(rename = "response.done")]
227 ResponseDone {
228 response: Box<RealtimeResponse>,
229 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
230 extra: Extra,
231 },
232 #[serde(rename = "response.output_item.added")]
233 ResponseOutputItemAdded {
234 response_id: String,
235 output_index: u32,
236 item: RealtimeItem,
237 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
238 extra: Extra,
239 },
240 #[serde(rename = "response.output_item.done")]
241 ResponseOutputItemDone {
242 response_id: String,
243 output_index: u32,
244 item: RealtimeItem,
245 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
246 extra: Extra,
247 },
248 #[serde(rename = "response.content_part.added")]
249 ResponseContentPartAdded {
250 response_id: String,
251 item_id: String,
252 output_index: u32,
253 content_index: u32,
254 part: RealtimeContentPart,
255 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
256 extra: Extra,
257 },
258 #[serde(rename = "response.content_part.done")]
259 ResponseContentPartDone {
260 response_id: String,
261 item_id: String,
262 output_index: u32,
263 content_index: u32,
264 part: RealtimeContentPart,
265 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
266 extra: Extra,
267 },
268 #[serde(rename = "response.output_text.delta")]
269 OutputTextDelta {
270 response_id: String,
271 item_id: String,
272 output_index: u32,
273 content_index: u32,
274 delta: String,
275 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
276 extra: Extra,
277 },
278 #[serde(rename = "response.output_text.done")]
279 OutputTextDone {
280 response_id: String,
281 item_id: String,
282 output_index: u32,
283 content_index: u32,
284 text: String,
285 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
286 extra: Extra,
287 },
288 #[serde(rename = "response.output_audio_transcript.delta")]
289 OutputAudioTranscriptDelta {
290 response_id: String,
291 item_id: String,
292 output_index: u32,
293 content_index: u32,
294 delta: String,
295 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
296 extra: Extra,
297 },
298 #[serde(rename = "response.output_audio_transcript.done")]
299 OutputAudioTranscriptDone {
300 response_id: String,
301 item_id: String,
302 output_index: u32,
303 content_index: u32,
304 transcript: String,
305 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
306 extra: Extra,
307 },
308 #[serde(rename = "response.output_audio.delta")]
309 OutputAudioDelta {
310 response_id: String,
311 item_id: String,
312 output_index: u32,
313 content_index: u32,
314 delta: String,
316 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
317 extra: Extra,
318 },
319 #[serde(rename = "response.output_audio.done")]
320 OutputAudioDone {
321 response_id: String,
322 item_id: String,
323 output_index: u32,
324 content_index: u32,
325 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
326 extra: Extra,
327 },
328 #[serde(rename = "response.function_call_arguments.delta")]
329 FunctionCallArgumentsDelta {
330 response_id: String,
331 item_id: String,
332 output_index: u32,
333 call_id: String,
334 delta: String,
335 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
336 extra: Extra,
337 },
338 #[serde(rename = "response.function_call_arguments.done")]
339 FunctionCallArgumentsDone {
340 response_id: String,
341 item_id: String,
342 output_index: u32,
343 call_id: String,
344 arguments: String,
345 #[serde(skip_serializing_if = "Option::is_none")]
346 name: Option<String>,
347 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
348 extra: Extra,
349 },
350 #[serde(rename = "response.mcp_call_arguments.delta")]
351 McpCallArgumentsDelta {
352 response_id: String,
353 item_id: String,
354 output_index: u32,
355 delta: String,
356 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
357 extra: Extra,
358 },
359 #[serde(rename = "response.mcp_call_arguments.done")]
360 McpCallArgumentsDone {
361 response_id: String,
362 item_id: String,
363 output_index: u32,
364 arguments: String,
365 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
366 extra: Extra,
367 },
368 #[serde(rename = "response.mcp_call.in_progress")]
369 McpCallInProgress {
370 item_id: String,
371 output_index: u32,
372 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
373 extra: Extra,
374 },
375 #[serde(rename = "response.mcp_call.completed")]
376 McpCallCompleted {
377 item_id: String,
378 output_index: u32,
379 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
380 extra: Extra,
381 },
382 #[serde(rename = "response.mcp_call.failed")]
383 McpCallFailed {
384 item_id: String,
385 output_index: u32,
386 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
387 extra: Extra,
388 },
389 #[serde(rename = "mcp_list_tools.in_progress")]
390 McpListToolsInProgress {
391 #[serde(skip_serializing_if = "Option::is_none")]
392 item_id: Option<String>,
393 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
394 extra: Extra,
395 },
396 #[serde(rename = "mcp_list_tools.completed")]
397 McpListToolsCompleted {
398 #[serde(skip_serializing_if = "Option::is_none")]
399 item_id: Option<String>,
400 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
401 extra: Extra,
402 },
403 #[serde(rename = "mcp_list_tools.failed")]
404 McpListToolsFailed {
405 #[serde(skip_serializing_if = "Option::is_none")]
406 item_id: Option<String>,
407 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
408 extra: Extra,
409 },
410}