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