Skip to main content

gproxy_protocol/protocol/openai/generate_content/response_items/
content.rs

1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4
5use super::super::super::common::*;
6use super::ResponseAnnotation;
7
8#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
9#[serde(untagged)]
10#[non_exhaustive]
11pub enum ResponseOutput {
12    Text(String),
13    Parts(Vec<ResponseToolOutputContentPart>),
14}
15
16#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
17#[serde(untagged)]
18#[non_exhaustive]
19pub enum ResponseEasyInputContent {
20    Text(String),
21    Parts(Vec<ResponseInputContentPart>),
22    /// Replayed assistant history. The upstream spec describes easy-input content as
23    /// "Can also contain previous assistant responses"; clients like Codex CLI replay
24    /// assistant turns as `output_text` / `refusal` parts without `id` / `status`, which
25    /// routes them here instead of `ResponseOutputMessageItem`.
26    ///
27    /// Must stay declared after [`Self::Parts`]: the untagged dispatch relies on the
28    /// `input_*` and `output_text` / `refusal` tag sets being disjoint, so ordinary
29    /// input bodies keep matching `Parts` first.
30    OutputParts(Vec<ResponseMessageOutputContentPart>),
31}
32
33#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
34#[serde(tag = "type")]
35#[non_exhaustive]
36pub enum ResponseInputContentPart {
37    #[serde(rename = "input_text")]
38    InputText {
39        text: String,
40        #[serde(skip_serializing_if = "Option::is_none")]
41        prompt_cache_breakpoint: Option<PromptCacheBreakpoint>,
42        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
43        extra: Extra,
44    },
45    #[serde(rename = "input_image")]
46    InputImage {
47        #[serde(skip_serializing_if = "Option::is_none")]
48        detail: Option<DetailLevel>,
49        #[serde(skip_serializing_if = "Option::is_none")]
50        file_id: Option<String>,
51        #[serde(skip_serializing_if = "Option::is_none")]
52        image_url: Option<String>,
53        #[serde(skip_serializing_if = "Option::is_none")]
54        prompt_cache_breakpoint: Option<PromptCacheBreakpoint>,
55        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
56        extra: Extra,
57    },
58    #[serde(rename = "input_file")]
59    InputFile {
60        #[serde(skip_serializing_if = "Option::is_none")]
61        detail: Option<InputFileDetailLevel>,
62        #[serde(skip_serializing_if = "Option::is_none")]
63        file_data: Option<String>,
64        #[serde(skip_serializing_if = "Option::is_none")]
65        file_id: Option<String>,
66        #[serde(skip_serializing_if = "Option::is_none")]
67        file_url: Option<String>,
68        #[serde(skip_serializing_if = "Option::is_none")]
69        filename: Option<String>,
70        #[serde(skip_serializing_if = "Option::is_none")]
71        prompt_cache_breakpoint: Option<PromptCacheBreakpoint>,
72        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
73        extra: Extra,
74    },
75    #[serde(rename = "input_audio")]
76    InputAudio {
77        input_audio: InputAudioContent,
78        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
79        extra: Extra,
80    },
81}
82
83#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
84#[serde(tag = "type")]
85#[non_exhaustive]
86pub enum ResponseToolOutputContentPart {
87    #[serde(rename = "input_text")]
88    InputText {
89        text: String,
90        #[serde(skip_serializing_if = "Option::is_none")]
91        prompt_cache_breakpoint: Option<PromptCacheBreakpoint>,
92        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
93        extra: Extra,
94    },
95    #[serde(rename = "input_image")]
96    InputImage {
97        #[serde(skip_serializing_if = "Option::is_none")]
98        detail: Option<DetailLevel>,
99        #[serde(skip_serializing_if = "Option::is_none")]
100        file_id: Option<String>,
101        #[serde(skip_serializing_if = "Option::is_none")]
102        image_url: Option<String>,
103        #[serde(skip_serializing_if = "Option::is_none")]
104        prompt_cache_breakpoint: Option<PromptCacheBreakpoint>,
105        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
106        extra: Extra,
107    },
108    #[serde(rename = "input_file")]
109    InputFile {
110        #[serde(skip_serializing_if = "Option::is_none")]
111        detail: Option<InputFileDetailLevel>,
112        #[serde(skip_serializing_if = "Option::is_none")]
113        file_data: Option<String>,
114        #[serde(skip_serializing_if = "Option::is_none")]
115        file_id: Option<String>,
116        #[serde(skip_serializing_if = "Option::is_none")]
117        file_url: Option<String>,
118        #[serde(skip_serializing_if = "Option::is_none")]
119        filename: Option<String>,
120        #[serde(skip_serializing_if = "Option::is_none")]
121        prompt_cache_breakpoint: Option<PromptCacheBreakpoint>,
122        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
123        extra: Extra,
124    },
125}
126
127#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
128#[serde(tag = "type")]
129#[non_exhaustive]
130pub enum ResponseMessageOutputContentPart {
131    #[serde(rename = "output_text")]
132    OutputText {
133        #[serde(default, skip_serializing_if = "Vec::is_empty")]
134        annotations: Vec<ResponseAnnotation>,
135        #[serde(skip_serializing_if = "Option::is_none")]
136        logprobs: Option<Vec<TokenLogprob>>,
137        text: String,
138        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
139        extra: Extra,
140    },
141    #[serde(rename = "refusal")]
142    Refusal {
143        refusal: String,
144        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
145        extra: Extra,
146    },
147}
148
149#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
150#[serde(tag = "type")]
151#[non_exhaustive]
152pub enum ResponseOutputContentPart {
153    #[serde(rename = "output_text")]
154    OutputText {
155        #[serde(default, skip_serializing_if = "Vec::is_empty")]
156        annotations: Vec<ResponseAnnotation>,
157        #[serde(skip_serializing_if = "Option::is_none")]
158        logprobs: Option<Vec<TokenLogprob>>,
159        text: String,
160        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
161        extra: Extra,
162    },
163    #[serde(rename = "refusal")]
164    Refusal {
165        refusal: String,
166        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
167        extra: Extra,
168    },
169    #[serde(rename = "reasoning_text")]
170    ReasoningText {
171        text: String,
172        #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
173        extra: Extra,
174    },
175}
176
177pub type ResponseContentPart = ResponseOutputContentPart;
178
179#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
180#[non_exhaustive]
181pub struct InputAudioContent {
182    pub data: String,
183    pub format: InputAudioFormat,
184    #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
185    pub extra: Extra,
186}