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