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")]
38 pub top_k: Option<i64>,
39
40 #[serde(skip_serializing_if = "Option::is_none")]
43 pub min_p: Option<f32>,
44
45 #[serde(skip_serializing_if = "Option::is_none")]
47 pub repetition_penalty: Option<f32>,
48
49 #[serde(skip_serializing_if = "Option::is_none")]
51 pub n: Option<u32>,
52
53 #[serde(skip_serializing_if = "Option::is_none")]
55 pub stream: Option<bool>,
56
57 #[serde(skip_serializing_if = "Option::is_none")]
61 pub ignore_eos: Option<bool>,
62
63 #[serde(default, deserialize_with = "deserialize_stop_sequences")]
65 #[serde(skip_serializing_if = "Option::is_none")]
66 pub stop: Option<Vec<String>>,
67
68 #[serde(skip_serializing_if = "Option::is_none")]
70 pub presence_penalty: Option<f32>,
71
72 #[serde(skip_serializing_if = "Option::is_none")]
74 pub frequency_penalty: Option<f32>,
75
76 #[serde(skip_serializing_if = "Option::is_none")]
78 pub logit_bias: Option<HashMap<String, f32>>,
79
80 #[serde(skip_serializing_if = "Option::is_none")]
83 pub logprobs: Option<bool>,
84
85 #[serde(skip_serializing_if = "Option::is_none")]
87 pub top_logprobs: Option<u32>,
88
89 #[serde(skip_serializing_if = "Option::is_none")]
91 pub user: Option<String>,
92
93 #[serde(skip_serializing_if = "Option::is_none")]
95 pub seed: Option<u64>,
96
97 #[serde(skip_serializing_if = "Option::is_none")]
99 pub response_format: Option<OpenAiResponseFormat>,
100
101 #[serde(skip_serializing_if = "Option::is_none")]
105 pub tools: Option<Vec<ChatTool>>,
106
107 #[serde(skip_serializing_if = "Option::is_none")]
109 pub tool_choice: Option<ToolChoice>,
110
111 #[serde(skip_serializing_if = "Option::is_none")]
113 pub stream_options: Option<StreamOptions>,
114
115 #[serde(skip_serializing_if = "Option::is_none")]
117 pub functions: Option<Vec<ChatFunction>>,
118
119 #[serde(skip_serializing_if = "Option::is_none")]
121 pub function_call: Option<FunctionCallChoice>,
122
123 #[serde(default, skip_serializing_if = "Option::is_none")]
127 pub metadata: Option<HashMap<String, serde_json::Value>>,
128
129 #[serde(default, skip_serializing_if = "Option::is_none")]
133 pub chat_template_kwargs: Option<HashMap<String, serde_json::Value>>,
134}
135
136#[derive(Debug, Clone, Serialize)]
138pub struct StreamOptions {
139 #[serde(default, skip_serializing_if = "Option::is_none")]
140 pub include_usage: Option<bool>,
141}
142
143impl<'de> Deserialize<'de> for StreamOptions {
144 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
145 where
146 D: serde::Deserializer<'de>,
147 {
148 #[derive(Deserialize)]
149 #[serde(deny_unknown_fields)]
150 struct Object {
151 #[serde(default)]
152 include_usage: Option<bool>,
153 }
154
155 let value = serde_json::Value::deserialize(deserializer)?;
156 if !value.is_object() {
157 return Err(de::Error::custom("stream_options must be a JSON object"));
158 }
159 let parsed = serde_json::from_value::<Object>(value).map_err(de::Error::custom)?;
160 Ok(Self {
161 include_usage: parsed.include_usage,
162 })
163 }
164}
165
166#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct ChatTool {
169 #[serde(rename = "type")]
170 pub tool_type: String,
171 pub function: ChatFunction,
172}
173
174#[derive(Debug, Clone, Serialize, Deserialize)]
176pub struct ChatFunction {
177 pub name: String,
178 #[serde(default, skip_serializing_if = "Option::is_none")]
179 pub description: Option<String>,
180 #[serde(default, skip_serializing_if = "Option::is_none")]
181 pub parameters: Option<serde_json::Value>,
182 #[serde(default, skip_serializing_if = "Option::is_none")]
183 pub strict: Option<bool>,
184}
185
186#[derive(Debug, Clone, Serialize, Deserialize)]
189#[serde(untagged)]
190pub enum ToolChoice {
191 Mode(String),
192 Function {
193 #[serde(rename = "type")]
194 tool_type: String,
195 function: ToolChoiceFunction,
196 },
197}
198
199#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct ToolChoiceFunction {
201 pub name: String,
202}
203
204#[derive(Debug, Clone, Serialize, Deserialize)]
206#[serde(untagged)]
207pub enum FunctionCallChoice {
208 Mode(String),
209 Function { name: String },
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize)]
220pub struct OpenAiResponseFormat {
221 #[serde(rename = "type")]
222 pub format_type: String,
223 #[serde(default, skip_serializing_if = "Option::is_none")]
225 pub json_schema: Option<OpenAiJsonSchema>,
226}
227
228#[derive(Debug, Clone, Serialize, Deserialize)]
229pub struct OpenAiJsonSchema {
230 #[serde(default, skip_serializing_if = "Option::is_none")]
232 pub name: Option<String>,
233 #[serde(default, skip_serializing_if = "Option::is_none")]
239 pub schema: Option<serde_json::Value>,
240 #[serde(default, skip_serializing_if = "Option::is_none")]
244 pub strict: Option<bool>,
245}
246
247#[derive(Debug, Clone, Serialize, Deserialize)]
249pub struct ChatMessage {
250 pub role: MessageRole,
252
253 #[serde(default)]
259 #[serde(deserialize_with = "deserialize_message_content")]
260 pub content: String,
261
262 #[serde(default, skip_serializing_if = "Option::is_none")]
266 pub reasoning: Option<String>,
267
268 #[serde(skip_serializing_if = "Option::is_none")]
270 pub name: Option<String>,
271
272 #[serde(skip_serializing_if = "Option::is_none")]
274 pub tool_calls: Option<Vec<ChatToolCall>>,
275
276 #[serde(skip_serializing_if = "Option::is_none")]
278 pub tool_call_id: Option<String>,
279
280 #[serde(skip_serializing_if = "Option::is_none")]
282 pub function_call: Option<ChatFunctionCall>,
283}
284
285#[derive(Debug, Clone, Serialize, Deserialize)]
287pub struct ChatToolCall {
288 #[serde(skip_serializing_if = "Option::is_none")]
289 pub index: Option<u32>,
290 pub id: String,
291 #[serde(rename = "type")]
292 pub tool_type: String,
293 pub function: ChatFunctionCall,
294}
295
296#[derive(Debug, Clone, Serialize, Deserialize)]
298pub struct ChatFunctionCall {
299 pub name: String,
300 pub arguments: String,
301}
302
303fn deserialize_message_content<'de, D>(deserializer: D) -> Result<String, D::Error>
309where
310 D: serde::Deserializer<'de>,
311{
312 let value = serde_json::Value::deserialize(deserializer)?;
313 match value {
314 serde_json::Value::Null => Ok(String::new()),
315 serde_json::Value::String(s) => Ok(s),
316 serde_json::Value::Array(parts) => {
317 let mut text_parts = Vec::with_capacity(parts.len());
318 for part in parts {
319 let ty = part
320 .get("type")
321 .and_then(|v| v.as_str())
322 .ok_or_else(|| de::Error::custom("message content part missing type"))?;
323 if ty != "text" {
324 return Err(de::Error::custom(format!(
325 "unsupported message content part type `{ty}`"
326 )));
327 }
328 if let Some(text) = part.get("text").and_then(|v| v.as_str()) {
329 text_parts.push(text.to_string());
330 }
331 }
332 Ok(text_parts.join("\n"))
333 }
334 _ => Err(de::Error::custom(
335 "message content must be a string, null, or an array of text parts",
336 )),
337 }
338}
339
340fn deserialize_stop_sequences<'de, D>(deserializer: D) -> Result<Option<Vec<String>>, D::Error>
341where
342 D: serde::Deserializer<'de>,
343{
344 let value = Option::<serde_json::Value>::deserialize(deserializer)?;
345 match value {
346 None | Some(serde_json::Value::Null) => Ok(None),
347 Some(serde_json::Value::String(stop)) => Ok(Some(vec![stop])),
348 Some(serde_json::Value::Array(values)) => {
349 let mut stops = Vec::with_capacity(values.len());
350 for value in values {
351 match value {
352 serde_json::Value::String(stop) => stops.push(stop),
353 _ => {
354 return Err(de::Error::custom(
355 "stop must be a string or an array of strings",
356 ))
357 }
358 }
359 }
360 Ok(Some(stops))
361 }
362 _ => Err(de::Error::custom(
363 "stop must be a string or an array of strings",
364 )),
365 }
366}
367
368#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
370#[serde(rename_all = "lowercase")]
371pub enum MessageRole {
372 System,
373 User,
374 Assistant,
375 Function,
376 Tool,
377}
378
379#[derive(Debug, Clone, Serialize, Deserialize)]
381pub struct ChatCompletionsResponse {
382 pub id: String,
384
385 pub object: String,
387
388 pub created: u64,
390
391 pub model: String,
393
394 pub choices: Vec<ChatChoice>,
396
397 #[serde(skip_serializing_if = "Option::is_none")]
399 pub usage: Option<Usage>,
400}
401
402#[derive(Debug, Clone, Serialize, Deserialize)]
404pub struct ChatChoice {
405 pub index: u32,
407
408 #[serde(skip_serializing_if = "Option::is_none")]
410 pub message: Option<ChatMessage>,
411
412 #[serde(skip_serializing_if = "Option::is_none")]
414 pub delta: Option<ChatMessage>,
415
416 #[serde(skip_serializing_if = "Option::is_none")]
418 pub finish_reason: Option<String>,
419}
420
421#[derive(Debug, Clone, Serialize, Deserialize)]
423pub struct CompletionsRequest {
424 pub model: String,
426
427 #[serde(default)]
431 pub prompt: CompletionPrompt,
432
433 #[serde(skip_serializing_if = "Option::is_none")]
435 pub max_tokens: Option<u32>,
436
437 #[serde(skip_serializing_if = "Option::is_none")]
439 pub temperature: Option<f32>,
440
441 #[serde(skip_serializing_if = "Option::is_none")]
443 pub top_p: Option<f32>,
444
445 #[serde(skip_serializing_if = "Option::is_none")]
448 pub n: Option<u32>,
449
450 #[serde(skip_serializing_if = "Option::is_none")]
452 pub stream: Option<bool>,
453
454 #[serde(default, deserialize_with = "deserialize_stop_sequences")]
456 #[serde(skip_serializing_if = "Option::is_none")]
457 pub stop: Option<Vec<String>>,
458
459 #[serde(skip_serializing_if = "Option::is_none")]
462 pub logprobs: Option<u32>,
463
464 #[serde(skip_serializing_if = "Option::is_none")]
466 pub logit_bias: Option<HashMap<String, f32>>,
467}
468
469#[derive(Debug, Clone, Serialize, Deserialize)]
472#[serde(untagged)]
473pub enum CompletionPrompt {
474 Text(String),
475 Unsupported(serde_json::Value),
476}
477
478impl Default for CompletionPrompt {
479 fn default() -> Self {
480 Self::Unsupported(serde_json::Value::Null)
481 }
482}
483
484impl CompletionPrompt {
485 pub fn as_text(&self) -> Option<&str> {
486 match self {
487 Self::Text(text) => Some(text),
488 Self::Unsupported(_) => None,
489 }
490 }
491}
492
493#[derive(Debug, Clone, Serialize, Deserialize)]
495pub struct CompletionsResponse {
496 pub id: String,
497 pub object: String,
498 pub created: u64,
499 pub model: String,
500 pub choices: Vec<CompletionChoice>,
501 pub usage: Option<Usage>,
502}
503
504#[derive(Debug, Clone, Serialize, Deserialize)]
506pub struct CompletionChoice {
507 pub text: String,
508 pub index: u32,
509 pub finish_reason: Option<String>,
510}
511
512#[derive(Debug, Clone, Serialize, Deserialize)]
514pub struct Usage {
515 pub prompt_tokens: u32,
516 pub completion_tokens: u32,
517 pub total_tokens: u32,
518}
519
520#[derive(Debug, Clone, Serialize, Deserialize)]
522pub struct ModelListResponse {
523 pub object: String,
524 pub data: Vec<ModelInfo>,
525}
526
527#[derive(Debug, Clone, Serialize, Deserialize)]
529pub struct ModelInfo {
530 pub id: String,
531 pub object: String,
532 pub created: u64,
533 pub owned_by: String,
534 pub modalities: Vec<String>,
535 pub permission: Vec<ModelPermission>,
536 pub root: Option<String>,
537 pub parent: Option<String>,
538}
539
540#[derive(Debug, Clone, Serialize, Deserialize)]
542pub struct ModelPermission {
543 pub id: String,
544 pub object: String,
545 pub created: u64,
546 pub allow_create_engine: bool,
547 pub allow_sampling: bool,
548 pub allow_logprobs: bool,
549 pub allow_search_indices: bool,
550 pub allow_view: bool,
551 pub allow_fine_tuning: bool,
552 pub organization: String,
553 pub group: Option<String>,
554 pub is_blocking: bool,
555}
556
557#[derive(Debug, Clone, Serialize, Deserialize)]
561pub struct EmbeddingsRequest {
562 pub model: String,
564
565 pub input: EmbeddingInput,
567
568 #[serde(skip_serializing_if = "Option::is_none")]
570 pub encoding_format: Option<String>,
571}
572
573#[derive(Debug, Clone, Serialize, Deserialize)]
576#[serde(untagged)]
577pub enum EmbeddingInput {
578 Single(String),
580 Batch(Vec<String>),
582 SingleObject(EmbeddingItem),
584 BatchObjects(Vec<EmbeddingItem>),
586}
587
588#[derive(Debug, Clone, Serialize, Deserialize)]
590pub struct EmbeddingItem {
591 #[serde(skip_serializing_if = "Option::is_none")]
593 pub text: Option<String>,
594 #[serde(skip_serializing_if = "Option::is_none")]
596 pub image: Option<String>,
597}
598
599#[derive(Debug, Clone, Serialize, Deserialize)]
601pub struct EmbeddingsResponse {
602 pub object: String,
603 pub data: Vec<EmbeddingData>,
604 pub model: String,
605 pub usage: EmbeddingUsage,
606}
607
608#[derive(Debug, Clone, Serialize, Deserialize)]
610pub struct EmbeddingData {
611 pub object: String,
612 pub embedding: Vec<f32>,
613 pub index: usize,
614}
615
616#[derive(Debug, Clone, Serialize, Deserialize)]
618pub struct EmbeddingUsage {
619 pub prompt_tokens: u32,
620 pub total_tokens: u32,
621}
622
623#[derive(Debug, Clone, Serialize, Deserialize)]
627pub struct TranscriptionResponse {
628 pub text: String,
629}
630
631#[derive(Debug, Clone, Serialize, Deserialize)]
635pub struct OpenAiError {
636 pub error: OpenAiErrorDetail,
637}
638
639#[derive(Debug, Clone, Serialize, Deserialize)]
641pub struct OpenAiErrorDetail {
642 pub message: String,
643 #[serde(rename = "type")]
644 pub error_type: String,
645 pub param: Option<String>,
646 pub code: Option<String>,
647}
648
649#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
651pub enum OpenAiErrorType {
652 InvalidRequestError,
653 AuthenticationError,
654 PermissionError,
655 NotFoundError,
656 RateLimitError,
657 InternalServerError,
658 ServiceUnavailableError,
659}
660
661#[derive(Debug, Clone)]
663pub struct SseEvent {
664 pub event: Option<String>,
665 pub data: String,
666 pub id: Option<String>,
667 pub retry: Option<u32>,
668}
669
670impl SseEvent {
671 pub fn data(data: String) -> Self {
672 Self {
673 event: None,
674 data,
675 id: None,
676 retry: None,
677 }
678 }
679
680 pub fn json(value: &serde_json::Value) -> Result<Self, serde_json::Error> {
681 Ok(Self::data(serde_json::to_string(value)?))
682 }
683
684 pub fn to_string(&self) -> String {
685 let mut result = String::new();
686
687 if let Some(event) = &self.event {
688 result.push_str(&format!("event: {}\n", event));
689 }
690
691 if let Some(id) = &self.id {
692 result.push_str(&format!("id: {}\n", id));
693 }
694
695 if let Some(retry) = self.retry {
696 result.push_str(&format!("retry: {}\n", retry));
697 }
698
699 result.push_str(&format!("data: {}\n\n", self.data));
700 result
701 }
702}
703
704#[derive(Debug, Clone, Serialize, Deserialize)]
706pub struct SpeechRequest {
707 #[serde(default = "default_tts_model")]
709 pub model: String,
710
711 pub input: String,
713
714 #[serde(default = "default_voice")]
716 pub voice: String,
717
718 #[serde(default = "default_audio_format")]
720 pub response_format: String,
721
722 #[serde(default = "default_language")]
724 pub language: String,
725
726 #[serde(default)]
728 pub stream: bool,
729}
730
731fn default_tts_model() -> String {
732 "qwen3-tts".to_string()
733}
734fn default_voice() -> String {
735 "default".to_string()
736}
737fn default_audio_format() -> String {
738 "wav".to_string()
739}
740fn default_language() -> String {
741 "auto".to_string()
742}