gproxy_protocol/protocol/openai/images/
requests.rs1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize, de, de::DeserializeOwned};
4use serde_json::{Map, Value};
5
6use super::super::common::*;
7
8#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
9pub struct ImageGenerationRequest {
10 pub prompt: String,
11 #[serde(skip_serializing_if = "Option::is_none")]
12 pub background: Option<ImageBackground>,
13 #[serde(skip_serializing_if = "Option::is_none")]
14 pub model: Option<OpenAiModelId>,
15 #[serde(skip_serializing_if = "Option::is_none")]
16 pub moderation: Option<ImageModeration>,
17 #[serde(skip_serializing_if = "Option::is_none")]
18 pub n: Option<u32>,
19 #[serde(skip_serializing_if = "Option::is_none")]
20 pub output_compression: Option<u32>,
21 #[serde(skip_serializing_if = "Option::is_none")]
22 pub output_format: Option<ImageOutputFormat>,
23 #[serde(skip_serializing_if = "Option::is_none")]
24 pub partial_images: Option<u32>,
25 #[serde(skip_serializing_if = "Option::is_none")]
26 pub quality: Option<ImageQuality>,
27 #[serde(skip_serializing_if = "Option::is_none")]
28 pub response_format: Option<ImageResponseFormat>,
29 #[serde(skip_serializing_if = "Option::is_none")]
30 pub size: Option<ImageSize>,
31 #[serde(skip_serializing_if = "Option::is_none")]
32 pub stream: Option<bool>,
33 #[serde(skip_serializing_if = "Option::is_none")]
34 pub style: Option<ImageStyle>,
35 #[serde(skip_serializing_if = "Option::is_none")]
36 pub user: Option<String>,
37 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
38 pub extra: Extra,
39}
40
41#[derive(Debug, Clone, PartialEq, Serialize)]
42pub struct ImageEditRequest {
43 pub images: Vec<ImageReference>,
44 pub prompt: String,
45 #[serde(skip_serializing_if = "Option::is_none")]
46 pub background: Option<ImageBackground>,
47 #[serde(skip_serializing_if = "Option::is_none")]
48 pub input_fidelity: Option<ImageInputFidelity>,
49 #[serde(skip_serializing_if = "Option::is_none")]
50 pub mask: Option<ImageReference>,
51 #[serde(skip_serializing_if = "Option::is_none")]
52 pub model: Option<OpenAiModelId>,
53 #[serde(skip_serializing_if = "Option::is_none")]
54 pub moderation: Option<ImageModeration>,
55 #[serde(skip_serializing_if = "Option::is_none")]
56 pub n: Option<u32>,
57 #[serde(skip_serializing_if = "Option::is_none")]
58 pub output_compression: Option<u32>,
59 #[serde(skip_serializing_if = "Option::is_none")]
60 pub output_format: Option<ImageOutputFormat>,
61 #[serde(skip_serializing_if = "Option::is_none")]
62 pub partial_images: Option<u32>,
63 #[serde(skip_serializing_if = "Option::is_none")]
64 pub quality: Option<ImageEditQuality>,
65 #[serde(skip_serializing_if = "Option::is_none")]
66 pub size: Option<ImageEditSize>,
67 #[serde(skip_serializing_if = "Option::is_none")]
68 pub stream: Option<bool>,
69 #[serde(skip_serializing_if = "Option::is_none")]
70 pub user: Option<String>,
71 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
72 pub extra: Extra,
73}
74
75impl<'de> Deserialize<'de> for ImageEditRequest {
76 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
77 where
78 D: serde::Deserializer<'de>,
79 {
80 let value = Value::deserialize(deserializer)?;
81 let Value::Object(mut map) = value else {
82 return Err(de::Error::custom("image edit request must be an object"));
83 };
84
85 Ok(Self {
86 images: take_image_references(&mut map).map_err(de::Error::custom)?,
87 prompt: take_required(&mut map, "prompt").map_err(de::Error::custom)?,
88 background: take_optional(&mut map, "background").map_err(de::Error::custom)?,
89 input_fidelity: take_optional(&mut map, "input_fidelity").map_err(de::Error::custom)?,
90 mask: take_optional_image_reference(&mut map, "mask").map_err(de::Error::custom)?,
91 model: take_optional(&mut map, "model").map_err(de::Error::custom)?,
92 moderation: take_optional(&mut map, "moderation").map_err(de::Error::custom)?,
93 n: take_optional_u32(&mut map, "n").map_err(de::Error::custom)?,
94 output_compression: take_optional_u32(&mut map, "output_compression")
95 .map_err(de::Error::custom)?,
96 output_format: take_optional(&mut map, "output_format").map_err(de::Error::custom)?,
97 partial_images: take_optional_u32(&mut map, "partial_images")
98 .map_err(de::Error::custom)?,
99 quality: take_optional(&mut map, "quality").map_err(de::Error::custom)?,
100 size: take_optional(&mut map, "size").map_err(de::Error::custom)?,
101 stream: take_optional_bool(&mut map, "stream").map_err(de::Error::custom)?,
102 user: take_optional(&mut map, "user").map_err(de::Error::custom)?,
103 extra: map.into_iter().collect(),
104 })
105 }
106}
107
108#[derive(Debug, Clone, PartialEq, Serialize)]
109pub struct ImageReference {
110 #[serde(skip_serializing_if = "Option::is_none")]
111 pub file_id: Option<String>,
112 #[serde(skip_serializing_if = "Option::is_none")]
113 pub image_url: Option<String>,
114 #[serde(default, flatten, skip_serializing_if = "BTreeMap::is_empty")]
115 pub extra: Extra,
116}
117
118impl<'de> Deserialize<'de> for ImageReference {
119 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
120 where
121 D: serde::Deserializer<'de>,
122 {
123 let value = Value::deserialize(deserializer)?;
124 if let Value::String(value) = value {
125 return string_image_reference(value).map_err(de::Error::custom);
126 }
127
128 #[derive(Deserialize)]
129 struct RawImageReference {
130 file_id: Option<String>,
131 image_url: Option<String>,
132 #[serde(default, flatten)]
133 extra: Extra,
134 }
135
136 let raw: RawImageReference = serde_json::from_value(value).map_err(de::Error::custom)?;
137 match (raw.file_id.is_some(), raw.image_url.is_some()) {
138 (true, false) | (false, true) => Ok(Self {
139 file_id: raw.file_id,
140 image_url: raw.image_url,
141 extra: raw.extra,
142 }),
143 (true, true) => Err(de::Error::custom(
144 "image reference must contain exactly one of file_id or image_url",
145 )),
146 (false, false) => Err(de::Error::custom(
147 "image reference must contain file_id or image_url",
148 )),
149 }
150 }
151}
152
153fn take_required<T: DeserializeOwned>(
154 map: &mut Map<String, Value>,
155 key: &str,
156) -> Result<T, String> {
157 let Some(value) = map.remove(key) else {
158 return Err(format!("missing required field `{key}`"));
159 };
160 serde_json::from_value(value).map_err(|e| format!("{key}: {e}"))
161}
162
163fn take_optional<T: DeserializeOwned>(
164 map: &mut Map<String, Value>,
165 key: &str,
166) -> Result<Option<T>, String> {
167 match map.remove(key) {
168 Some(Value::Null) | None => Ok(None),
169 Some(value) => serde_json::from_value(value)
170 .map(Some)
171 .map_err(|e| format!("{key}: {e}")),
172 }
173}
174
175fn take_optional_u32(map: &mut Map<String, Value>, key: &str) -> Result<Option<u32>, String> {
176 match map.remove(key) {
177 Some(Value::Null) | None => Ok(None),
178 Some(Value::String(value)) => value
179 .parse::<u32>()
180 .map(Some)
181 .map_err(|e| format!("{key}: {e}")),
182 Some(value) => serde_json::from_value(value)
183 .map(Some)
184 .map_err(|e| format!("{key}: {e}")),
185 }
186}
187
188fn take_optional_bool(map: &mut Map<String, Value>, key: &str) -> Result<Option<bool>, String> {
189 match map.remove(key) {
190 Some(Value::Null) | None => Ok(None),
191 Some(Value::String(value)) => value
192 .parse::<bool>()
193 .map(Some)
194 .map_err(|e| format!("{key}: {e}")),
195 Some(value) => serde_json::from_value(value)
196 .map(Some)
197 .map_err(|e| format!("{key}: {e}")),
198 }
199}
200
201fn take_image_references(map: &mut Map<String, Value>) -> Result<Vec<ImageReference>, String> {
202 let mut images = Vec::new();
203 if let Some(value) = map.remove("image") {
204 images.extend(image_references_from_value(value)?);
205 }
206 if let Some(value) = map.remove("images") {
207 images.extend(image_references_from_value(value)?);
208 }
209 if images.is_empty() {
210 return Err("missing required field `images`".to_owned());
211 }
212 Ok(images)
213}
214
215fn take_optional_image_reference(
216 map: &mut Map<String, Value>,
217 key: &str,
218) -> Result<Option<ImageReference>, String> {
219 match map.remove(key) {
220 Some(Value::Null) | None => Ok(None),
221 Some(value) => serde_json::from_value(value)
222 .map(Some)
223 .map_err(|e| format!("{key}: {e}")),
224 }
225}
226
227fn image_references_from_value(value: Value) -> Result<Vec<ImageReference>, String> {
228 match value {
229 Value::Array(values) => values
230 .into_iter()
231 .map(|value| serde_json::from_value(value).map_err(|e| e.to_string()))
232 .collect(),
233 value => serde_json::from_value(value)
234 .map(|reference| vec![reference])
235 .map_err(|e| e.to_string()),
236 }
237}
238
239fn string_image_reference(value: String) -> Result<ImageReference, String> {
240 if value.trim().is_empty() {
241 return Err("image reference string must not be empty".to_owned());
242 }
243 if value.starts_with("http://") || value.starts_with("https://") || value.starts_with("data:") {
244 Ok(ImageReference {
245 file_id: None,
246 image_url: Some(value),
247 extra: Default::default(),
248 })
249 } else {
250 Ok(ImageReference {
251 file_id: Some(value),
252 image_url: None,
253 extra: Default::default(),
254 })
255 }
256}