Skip to main content

openai_protocol/builders/realtime/
client_event.rs

1//! Constructors for ClientEvent
2//!
3//! Associated functions on `ClientEvent` for constructing client-to-server
4//! events programmatically. Useful when the gateway needs to inject events
5//! (e.g., MCP tool call results) into the upstream connection.
6//!
7//! ```ignore
8//! ClientEvent::session_update(Some("evt_1".into()), config)
9//! ClientEvent::conversation_item_create(Some("evt_2".into()), None, item)
10//! ClientEvent::response_create(Some("evt_3".into()), Some(params))
11//! ```
12
13use crate::{
14    realtime_conversation::RealtimeConversationItem,
15    realtime_events::{ClientEvent, SessionConfig},
16    realtime_response::RealtimeResponseCreateParams,
17};
18
19impl ClientEvent {
20    // ---- Session ----
21
22    /// Build a `session.update` event.
23    pub fn session_update(event_id: Option<String>, session: SessionConfig) -> Self {
24        Self::SessionUpdate {
25            event_id,
26            session: Box::new(session),
27        }
28    }
29
30    // ---- Conversation items ----
31
32    /// Build a `conversation.item.create` event.
33    pub fn conversation_item_create(
34        event_id: Option<String>,
35        previous_item_id: Option<String>,
36        item: RealtimeConversationItem,
37    ) -> Self {
38        Self::ConversationItemCreate {
39            event_id,
40            previous_item_id,
41            item,
42        }
43    }
44
45    /// Build a `conversation.item.delete` event.
46    pub fn conversation_item_delete(event_id: Option<String>, item_id: impl Into<String>) -> Self {
47        Self::ConversationItemDelete {
48            event_id,
49            item_id: item_id.into(),
50        }
51    }
52
53    /// Build a `conversation.item.retrieve` event.
54    pub fn conversation_item_retrieve(
55        event_id: Option<String>,
56        item_id: impl Into<String>,
57    ) -> Self {
58        Self::ConversationItemRetrieve {
59            event_id,
60            item_id: item_id.into(),
61        }
62    }
63
64    /// Build a `conversation.item.truncate` event.
65    pub fn conversation_item_truncate(
66        event_id: Option<String>,
67        item_id: impl Into<String>,
68        content_index: u32,
69        audio_end_ms: u32,
70    ) -> Self {
71        Self::ConversationItemTruncate {
72            event_id,
73            item_id: item_id.into(),
74            content_index,
75            audio_end_ms,
76        }
77    }
78
79    // ---- Input audio buffer ----
80
81    /// Build an `input_audio_buffer.append` event.
82    pub fn input_audio_buffer_append(event_id: Option<String>, audio: impl Into<String>) -> Self {
83        Self::InputAudioBufferAppend {
84            event_id,
85            audio: audio.into(),
86        }
87    }
88
89    /// Build an `input_audio_buffer.clear` event.
90    pub fn input_audio_buffer_clear(event_id: Option<String>) -> Self {
91        Self::InputAudioBufferClear { event_id }
92    }
93
94    /// Build an `input_audio_buffer.commit` event.
95    pub fn input_audio_buffer_commit(event_id: Option<String>) -> Self {
96        Self::InputAudioBufferCommit { event_id }
97    }
98
99    // ---- Output audio buffer (WebRTC/SIP only) ----
100
101    /// Build an `output_audio_buffer.clear` event.
102    pub fn output_audio_buffer_clear(event_id: Option<String>) -> Self {
103        Self::OutputAudioBufferClear { event_id }
104    }
105
106    // ---- Response ----
107
108    /// Build a `response.cancel` event.
109    pub fn response_cancel(event_id: Option<String>, response_id: Option<String>) -> Self {
110        Self::ResponseCancel {
111            event_id,
112            response_id,
113        }
114    }
115
116    /// Build a `response.create` event.
117    pub fn response_create(
118        event_id: Option<String>,
119        response: Option<RealtimeResponseCreateParams>,
120    ) -> Self {
121        Self::ResponseCreate {
122            event_id,
123            response: response.map(Box::new),
124        }
125    }
126}
127
128// ============================================================================
129// Tests
130// ============================================================================
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135    use crate::realtime_session::{
136        OutputModality, RealtimeSessionCreateRequest, RealtimeSessionType,
137    };
138
139    #[test]
140    fn test_session_update() {
141        let config = SessionConfig::Realtime(Box::new(RealtimeSessionCreateRequest {
142            r#type: RealtimeSessionType::Realtime,
143            output_modalities: Some(vec![OutputModality::Audio]),
144            model: None,
145            instructions: None,
146            audio: None,
147            include: None,
148            tracing: None,
149            tools: None,
150            tool_choice: None,
151            max_output_tokens: None,
152            truncation: None,
153            prompt: None,
154        }));
155
156        let event = ClientEvent::session_update(Some("evt_1".into()), config);
157        assert_eq!(event.event_type(), "session.update");
158    }
159
160    #[test]
161    fn test_response_create_empty() {
162        let event = ClientEvent::response_create(None, None);
163        assert_eq!(event.event_type(), "response.create");
164    }
165}