1use serde::{de, Deserialize, Serialize};
7use std::collections::HashMap;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct ChatCompletionsRequest {
12 pub model: String,
14
15 pub messages: Vec<ChatMessage>,
17
18 #[serde(skip_serializing_if = "Option::is_none")]
20 pub max_tokens: Option<u32>,
21
22 #[serde(skip_serializing_if = "Option::is_none")]
25 pub max_completion_tokens: Option<u32>,
26
27 #[serde(skip_serializing_if = "Option::is_none")]
29 pub temperature: Option<f32>,
30
31 #[serde(skip_serializing_if = "Option::is_none")]
33 pub top_p: Option<f32>,
34
35 #[serde(skip_serializing_if = "Option::is_none")]
37 pub n: Option<u32>,
38
39 #[serde(skip_serializing_if = "Option::is_none")]
41 pub stream: Option<bool>,
42
43 #[serde(skip_serializing_if = "Option::is_none")]
47 pub ignore_eos: Option<bool>,
48
49 #[serde(default, deserialize_with = "deserialize_stop_sequences")]
51 #[serde(skip_serializing_if = "Option::is_none")]
52 pub stop: Option<Vec<String>>,
53
54 #[serde(skip_serializing_if = "Option::is_none")]
56 pub presence_penalty: Option<f32>,
57
58 #[serde(skip_serializing_if = "Option::is_none")]
60 pub frequency_penalty: Option<f32>,
61
62 #[serde(skip_serializing_if = "Option::is_none")]
64 pub logit_bias: Option<HashMap<String, f32>>,
65
66 #[serde(skip_serializing_if = "Option::is_none")]
69 pub logprobs: Option<bool>,
70
71 #[serde(skip_serializing_if = "Option::is_none")]
73 pub top_logprobs: Option<u32>,
74
75 #[serde(skip_serializing_if = "Option::is_none")]
77 pub user: Option<String>,
78
79 #[serde(skip_serializing_if = "Option::is_none")]
81 pub seed: Option<u64>,
82
83 #[serde(skip_serializing_if = "Option::is_none")]
85 pub response_format: Option<OpenAiResponseFormat>,
86
87 #[serde(skip_serializing_if = "Option::is_none")]
91 pub tools: Option<Vec<ChatTool>>,
92
93 #[serde(skip_serializing_if = "Option::is_none")]
95 pub tool_choice: Option<ToolChoice>,
96
97 #[serde(skip_serializing_if = "Option::is_none")]
99 pub stream_options: Option<StreamOptions>,
100
101 #[serde(skip_serializing_if = "Option::is_none")]
103 pub functions: Option<Vec<ChatFunction>>,
104
105 #[serde(skip_serializing_if = "Option::is_none")]
107 pub function_call: Option<FunctionCallChoice>,
108}
109
110#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct StreamOptions {
113 #[serde(default, skip_serializing_if = "Option::is_none")]
114 pub include_usage: Option<bool>,
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct ChatTool {
120 #[serde(rename = "type")]
121 pub tool_type: String,
122 pub function: ChatFunction,
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct ChatFunction {
128 pub name: String,
129 #[serde(default, skip_serializing_if = "Option::is_none")]
130 pub description: Option<String>,
131 #[serde(default, skip_serializing_if = "Option::is_none")]
132 pub parameters: Option<serde_json::Value>,
133 #[serde(default, skip_serializing_if = "Option::is_none")]
134 pub strict: Option<bool>,
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize)]
140#[serde(untagged)]
141pub enum ToolChoice {
142 Mode(String),
143 Function {
144 #[serde(rename = "type")]
145 tool_type: String,
146 function: ToolChoiceFunction,
147 },
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize)]
151pub struct ToolChoiceFunction {
152 pub name: String,
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize)]
157#[serde(untagged)]
158pub enum FunctionCallChoice {
159 Mode(String),
160 Function { name: String },
161}
162
163#[derive(Debug, Clone, Serialize, Deserialize)]
171pub struct OpenAiResponseFormat {
172 #[serde(rename = "type")]
173 pub format_type: String,
174 #[serde(default, skip_serializing_if = "Option::is_none")]
176 pub json_schema: Option<OpenAiJsonSchema>,
177}
178
179#[derive(Debug, Clone, Serialize, Deserialize)]
180pub struct OpenAiJsonSchema {
181 #[serde(default, skip_serializing_if = "Option::is_none")]
183 pub name: Option<String>,
184 #[serde(default, skip_serializing_if = "Option::is_none")]
190 pub schema: Option<serde_json::Value>,
191 #[serde(default, skip_serializing_if = "Option::is_none")]
195 pub strict: Option<bool>,
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct ChatMessage {
201 pub role: MessageRole,
203
204 #[serde(default)]
210 #[serde(deserialize_with = "deserialize_message_content")]
211 pub content: String,
212
213 #[serde(default, skip_serializing_if = "Option::is_none")]
217 pub reasoning: Option<String>,
218
219 #[serde(skip_serializing_if = "Option::is_none")]
221 pub name: Option<String>,
222
223 #[serde(skip_serializing_if = "Option::is_none")]
225 pub tool_calls: Option<Vec<ChatToolCall>>,
226
227 #[serde(skip_serializing_if = "Option::is_none")]
229 pub tool_call_id: Option<String>,
230
231 #[serde(skip_serializing_if = "Option::is_none")]
233 pub function_call: Option<ChatFunctionCall>,
234}
235
236#[derive(Debug, Clone, Serialize, Deserialize)]
238pub struct ChatToolCall {
239 #[serde(skip_serializing_if = "Option::is_none")]
240 pub index: Option<u32>,
241 pub id: String,
242 #[serde(rename = "type")]
243 pub tool_type: String,
244 pub function: ChatFunctionCall,
245}
246
247#[derive(Debug, Clone, Serialize, Deserialize)]
249pub struct ChatFunctionCall {
250 pub name: String,
251 pub arguments: String,
252}
253
254fn deserialize_message_content<'de, D>(deserializer: D) -> Result<String, D::Error>
260where
261 D: serde::Deserializer<'de>,
262{
263 let value = serde_json::Value::deserialize(deserializer)?;
264 match value {
265 serde_json::Value::Null => Ok(String::new()),
266 serde_json::Value::String(s) => Ok(s),
267 serde_json::Value::Array(parts) => {
268 let mut text_parts = Vec::with_capacity(parts.len());
269 for part in parts {
270 let ty = part
271 .get("type")
272 .and_then(|v| v.as_str())
273 .ok_or_else(|| de::Error::custom("message content part missing type"))?;
274 if ty != "text" {
275 return Err(de::Error::custom(format!(
276 "unsupported message content part type `{ty}`"
277 )));
278 }
279 if let Some(text) = part.get("text").and_then(|v| v.as_str()) {
280 text_parts.push(text.to_string());
281 }
282 }
283 Ok(text_parts.join("\n"))
284 }
285 _ => Err(de::Error::custom(
286 "message content must be a string, null, or an array of text parts",
287 )),
288 }
289}
290
291fn deserialize_stop_sequences<'de, D>(deserializer: D) -> Result<Option<Vec<String>>, D::Error>
292where
293 D: serde::Deserializer<'de>,
294{
295 let value = Option::<serde_json::Value>::deserialize(deserializer)?;
296 match value {
297 None | Some(serde_json::Value::Null) => Ok(None),
298 Some(serde_json::Value::String(stop)) => Ok(Some(vec![stop])),
299 Some(serde_json::Value::Array(values)) => {
300 let mut stops = Vec::with_capacity(values.len());
301 for value in values {
302 match value {
303 serde_json::Value::String(stop) => stops.push(stop),
304 _ => {
305 return Err(de::Error::custom(
306 "stop must be a string or an array of strings",
307 ))
308 }
309 }
310 }
311 Ok(Some(stops))
312 }
313 _ => Err(de::Error::custom(
314 "stop must be a string or an array of strings",
315 )),
316 }
317}
318
319#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
321#[serde(rename_all = "lowercase")]
322pub enum MessageRole {
323 System,
324 User,
325 Assistant,
326 Function,
327 Tool,
328}
329
330#[derive(Debug, Clone, Serialize, Deserialize)]
332pub struct ChatCompletionsResponse {
333 pub id: String,
335
336 pub object: String,
338
339 pub created: u64,
341
342 pub model: String,
344
345 pub choices: Vec<ChatChoice>,
347
348 #[serde(skip_serializing_if = "Option::is_none")]
350 pub usage: Option<Usage>,
351}
352
353#[derive(Debug, Clone, Serialize, Deserialize)]
355pub struct ChatChoice {
356 pub index: u32,
358
359 #[serde(skip_serializing_if = "Option::is_none")]
361 pub message: Option<ChatMessage>,
362
363 #[serde(skip_serializing_if = "Option::is_none")]
365 pub delta: Option<ChatMessage>,
366
367 #[serde(skip_serializing_if = "Option::is_none")]
369 pub finish_reason: Option<String>,
370}
371
372#[derive(Debug, Clone, Serialize, Deserialize)]
374pub struct CompletionsRequest {
375 pub model: String,
377
378 #[serde(default)]
382 pub prompt: CompletionPrompt,
383
384 #[serde(skip_serializing_if = "Option::is_none")]
386 pub max_tokens: Option<u32>,
387
388 #[serde(skip_serializing_if = "Option::is_none")]
390 pub temperature: Option<f32>,
391
392 #[serde(skip_serializing_if = "Option::is_none")]
394 pub top_p: Option<f32>,
395
396 #[serde(skip_serializing_if = "Option::is_none")]
399 pub n: Option<u32>,
400
401 #[serde(skip_serializing_if = "Option::is_none")]
403 pub stream: Option<bool>,
404
405 #[serde(default, deserialize_with = "deserialize_stop_sequences")]
407 #[serde(skip_serializing_if = "Option::is_none")]
408 pub stop: Option<Vec<String>>,
409
410 #[serde(skip_serializing_if = "Option::is_none")]
413 pub logprobs: Option<u32>,
414
415 #[serde(skip_serializing_if = "Option::is_none")]
417 pub logit_bias: Option<HashMap<String, f32>>,
418}
419
420#[derive(Debug, Clone, Serialize, Deserialize)]
423#[serde(untagged)]
424pub enum CompletionPrompt {
425 Text(String),
426 Unsupported(serde_json::Value),
427}
428
429impl Default for CompletionPrompt {
430 fn default() -> Self {
431 Self::Unsupported(serde_json::Value::Null)
432 }
433}
434
435impl CompletionPrompt {
436 pub fn as_text(&self) -> Option<&str> {
437 match self {
438 Self::Text(text) => Some(text),
439 Self::Unsupported(_) => None,
440 }
441 }
442}
443
444#[derive(Debug, Clone, Serialize, Deserialize)]
446pub struct CompletionsResponse {
447 pub id: String,
448 pub object: String,
449 pub created: u64,
450 pub model: String,
451 pub choices: Vec<CompletionChoice>,
452 pub usage: Option<Usage>,
453}
454
455#[derive(Debug, Clone, Serialize, Deserialize)]
457pub struct CompletionChoice {
458 pub text: String,
459 pub index: u32,
460 pub finish_reason: Option<String>,
461}
462
463#[derive(Debug, Clone, Serialize, Deserialize)]
465pub struct Usage {
466 pub prompt_tokens: u32,
467 pub completion_tokens: u32,
468 pub total_tokens: u32,
469}
470
471#[derive(Debug, Clone, Serialize, Deserialize)]
473pub struct ModelListResponse {
474 pub object: String,
475 pub data: Vec<ModelInfo>,
476}
477
478#[derive(Debug, Clone, Serialize, Deserialize)]
480pub struct ModelInfo {
481 pub id: String,
482 pub object: String,
483 pub created: u64,
484 pub owned_by: String,
485 pub permission: Vec<ModelPermission>,
486 pub root: Option<String>,
487 pub parent: Option<String>,
488}
489
490#[derive(Debug, Clone, Serialize, Deserialize)]
492pub struct ModelPermission {
493 pub id: String,
494 pub object: String,
495 pub created: u64,
496 pub allow_create_engine: bool,
497 pub allow_sampling: bool,
498 pub allow_logprobs: bool,
499 pub allow_search_indices: bool,
500 pub allow_view: bool,
501 pub allow_fine_tuning: bool,
502 pub organization: String,
503 pub group: Option<String>,
504 pub is_blocking: bool,
505}
506
507#[derive(Debug, Clone, Serialize, Deserialize)]
511pub struct EmbeddingsRequest {
512 pub model: String,
514
515 pub input: EmbeddingInput,
517
518 #[serde(skip_serializing_if = "Option::is_none")]
520 pub encoding_format: Option<String>,
521}
522
523#[derive(Debug, Clone, Serialize, Deserialize)]
526#[serde(untagged)]
527pub enum EmbeddingInput {
528 Single(String),
530 Batch(Vec<String>),
532 SingleObject(EmbeddingItem),
534 BatchObjects(Vec<EmbeddingItem>),
536}
537
538#[derive(Debug, Clone, Serialize, Deserialize)]
540pub struct EmbeddingItem {
541 #[serde(skip_serializing_if = "Option::is_none")]
543 pub text: Option<String>,
544 #[serde(skip_serializing_if = "Option::is_none")]
546 pub image: Option<String>,
547}
548
549#[derive(Debug, Clone, Serialize, Deserialize)]
551pub struct EmbeddingsResponse {
552 pub object: String,
553 pub data: Vec<EmbeddingData>,
554 pub model: String,
555 pub usage: EmbeddingUsage,
556}
557
558#[derive(Debug, Clone, Serialize, Deserialize)]
560pub struct EmbeddingData {
561 pub object: String,
562 pub embedding: Vec<f32>,
563 pub index: usize,
564}
565
566#[derive(Debug, Clone, Serialize, Deserialize)]
568pub struct EmbeddingUsage {
569 pub prompt_tokens: u32,
570 pub total_tokens: u32,
571}
572
573#[derive(Debug, Clone, Serialize, Deserialize)]
577pub struct TranscriptionResponse {
578 pub text: String,
579}
580
581#[derive(Debug, Clone, Serialize, Deserialize)]
585pub struct OpenAiError {
586 pub error: OpenAiErrorDetail,
587}
588
589#[derive(Debug, Clone, Serialize, Deserialize)]
591pub struct OpenAiErrorDetail {
592 pub message: String,
593 #[serde(rename = "type")]
594 pub error_type: String,
595 pub param: Option<String>,
596 pub code: Option<String>,
597}
598
599#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
601pub enum OpenAiErrorType {
602 InvalidRequestError,
603 AuthenticationError,
604 PermissionError,
605 NotFoundError,
606 RateLimitError,
607 InternalServerError,
608 ServiceUnavailableError,
609}
610
611#[derive(Debug, Clone)]
613pub struct SseEvent {
614 pub event: Option<String>,
615 pub data: String,
616 pub id: Option<String>,
617 pub retry: Option<u32>,
618}
619
620impl SseEvent {
621 pub fn data(data: String) -> Self {
622 Self {
623 event: None,
624 data,
625 id: None,
626 retry: None,
627 }
628 }
629
630 pub fn json(value: &serde_json::Value) -> Result<Self, serde_json::Error> {
631 Ok(Self::data(serde_json::to_string(value)?))
632 }
633
634 pub fn to_string(&self) -> String {
635 let mut result = String::new();
636
637 if let Some(event) = &self.event {
638 result.push_str(&format!("event: {}\n", event));
639 }
640
641 if let Some(id) = &self.id {
642 result.push_str(&format!("id: {}\n", id));
643 }
644
645 if let Some(retry) = self.retry {
646 result.push_str(&format!("retry: {}\n", retry));
647 }
648
649 result.push_str(&format!("data: {}\n\n", self.data));
650 result
651 }
652}
653
654#[derive(Debug, Clone, Serialize, Deserialize)]
656pub struct SpeechRequest {
657 #[serde(default = "default_tts_model")]
659 pub model: String,
660
661 pub input: String,
663
664 #[serde(default = "default_voice")]
666 pub voice: String,
667
668 #[serde(default = "default_audio_format")]
670 pub response_format: String,
671
672 #[serde(default = "default_language")]
674 pub language: String,
675
676 #[serde(default)]
678 pub stream: bool,
679}
680
681fn default_tts_model() -> String {
682 "qwen3-tts".to_string()
683}
684fn default_voice() -> String {
685 "default".to_string()
686}
687fn default_audio_format() -> String {
688 "wav".to_string()
689}
690fn default_language() -> String {
691 "auto".to_string()
692}