Skip to main content

gproxy_protocol/openai/realtime/
client_events.rs

1use serde::{Deserialize, Serialize, de};
2use serde_json::Value;
3
4use crate::openai::common::Rest;
5
6use super::{RealtimeItem, RealtimeResponseOptions, RealtimeSession};
7
8/// A client event, or whatever the client actually sent.
9///
10/// The server side has always had this fallback; the client side did not, so an
11/// event OpenAI adds — they have already added a whole translation surface with
12/// `session.close` and `session.input_audio_buffer.append` — would have failed the
13/// connection outright instead of reaching an upstream that understands it.
14#[derive(Debug, Clone, PartialEq, Serialize)]
15#[serde(untagged)]
16#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
17pub enum RealtimeClientEvent {
18    Known(Box<KnownRealtimeClientEvent>),
19    Unknown(UnknownRealtimeClientEvent),
20}
21
22impl<'de> Deserialize<'de> for RealtimeClientEvent {
23    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
24        let value = Value::deserialize(deserializer)?;
25        if let Ok(event) = serde_json::from_value::<KnownRealtimeClientEvent>(value.clone()) {
26            return Ok(Self::Known(Box::new(event)));
27        }
28        serde_json::from_value(value)
29            .map(Self::Unknown)
30            .map_err(de::Error::custom)
31    }
32}
33
34#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
35#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
36pub struct UnknownRealtimeClientEvent {
37    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
38    pub type_: Option<String>,
39    #[serde(default, flatten)]
40    pub rest: Rest,
41}
42
43#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
44#[serde(tag = "type")]
45#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
46pub enum KnownRealtimeClientEvent {
47    #[serde(rename = "session.update")]
48    SessionUpdate {
49        session: RealtimeSession,
50        #[serde(skip_serializing_if = "Option::is_none")]
51        event_id: Option<String>,
52        #[serde(default, flatten)]
53        rest: Rest,
54    },
55    #[serde(rename = "input_audio_buffer.append")]
56    InputAudioBufferAppend {
57        audio: String,
58        #[serde(skip_serializing_if = "Option::is_none")]
59        event_id: Option<String>,
60        #[serde(default, flatten)]
61        rest: Rest,
62    },
63    #[serde(rename = "input_audio_buffer.commit")]
64    InputAudioBufferCommit {
65        #[serde(skip_serializing_if = "Option::is_none")]
66        event_id: Option<String>,
67        #[serde(default, flatten)]
68        rest: Rest,
69    },
70    #[serde(rename = "input_audio_buffer.clear")]
71    InputAudioBufferClear {
72        #[serde(skip_serializing_if = "Option::is_none")]
73        event_id: Option<String>,
74        #[serde(default, flatten)]
75        rest: Rest,
76    },
77    #[serde(rename = "output_audio_buffer.clear")]
78    OutputAudioBufferClear {
79        #[serde(skip_serializing_if = "Option::is_none")]
80        event_id: Option<String>,
81        #[serde(default, flatten)]
82        rest: Rest,
83    },
84    #[serde(rename = "conversation.item.create")]
85    ConversationItemCreate {
86        item: RealtimeItem,
87        #[serde(skip_serializing_if = "Option::is_none")]
88        previous_item_id: Option<String>,
89        #[serde(skip_serializing_if = "Option::is_none")]
90        event_id: Option<String>,
91        #[serde(default, flatten)]
92        rest: Rest,
93    },
94    #[serde(rename = "conversation.item.retrieve")]
95    ConversationItemRetrieve {
96        item_id: String,
97        #[serde(skip_serializing_if = "Option::is_none")]
98        event_id: Option<String>,
99        #[serde(default, flatten)]
100        rest: Rest,
101    },
102    #[serde(rename = "conversation.item.truncate")]
103    ConversationItemTruncate {
104        item_id: String,
105        content_index: u32,
106        audio_end_ms: u64,
107        #[serde(skip_serializing_if = "Option::is_none")]
108        event_id: Option<String>,
109        #[serde(default, flatten)]
110        rest: Rest,
111    },
112    #[serde(rename = "conversation.item.delete")]
113    ConversationItemDelete {
114        item_id: String,
115        #[serde(skip_serializing_if = "Option::is_none")]
116        event_id: Option<String>,
117        #[serde(default, flatten)]
118        rest: Rest,
119    },
120    #[serde(rename = "response.create")]
121    ResponseCreate {
122        #[serde(skip_serializing_if = "Option::is_none")]
123        response: Option<RealtimeResponseOptions>,
124        #[serde(skip_serializing_if = "Option::is_none")]
125        event_id: Option<String>,
126        #[serde(default, flatten)]
127        rest: Rest,
128    },
129    #[serde(rename = "response.cancel")]
130    ResponseCancel {
131        #[serde(skip_serializing_if = "Option::is_none")]
132        response_id: Option<String>,
133        #[serde(skip_serializing_if = "Option::is_none")]
134        event_id: Option<String>,
135        #[serde(default, flatten)]
136        rest: Rest,
137    },
138}