Skip to main content

gproxy_protocol/openai/images/
request.rs

1use serde::{Deserialize, Deserializer, Serialize, de};
2
3use crate::openai::common::{
4    ImageBackground, ImageEditQuality, ImageEditSize, ImageInputFidelity, ImageModeration,
5    ImageOutputFormat, ImageQuality, ImageResponseFormat, ImageSize, ImageStyle, OpenAiModelId,
6    Rest,
7};
8
9#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
10#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
11pub struct CreateImageRequest {
12    pub prompt: String,
13    #[serde(skip_serializing_if = "Option::is_none")]
14    pub background: Option<ImageBackground>,
15    #[serde(skip_serializing_if = "Option::is_none")]
16    pub model: Option<OpenAiModelId>,
17    #[serde(skip_serializing_if = "Option::is_none")]
18    pub moderation: Option<ImageModeration>,
19    #[serde(skip_serializing_if = "Option::is_none")]
20    pub n: Option<u32>,
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub output_compression: Option<u32>,
23    #[serde(skip_serializing_if = "Option::is_none")]
24    pub output_format: Option<ImageOutputFormat>,
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub partial_images: Option<u32>,
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub quality: Option<ImageQuality>,
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub response_format: Option<ImageResponseFormat>,
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub size: Option<ImageSize>,
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pub stream: Option<bool>,
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub style: Option<ImageStyle>,
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub user: Option<String>,
39    #[serde(default, flatten)]
40    pub rest: Rest,
41}
42
43#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
44#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
45pub struct EditImageRequest {
46    #[serde(alias = "image")]
47    pub images: Vec<ImageReference>,
48    pub prompt: String,
49    #[serde(skip_serializing_if = "Option::is_none")]
50    pub background: Option<ImageBackground>,
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub input_fidelity: Option<ImageInputFidelity>,
53    #[serde(skip_serializing_if = "Option::is_none")]
54    pub mask: Option<ImageReference>,
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub model: Option<OpenAiModelId>,
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub moderation: Option<ImageModeration>,
59    #[serde(skip_serializing_if = "Option::is_none")]
60    pub n: Option<u32>,
61    #[serde(skip_serializing_if = "Option::is_none")]
62    pub output_compression: Option<u32>,
63    #[serde(skip_serializing_if = "Option::is_none")]
64    pub output_format: Option<ImageOutputFormat>,
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub partial_images: Option<u32>,
67    #[serde(skip_serializing_if = "Option::is_none")]
68    pub quality: Option<ImageEditQuality>,
69    #[serde(skip_serializing_if = "Option::is_none")]
70    pub size: Option<ImageEditSize>,
71    #[serde(skip_serializing_if = "Option::is_none")]
72    pub stream: Option<bool>,
73    #[serde(skip_serializing_if = "Option::is_none")]
74    pub user: Option<String>,
75    #[serde(default, flatten)]
76    pub rest: Rest,
77}
78
79#[derive(Debug, Clone, PartialEq, Serialize, gproxy_protocol_macros::WireBuilder)]
80#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
81pub struct ImageReference {
82    #[serde(skip_serializing_if = "Option::is_none")]
83    pub file_id: Option<String>,
84    #[serde(skip_serializing_if = "Option::is_none")]
85    pub image_url: Option<String>,
86    #[serde(default, flatten)]
87    pub rest: Rest,
88}
89
90impl<'de> Deserialize<'de> for ImageReference {
91    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
92        let value = serde_json::Value::deserialize(deserializer)?;
93        if let serde_json::Value::String(value) = value {
94            if value.trim().is_empty() {
95                return Err(de::Error::custom("image reference must not be empty"));
96            }
97            let image_url = value.starts_with("http://")
98                || value.starts_with("https://")
99                || value.starts_with("data:");
100            return Ok(Self {
101                file_id: (!image_url).then_some(value.clone()),
102                image_url: image_url.then_some(value),
103                rest: Default::default(),
104            });
105        }
106        #[derive(Deserialize)]
107        struct Object {
108            file_id: Option<String>,
109            image_url: Option<String>,
110            #[serde(default, flatten)]
111            rest: Rest,
112        }
113        let object: Object = serde_json::from_value(value).map_err(de::Error::custom)?;
114        if object.file_id.is_some() == object.image_url.is_some() {
115            return Err(de::Error::custom(
116                "image reference requires exactly one of file_id or image_url",
117            ));
118        }
119        Ok(Self {
120            file_id: object.file_id,
121            image_url: object.image_url,
122            rest: object.rest,
123        })
124    }
125}