1use serde::{Deserialize, Deserializer, Serialize};
2
3use crate::client::Client;
4use crate::error::Result;
5
6fn deserialize_null_as_default<'de, D, T>(deserializer: D) -> std::result::Result<T, D::Error>
8where
9 D: Deserializer<'de>,
10 T: Default + Deserialize<'de>,
11{
12 Ok(Option::<T>::deserialize(deserializer)?.unwrap_or_default())
13}
14
15#[derive(Debug, Clone, Serialize, Default)]
17pub struct ImageRequest {
18 pub model: String,
20
21 pub prompt: String,
23
24 #[serde(skip_serializing_if = "Option::is_none")]
26 pub count: Option<i32>,
27
28 #[serde(skip_serializing_if = "Option::is_none")]
30 pub size: Option<String>,
31
32 #[serde(skip_serializing_if = "Option::is_none")]
34 pub aspect_ratio: Option<String>,
35
36 #[serde(skip_serializing_if = "Option::is_none")]
38 pub quality: Option<String>,
39
40 #[serde(skip_serializing_if = "Option::is_none")]
42 pub output_format: Option<String>,
43
44 #[serde(skip_serializing_if = "Option::is_none")]
46 pub style: Option<String>,
47
48 #[serde(skip_serializing_if = "Option::is_none")]
50 pub background: Option<String>,
51
52 #[serde(skip_serializing_if = "Option::is_none")]
54 pub image_url: Option<String>,
55
56 #[serde(skip_serializing_if = "Option::is_none")]
60 pub topology: Option<String>,
61
62 #[serde(skip_serializing_if = "Option::is_none")]
64 pub target_polycount: Option<i32>,
65
66 #[serde(skip_serializing_if = "Option::is_none")]
68 pub symmetry_mode: Option<String>,
69
70 #[serde(skip_serializing_if = "Option::is_none")]
72 pub pose_mode: Option<String>,
73
74 #[serde(skip_serializing_if = "Option::is_none")]
76 pub enable_pbr: Option<bool>,
77}
78
79#[derive(Debug, Clone, Deserialize)]
81pub struct ImageResponse {
82 #[serde(default, deserialize_with = "deserialize_null_as_default")]
84 pub images: Vec<GeneratedImage>,
85
86 #[serde(default)]
88 pub model: String,
89
90 #[serde(default)]
92 pub cost_ticks: i64,
93
94 #[serde(default)]
96 pub request_id: String,
97}
98
99#[derive(Debug, Clone, Deserialize)]
101pub struct GeneratedImage {
102 pub base64: String,
104
105 pub format: String,
107
108 pub index: i32,
110}
111
112#[derive(Debug, Clone, Serialize, Default)]
114pub struct ImageEditRequest {
115 pub model: String,
117
118 pub prompt: String,
120
121 pub input_images: Vec<String>,
123
124 #[serde(skip_serializing_if = "Option::is_none")]
126 pub count: Option<i32>,
127
128 #[serde(skip_serializing_if = "Option::is_none")]
130 pub size: Option<String>,
131}
132
133pub type ImageEditResponse = ImageResponse;
135
136impl Client {
137 pub async fn generate_image(&self, req: &ImageRequest) -> Result<ImageResponse> {
139 let (mut resp, meta) = self
140 .post_json::<ImageRequest, ImageResponse>("/qai/v1/images/generate", req)
141 .await?;
142 if resp.cost_ticks == 0 {
143 resp.cost_ticks = meta.cost_ticks;
144 }
145 if resp.request_id.is_empty() {
146 resp.request_id = meta.request_id;
147 }
148 Ok(resp)
149 }
150
151 pub async fn edit_image(&self, req: &ImageEditRequest) -> Result<ImageEditResponse> {
153 let (mut resp, meta) = self
154 .post_json::<ImageEditRequest, ImageEditResponse>("/qai/v1/images/edit", req)
155 .await?;
156 if resp.cost_ticks == 0 {
157 resp.cost_ticks = meta.cost_ticks;
158 }
159 if resp.request_id.is_empty() {
160 resp.request_id = meta.request_id;
161 }
162 Ok(resp)
163 }
164}