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 #[serde(default, skip_serializing_if = "Option::is_none")]
113 pub metadata: Option<HashMap<String, serde_json::Value>>,
114
115 #[serde(default, skip_serializing_if = "Option::is_none")]
119 pub chat_template_kwargs: Option<HashMap<String, serde_json::Value>>,
120}
121
122#[derive(Debug, Clone, Serialize, Deserialize)]
124pub struct StreamOptions {
125 #[serde(default, skip_serializing_if = "Option::is_none")]
126 pub include_usage: Option<bool>,
127}
128
129#[derive(Debug, Clone, Serialize, Deserialize)]
131pub struct ChatTool {
132 #[serde(rename = "type")]
133 pub tool_type: String,
134 pub function: ChatFunction,
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize)]
139pub struct ChatFunction {
140 pub name: String,
141 #[serde(default, skip_serializing_if = "Option::is_none")]
142 pub description: Option<String>,
143 #[serde(default, skip_serializing_if = "Option::is_none")]
144 pub parameters: Option<serde_json::Value>,
145 #[serde(default, skip_serializing_if = "Option::is_none")]
146 pub strict: Option<bool>,
147}
148
149#[derive(Debug, Clone, Serialize, Deserialize)]
152#[serde(untagged)]
153pub enum ToolChoice {
154 Mode(String),
155 Function {
156 #[serde(rename = "type")]
157 tool_type: String,
158 function: ToolChoiceFunction,
159 },
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize)]
163pub struct ToolChoiceFunction {
164 pub name: String,
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize)]
169#[serde(untagged)]
170pub enum FunctionCallChoice {
171 Mode(String),
172 Function { name: String },
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize)]
183pub struct OpenAiResponseFormat {
184 #[serde(rename = "type")]
185 pub format_type: String,
186 #[serde(default, skip_serializing_if = "Option::is_none")]
188 pub json_schema: Option<OpenAiJsonSchema>,
189}
190
191#[derive(Debug, Clone, Serialize, Deserialize)]
192pub struct OpenAiJsonSchema {
193 #[serde(default, skip_serializing_if = "Option::is_none")]
195 pub name: Option<String>,
196 #[serde(default, skip_serializing_if = "Option::is_none")]
202 pub schema: Option<serde_json::Value>,
203 #[serde(default, skip_serializing_if = "Option::is_none")]
207 pub strict: Option<bool>,
208}
209
210#[derive(Debug, Clone, Serialize, Deserialize)]
212pub struct ChatMessage {
213 pub role: MessageRole,
215
216 #[serde(default)]
222 #[serde(deserialize_with = "deserialize_message_content")]
223 pub content: String,
224
225 #[serde(default, skip_serializing_if = "Option::is_none")]
229 pub reasoning: Option<String>,
230
231 #[serde(skip_serializing_if = "Option::is_none")]
233 pub name: Option<String>,
234
235 #[serde(skip_serializing_if = "Option::is_none")]
237 pub tool_calls: Option<Vec<ChatToolCall>>,
238
239 #[serde(skip_serializing_if = "Option::is_none")]
241 pub tool_call_id: Option<String>,
242
243 #[serde(skip_serializing_if = "Option::is_none")]
245 pub function_call: Option<ChatFunctionCall>,
246}
247
248#[derive(Debug, Clone, Serialize, Deserialize)]
250pub struct ChatToolCall {
251 #[serde(skip_serializing_if = "Option::is_none")]
252 pub index: Option<u32>,
253 pub id: String,
254 #[serde(rename = "type")]
255 pub tool_type: String,
256 pub function: ChatFunctionCall,
257}
258
259#[derive(Debug, Clone, Serialize, Deserialize)]
261pub struct ChatFunctionCall {
262 pub name: String,
263 pub arguments: String,
264}
265
266fn deserialize_message_content<'de, D>(deserializer: D) -> Result<String, D::Error>
272where
273 D: serde::Deserializer<'de>,
274{
275 let value = serde_json::Value::deserialize(deserializer)?;
276 match value {
277 serde_json::Value::Null => Ok(String::new()),
278 serde_json::Value::String(s) => Ok(s),
279 serde_json::Value::Array(parts) => {
280 let mut text_parts = Vec::with_capacity(parts.len());
281 for part in parts {
282 let ty = part
283 .get("type")
284 .and_then(|v| v.as_str())
285 .ok_or_else(|| de::Error::custom("message content part missing type"))?;
286 if ty != "text" {
287 return Err(de::Error::custom(format!(
288 "unsupported message content part type `{ty}`"
289 )));
290 }
291 if let Some(text) = part.get("text").and_then(|v| v.as_str()) {
292 text_parts.push(text.to_string());
293 }
294 }
295 Ok(text_parts.join("\n"))
296 }
297 _ => Err(de::Error::custom(
298 "message content must be a string, null, or an array of text parts",
299 )),
300 }
301}
302
303fn deserialize_stop_sequences<'de, D>(deserializer: D) -> Result<Option<Vec<String>>, D::Error>
304where
305 D: serde::Deserializer<'de>,
306{
307 let value = Option::<serde_json::Value>::deserialize(deserializer)?;
308 match value {
309 None | Some(serde_json::Value::Null) => Ok(None),
310 Some(serde_json::Value::String(stop)) => Ok(Some(vec![stop])),
311 Some(serde_json::Value::Array(values)) => {
312 let mut stops = Vec::with_capacity(values.len());
313 for value in values {
314 match value {
315 serde_json::Value::String(stop) => stops.push(stop),
316 _ => {
317 return Err(de::Error::custom(
318 "stop must be a string or an array of strings",
319 ))
320 }
321 }
322 }
323 Ok(Some(stops))
324 }
325 _ => Err(de::Error::custom(
326 "stop must be a string or an array of strings",
327 )),
328 }
329}
330
331#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
333#[serde(rename_all = "lowercase")]
334pub enum MessageRole {
335 System,
336 User,
337 Assistant,
338 Function,
339 Tool,
340}
341
342#[derive(Debug, Clone, Serialize, Deserialize)]
344pub struct ChatCompletionsResponse {
345 pub id: String,
347
348 pub object: String,
350
351 pub created: u64,
353
354 pub model: String,
356
357 pub choices: Vec<ChatChoice>,
359
360 #[serde(skip_serializing_if = "Option::is_none")]
362 pub usage: Option<Usage>,
363}
364
365#[derive(Debug, Clone, Serialize, Deserialize)]
367pub struct ChatChoice {
368 pub index: u32,
370
371 #[serde(skip_serializing_if = "Option::is_none")]
373 pub message: Option<ChatMessage>,
374
375 #[serde(skip_serializing_if = "Option::is_none")]
377 pub delta: Option<ChatMessage>,
378
379 #[serde(skip_serializing_if = "Option::is_none")]
381 pub finish_reason: Option<String>,
382}
383
384#[derive(Debug, Clone, Serialize, Deserialize)]
386pub struct CompletionsRequest {
387 pub model: String,
389
390 #[serde(default)]
394 pub prompt: CompletionPrompt,
395
396 #[serde(skip_serializing_if = "Option::is_none")]
398 pub max_tokens: Option<u32>,
399
400 #[serde(skip_serializing_if = "Option::is_none")]
402 pub temperature: Option<f32>,
403
404 #[serde(skip_serializing_if = "Option::is_none")]
406 pub top_p: Option<f32>,
407
408 #[serde(skip_serializing_if = "Option::is_none")]
411 pub n: Option<u32>,
412
413 #[serde(skip_serializing_if = "Option::is_none")]
415 pub stream: Option<bool>,
416
417 #[serde(default, deserialize_with = "deserialize_stop_sequences")]
419 #[serde(skip_serializing_if = "Option::is_none")]
420 pub stop: Option<Vec<String>>,
421
422 #[serde(skip_serializing_if = "Option::is_none")]
425 pub logprobs: Option<u32>,
426
427 #[serde(skip_serializing_if = "Option::is_none")]
429 pub logit_bias: Option<HashMap<String, f32>>,
430}
431
432#[derive(Debug, Clone, Serialize, Deserialize)]
435#[serde(untagged)]
436pub enum CompletionPrompt {
437 Text(String),
438 Unsupported(serde_json::Value),
439}
440
441impl Default for CompletionPrompt {
442 fn default() -> Self {
443 Self::Unsupported(serde_json::Value::Null)
444 }
445}
446
447impl CompletionPrompt {
448 pub fn as_text(&self) -> Option<&str> {
449 match self {
450 Self::Text(text) => Some(text),
451 Self::Unsupported(_) => None,
452 }
453 }
454}
455
456#[derive(Debug, Clone, Serialize, Deserialize)]
458pub struct CompletionsResponse {
459 pub id: String,
460 pub object: String,
461 pub created: u64,
462 pub model: String,
463 pub choices: Vec<CompletionChoice>,
464 pub usage: Option<Usage>,
465}
466
467#[derive(Debug, Clone, Serialize, Deserialize)]
469pub struct CompletionChoice {
470 pub text: String,
471 pub index: u32,
472 pub finish_reason: Option<String>,
473}
474
475#[derive(Debug, Clone, Serialize, Deserialize)]
477pub struct Usage {
478 pub prompt_tokens: u32,
479 pub completion_tokens: u32,
480 pub total_tokens: u32,
481}
482
483#[derive(Debug, Clone, Serialize, Deserialize)]
485pub struct ModelListResponse {
486 pub object: String,
487 pub data: Vec<ModelInfo>,
488}
489
490#[derive(Debug, Clone, Serialize, Deserialize)]
492pub struct ModelInfo {
493 pub id: String,
494 pub object: String,
495 pub created: u64,
496 pub owned_by: String,
497 pub permission: Vec<ModelPermission>,
498 pub root: Option<String>,
499 pub parent: Option<String>,
500}
501
502#[derive(Debug, Clone, Serialize, Deserialize)]
504pub struct ModelPermission {
505 pub id: String,
506 pub object: String,
507 pub created: u64,
508 pub allow_create_engine: bool,
509 pub allow_sampling: bool,
510 pub allow_logprobs: bool,
511 pub allow_search_indices: bool,
512 pub allow_view: bool,
513 pub allow_fine_tuning: bool,
514 pub organization: String,
515 pub group: Option<String>,
516 pub is_blocking: bool,
517}
518
519#[derive(Debug, Clone, Serialize, Deserialize)]
523pub struct EmbeddingsRequest {
524 pub model: String,
526
527 pub input: EmbeddingInput,
529
530 #[serde(skip_serializing_if = "Option::is_none")]
532 pub encoding_format: Option<String>,
533}
534
535#[derive(Debug, Clone, Serialize, Deserialize)]
538#[serde(untagged)]
539pub enum EmbeddingInput {
540 Single(String),
542 Batch(Vec<String>),
544 SingleObject(EmbeddingItem),
546 BatchObjects(Vec<EmbeddingItem>),
548}
549
550#[derive(Debug, Clone, Serialize, Deserialize)]
552pub struct EmbeddingItem {
553 #[serde(skip_serializing_if = "Option::is_none")]
555 pub text: Option<String>,
556 #[serde(skip_serializing_if = "Option::is_none")]
558 pub image: Option<String>,
559}
560
561#[derive(Debug, Clone, Serialize, Deserialize)]
563pub struct EmbeddingsResponse {
564 pub object: String,
565 pub data: Vec<EmbeddingData>,
566 pub model: String,
567 pub usage: EmbeddingUsage,
568}
569
570#[derive(Debug, Clone, Serialize, Deserialize)]
572pub struct EmbeddingData {
573 pub object: String,
574 pub embedding: Vec<f32>,
575 pub index: usize,
576}
577
578#[derive(Debug, Clone, Serialize, Deserialize)]
580pub struct EmbeddingUsage {
581 pub prompt_tokens: u32,
582 pub total_tokens: u32,
583}
584
585#[derive(Debug, Clone, Serialize, Deserialize)]
589pub struct TranscriptionResponse {
590 pub text: String,
591}
592
593#[derive(Debug, Clone, Serialize, Deserialize)]
597pub struct OpenAiError {
598 pub error: OpenAiErrorDetail,
599}
600
601#[derive(Debug, Clone, Serialize, Deserialize)]
603pub struct OpenAiErrorDetail {
604 pub message: String,
605 #[serde(rename = "type")]
606 pub error_type: String,
607 pub param: Option<String>,
608 pub code: Option<String>,
609}
610
611#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
613pub enum OpenAiErrorType {
614 InvalidRequestError,
615 AuthenticationError,
616 PermissionError,
617 NotFoundError,
618 RateLimitError,
619 InternalServerError,
620 ServiceUnavailableError,
621}
622
623#[derive(Debug, Clone)]
625pub struct SseEvent {
626 pub event: Option<String>,
627 pub data: String,
628 pub id: Option<String>,
629 pub retry: Option<u32>,
630}
631
632impl SseEvent {
633 pub fn data(data: String) -> Self {
634 Self {
635 event: None,
636 data,
637 id: None,
638 retry: None,
639 }
640 }
641
642 pub fn json(value: &serde_json::Value) -> Result<Self, serde_json::Error> {
643 Ok(Self::data(serde_json::to_string(value)?))
644 }
645
646 pub fn to_string(&self) -> String {
647 let mut result = String::new();
648
649 if let Some(event) = &self.event {
650 result.push_str(&format!("event: {}\n", event));
651 }
652
653 if let Some(id) = &self.id {
654 result.push_str(&format!("id: {}\n", id));
655 }
656
657 if let Some(retry) = self.retry {
658 result.push_str(&format!("retry: {}\n", retry));
659 }
660
661 result.push_str(&format!("data: {}\n\n", self.data));
662 result
663 }
664}
665
666#[derive(Debug, Clone, Serialize, Deserialize)]
668pub struct SpeechRequest {
669 #[serde(default = "default_tts_model")]
671 pub model: String,
672
673 pub input: String,
675
676 #[serde(default = "default_voice")]
678 pub voice: String,
679
680 #[serde(default = "default_audio_format")]
682 pub response_format: String,
683
684 #[serde(default = "default_language")]
686 pub language: String,
687
688 #[serde(default)]
690 pub stream: bool,
691}
692
693fn default_tts_model() -> String {
694 "qwen3-tts".to_string()
695}
696fn default_voice() -> String {
697 "default".to_string()
698}
699fn default_audio_format() -> String {
700 "wav".to_string()
701}
702fn default_language() -> String {
703 "auto".to_string()
704}