1use serde::{Deserialize, Serialize};
28
29#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
36pub struct Usage {
37 #[serde(default)]
38 pub prompt_tokens: u32,
39 #[serde(default)]
40 pub completion_tokens: u32,
41 #[serde(default)]
42 pub total_tokens: u32,
43 #[serde(default, skip_serializing_if = "Option::is_none")]
44 pub cost_usd: Option<f64>,
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
53#[serde(rename_all = "snake_case")]
54pub enum Role {
55 System,
56 User,
57 Assistant,
58 Tool,
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
63#[serde(rename_all = "snake_case")]
64pub enum FinishReason {
65 Stop,
66 Length,
67 ToolCalls,
68 ContentFilter,
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
73#[serde(rename_all = "snake_case")]
74pub enum BatchStatus {
75 Validating,
76 InProgress,
77 Finalizing,
78 Completed,
79 Failed,
80 Expired,
81 Canceling,
82 Canceled,
83}
84
85#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
91pub struct ImageUrl {
92 pub url: String,
93 #[serde(default, skip_serializing_if = "Option::is_none")]
94 pub detail: Option<String>,
95}
96
97#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
103#[serde(tag = "type", rename_all = "snake_case")]
104pub enum ContentPart {
105 Text { text: String },
106 ImageUrl { image_url: ImageUrl },
107}
108
109impl ContentPart {
110 pub fn text(text: impl Into<String>) -> Self {
112 ContentPart::Text { text: text.into() }
113 }
114
115 pub fn image_url(url: impl Into<String>) -> Self {
117 ContentPart::ImageUrl {
118 image_url: ImageUrl {
119 url: url.into(),
120 detail: None,
121 },
122 }
123 }
124}
125
126#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
129#[serde(untagged)]
130pub enum Content {
131 Text(String),
132 Parts(Vec<ContentPart>),
133}
134
135impl From<String> for Content {
136 fn from(s: String) -> Self {
137 Content::Text(s)
138 }
139}
140
141impl From<&str> for Content {
142 fn from(s: &str) -> Self {
143 Content::Text(s.to_string())
144 }
145}
146
147impl From<Vec<ContentPart>> for Content {
148 fn from(p: Vec<ContentPart>) -> Self {
149 Content::Parts(p)
150 }
151}
152
153#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
159pub struct FunctionCall {
160 pub name: String,
161 pub arguments: String,
162}
163
164#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
166pub struct ToolCall {
167 pub id: String,
168 #[serde(rename = "type")]
169 pub kind: String,
170 pub function: FunctionCall,
171}
172
173#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
175pub struct FunctionCallDelta {
176 #[serde(default, skip_serializing_if = "Option::is_none")]
177 pub name: Option<String>,
178 #[serde(default, skip_serializing_if = "Option::is_none")]
179 pub arguments: Option<String>,
180}
181
182#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
184pub struct ToolCallDelta {
185 pub index: u32,
186 #[serde(default, skip_serializing_if = "Option::is_none")]
187 pub id: Option<String>,
188 #[serde(default, skip_serializing_if = "Option::is_none", rename = "type")]
189 pub kind: Option<String>,
190 #[serde(default, skip_serializing_if = "Option::is_none")]
191 pub function: Option<FunctionCallDelta>,
192}
193
194#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
200pub struct ChatMessage {
201 pub role: Role,
202 #[serde(default, skip_serializing_if = "Option::is_none")]
205 pub content: Option<Content>,
206 #[serde(default, skip_serializing_if = "Option::is_none")]
207 pub name: Option<String>,
208 #[serde(default, skip_serializing_if = "Vec::is_empty")]
209 pub tool_calls: Vec<ToolCall>,
210 #[serde(default, skip_serializing_if = "Option::is_none")]
212 pub tool_call_id: Option<String>,
213}
214
215impl ChatMessage {
216 pub fn system(text: impl Into<String>) -> Self {
218 Self::with_text(Role::System, text)
219 }
220
221 pub fn user(text: impl Into<String>) -> Self {
223 Self::with_text(Role::User, text)
224 }
225
226 pub fn assistant(text: impl Into<String>) -> Self {
228 Self::with_text(Role::Assistant, text)
229 }
230
231 fn with_text(role: Role, text: impl Into<String>) -> Self {
232 ChatMessage {
233 role,
234 content: Some(Content::Text(text.into())),
235 name: None,
236 tool_calls: Vec::new(),
237 tool_call_id: None,
238 }
239 }
240
241 pub fn text(&self) -> Option<&str> {
243 match &self.content {
244 Some(Content::Text(s)) => Some(s.as_str()),
245 _ => None,
246 }
247 }
248}
249
250#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
256pub struct FunctionDef {
257 pub name: String,
258 #[serde(default, skip_serializing_if = "Option::is_none")]
259 pub description: Option<String>,
260 #[serde(default, skip_serializing_if = "Option::is_none")]
262 pub parameters: Option<serde_json::Value>,
263}
264
265#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
267pub struct ToolDef {
268 #[serde(rename = "type")]
269 pub kind: String,
270 pub function: FunctionDef,
271}
272
273impl ToolDef {
274 pub fn function(function: FunctionDef) -> Self {
276 ToolDef {
277 kind: "function".to_string(),
278 function,
279 }
280 }
281}
282
283#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
285pub struct FunctionName {
286 pub name: String,
287}
288
289#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
291pub struct NamedToolChoice {
292 #[serde(rename = "type")]
293 pub kind: String,
294 pub function: FunctionName,
295}
296
297#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
300#[serde(untagged)]
301pub enum ToolChoice {
302 Mode(String),
303 Named(NamedToolChoice),
304}
305
306impl ToolChoice {
307 pub fn mode(mode: impl Into<String>) -> Self {
309 ToolChoice::Mode(mode.into())
310 }
311
312 pub fn named(name: impl Into<String>) -> Self {
314 ToolChoice::Named(NamedToolChoice {
315 kind: "function".to_string(),
316 function: FunctionName { name: name.into() },
317 })
318 }
319}
320
321#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
323pub struct ResponseFormat {
324 #[serde(rename = "type")]
325 pub kind: String,
326 #[serde(default, skip_serializing_if = "Option::is_none")]
328 pub json_schema: Option<serde_json::Value>,
329}
330
331#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
338#[serde(untagged)]
339pub enum Stop {
340 One(String),
341 Many(Vec<String>),
342}
343
344impl Stop {
345 pub fn from_vec(mut v: Vec<String>) -> Option<Self> {
348 match v.len() {
349 0 => None,
350 1 => Some(Stop::One(v.pop().unwrap())),
351 _ => Some(Stop::Many(v)),
352 }
353 }
354}
355
356#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
358pub struct ChatRequest {
359 pub model: String,
360 pub messages: Vec<ChatMessage>,
361 #[serde(default, skip_serializing_if = "Option::is_none")]
362 pub stream: Option<bool>,
363 #[serde(default, skip_serializing_if = "Option::is_none")]
364 pub temperature: Option<f32>,
365 #[serde(default, skip_serializing_if = "Option::is_none")]
366 pub top_p: Option<f32>,
367 #[serde(default, skip_serializing_if = "Option::is_none")]
370 pub max_tokens: Option<u32>,
371 #[serde(default, skip_serializing_if = "Option::is_none")]
373 pub max_completion_tokens: Option<u32>,
374 #[serde(default, skip_serializing_if = "Option::is_none")]
375 pub stop: Option<Stop>,
376 #[serde(default, skip_serializing_if = "Option::is_none")]
377 pub n: Option<u32>,
378 #[serde(default, skip_serializing_if = "Option::is_none")]
379 pub seed: Option<i64>,
380 #[serde(default, skip_serializing_if = "Option::is_none")]
381 pub frequency_penalty: Option<f32>,
382 #[serde(default, skip_serializing_if = "Option::is_none")]
383 pub presence_penalty: Option<f32>,
384 #[serde(default, skip_serializing_if = "Vec::is_empty")]
385 pub tools: Vec<ToolDef>,
386 #[serde(default, skip_serializing_if = "Option::is_none")]
387 pub tool_choice: Option<ToolChoice>,
388 #[serde(default, skip_serializing_if = "Option::is_none")]
389 pub response_format: Option<ResponseFormat>,
390 #[serde(default, skip_serializing_if = "Option::is_none")]
392 pub reasoning_effort: Option<String>,
393 #[serde(default, flatten, skip_serializing_if = "Option::is_none")]
396 pub extra: Option<serde_json::Map<String, serde_json::Value>>,
397}
398
399impl ChatRequest {
400 pub fn new(model: impl Into<String>, messages: Vec<ChatMessage>) -> Self {
402 ChatRequest {
403 model: model.into(),
404 messages,
405 ..Default::default()
406 }
407 }
408}
409
410#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
416pub struct Choice {
417 #[serde(default)]
418 pub index: u32,
419 pub message: ChatMessage,
420 #[serde(default, skip_serializing_if = "Option::is_none")]
421 pub finish_reason: Option<FinishReason>,
422}
423
424#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
426pub struct ChatResponse {
427 pub id: String,
428 pub object: String,
429 pub created: i64,
430 pub model: String,
431 pub choices: Vec<Choice>,
432 #[serde(default, skip_serializing_if = "Option::is_none")]
433 pub usage: Option<Usage>,
434}
435
436impl ChatResponse {
437 pub fn first_text(&self) -> Option<&str> {
439 self.choices.first().and_then(|c| c.message.text())
440 }
441}
442
443#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
449pub struct Delta {
450 #[serde(default, skip_serializing_if = "Option::is_none")]
452 pub role: Option<Role>,
453 #[serde(default, skip_serializing_if = "Option::is_none")]
455 pub content: Option<String>,
456 #[serde(default, skip_serializing_if = "Vec::is_empty")]
457 pub tool_calls: Vec<ToolCallDelta>,
458}
459
460#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
462pub struct ChunkChoice {
463 #[serde(default)]
464 pub index: u32,
465 pub delta: Delta,
466 #[serde(default, skip_serializing_if = "Option::is_none")]
467 pub finish_reason: Option<FinishReason>,
468}
469
470#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
472pub struct ChatCompletionChunk {
473 pub id: String,
474 pub object: String,
475 pub created: i64,
476 pub model: String,
477 pub choices: Vec<ChunkChoice>,
478 #[serde(default, skip_serializing_if = "Option::is_none")]
480 pub usage: Option<Usage>,
481}
482
483impl ChatCompletionChunk {
484 pub fn first_delta_text(&self) -> Option<&str> {
486 self.choices
487 .first()
488 .and_then(|c| c.delta.content.as_deref())
489 }
490}
491
492#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
498#[serde(untagged)]
499pub enum EmbeddingInput {
500 One(String),
501 Many(Vec<String>),
502}
503
504impl From<String> for EmbeddingInput {
505 fn from(s: String) -> Self {
506 EmbeddingInput::One(s)
507 }
508}
509
510impl From<&str> for EmbeddingInput {
511 fn from(s: &str) -> Self {
512 EmbeddingInput::One(s.to_string())
513 }
514}
515
516impl From<Vec<String>> for EmbeddingInput {
517 fn from(v: Vec<String>) -> Self {
518 EmbeddingInput::Many(v)
519 }
520}
521
522#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
524pub struct EmbeddingRequest {
525 pub model: String,
526 pub input: EmbeddingInput,
527 #[serde(default, skip_serializing_if = "Option::is_none")]
528 pub dimensions: Option<u32>,
529 #[serde(default, skip_serializing_if = "Option::is_none")]
532 pub encoding_format: Option<String>,
533 #[serde(default, skip_serializing_if = "Option::is_none")]
535 pub extra: Option<serde_json::Value>,
536}
537
538impl EmbeddingRequest {
539 pub fn new(model: impl Into<String>, input: impl Into<EmbeddingInput>) -> Self {
541 EmbeddingRequest {
542 model: model.into(),
543 input: input.into(),
544 dimensions: None,
545 encoding_format: None,
546 extra: None,
547 }
548 }
549}
550
551#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
554pub struct Embedding {
555 pub object: String,
556 #[serde(default)]
557 pub index: u32,
558 pub embedding: Vec<f32>,
559}
560
561#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
563pub struct EmbeddingResponse {
564 pub object: String,
565 pub data: Vec<Embedding>,
566 pub model: String,
567 #[serde(default, skip_serializing_if = "Option::is_none")]
568 pub usage: Option<Usage>,
569}
570
571#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
577pub struct SpeechRequest {
578 pub model: String,
579 pub input: String,
580 pub voice: String,
581 #[serde(default, skip_serializing_if = "Option::is_none")]
583 pub response_format: Option<String>,
584 #[serde(default, skip_serializing_if = "Option::is_none")]
585 pub speed: Option<f32>,
586 #[serde(default, skip_serializing_if = "Option::is_none")]
588 pub extra: Option<serde_json::Value>,
589}
590
591impl SpeechRequest {
592 pub fn new(
594 model: impl Into<String>,
595 input: impl Into<String>,
596 voice: impl Into<String>,
597 ) -> Self {
598 SpeechRequest {
599 model: model.into(),
600 input: input.into(),
601 voice: voice.into(),
602 response_format: None,
603 speed: None,
604 extra: None,
605 }
606 }
607}
608
609#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
611pub struct Voice {
612 pub id: String,
613 #[serde(default, skip_serializing_if = "Option::is_none")]
614 pub name: Option<String>,
615 #[serde(default, skip_serializing_if = "Vec::is_empty")]
616 pub languages: Vec<String>,
617}
618
619#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
621pub struct VoicesResponse {
622 pub model: String,
623 pub voices: Vec<Voice>,
624}
625
626#[derive(Debug, Clone, PartialEq, Default)]
633pub struct TranscriptionRequest {
634 pub model: String,
635 pub language: Option<String>,
637 pub prompt: Option<String>,
639 pub response_format: Option<String>,
641 pub temperature: Option<f32>,
642}
643
644impl TranscriptionRequest {
645 pub fn new(model: impl Into<String>) -> Self {
647 TranscriptionRequest {
648 model: model.into(),
649 ..Default::default()
650 }
651 }
652}
653
654#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
656pub struct TranscriptionResponse {
657 pub text: String,
658 #[serde(default, skip_serializing_if = "Option::is_none")]
659 pub task: Option<String>,
660 #[serde(default, skip_serializing_if = "Option::is_none")]
661 pub language: Option<String>,
662 #[serde(default, skip_serializing_if = "Option::is_none")]
663 pub duration: Option<f32>,
664 #[serde(default, skip_serializing_if = "Option::is_none")]
665 pub usage: Option<Usage>,
666}
667
668#[derive(Debug, Clone, PartialEq)]
671pub enum Transcription {
672 Json(TranscriptionResponse),
673 Text(String),
674}
675
676#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
682pub struct Architecture {
683 #[serde(default, skip_serializing_if = "Vec::is_empty")]
684 pub input_modalities: Vec<String>,
685 #[serde(default, skip_serializing_if = "Vec::is_empty")]
686 pub output_modalities: Vec<String>,
687 #[serde(default, skip_serializing_if = "Option::is_none")]
688 pub modality: Option<String>,
689 #[serde(default)]
690 pub tokenizer: String,
691 #[serde(default, skip_serializing_if = "Option::is_none")]
692 pub instruct_type: Option<String>,
693}
694
695#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
697pub struct Pricing {
698 pub prompt: String,
699 pub completion: String,
700}
701
702#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
704pub struct TopProvider {
705 #[serde(default, skip_serializing_if = "Option::is_none")]
706 pub context_length: Option<u32>,
707 #[serde(default, skip_serializing_if = "Option::is_none")]
708 pub max_completion_tokens: Option<u32>,
709 #[serde(default)]
710 pub is_moderated: bool,
711 #[serde(default, skip_serializing_if = "Option::is_none")]
712 pub max_thinking_tokens: Option<u32>,
713}
714
715#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
717pub struct ModelEndpoint {
718 pub provider: String,
719 pub model: String,
720 #[serde(default)]
721 pub down: bool,
722 pub source: String,
724}
725
726#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
728pub struct ModelEntry {
729 pub id: String,
730 #[serde(default)]
731 pub canonical_slug: String,
732 #[serde(default)]
733 pub name: String,
734 #[serde(default)]
735 pub created: i64,
736 #[serde(default)]
737 pub description: String,
738 #[serde(default, skip_serializing_if = "Option::is_none")]
739 pub context_length: Option<u32>,
740 #[serde(default, skip_serializing_if = "Option::is_none")]
741 pub architecture: Option<Architecture>,
742 #[serde(default, skip_serializing_if = "Option::is_none")]
743 pub pricing: Option<Pricing>,
744 #[serde(default, skip_serializing_if = "Option::is_none")]
745 pub top_provider: Option<TopProvider>,
746 #[serde(default, skip_serializing_if = "Vec::is_empty")]
747 pub supported_parameters: Vec<String>,
748 #[serde(default, skip_serializing_if = "Vec::is_empty")]
749 pub unsupported_parameters: Vec<String>,
750 #[serde(default, skip_serializing_if = "Option::is_none")]
752 pub default_parameters: Option<serde_json::Value>,
753 #[serde(default, skip_serializing_if = "Vec::is_empty")]
755 pub endpoints: Vec<ModelEndpoint>,
756}
757
758#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
760pub struct ListModelsResponse {
761 #[serde(default)]
762 pub data: Vec<ModelEntry>,
763}
764
765#[derive(Debug, Clone, Copy, PartialEq, Eq)]
767pub enum ModelType {
768 All,
769 Llm,
770 Tts,
771 Stt,
772 Embedding,
773}
774
775impl ModelType {
776 pub(crate) fn as_str(self) -> &'static str {
777 match self {
778 ModelType::All => "all",
779 ModelType::Llm => "llm",
780 ModelType::Tts => "tts",
781 ModelType::Stt => "stt",
782 ModelType::Embedding => "embedding",
783 }
784 }
785}
786
787#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
793pub struct BatchRequestItem {
794 pub custom_id: String,
795 pub body: ChatRequest,
796}
797
798#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
800pub struct BatchCreateRequest {
801 pub requests: Vec<BatchRequestItem>,
802}
803
804#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
806pub struct BatchCounts {
807 #[serde(default)]
808 pub total: u64,
809 #[serde(default)]
810 pub processing: u64,
811 #[serde(default)]
812 pub succeeded: u64,
813 #[serde(default)]
814 pub errored: u64,
815 #[serde(default)]
816 pub canceled: u64,
817 #[serde(default)]
818 pub expired: u64,
819}
820
821#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
823pub struct BatchHandle {
824 pub id: String,
825 pub status: BatchStatus,
826 #[serde(default)]
827 pub counts: BatchCounts,
828 #[serde(default, skip_serializing_if = "Option::is_none")]
829 pub created_at: Option<i64>,
830 #[serde(default, skip_serializing_if = "Option::is_none")]
831 pub expires_at: Option<i64>,
832 #[serde(default, skip_serializing_if = "Option::is_none")]
833 pub ended_at: Option<i64>,
834 #[serde(default, skip_serializing_if = "Option::is_none")]
835 pub endpoint: Option<String>,
836}
837
838#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
840pub struct BatchResponse {
841 pub status_code: u32,
842 pub body: ChatResponse,
843}
844
845#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
847pub struct BatchError {
848 pub code: String,
849 pub message: String,
850}
851
852#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
854pub struct BatchResultLine {
855 pub custom_id: String,
856 #[serde(default, skip_serializing_if = "Option::is_none")]
857 pub response: Option<BatchResponse>,
858 #[serde(default, skip_serializing_if = "Option::is_none")]
859 pub error: Option<BatchError>,
860}