Skip to main content

openai_tools/realtime/events/
client.rs

1//! Client-to-server events for the Realtime API.
2
3use serde::Serialize;
4
5use crate::realtime::conversation::ConversationItem;
6use crate::realtime::session::{ResponseCreateConfig, SessionConfig};
7
8/// Client events sent to the OpenAI Realtime API server.
9#[derive(Debug, Clone, Serialize)]
10#[serde(tag = "type")]
11pub enum ClientEvent {
12    // ==================== Session Events ====================
13    /// Update the session configuration.
14    #[serde(rename = "session.update")]
15    SessionUpdate {
16        /// Optional client-generated event ID.
17        #[serde(skip_serializing_if = "Option::is_none")]
18        event_id: Option<String>,
19        /// Session configuration to update.
20        session: SessionConfig,
21    },
22
23    // ==================== Input Audio Buffer Events ====================
24    /// Append audio data to the input buffer.
25    #[serde(rename = "input_audio_buffer.append")]
26    InputAudioBufferAppend {
27        /// Optional client-generated event ID.
28        #[serde(skip_serializing_if = "Option::is_none")]
29        event_id: Option<String>,
30        /// Base64-encoded audio data.
31        audio: String,
32    },
33
34    /// Clear the input audio buffer.
35    #[serde(rename = "input_audio_buffer.clear")]
36    InputAudioBufferClear {
37        /// Optional client-generated event ID.
38        #[serde(skip_serializing_if = "Option::is_none")]
39        event_id: Option<String>,
40    },
41
42    /// Commit the input audio buffer.
43    #[serde(rename = "input_audio_buffer.commit")]
44    InputAudioBufferCommit {
45        /// Optional client-generated event ID.
46        #[serde(skip_serializing_if = "Option::is_none")]
47        event_id: Option<String>,
48    },
49
50    // ==================== Output Audio Buffer Events (WebRTC only) ====================
51    /// Clear the output audio buffer (WebRTC only).
52    #[serde(rename = "output_audio_buffer.clear")]
53    OutputAudioBufferClear {
54        /// Optional client-generated event ID.
55        #[serde(skip_serializing_if = "Option::is_none")]
56        event_id: Option<String>,
57    },
58
59    // ==================== Conversation Item Events ====================
60    /// Create a new conversation item.
61    #[serde(rename = "conversation.item.create")]
62    ConversationItemCreate {
63        /// Optional client-generated event ID.
64        #[serde(skip_serializing_if = "Option::is_none")]
65        event_id: Option<String>,
66        /// ID of the item to insert after (omit to append).
67        #[serde(skip_serializing_if = "Option::is_none")]
68        previous_item_id: Option<String>,
69        /// The conversation item to create.
70        item: ConversationItem,
71    },
72
73    /// Delete a conversation item.
74    #[serde(rename = "conversation.item.delete")]
75    ConversationItemDelete {
76        /// Optional client-generated event ID.
77        #[serde(skip_serializing_if = "Option::is_none")]
78        event_id: Option<String>,
79        /// ID of the item to delete.
80        item_id: String,
81    },
82
83    /// Retrieve a conversation item.
84    #[serde(rename = "conversation.item.retrieve")]
85    ConversationItemRetrieve {
86        /// Optional client-generated event ID.
87        #[serde(skip_serializing_if = "Option::is_none")]
88        event_id: Option<String>,
89        /// ID of the item to retrieve.
90        item_id: String,
91    },
92
93    /// Truncate a conversation item's audio.
94    #[serde(rename = "conversation.item.truncate")]
95    ConversationItemTruncate {
96        /// Optional client-generated event ID.
97        #[serde(skip_serializing_if = "Option::is_none")]
98        event_id: Option<String>,
99        /// ID of the item to truncate.
100        item_id: String,
101        /// Index of the content part to truncate.
102        content_index: u32,
103        /// Audio end position in milliseconds.
104        audio_end_ms: u32,
105    },
106
107    // ==================== Response Events ====================
108    /// Create a new response.
109    #[serde(rename = "response.create")]
110    ResponseCreate {
111        /// Optional client-generated event ID.
112        #[serde(skip_serializing_if = "Option::is_none")]
113        event_id: Option<String>,
114        /// Response configuration.
115        #[serde(skip_serializing_if = "Option::is_none")]
116        response: Option<ResponseCreateConfig>,
117    },
118
119    /// Cancel the current response.
120    #[serde(rename = "response.cancel")]
121    ResponseCancel {
122        /// Optional client-generated event ID.
123        #[serde(skip_serializing_if = "Option::is_none")]
124        event_id: Option<String>,
125    },
126}
127
128impl ClientEvent {
129    /// Create a session update event.
130    pub fn session_update(config: SessionConfig) -> Self {
131        Self::SessionUpdate { event_id: None, session: config }
132    }
133
134    /// Create an input audio buffer append event.
135    pub fn append_audio(audio_base64: impl Into<String>) -> Self {
136        Self::InputAudioBufferAppend { event_id: None, audio: audio_base64.into() }
137    }
138
139    /// Create an input audio buffer clear event.
140    pub fn clear_audio() -> Self {
141        Self::InputAudioBufferClear { event_id: None }
142    }
143
144    /// Create an input audio buffer commit event.
145    pub fn commit_audio() -> Self {
146        Self::InputAudioBufferCommit { event_id: None }
147    }
148
149    /// Create a conversation item create event.
150    pub fn create_item(item: ConversationItem) -> Self {
151        Self::ConversationItemCreate { event_id: None, previous_item_id: None, item }
152    }
153
154    /// Create a conversation item create event with a specific position.
155    pub fn create_item_after(item: ConversationItem, previous_item_id: impl Into<String>) -> Self {
156        Self::ConversationItemCreate { event_id: None, previous_item_id: Some(previous_item_id.into()), item }
157    }
158
159    /// Create a conversation item delete event.
160    pub fn delete_item(item_id: impl Into<String>) -> Self {
161        Self::ConversationItemDelete { event_id: None, item_id: item_id.into() }
162    }
163
164    /// Create a response create event.
165    pub fn create_response(config: Option<ResponseCreateConfig>) -> Self {
166        Self::ResponseCreate { event_id: None, response: config }
167    }
168
169    /// Create a response cancel event.
170    pub fn cancel_response() -> Self {
171        Self::ResponseCancel { event_id: None }
172    }
173
174    /// Set a custom event ID.
175    pub fn with_event_id(mut self, id: impl Into<String>) -> Self {
176        let id = Some(id.into());
177        match &mut self {
178            Self::SessionUpdate { event_id, .. } => *event_id = id,
179            Self::InputAudioBufferAppend { event_id, .. } => *event_id = id,
180            Self::InputAudioBufferClear { event_id } => *event_id = id,
181            Self::InputAudioBufferCommit { event_id } => *event_id = id,
182            Self::OutputAudioBufferClear { event_id } => *event_id = id,
183            Self::ConversationItemCreate { event_id, .. } => *event_id = id,
184            Self::ConversationItemDelete { event_id, .. } => *event_id = id,
185            Self::ConversationItemRetrieve { event_id, .. } => *event_id = id,
186            Self::ConversationItemTruncate { event_id, .. } => *event_id = id,
187            Self::ResponseCreate { event_id, .. } => *event_id = id,
188            Self::ResponseCancel { event_id } => *event_id = id,
189        }
190        self
191    }
192}