openai_compat/types/responses/
events.rs1use serde::Deserialize;
16
17use super::response::{OutputItem, Response};
18
19#[derive(Debug, Clone, Deserialize)]
21#[serde(tag = "type", rename_all = "snake_case")]
22#[non_exhaustive]
23pub enum ResponseStreamEvent {
24 #[serde(rename = "response.created")]
25 Created {
26 response: Response,
27 sequence_number: u64,
28 },
29 #[serde(rename = "response.in_progress")]
30 InProgress {
31 response: Response,
32 sequence_number: u64,
33 },
34 #[serde(rename = "response.queued")]
35 Queued {
36 response: Response,
37 sequence_number: u64,
38 },
39 #[serde(rename = "response.completed")]
41 Completed {
42 response: Response,
43 sequence_number: u64,
44 },
45 #[serde(rename = "response.failed")]
48 Failed {
49 response: Response,
50 sequence_number: u64,
51 },
52 #[serde(rename = "response.incomplete")]
54 Incomplete {
55 response: Response,
56 sequence_number: u64,
57 },
58 #[serde(rename = "response.output_item.added")]
59 OutputItemAdded {
60 output_index: u32,
61 item: OutputItem,
62 sequence_number: u64,
63 },
64 #[serde(rename = "response.output_item.done")]
65 OutputItemDone {
66 output_index: u32,
67 item: OutputItem,
68 sequence_number: u64,
69 },
70 #[serde(rename = "response.content_part.added")]
71 ContentPartAdded {
72 item_id: String,
73 output_index: u32,
74 content_index: u32,
75 part: serde_json::Value,
76 sequence_number: u64,
77 },
78 #[serde(rename = "response.content_part.done")]
79 ContentPartDone {
80 item_id: String,
81 output_index: u32,
82 content_index: u32,
83 part: serde_json::Value,
84 sequence_number: u64,
85 },
86 #[serde(rename = "response.output_text.delta")]
87 OutputTextDelta {
88 item_id: String,
89 output_index: u32,
90 content_index: u32,
91 delta: String,
92 sequence_number: u64,
93 },
94 #[serde(rename = "response.output_text.done")]
95 OutputTextDone {
96 item_id: String,
97 output_index: u32,
98 content_index: u32,
99 text: String,
100 sequence_number: u64,
101 },
102 #[serde(rename = "response.function_call_arguments.delta")]
103 FunctionCallArgumentsDelta {
104 item_id: String,
105 output_index: u32,
106 delta: String,
107 sequence_number: u64,
108 },
109 #[serde(rename = "response.function_call_arguments.done")]
110 FunctionCallArgumentsDone {
111 item_id: String,
112 output_index: u32,
113 arguments: String,
114 sequence_number: u64,
115 },
116 #[serde(rename = "error")]
117 Error {
118 #[serde(default)]
119 code: Option<String>,
120 message: String,
121 #[serde(default)]
122 param: Option<String>,
123 sequence_number: u64,
124 },
125 #[serde(other)]
128 Unknown,
129}
130
131impl ResponseStreamEvent {
132 pub fn sequence_number(&self) -> Option<u64> {
136 match self {
137 Self::Created { sequence_number, .. }
138 | Self::InProgress { sequence_number, .. }
139 | Self::Queued { sequence_number, .. }
140 | Self::Completed { sequence_number, .. }
141 | Self::Failed { sequence_number, .. }
142 | Self::Incomplete { sequence_number, .. }
143 | Self::OutputItemAdded { sequence_number, .. }
144 | Self::OutputItemDone { sequence_number, .. }
145 | Self::ContentPartAdded { sequence_number, .. }
146 | Self::ContentPartDone { sequence_number, .. }
147 | Self::OutputTextDelta { sequence_number, .. }
148 | Self::OutputTextDone { sequence_number, .. }
149 | Self::FunctionCallArgumentsDelta { sequence_number, .. }
150 | Self::FunctionCallArgumentsDone { sequence_number, .. }
151 | Self::Error { sequence_number, .. } => Some(*sequence_number),
152 Self::Unknown => None,
153 }
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 fn response_json() -> serde_json::Value {
162 serde_json::json!({
163 "id": "resp_1", "object": "response", "created_at": 1.0,
164 "model": "gpt-5", "status": "completed", "output": []
165 })
166 }
167
168 #[test]
169 fn stream_event_created() {
170 let json = serde_json::json!({
171 "type": "response.created", "response": response_json(), "sequence_number": 1
172 });
173 let event: ResponseStreamEvent = serde_json::from_value(json).unwrap();
174 assert!(matches!(event, ResponseStreamEvent::Created { .. }));
175 assert_eq!(event.sequence_number(), Some(1));
176 }
177
178 #[test]
179 fn stream_event_output_text_delta() {
180 let json = serde_json::json!({
181 "type": "response.output_text.delta",
182 "item_id": "msg_1", "output_index": 0, "content_index": 0, "delta": "Hel",
183 "sequence_number": 3
184 });
185 let event: ResponseStreamEvent = serde_json::from_value(json).unwrap();
186 assert_eq!(event.sequence_number(), Some(3));
187 match event {
188 ResponseStreamEvent::OutputTextDelta { delta, .. } => assert_eq!(delta, "Hel"),
189 _ => panic!("expected OutputTextDelta"),
190 }
191 }
192
193 #[test]
194 fn unknown_stream_event_maps_to_unknown() {
195 let json = serde_json::json!({
196 "type": "response.reasoning_summary_text.delta",
197 "item_id": "r_1", "delta": "thinking"
198 });
199 let event: ResponseStreamEvent = serde_json::from_value(json).unwrap();
200 assert!(matches!(event, ResponseStreamEvent::Unknown));
201 assert_eq!(event.sequence_number(), None);
202 }
203
204 #[test]
205 fn failed_event_is_typed_not_error() {
206 let mut response = response_json();
207 response["status"] = serde_json::json!("failed");
208 response["error"] = serde_json::json!({"code": "server_error", "message": "boom"});
209 let json = serde_json::json!({
210 "type": "response.failed", "response": response, "sequence_number": 5
211 });
212 let event: ResponseStreamEvent = serde_json::from_value(json).unwrap();
213 match event {
214 ResponseStreamEvent::Failed { response, .. } => {
215 assert_eq!(response.error.unwrap().code, "server_error");
216 }
217 _ => panic!("expected a typed Ok(Failed), not a stream error"),
218 }
219 }
220
221 #[test]
222 fn error_event_uses_bare_type_tag() {
223 let json = serde_json::json!({
224 "type": "error", "message": "boom", "code": "server_error", "sequence_number": 2
225 });
226 let event: ResponseStreamEvent = serde_json::from_value(json).unwrap();
227 match event {
228 ResponseStreamEvent::Error { message, code, .. } => {
229 assert_eq!(message, "boom");
230 assert_eq!(code.as_deref(), Some("server_error"));
231 }
232 _ => panic!("expected Error"),
233 }
234 }
235}