Skip to main content

openai_tools/live/
events.rs

1//! Client and server events for the OpenAI Live API.
2
3use serde::{Deserialize, Serialize};
4
5use crate::common::models::LiveModel;
6
7use super::session::{LiveAudioConfig, LiveSessionConfig, LiveSessionUpdate};
8
9/// Events sent from the client to a Live WebSocket.
10#[derive(Debug, Clone, Serialize)]
11#[serde(tag = "type")]
12pub enum ClientEvent {
13    #[serde(rename = "session.start")]
14    SessionStart {
15        #[serde(skip_serializing_if = "Option::is_none")]
16        event_id: Option<String>,
17        session: LiveSessionConfig,
18    },
19    #[serde(rename = "session.update")]
20    SessionUpdate {
21        #[serde(skip_serializing_if = "Option::is_none")]
22        event_id: Option<String>,
23        session: LiveSessionUpdate,
24    },
25    #[serde(rename = "session.input_audio.append")]
26    InputAudioAppend {
27        #[serde(skip_serializing_if = "Option::is_none")]
28        event_id: Option<String>,
29        audio: String,
30    },
31    #[serde(rename = "session.input_audio.mute")]
32    InputAudioMute {
33        #[serde(skip_serializing_if = "Option::is_none")]
34        event_id: Option<String>,
35    },
36    #[serde(rename = "session.input_audio.unmute")]
37    InputAudioUnmute {
38        #[serde(skip_serializing_if = "Option::is_none")]
39        event_id: Option<String>,
40    },
41    #[serde(rename = "session.instructions.append")]
42    InstructionsAppend {
43        #[serde(skip_serializing_if = "Option::is_none")]
44        event_id: Option<String>,
45        content: String,
46        delegation_id: Option<String>,
47    },
48    #[serde(rename = "session.thinking.append")]
49    ThinkingAppend {
50        #[serde(skip_serializing_if = "Option::is_none")]
51        event_id: Option<String>,
52        content: String,
53        delegation_id: Option<String>,
54    },
55    #[serde(rename = "session.commentary.append")]
56    CommentaryAppend {
57        #[serde(skip_serializing_if = "Option::is_none")]
58        event_id: Option<String>,
59        content: String,
60        delegation_id: Option<String>,
61    },
62    #[serde(rename = "response.item.create")]
63    ResponseItemCreate {
64        #[serde(skip_serializing_if = "Option::is_none")]
65        event_id: Option<String>,
66        item: serde_json::Value,
67    },
68    #[serde(rename = "response.create")]
69    ResponseCreate {
70        #[serde(skip_serializing_if = "Option::is_none")]
71        event_id: Option<String>,
72    },
73    #[serde(rename = "session.close")]
74    SessionClose {
75        #[serde(skip_serializing_if = "Option::is_none")]
76        event_id: Option<String>,
77    },
78}
79
80/// Events received from a Live WebSocket.
81#[derive(Debug, Clone, Deserialize)]
82#[serde(tag = "type")]
83#[non_exhaustive]
84pub enum ServerEvent {
85    #[serde(rename = "session.started")]
86    SessionStarted(SessionStartedEvent),
87    #[serde(rename = "session.updated")]
88    SessionUpdated(SessionUpdatedEvent),
89    #[serde(rename = "session.input_audio.muted")]
90    InputAudioMuted(AcknowledgementEvent),
91    #[serde(rename = "session.input_audio.unmuted")]
92    InputAudioUnmuted(AcknowledgementEvent),
93    #[serde(rename = "session.instructions.appended")]
94    InstructionsAppended(TimelineEvent),
95    #[serde(rename = "session.thinking.appended")]
96    ThinkingAppended(TimelineEvent),
97    #[serde(rename = "session.commentary.appended")]
98    CommentaryAppended(TimelineEvent),
99    #[serde(rename = "session.output_audio.delta")]
100    OutputAudioDelta(OutputAudioDeltaEvent),
101    #[serde(rename = "session.input_transcript.delta")]
102    InputTranscriptDelta(TranscriptDeltaEvent),
103    #[serde(rename = "session.output_transcript.delta")]
104    OutputTranscriptDelta(TranscriptDeltaEvent),
105    #[serde(rename = "session.delegation.created")]
106    DelegationCreated(DelegationCreatedEvent),
107    #[serde(rename = "response.event")]
108    ResponseEvent(ResponseEvent),
109    #[serde(rename = "session.usage.updated")]
110    SessionUsageUpdated(SessionUsageUpdatedEvent),
111    #[serde(rename = "session.closed")]
112    SessionClosed(SessionClosedEvent),
113    #[serde(rename = "error")]
114    Error(ErrorEvent),
115    #[serde(rename = "info")]
116    Info(InfoEvent),
117    /// An event added after this library version.
118    #[serde(other)]
119    Unknown,
120}
121
122/// Resolved Live session state.
123#[derive(Debug, Clone, Deserialize)]
124pub struct SessionResource {
125    pub id: String,
126    pub expires_at: u64,
127    pub model: LiveModel,
128    pub status: String,
129    pub audio: Option<LiveAudioConfig>,
130    pub client: Option<serde_json::Value>,
131    pub delegation: Option<serde_json::Value>,
132    #[serde(default)]
133    pub input: Vec<serde_json::Value>,
134    pub instructions: Option<String>,
135    pub store: Option<bool>,
136}
137
138/// Successful response to `session.start`.
139#[derive(Debug, Clone, Deserialize)]
140pub struct SessionStartedEvent {
141    pub event_id: Option<String>,
142    pub client_event_id: Option<String>,
143    pub session: SessionResource,
144}
145
146/// Successful response to `session.update`.
147#[derive(Debug, Clone, Deserialize)]
148pub struct SessionUpdatedEvent {
149    pub event_id: Option<String>,
150    pub client_event_id: Option<String>,
151    pub session: SessionResource,
152}
153
154/// Acknowledgement for an event without a payload.
155#[derive(Debug, Clone, Deserialize)]
156pub struct AcknowledgementEvent {
157    pub event_id: Option<String>,
158    pub client_event_id: Option<String>,
159}
160
161/// Acknowledgement positioned on the Live session timeline.
162#[derive(Debug, Clone, Deserialize)]
163pub struct TimelineEvent {
164    pub event_id: Option<String>,
165    pub client_event_id: Option<String>,
166    pub start_ms: Option<u64>,
167    pub end_ms: Option<u64>,
168}
169
170/// Generated output audio bytes, encoded as base64.
171#[derive(Debug, Clone, Deserialize)]
172pub struct OutputAudioDeltaEvent {
173    pub event_id: Option<String>,
174    pub client_event_id: Option<String>,
175    pub delta: String,
176    pub start_ms: Option<u64>,
177    pub end_ms: Option<u64>,
178}
179
180/// Incremental input or output speech transcript.
181#[derive(Debug, Clone, Deserialize)]
182pub struct TranscriptDeltaEvent {
183    pub event_id: Option<String>,
184    pub client_event_id: Option<String>,
185    pub delta: String,
186    pub start_ms: Option<u64>,
187    pub end_ms: Option<u64>,
188}
189
190/// A delegation created by the Live model.
191#[derive(Debug, Clone, Deserialize)]
192pub struct DelegationCreatedEvent {
193    pub event_id: Option<String>,
194    pub client_event_id: Option<String>,
195    pub offset_ms: Option<u64>,
196    pub delegation: DelegationResource,
197}
198
199/// Delegation metadata.
200#[derive(Debug, Clone, Deserialize)]
201pub struct DelegationResource {
202    pub id: String,
203    #[serde(rename = "type")]
204    pub type_name: String,
205    pub target: DelegationTarget,
206    pub response_id: Option<String>,
207}
208
209/// The service handling delegated work.
210#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
211#[serde(rename_all = "snake_case")]
212#[non_exhaustive]
213pub enum DelegationTarget {
214    Client,
215    Responses,
216    /// A delegation target added after this library version.
217    #[serde(untagged)]
218    Other(String),
219}
220
221/// Envelope for a Responses API streaming event.
222#[derive(Debug, Clone, Deserialize)]
223pub struct ResponseEvent {
224    pub event_id: Option<String>,
225    pub client_event_id: Option<String>,
226    pub delegation_id: Option<String>,
227    pub event: serde_json::Value,
228}
229
230/// Cumulative Live audio usage.
231///
232/// `seconds` is cumulative; do not add values from successive events.
233#[derive(Debug, Clone, Deserialize)]
234pub struct SessionUsage {
235    pub seconds: f64,
236}
237
238/// Current Live context-window utilization.
239#[derive(Debug, Clone, Deserialize)]
240pub struct ContextWindowUsage {
241    pub usage_ratio: f64,
242}
243
244/// Updated cumulative usage and optional context-window utilization.
245#[derive(Debug, Clone, Deserialize)]
246pub struct SessionUsageUpdatedEvent {
247    pub event_id: Option<String>,
248    pub client_event_id: Option<String>,
249    pub usage: SessionUsage,
250    pub context_window: Option<ContextWindowUsage>,
251}
252
253/// Terminal Live session event.
254#[derive(Debug, Clone, Deserialize)]
255pub struct SessionClosedEvent {
256    pub event_id: Option<String>,
257    pub client_event_id: Option<String>,
258    pub reason: CloseReason,
259    pub session: SessionResource,
260    pub usage: SessionUsage,
261}
262
263/// Why a Live session ended.
264#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
265#[serde(rename_all = "snake_case")]
266#[non_exhaustive]
267pub enum CloseReason {
268    CloseRequested,
269    Expired,
270    Content,
271    RemoteHangup,
272    ConnectionLost,
273    /// A close reason added after this library version.
274    #[serde(untagged)]
275    Other(String),
276}
277
278/// Live API error payload.
279#[derive(Debug, Clone, Deserialize)]
280pub struct LiveError {
281    #[serde(rename = "type")]
282    pub type_name: String,
283    pub code: String,
284    pub message: String,
285    pub param: Option<String>,
286    pub client_event_id: Option<String>,
287}
288
289/// Error event sent by the Live service.
290#[derive(Debug, Clone, Deserialize)]
291pub struct ErrorEvent {
292    pub event_id: Option<String>,
293    pub client_event_id: Option<String>,
294    pub error: LiveError,
295}
296
297/// Informational event sent by the Live service.
298#[derive(Debug, Clone, Deserialize)]
299pub struct InfoEvent {
300    pub event_id: Option<String>,
301    pub client_event_id: Option<String>,
302    pub code: String,
303    pub message: String,
304}