1use serde::{Deserialize, Serialize};
2use serde_json::{Map, Value};
3use std::borrow::Cow;
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
6#[serde(untagged)]
7pub enum ChatMessageContent {
8 Text(String),
9 Parts(Vec<ChatContentPart>),
10}
11
12impl ChatMessageContent {
13 pub fn text(text: impl Into<String>) -> Self {
14 Self::Text(text.into())
15 }
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
19#[serde(untagged)]
20pub enum ChatContentPart {
21 Text(ChatContentPartText),
22 ImageUrl(ChatContentPartImageUrl),
23 InputAudio(ChatContentPartInputAudio),
24 File(ChatContentPartFile),
25 Other(Value),
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct ChatContentPartText {
30 #[serde(rename = "type")]
31 pub kind: Cow<'static, str>,
32 pub text: String,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct ChatContentPartImageUrl {
37 #[serde(rename = "type")]
38 pub kind: Cow<'static, str>,
39 pub image_url: ChatImageUrl,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
43#[serde(untagged)]
44pub enum ChatImageUrl {
45 Url(String),
46 Object(ChatImageUrlObject),
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct ChatImageUrlObject {
51 pub url: String,
52 pub detail: Option<String>,
53 pub format: Option<String>,
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct ChatContentPartInputAudio {
58 #[serde(rename = "type")]
59 pub kind: Cow<'static, str>,
60 pub input_audio: ChatInputAudio,
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct ChatInputAudio {
65 pub data: String,
66 pub format: String,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct ChatContentPartFile {
71 #[serde(rename = "type")]
72 pub kind: Cow<'static, str>,
73 pub file: ChatFile,
74}
75
76#[derive(Debug, Clone, Serialize, Deserialize)]
77pub struct ChatFile {
78 pub file_id: Option<String>,
79 pub file_data: Option<String>,
80 pub format: Option<String>,
81 pub detail: Option<String>,
82 pub video_metadata: Option<Value>,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct ChatMessage {
87 pub role: String,
88 pub content: ChatMessageContent,
89 #[serde(skip_serializing_if = "Option::is_none")]
90 pub name: Option<String>,
91 #[serde(skip_serializing_if = "Option::is_none")]
92 pub tool_call_id: Option<String>,
93 #[serde(skip_serializing_if = "Option::is_none")]
94 pub tool_calls: Option<Value>,
95 #[serde(skip_serializing_if = "Option::is_none")]
96 pub function_call: Option<Value>,
97 #[serde(skip_serializing_if = "Option::is_none")]
98 pub provider_specific_fields: Option<Value>,
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct ChatRequest {
103 pub model: String,
104 pub messages: Vec<ChatMessage>,
105 pub temperature: Option<f32>,
106 pub max_tokens: Option<u32>,
107 pub response_format: Option<Value>,
108 pub max_completion_tokens: Option<u32>,
109 pub tools: Option<Value>,
110 pub tool_choice: Option<Value>,
111 pub parallel_tool_calls: Option<bool>,
112 pub stop: Option<Value>,
113 pub top_p: Option<f32>,
114 pub presence_penalty: Option<f32>,
115 pub frequency_penalty: Option<f32>,
116 pub seed: Option<u64>,
117 pub user: Option<String>,
118 pub metadata: Option<Value>,
119 pub reasoning_effort: Option<Value>,
120 pub thinking: Option<Value>,
121 pub extra_body: Option<Map<String, Value>>,
126}
127
128impl ChatRequest {
129 pub fn new(model: impl Into<String>) -> Self {
130 Self {
131 model: model.into(),
132 messages: Vec::new(),
133 temperature: None,
134 max_tokens: None,
135 response_format: None,
136 max_completion_tokens: None,
137 tools: None,
138 tool_choice: None,
139 parallel_tool_calls: None,
140 stop: None,
141 top_p: None,
142 presence_penalty: None,
143 frequency_penalty: None,
144 seed: None,
145 user: None,
146 metadata: None,
147 reasoning_effort: None,
148 thinking: None,
149 extra_body: None,
150 }
151 }
152
153 pub fn message(mut self, role: impl Into<String>, content: impl Into<String>) -> Self {
154 self.messages.push(ChatMessage {
155 role: role.into(),
156 content: ChatMessageContent::Text(content.into()),
157 name: None,
158 tool_call_id: None,
159 tool_calls: None,
160 function_call: None,
161 provider_specific_fields: None,
162 });
163 self
164 }
165
166 pub fn message_with_content(
167 mut self,
168 role: impl Into<String>,
169 content: ChatMessageContent,
170 ) -> Self {
171 self.messages.push(ChatMessage {
172 role: role.into(),
173 content,
174 name: None,
175 tool_call_id: None,
176 tool_calls: None,
177 function_call: None,
178 provider_specific_fields: None,
179 });
180 self
181 }
182
183 pub fn temperature(mut self, temperature: f32) -> Self {
184 self.temperature = Some(temperature);
185 self
186 }
187
188 pub fn max_tokens(mut self, max_tokens: u32) -> Self {
189 self.max_tokens = Some(max_tokens);
190 self
191 }
192
193 pub fn response_format(mut self, format: Value) -> Self {
194 self.response_format = Some(format);
195 self
196 }
197
198 pub fn extra_body(mut self, body: Map<String, Value>) -> Self {
199 self.extra_body = Some(body);
200 self
201 }
202}
203
204#[derive(Debug, Clone, Serialize, Deserialize)]
205pub struct ChatResponse {
206 pub content: String,
207 pub reasoning: Option<Reasoning>,
209 pub usage: Usage,
210 pub response_id: Option<String>,
211 pub header_cost: Option<f64>,
212 pub raw: Option<Value>,
213}
214
215#[derive(Debug, Clone, Serialize, Deserialize)]
220pub struct Reasoning {
221 pub details: Vec<Value>,
222}
223
224impl Reasoning {
225 pub fn from_text(text: impl Into<String>) -> Option<Self> {
227 let text = text.into();
228 if text.trim().is_empty() {
229 return None;
230 }
231 Some(Self {
232 details: vec![serde_json::json!({
233 "type": "reasoning.text",
234 "text": text,
235 })],
236 })
237 }
238
239 pub fn from_details(details: Vec<Value>) -> Option<Self> {
240 (!details.is_empty()).then_some(Self { details })
241 }
242
243 pub fn text(&self) -> Option<String> {
244 let mut out = String::new();
245 for detail in &self.details {
246 collect_reasoning_text(detail, &mut out);
247 }
248 (!out.trim().is_empty()).then_some(out)
249 }
250}
251
252fn collect_reasoning_text(value: &Value, out: &mut String) {
253 match value {
254 Value::String(text) => out.push_str(text),
255 Value::Array(items) => {
256 for item in items {
257 collect_reasoning_text(item, out);
258 }
259 }
260 Value::Object(map) => {
261 if let Some(text) = map.get("text") {
262 collect_reasoning_text(text, out);
263 } else if let Some(summary) = map.get("summary") {
264 collect_reasoning_text(summary, out);
265 }
266 }
267 _ => {}
268 }
269}
270
271#[derive(Debug, Clone, Serialize, Deserialize, Default)]
276pub struct Usage {
277 pub prompt_tokens: Option<u64>,
278 pub completion_tokens: Option<u64>,
279 pub thoughts_tokens: Option<u64>,
280 pub total_tokens: Option<u64>,
281 pub cost_usd: Option<f64>,
282}
283
284#[derive(Debug, Clone, Serialize, Deserialize)]
285pub struct EmbeddingRequest {
286 pub model: String,
287 pub input: Value,
288 pub extra_body: Option<Map<String, Value>>,
290}
291
292#[derive(Debug, Clone, Serialize, Deserialize)]
293pub struct EmbeddingResponse {
294 pub vectors: Vec<Vec<f32>>,
295 pub usage: Usage,
296 pub raw: Option<Value>,
297}
298
299#[derive(Debug, Clone, Serialize, Deserialize)]
300pub struct ImageRequest {
301 pub model: String,
302 pub prompt: String,
303 pub n: Option<u32>,
304 pub size: Option<String>,
305 pub quality: Option<String>,
306 pub background: Option<String>,
307 pub extra_body: Option<Map<String, Value>>,
309}
310
311#[derive(Debug, Clone, Serialize, Deserialize)]
312pub struct ImageData {
313 pub b64_json: Option<String>,
314 pub url: Option<String>,
315 pub revised_prompt: Option<String>,
316 pub mime_type: Option<String>,
317}
318
319#[derive(Debug, Clone, Serialize, Deserialize)]
320pub struct ImageEditRequest {
321 pub model: String,
322 pub prompt: String,
323 pub images: Vec<ImageInputData>,
324 pub n: Option<u32>,
325 pub size: Option<String>,
326}
327
328#[derive(Debug, Clone, Serialize, Deserialize)]
329pub struct ImageInputData {
330 pub b64_json: Option<String>,
331 pub url: Option<String>,
332 pub mime_type: Option<String>,
333}
334
335#[derive(Debug, Clone, Serialize, Deserialize)]
336pub struct ImageResponse {
337 pub images: Vec<ImageData>,
338 pub usage: Usage,
339 pub raw: Option<Value>,
340}
341
342#[derive(Debug, Clone, Serialize, Deserialize)]
343pub struct VideoRequest {
344 pub model: String,
345 pub prompt: String,
346 pub seconds: Option<u32>,
347 pub size: Option<String>,
348}
349
350#[derive(Debug, Clone, Serialize, Deserialize)]
351pub struct VideoResponse {
352 pub video_url: Option<String>,
353 pub raw: Option<Value>,
354}