Skip to main content

gproxy_protocol/protocol/openai/generate_content/
stream.rs

1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize, de};
4use serde_json::Value;
5
6use super::super::common::*;
7use super::{
8    ResponseContentPart, ResponseObject, ResponseOutputItem, ResponseReasoningSummaryPart,
9    ResponseSteerError, ResponseSteerFailure, ResponseSteerReference,
10};
11
12#[derive(Debug, Clone, PartialEq)]
13#[non_exhaustive]
14pub enum ResponseStreamEvent {
15    Known(KnownResponseStreamEvent),
16    Unknown(UnknownResponseStreamEvent),
17}
18
19impl ResponseStreamEvent {
20    /// SSE event name: the wire `type` of this event, if any.
21    pub fn event_name(&self) -> Option<&str> {
22        match self {
23            Self::Known(event) => Some(event.event_name()),
24            Self::Unknown(event) => event.type_.as_ref().map(ResponseStreamEventType::as_str),
25        }
26    }
27}
28
29impl Serialize for ResponseStreamEvent {
30    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
31    where
32        S: serde::Serializer,
33    {
34        match self {
35            Self::Known(event) => event.serialize(serializer),
36            Self::Unknown(event) => event.serialize(serializer),
37        }
38    }
39}
40
41impl<'de> Deserialize<'de> for ResponseStreamEvent {
42    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
43    where
44        D: serde::Deserializer<'de>,
45    {
46        let value = Value::deserialize(deserializer)?;
47        let Some(type_name) = value.get("type").and_then(Value::as_str) else {
48            return serde_json::from_value(value)
49                .map(Self::Unknown)
50                .map_err(de::Error::custom);
51        };
52
53        let event_type =
54            serde_json::from_value::<ResponseStreamEventType>(Value::String(type_name.to_owned()))
55                .map_err(de::Error::custom)?;
56
57        match event_type {
58            ResponseStreamEventType::Known(_) => serde_json::from_value(value)
59                .map(Self::Known)
60                .map_err(de::Error::custom),
61            ResponseStreamEventType::Unknown(_) => serde_json::from_value(value)
62                .map(Self::Unknown)
63                .map_err(de::Error::custom),
64        }
65    }
66}
67
68#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
69#[serde(tag = "type")]
70#[non_exhaustive]
71pub enum KnownResponseStreamEvent {
72    #[serde(rename = "response.created")]
73    ResponseCreated {
74        response: Box<ResponseObject>,
75        #[serde(skip_serializing_if = "Option::is_none")]
76        sequence_number: Option<u64>,
77        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
78        extra: Extra,
79    },
80    #[serde(rename = "response.in_progress")]
81    ResponseInProgress {
82        response: Box<ResponseObject>,
83        #[serde(skip_serializing_if = "Option::is_none")]
84        sequence_number: Option<u64>,
85        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
86        extra: Extra,
87    },
88    #[serde(rename = "response.completed")]
89    ResponseCompleted {
90        response: Box<ResponseObject>,
91        #[serde(skip_serializing_if = "Option::is_none")]
92        sequence_number: Option<u64>,
93        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
94        extra: Extra,
95    },
96    #[serde(rename = "response.failed")]
97    ResponseFailed {
98        response: Box<ResponseObject>,
99        #[serde(skip_serializing_if = "Option::is_none")]
100        sequence_number: Option<u64>,
101        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
102        extra: Extra,
103    },
104    #[serde(rename = "response.incomplete")]
105    ResponseIncomplete {
106        response: Box<ResponseObject>,
107        #[serde(skip_serializing_if = "Option::is_none")]
108        sequence_number: Option<u64>,
109        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
110        extra: Extra,
111    },
112    #[serde(rename = "response.queued")]
113    ResponseQueued {
114        response: Box<ResponseObject>,
115        #[serde(skip_serializing_if = "Option::is_none")]
116        sequence_number: Option<u64>,
117        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
118        extra: Extra,
119    },
120    #[serde(rename = "response.steer.accepted")]
121    ResponseSteerAccepted {
122        steer: ResponseSteerReference,
123        #[serde(skip_serializing_if = "Option::is_none")]
124        sequence_number: Option<u64>,
125        #[serde(skip_serializing_if = "Option::is_none")]
126        stream_id: Option<String>,
127        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
128        extra: Extra,
129    },
130    #[serde(rename = "response.steer.pending")]
131    ResponseSteerPending {
132        steer: ResponseSteerReference,
133        reason: String,
134        required_input: Vec<Value>,
135        #[serde(skip_serializing_if = "Option::is_none")]
136        sequence_number: Option<u64>,
137        #[serde(skip_serializing_if = "Option::is_none")]
138        stream_id: Option<String>,
139        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
140        extra: Extra,
141    },
142    #[serde(rename = "response.steer.failed")]
143    ResponseSteerFailed {
144        steer: ResponseSteerFailure,
145        error: ResponseSteerError,
146        #[serde(skip_serializing_if = "Option::is_none")]
147        sequence_number: Option<u64>,
148        #[serde(skip_serializing_if = "Option::is_none")]
149        stream_id: Option<String>,
150        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
151        extra: Extra,
152    },
153    #[serde(rename = "response.output_item.added")]
154    ResponseOutputItemAdded {
155        item: Box<ResponseOutputItem>,
156        output_index: u32,
157        #[serde(skip_serializing_if = "Option::is_none")]
158        sequence_number: Option<u64>,
159        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
160        extra: Extra,
161    },
162    #[serde(rename = "response.output_item.done")]
163    ResponseOutputItemDone {
164        item: Box<ResponseOutputItem>,
165        output_index: u32,
166        #[serde(skip_serializing_if = "Option::is_none")]
167        sequence_number: Option<u64>,
168        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
169        extra: Extra,
170    },
171    #[serde(rename = "response.content_part.added")]
172    ResponseContentPartAdded {
173        content_index: u32,
174        item_id: String,
175        output_index: u32,
176        part: ResponseContentPart,
177        #[serde(skip_serializing_if = "Option::is_none")]
178        sequence_number: Option<u64>,
179        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
180        extra: Extra,
181    },
182    #[serde(rename = "response.content_part.done")]
183    ResponseContentPartDone {
184        content_index: u32,
185        item_id: String,
186        output_index: u32,
187        part: ResponseContentPart,
188        #[serde(skip_serializing_if = "Option::is_none")]
189        sequence_number: Option<u64>,
190        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
191        extra: Extra,
192    },
193    #[serde(rename = "response.output_text.delta")]
194    ResponseOutputTextDelta {
195        content_index: u32,
196        delta: String,
197        item_id: String,
198        #[serde(skip_serializing_if = "Option::is_none")]
199        logprobs: Option<Vec<StreamTokenLogprob>>,
200        output_index: u32,
201        #[serde(skip_serializing_if = "Option::is_none")]
202        sequence_number: Option<u64>,
203        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
204        extra: Extra,
205    },
206    #[serde(rename = "response.output_text.done")]
207    ResponseOutputTextDone {
208        content_index: u32,
209        item_id: String,
210        #[serde(skip_serializing_if = "Option::is_none")]
211        logprobs: Option<Vec<StreamTokenLogprob>>,
212        output_index: u32,
213        #[serde(skip_serializing_if = "Option::is_none")]
214        sequence_number: Option<u64>,
215        text: String,
216        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
217        extra: Extra,
218    },
219    #[serde(rename = "response.output_text.annotation.added")]
220    ResponseOutputTextAnnotationAdded {
221        annotation: Value,
222        annotation_index: u32,
223        content_index: u32,
224        item_id: String,
225        output_index: u32,
226        #[serde(skip_serializing_if = "Option::is_none")]
227        sequence_number: Option<u64>,
228        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
229        extra: Extra,
230    },
231    #[serde(rename = "response.function_call_arguments.delta")]
232    ResponseFunctionCallArgumentsDelta {
233        delta: String,
234        item_id: String,
235        output_index: u32,
236        #[serde(skip_serializing_if = "Option::is_none")]
237        sequence_number: Option<u64>,
238        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
239        extra: Extra,
240    },
241    #[serde(rename = "response.function_call_arguments.done")]
242    ResponseFunctionCallArgumentsDone {
243        arguments: String,
244        item_id: String,
245        // The ChatGPT Codex backend can omit this field. Keep the public wire
246        // type stable and let stateful consumers recover it from the matching
247        // output item.
248        #[serde(default, skip_serializing_if = "String::is_empty")]
249        name: String,
250        output_index: u32,
251        #[serde(skip_serializing_if = "Option::is_none")]
252        sequence_number: Option<u64>,
253        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
254        extra: Extra,
255    },
256    #[serde(rename = "response.custom_tool_call_input.delta")]
257    ResponseCustomToolCallInputDelta {
258        delta: String,
259        item_id: String,
260        output_index: u32,
261        #[serde(skip_serializing_if = "Option::is_none")]
262        sequence_number: Option<u64>,
263        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
264        extra: Extra,
265    },
266    #[serde(rename = "response.custom_tool_call_input.done")]
267    ResponseCustomToolCallInputDone {
268        input: String,
269        item_id: String,
270        output_index: u32,
271        #[serde(skip_serializing_if = "Option::is_none")]
272        sequence_number: Option<u64>,
273        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
274        extra: Extra,
275    },
276    #[serde(rename = "response.refusal.delta")]
277    ResponseRefusalDelta {
278        content_index: u32,
279        delta: String,
280        item_id: String,
281        output_index: u32,
282        #[serde(skip_serializing_if = "Option::is_none")]
283        sequence_number: Option<u64>,
284        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
285        extra: Extra,
286    },
287    #[serde(rename = "response.refusal.done")]
288    ResponseRefusalDone {
289        content_index: u32,
290        item_id: String,
291        output_index: u32,
292        refusal: String,
293        #[serde(skip_serializing_if = "Option::is_none")]
294        sequence_number: Option<u64>,
295        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
296        extra: Extra,
297    },
298    #[serde(rename = "response.reasoning_summary_part.added")]
299    ResponseReasoningSummaryPartAdded {
300        item_id: String,
301        output_index: u32,
302        part: ResponseReasoningSummaryPart,
303        #[serde(skip_serializing_if = "Option::is_none")]
304        sequence_number: Option<u64>,
305        summary_index: u32,
306        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
307        extra: Extra,
308    },
309    #[serde(rename = "response.reasoning_summary_part.done")]
310    ResponseReasoningSummaryPartDone {
311        item_id: String,
312        output_index: u32,
313        part: ResponseReasoningSummaryPart,
314        #[serde(skip_serializing_if = "Option::is_none")]
315        sequence_number: Option<u64>,
316        summary_index: u32,
317        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
318        extra: Extra,
319    },
320    #[serde(rename = "response.reasoning_summary_text.delta")]
321    ResponseReasoningSummaryTextDelta {
322        delta: String,
323        item_id: String,
324        output_index: u32,
325        #[serde(skip_serializing_if = "Option::is_none")]
326        sequence_number: Option<u64>,
327        summary_index: u32,
328        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
329        extra: Extra,
330    },
331    #[serde(rename = "response.reasoning_summary_text.done")]
332    ResponseReasoningSummaryTextDone {
333        item_id: String,
334        output_index: u32,
335        #[serde(skip_serializing_if = "Option::is_none")]
336        sequence_number: Option<u64>,
337        summary_index: u32,
338        text: String,
339        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
340        extra: Extra,
341    },
342    #[serde(rename = "response.reasoning_text.delta")]
343    ResponseReasoningTextDelta {
344        content_index: u32,
345        delta: String,
346        item_id: String,
347        output_index: u32,
348        #[serde(skip_serializing_if = "Option::is_none")]
349        sequence_number: Option<u64>,
350        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
351        extra: Extra,
352    },
353    #[serde(rename = "response.reasoning_text.done")]
354    ResponseReasoningTextDone {
355        content_index: u32,
356        item_id: String,
357        output_index: u32,
358        #[serde(skip_serializing_if = "Option::is_none")]
359        sequence_number: Option<u64>,
360        text: String,
361        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
362        extra: Extra,
363    },
364    #[serde(rename = "response.audio.delta")]
365    ResponseAudioDelta {
366        delta: String,
367        #[serde(skip_serializing_if = "Option::is_none")]
368        sequence_number: Option<u64>,
369        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
370        extra: Extra,
371    },
372    #[serde(rename = "response.audio.done")]
373    ResponseAudioDone {
374        #[serde(skip_serializing_if = "Option::is_none")]
375        sequence_number: Option<u64>,
376        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
377        extra: Extra,
378    },
379    #[serde(rename = "response.audio.transcript.delta")]
380    ResponseAudioTranscriptDelta {
381        delta: String,
382        #[serde(skip_serializing_if = "Option::is_none")]
383        sequence_number: Option<u64>,
384        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
385        extra: Extra,
386    },
387    #[serde(rename = "response.audio.transcript.done")]
388    ResponseAudioTranscriptDone {
389        #[serde(skip_serializing_if = "Option::is_none")]
390        sequence_number: Option<u64>,
391        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
392        extra: Extra,
393    },
394    #[serde(rename = "response.image_generation_call.completed")]
395    ResponseImageGenerationCallCompleted {
396        item_id: String,
397        output_index: u32,
398        #[serde(skip_serializing_if = "Option::is_none")]
399        sequence_number: Option<u64>,
400        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
401        extra: Extra,
402    },
403    #[serde(rename = "response.image_generation_call.generating")]
404    ResponseImageGenerationCallGenerating {
405        item_id: String,
406        output_index: u32,
407        #[serde(skip_serializing_if = "Option::is_none")]
408        sequence_number: Option<u64>,
409        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
410        extra: Extra,
411    },
412    #[serde(rename = "response.image_generation_call.in_progress")]
413    ResponseImageGenerationCallInProgress {
414        item_id: String,
415        output_index: u32,
416        #[serde(skip_serializing_if = "Option::is_none")]
417        sequence_number: Option<u64>,
418        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
419        extra: Extra,
420    },
421    #[serde(rename = "response.image_generation_call.partial_image")]
422    ResponseImageGenerationCallPartialImage {
423        item_id: String,
424        output_index: u32,
425        partial_image_b64: String,
426        partial_image_index: u32,
427        #[serde(skip_serializing_if = "Option::is_none")]
428        sequence_number: Option<u64>,
429        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
430        extra: Extra,
431    },
432    #[serde(rename = "response.file_search_call.in_progress")]
433    ResponseFileSearchCallInProgress {
434        item_id: String,
435        output_index: u32,
436        #[serde(skip_serializing_if = "Option::is_none")]
437        sequence_number: Option<u64>,
438        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
439        extra: Extra,
440    },
441    #[serde(rename = "response.file_search_call.searching")]
442    ResponseFileSearchCallSearching {
443        item_id: String,
444        output_index: u32,
445        #[serde(skip_serializing_if = "Option::is_none")]
446        sequence_number: Option<u64>,
447        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
448        extra: Extra,
449    },
450    #[serde(rename = "response.file_search_call.completed")]
451    ResponseFileSearchCallCompleted {
452        item_id: String,
453        output_index: u32,
454        #[serde(skip_serializing_if = "Option::is_none")]
455        sequence_number: Option<u64>,
456        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
457        extra: Extra,
458    },
459    #[serde(rename = "response.web_search_call.in_progress")]
460    ResponseWebSearchCallInProgress {
461        item_id: String,
462        output_index: u32,
463        #[serde(skip_serializing_if = "Option::is_none")]
464        sequence_number: Option<u64>,
465        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
466        extra: Extra,
467    },
468    #[serde(rename = "response.web_search_call.searching")]
469    ResponseWebSearchCallSearching {
470        item_id: String,
471        output_index: u32,
472        #[serde(skip_serializing_if = "Option::is_none")]
473        sequence_number: Option<u64>,
474        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
475        extra: Extra,
476    },
477    #[serde(rename = "response.web_search_call.completed")]
478    ResponseWebSearchCallCompleted {
479        item_id: String,
480        output_index: u32,
481        #[serde(skip_serializing_if = "Option::is_none")]
482        sequence_number: Option<u64>,
483        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
484        extra: Extra,
485    },
486    #[serde(rename = "response.code_interpreter_call.in_progress")]
487    ResponseCodeInterpreterCallInProgress {
488        item_id: String,
489        output_index: u32,
490        #[serde(skip_serializing_if = "Option::is_none")]
491        sequence_number: Option<u64>,
492        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
493        extra: Extra,
494    },
495    #[serde(rename = "response.code_interpreter_call.interpreting")]
496    ResponseCodeInterpreterCallInterpreting {
497        item_id: String,
498        output_index: u32,
499        #[serde(skip_serializing_if = "Option::is_none")]
500        sequence_number: Option<u64>,
501        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
502        extra: Extra,
503    },
504    #[serde(rename = "response.code_interpreter_call.completed")]
505    ResponseCodeInterpreterCallCompleted {
506        item_id: String,
507        output_index: u32,
508        #[serde(skip_serializing_if = "Option::is_none")]
509        sequence_number: Option<u64>,
510        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
511        extra: Extra,
512    },
513    #[serde(rename = "response.code_interpreter_call_code.delta")]
514    ResponseCodeInterpreterCallCodeDelta {
515        delta: String,
516        item_id: String,
517        output_index: u32,
518        #[serde(skip_serializing_if = "Option::is_none")]
519        sequence_number: Option<u64>,
520        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
521        extra: Extra,
522    },
523    #[serde(rename = "response.code_interpreter_call_code.done")]
524    ResponseCodeInterpreterCallCodeDone {
525        code: String,
526        item_id: String,
527        output_index: u32,
528        #[serde(skip_serializing_if = "Option::is_none")]
529        sequence_number: Option<u64>,
530        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
531        extra: Extra,
532    },
533    #[serde(rename = "response.mcp_call_arguments.delta")]
534    ResponseMcpCallArgumentsDelta {
535        delta: String,
536        item_id: String,
537        output_index: u32,
538        #[serde(skip_serializing_if = "Option::is_none")]
539        sequence_number: Option<u64>,
540        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
541        extra: Extra,
542    },
543    #[serde(rename = "response.mcp_call_arguments.done")]
544    ResponseMcpCallArgumentsDone {
545        arguments: String,
546        item_id: String,
547        output_index: u32,
548        #[serde(skip_serializing_if = "Option::is_none")]
549        sequence_number: Option<u64>,
550        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
551        extra: Extra,
552    },
553    #[serde(rename = "response.mcp_call.in_progress")]
554    ResponseMcpCallInProgress {
555        item_id: String,
556        output_index: u32,
557        #[serde(skip_serializing_if = "Option::is_none")]
558        sequence_number: Option<u64>,
559        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
560        extra: Extra,
561    },
562    #[serde(rename = "response.mcp_call.completed")]
563    ResponseMcpCallCompleted {
564        item_id: String,
565        output_index: u32,
566        #[serde(skip_serializing_if = "Option::is_none")]
567        sequence_number: Option<u64>,
568        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
569        extra: Extra,
570    },
571    #[serde(rename = "response.mcp_call.failed")]
572    ResponseMcpCallFailed {
573        item_id: String,
574        output_index: u32,
575        #[serde(skip_serializing_if = "Option::is_none")]
576        sequence_number: Option<u64>,
577        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
578        extra: Extra,
579    },
580    #[serde(rename = "response.mcp_list_tools.in_progress")]
581    ResponseMcpListToolsInProgress {
582        item_id: String,
583        output_index: u32,
584        #[serde(skip_serializing_if = "Option::is_none")]
585        sequence_number: Option<u64>,
586        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
587        extra: Extra,
588    },
589    #[serde(rename = "response.mcp_list_tools.completed")]
590    ResponseMcpListToolsCompleted {
591        item_id: String,
592        output_index: u32,
593        #[serde(skip_serializing_if = "Option::is_none")]
594        sequence_number: Option<u64>,
595        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
596        extra: Extra,
597    },
598    #[serde(rename = "response.mcp_list_tools.failed")]
599    ResponseMcpListToolsFailed {
600        item_id: String,
601        output_index: u32,
602        #[serde(skip_serializing_if = "Option::is_none")]
603        sequence_number: Option<u64>,
604        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
605        extra: Extra,
606    },
607    #[serde(rename = "error")]
608    Error {
609        code: String,
610        message: String,
611        param: String,
612        #[serde(skip_serializing_if = "Option::is_none")]
613        sequence_number: Option<u64>,
614        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
615        extra: Extra,
616    },
617}
618
619impl KnownResponseStreamEvent {
620    /// Typed event type of this variant, mirroring the serde `type` tag.
621    pub fn event_type(&self) -> ResponseStreamEventTypeKnown {
622        use ResponseStreamEventTypeKnown as T;
623        match self {
624            Self::ResponseCreated { .. } => T::ResponseCreated,
625            Self::ResponseInProgress { .. } => T::ResponseInProgress,
626            Self::ResponseCompleted { .. } => T::ResponseCompleted,
627            Self::ResponseFailed { .. } => T::ResponseFailed,
628            Self::ResponseIncomplete { .. } => T::ResponseIncomplete,
629            Self::ResponseQueued { .. } => T::ResponseQueued,
630            Self::ResponseSteerAccepted { .. } => T::ResponseSteerAccepted,
631            Self::ResponseSteerPending { .. } => T::ResponseSteerPending,
632            Self::ResponseSteerFailed { .. } => T::ResponseSteerFailed,
633            Self::ResponseOutputItemAdded { .. } => T::ResponseOutputItemAdded,
634            Self::ResponseOutputItemDone { .. } => T::ResponseOutputItemDone,
635            Self::ResponseContentPartAdded { .. } => T::ResponseContentPartAdded,
636            Self::ResponseContentPartDone { .. } => T::ResponseContentPartDone,
637            Self::ResponseOutputTextDelta { .. } => T::ResponseOutputTextDelta,
638            Self::ResponseOutputTextDone { .. } => T::ResponseOutputTextDone,
639            Self::ResponseOutputTextAnnotationAdded { .. } => T::ResponseOutputTextAnnotationAdded,
640            Self::ResponseFunctionCallArgumentsDelta { .. } => {
641                T::ResponseFunctionCallArgumentsDelta
642            }
643            Self::ResponseFunctionCallArgumentsDone { .. } => T::ResponseFunctionCallArgumentsDone,
644            Self::ResponseCustomToolCallInputDelta { .. } => T::ResponseCustomToolCallInputDelta,
645            Self::ResponseCustomToolCallInputDone { .. } => T::ResponseCustomToolCallInputDone,
646            Self::ResponseRefusalDelta { .. } => T::ResponseRefusalDelta,
647            Self::ResponseRefusalDone { .. } => T::ResponseRefusalDone,
648            Self::ResponseReasoningSummaryPartAdded { .. } => T::ResponseReasoningSummaryPartAdded,
649            Self::ResponseReasoningSummaryPartDone { .. } => T::ResponseReasoningSummaryPartDone,
650            Self::ResponseReasoningSummaryTextDelta { .. } => T::ResponseReasoningSummaryTextDelta,
651            Self::ResponseReasoningSummaryTextDone { .. } => T::ResponseReasoningSummaryTextDone,
652            Self::ResponseReasoningTextDelta { .. } => T::ResponseReasoningTextDelta,
653            Self::ResponseReasoningTextDone { .. } => T::ResponseReasoningTextDone,
654            Self::ResponseAudioDelta { .. } => T::ResponseAudioDelta,
655            Self::ResponseAudioDone { .. } => T::ResponseAudioDone,
656            Self::ResponseAudioTranscriptDelta { .. } => T::ResponseAudioTranscriptDelta,
657            Self::ResponseAudioTranscriptDone { .. } => T::ResponseAudioTranscriptDone,
658            Self::ResponseImageGenerationCallCompleted { .. } => {
659                T::ResponseImageGenerationCallCompleted
660            }
661            Self::ResponseImageGenerationCallGenerating { .. } => {
662                T::ResponseImageGenerationCallGenerating
663            }
664            Self::ResponseImageGenerationCallInProgress { .. } => {
665                T::ResponseImageGenerationCallInProgress
666            }
667            Self::ResponseImageGenerationCallPartialImage { .. } => {
668                T::ResponseImageGenerationCallPartialImage
669            }
670            Self::ResponseFileSearchCallInProgress { .. } => T::ResponseFileSearchCallInProgress,
671            Self::ResponseFileSearchCallSearching { .. } => T::ResponseFileSearchCallSearching,
672            Self::ResponseFileSearchCallCompleted { .. } => T::ResponseFileSearchCallCompleted,
673            Self::ResponseWebSearchCallInProgress { .. } => T::ResponseWebSearchCallInProgress,
674            Self::ResponseWebSearchCallSearching { .. } => T::ResponseWebSearchCallSearching,
675            Self::ResponseWebSearchCallCompleted { .. } => T::ResponseWebSearchCallCompleted,
676            Self::ResponseCodeInterpreterCallInProgress { .. } => {
677                T::ResponseCodeInterpreterCallInProgress
678            }
679            Self::ResponseCodeInterpreterCallInterpreting { .. } => {
680                T::ResponseCodeInterpreterCallInterpreting
681            }
682            Self::ResponseCodeInterpreterCallCompleted { .. } => {
683                T::ResponseCodeInterpreterCallCompleted
684            }
685            Self::ResponseCodeInterpreterCallCodeDelta { .. } => {
686                T::ResponseCodeInterpreterCallCodeDelta
687            }
688            Self::ResponseCodeInterpreterCallCodeDone { .. } => {
689                T::ResponseCodeInterpreterCallCodeDone
690            }
691            Self::ResponseMcpCallArgumentsDelta { .. } => T::ResponseMcpCallArgumentsDelta,
692            Self::ResponseMcpCallArgumentsDone { .. } => T::ResponseMcpCallArgumentsDone,
693            Self::ResponseMcpCallInProgress { .. } => T::ResponseMcpCallInProgress,
694            Self::ResponseMcpCallCompleted { .. } => T::ResponseMcpCallCompleted,
695            Self::ResponseMcpCallFailed { .. } => T::ResponseMcpCallFailed,
696            Self::ResponseMcpListToolsInProgress { .. } => T::ResponseMcpListToolsInProgress,
697            Self::ResponseMcpListToolsCompleted { .. } => T::ResponseMcpListToolsCompleted,
698            Self::ResponseMcpListToolsFailed { .. } => T::ResponseMcpListToolsFailed,
699            Self::Error { .. } => T::Error,
700        }
701    }
702
703    /// SSE event name: the exact serde rename of this variant.
704    pub fn event_name(&self) -> &'static str {
705        self.event_type().as_str()
706    }
707}
708
709#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
710#[non_exhaustive]
711pub struct UnknownResponseStreamEvent {
712    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
713    pub type_: Option<ResponseStreamEventType>,
714    #[serde(skip_serializing_if = "Option::is_none")]
715    pub sequence_number: Option<u64>,
716    #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
717    pub extra: Extra,
718}
719
720#[cfg(test)]
721mod tests {
722    use super::*;
723
724    /// Regression: `event_name()` must equal the serialized `type` tag.
725    #[test]
726    fn event_name_matches_serialized_type_tag() {
727        let events: Vec<ResponseStreamEvent> = [
728            r#"{"type":"response.output_text.delta","content_index":0,"delta":"hi","item_id":"msg_0","output_index":0}"#,
729            r#"{"type":"response.output_item.done","output_index":0,"item":{"id":"fc_1","type":"function_call","call_id":"c1","name":"f","arguments":"{}"}}"#,
730            r#"{"type":"response.function_call_arguments.done","arguments":"{}","item_id":"fc_1","name":"f","output_index":0}"#,
731            r#"{"type":"response.completed","response":{"id":"r","object":"response","created_at":0,"output":[]}}"#,
732            r#"{"type":"response.some_future_event","sequence_number":1}"#,
733        ]
734        .iter()
735        .map(|raw| serde_json::from_str(raw).unwrap())
736        .collect();
737        for event in events {
738            let value = serde_json::to_value(&event).unwrap();
739            assert_eq!(
740                event.event_name(),
741                value.get("type").and_then(Value::as_str),
742                "{value}"
743            );
744        }
745    }
746}