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 #[serde(default, skip_serializing_if = "Option::is_none")]
48 pub prompt_tokens_details: Option<PromptTokensDetails>,
49 #[serde(default, skip_serializing_if = "Option::is_none")]
52 pub cache_creation_tokens: Option<u32>,
53}
54
55impl Usage {
56 pub fn cached_tokens(&self) -> u32 {
59 self.prompt_tokens_details
60 .as_ref()
61 .and_then(|d| d.cached_tokens)
62 .unwrap_or(0)
63 }
64
65 pub fn cache_writes(&self) -> u32 {
68 self.cache_creation_tokens.unwrap_or(0)
69 }
70}
71
72#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
75pub struct PromptTokensDetails {
76 #[serde(default, skip_serializing_if = "Option::is_none")]
77 pub cached_tokens: Option<u32>,
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
86#[serde(rename_all = "snake_case")]
87pub enum Role {
88 System,
89 User,
90 Assistant,
91 Tool,
92}
93
94#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
96#[serde(rename_all = "snake_case")]
97pub enum FinishReason {
98 Stop,
99 Length,
100 ToolCalls,
101 ContentFilter,
102}
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
106#[serde(rename_all = "snake_case")]
107pub enum BatchStatus {
108 Validating,
109 InProgress,
110 Finalizing,
111 Completed,
112 Failed,
113 Expired,
114 Canceling,
115 Canceled,
116}
117
118#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
124pub struct ImageUrl {
125 pub url: String,
126 #[serde(default, skip_serializing_if = "Option::is_none")]
127 pub detail: Option<String>,
128}
129
130#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
136#[serde(tag = "type", rename_all = "snake_case")]
137pub enum ContentPart {
138 Text { text: String },
139 ImageUrl { image_url: ImageUrl },
140}
141
142impl ContentPart {
143 pub fn text(text: impl Into<String>) -> Self {
145 ContentPart::Text { text: text.into() }
146 }
147
148 pub fn image_url(url: impl Into<String>) -> Self {
150 ContentPart::ImageUrl {
151 image_url: ImageUrl {
152 url: url.into(),
153 detail: None,
154 },
155 }
156 }
157}
158
159#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
162#[serde(untagged)]
163pub enum Content {
164 Text(String),
165 Parts(Vec<ContentPart>),
166}
167
168impl From<String> for Content {
169 fn from(s: String) -> Self {
170 Content::Text(s)
171 }
172}
173
174impl From<&str> for Content {
175 fn from(s: &str) -> Self {
176 Content::Text(s.to_string())
177 }
178}
179
180impl From<Vec<ContentPart>> for Content {
181 fn from(p: Vec<ContentPart>) -> Self {
182 Content::Parts(p)
183 }
184}
185
186#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
192pub struct FunctionCall {
193 pub name: String,
194 pub arguments: String,
195}
196
197#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
199pub struct ToolCall {
200 pub id: String,
201 #[serde(rename = "type")]
202 pub kind: String,
203 pub function: FunctionCall,
204}
205
206#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
208pub struct FunctionCallDelta {
209 #[serde(default, skip_serializing_if = "Option::is_none")]
210 pub name: Option<String>,
211 #[serde(default, skip_serializing_if = "Option::is_none")]
212 pub arguments: Option<String>,
213}
214
215#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
217pub struct ToolCallDelta {
218 pub index: u32,
219 #[serde(default, skip_serializing_if = "Option::is_none")]
220 pub id: Option<String>,
221 #[serde(default, skip_serializing_if = "Option::is_none", rename = "type")]
222 pub kind: Option<String>,
223 #[serde(default, skip_serializing_if = "Option::is_none")]
224 pub function: Option<FunctionCallDelta>,
225}
226
227#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
244pub struct ReasoningDetail {
245 #[serde(rename = "type")]
246 pub kind: String,
247 #[serde(default, skip_serializing_if = "Option::is_none")]
248 pub text: Option<String>,
249 #[serde(default, skip_serializing_if = "Option::is_none")]
250 pub summary: Option<String>,
251 #[serde(default, skip_serializing_if = "Option::is_none")]
252 pub data: Option<String>,
253 #[serde(default, skip_serializing_if = "Option::is_none")]
254 pub signature: Option<String>,
255 #[serde(default, skip_serializing_if = "Option::is_none")]
256 pub id: Option<String>,
257 #[serde(default, skip_serializing_if = "Option::is_none")]
258 pub format: Option<String>,
259 #[serde(default, skip_serializing_if = "Option::is_none")]
260 pub index: Option<u32>,
261}
262
263impl ReasoningDetail {
264 pub fn is_hidden(&self) -> bool {
266 self.kind == "reasoning.encrypted" || (self.data.is_some() && self.text.is_none())
267 }
268
269 pub fn open_text(&self) -> Option<&str> {
272 self.text.as_deref().or(self.summary.as_deref())
273 }
274}
275
276#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
282pub struct ChatMessage {
283 pub role: Role,
284 #[serde(default, skip_serializing_if = "Option::is_none")]
287 pub content: Option<Content>,
288 #[serde(default, skip_serializing_if = "Option::is_none")]
289 pub name: Option<String>,
290 #[serde(default, skip_serializing_if = "Vec::is_empty")]
291 pub tool_calls: Vec<ToolCall>,
292 #[serde(default, skip_serializing_if = "Option::is_none")]
294 pub tool_call_id: Option<String>,
295 #[serde(default, skip_serializing_if = "Option::is_none")]
299 pub reasoning: Option<String>,
300 #[serde(default, skip_serializing_if = "Vec::is_empty")]
303 pub reasoning_details: Vec<ReasoningDetail>,
304}
305
306impl ChatMessage {
307 pub fn system(text: impl Into<String>) -> Self {
309 Self::with_text(Role::System, text)
310 }
311
312 pub fn user(text: impl Into<String>) -> Self {
314 Self::with_text(Role::User, text)
315 }
316
317 pub fn assistant(text: impl Into<String>) -> Self {
319 Self::with_text(Role::Assistant, text)
320 }
321
322 fn with_text(role: Role, text: impl Into<String>) -> Self {
323 ChatMessage {
324 role,
325 content: Some(Content::Text(text.into())),
326 name: None,
327 tool_calls: Vec::new(),
328 tool_call_id: None,
329 reasoning: None,
330 reasoning_details: Vec::new(),
331 }
332 }
333
334 pub fn text(&self) -> Option<&str> {
336 match &self.content {
337 Some(Content::Text(s)) => Some(s.as_str()),
338 _ => None,
339 }
340 }
341
342 pub fn reasoning_text(&self) -> Option<String> {
346 if let Some(r) = &self.reasoning {
347 return Some(r.clone());
348 }
349 let joined: String = self
350 .reasoning_details
351 .iter()
352 .filter_map(ReasoningDetail::open_text)
353 .collect();
354 (!joined.is_empty()).then_some(joined)
355 }
356}
357
358#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
364pub struct FunctionDef {
365 pub name: String,
366 #[serde(default, skip_serializing_if = "Option::is_none")]
367 pub description: Option<String>,
368 #[serde(default, skip_serializing_if = "Option::is_none")]
370 pub parameters: Option<serde_json::Value>,
371}
372
373#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
375pub struct ToolDef {
376 #[serde(rename = "type")]
377 pub kind: String,
378 pub function: FunctionDef,
379}
380
381impl ToolDef {
382 pub fn function(function: FunctionDef) -> Self {
384 ToolDef {
385 kind: "function".to_string(),
386 function,
387 }
388 }
389}
390
391#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
393pub struct FunctionName {
394 pub name: String,
395}
396
397#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
399pub struct NamedToolChoice {
400 #[serde(rename = "type")]
401 pub kind: String,
402 pub function: FunctionName,
403}
404
405#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
408#[serde(untagged)]
409pub enum ToolChoice {
410 Mode(String),
411 Named(NamedToolChoice),
412}
413
414impl ToolChoice {
415 pub fn mode(mode: impl Into<String>) -> Self {
417 ToolChoice::Mode(mode.into())
418 }
419
420 pub fn named(name: impl Into<String>) -> Self {
422 ToolChoice::Named(NamedToolChoice {
423 kind: "function".to_string(),
424 function: FunctionName { name: name.into() },
425 })
426 }
427}
428
429#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
431pub struct ResponseFormat {
432 #[serde(rename = "type")]
433 pub kind: String,
434 #[serde(default, skip_serializing_if = "Option::is_none")]
436 pub json_schema: Option<serde_json::Value>,
437}
438
439#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
446#[serde(untagged)]
447pub enum Stop {
448 One(String),
449 Many(Vec<String>),
450}
451
452impl Stop {
453 pub fn from_vec(mut v: Vec<String>) -> Option<Self> {
456 match v.len() {
457 0 => None,
458 1 => Some(Stop::One(v.pop().unwrap())),
459 _ => Some(Stop::Many(v)),
460 }
461 }
462}
463
464#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
466pub struct ChatRequest {
467 pub model: String,
468 pub messages: Vec<ChatMessage>,
469 #[serde(default, skip_serializing_if = "Option::is_none")]
470 pub stream: Option<bool>,
471 #[serde(default, skip_serializing_if = "Option::is_none")]
472 pub temperature: Option<f32>,
473 #[serde(default, skip_serializing_if = "Option::is_none")]
474 pub top_p: Option<f32>,
475 #[serde(default, skip_serializing_if = "Option::is_none")]
478 pub max_tokens: Option<u32>,
479 #[serde(default, skip_serializing_if = "Option::is_none")]
481 pub max_completion_tokens: Option<u32>,
482 #[serde(default, skip_serializing_if = "Option::is_none")]
483 pub stop: Option<Stop>,
484 #[serde(default, skip_serializing_if = "Option::is_none")]
485 pub n: Option<u32>,
486 #[serde(default, skip_serializing_if = "Option::is_none")]
487 pub seed: Option<i64>,
488 #[serde(default, skip_serializing_if = "Option::is_none")]
489 pub frequency_penalty: Option<f32>,
490 #[serde(default, skip_serializing_if = "Option::is_none")]
491 pub presence_penalty: Option<f32>,
492 #[serde(default, skip_serializing_if = "Vec::is_empty")]
493 pub tools: Vec<ToolDef>,
494 #[serde(default, skip_serializing_if = "Option::is_none")]
495 pub tool_choice: Option<ToolChoice>,
496 #[serde(default, skip_serializing_if = "Option::is_none")]
497 pub response_format: Option<ResponseFormat>,
498 #[serde(default, skip_serializing_if = "Option::is_none")]
500 pub reasoning_effort: Option<String>,
501 #[serde(default, flatten, skip_serializing_if = "Option::is_none")]
504 pub extra: Option<serde_json::Map<String, serde_json::Value>>,
505}
506
507impl ChatRequest {
508 pub fn new(model: impl Into<String>, messages: Vec<ChatMessage>) -> Self {
510 ChatRequest {
511 model: model.into(),
512 messages,
513 ..Default::default()
514 }
515 }
516}
517
518#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
524pub struct Choice {
525 #[serde(default)]
526 pub index: u32,
527 pub message: ChatMessage,
528 #[serde(default, skip_serializing_if = "Option::is_none")]
529 pub finish_reason: Option<FinishReason>,
530}
531
532#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
534pub struct ChatResponse {
535 pub id: String,
536 pub object: String,
537 pub created: i64,
538 pub model: String,
539 pub choices: Vec<Choice>,
540 #[serde(default, skip_serializing_if = "Option::is_none")]
541 pub usage: Option<Usage>,
542}
543
544impl ChatResponse {
545 pub fn first_text(&self) -> Option<&str> {
547 self.choices.first().and_then(|c| c.message.text())
548 }
549}
550
551#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
557pub struct Delta {
558 #[serde(default, skip_serializing_if = "Option::is_none")]
560 pub role: Option<Role>,
561 #[serde(default, skip_serializing_if = "Option::is_none")]
563 pub content: Option<String>,
564 #[serde(default, skip_serializing_if = "Vec::is_empty")]
565 pub tool_calls: Vec<ToolCallDelta>,
566 #[serde(default, skip_serializing_if = "Option::is_none")]
568 pub reasoning: Option<String>,
569 #[serde(default, skip_serializing_if = "Vec::is_empty")]
571 pub reasoning_details: Vec<ReasoningDetail>,
572}
573
574#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
576pub struct ChunkChoice {
577 #[serde(default)]
578 pub index: u32,
579 pub delta: Delta,
580 #[serde(default, skip_serializing_if = "Option::is_none")]
581 pub finish_reason: Option<FinishReason>,
582}
583
584#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
586pub struct ChatCompletionChunk {
587 pub id: String,
588 pub object: String,
589 pub created: i64,
590 pub model: String,
591 pub choices: Vec<ChunkChoice>,
592 #[serde(default, skip_serializing_if = "Option::is_none")]
594 pub usage: Option<Usage>,
595}
596
597impl ChatCompletionChunk {
598 pub fn first_delta_text(&self) -> Option<&str> {
600 self.choices
601 .first()
602 .and_then(|c| c.delta.content.as_deref())
603 }
604
605 pub fn first_delta_reasoning(&self) -> Option<&str> {
607 self.choices
608 .first()
609 .and_then(|c| c.delta.reasoning.as_deref())
610 }
611}
612
613#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
619#[serde(untagged)]
620pub enum EmbeddingInput {
621 One(String),
622 Many(Vec<String>),
623}
624
625impl From<String> for EmbeddingInput {
626 fn from(s: String) -> Self {
627 EmbeddingInput::One(s)
628 }
629}
630
631impl From<&str> for EmbeddingInput {
632 fn from(s: &str) -> Self {
633 EmbeddingInput::One(s.to_string())
634 }
635}
636
637impl From<Vec<String>> for EmbeddingInput {
638 fn from(v: Vec<String>) -> Self {
639 EmbeddingInput::Many(v)
640 }
641}
642
643#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
645pub struct EmbeddingRequest {
646 pub model: String,
647 pub input: EmbeddingInput,
648 #[serde(default, skip_serializing_if = "Option::is_none")]
649 pub dimensions: Option<u32>,
650 #[serde(default, skip_serializing_if = "Option::is_none")]
653 pub encoding_format: Option<String>,
654 #[serde(default, skip_serializing_if = "Option::is_none")]
656 pub extra: Option<serde_json::Value>,
657}
658
659impl EmbeddingRequest {
660 pub fn new(model: impl Into<String>, input: impl Into<EmbeddingInput>) -> Self {
662 EmbeddingRequest {
663 model: model.into(),
664 input: input.into(),
665 dimensions: None,
666 encoding_format: None,
667 extra: None,
668 }
669 }
670}
671
672#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
675pub struct Embedding {
676 pub object: String,
677 #[serde(default)]
678 pub index: u32,
679 pub embedding: Vec<f32>,
680}
681
682#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
684pub struct EmbeddingResponse {
685 pub object: String,
686 pub data: Vec<Embedding>,
687 pub model: String,
688 #[serde(default, skip_serializing_if = "Option::is_none")]
689 pub usage: Option<Usage>,
690}
691
692#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
698pub struct SpeechRequest {
699 pub model: String,
700 pub input: String,
701 pub voice: String,
702 #[serde(default, skip_serializing_if = "Option::is_none")]
704 pub response_format: Option<String>,
705 #[serde(default, skip_serializing_if = "Option::is_none")]
706 pub speed: Option<f32>,
707 #[serde(default, skip_serializing_if = "Option::is_none")]
709 pub extra: Option<serde_json::Value>,
710}
711
712impl SpeechRequest {
713 pub fn new(
715 model: impl Into<String>,
716 input: impl Into<String>,
717 voice: impl Into<String>,
718 ) -> Self {
719 SpeechRequest {
720 model: model.into(),
721 input: input.into(),
722 voice: voice.into(),
723 response_format: None,
724 speed: None,
725 extra: None,
726 }
727 }
728}
729
730#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
732pub struct Voice {
733 pub id: String,
734 #[serde(default, skip_serializing_if = "Option::is_none")]
735 pub name: Option<String>,
736 #[serde(default, skip_serializing_if = "Vec::is_empty")]
737 pub languages: Vec<String>,
738}
739
740#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
742pub struct VoicesResponse {
743 pub model: String,
744 pub voices: Vec<Voice>,
745}
746
747#[derive(Debug, Clone, PartialEq, Default)]
754pub struct TranscriptionRequest {
755 pub model: String,
756 pub language: Option<String>,
758 pub prompt: Option<String>,
760 pub response_format: Option<String>,
762 pub temperature: Option<f32>,
763}
764
765impl TranscriptionRequest {
766 pub fn new(model: impl Into<String>) -> Self {
768 TranscriptionRequest {
769 model: model.into(),
770 ..Default::default()
771 }
772 }
773}
774
775#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
777pub struct TranscriptionResponse {
778 pub text: String,
779 #[serde(default, skip_serializing_if = "Option::is_none")]
780 pub task: Option<String>,
781 #[serde(default, skip_serializing_if = "Option::is_none")]
782 pub language: Option<String>,
783 #[serde(default, skip_serializing_if = "Option::is_none")]
784 pub duration: Option<f32>,
785 #[serde(default, skip_serializing_if = "Option::is_none")]
786 pub usage: Option<Usage>,
787}
788
789#[derive(Debug, Clone, PartialEq)]
792pub enum Transcription {
793 Json(TranscriptionResponse),
794 Text(String),
795}
796
797#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
803pub struct Architecture {
804 #[serde(default, skip_serializing_if = "Vec::is_empty")]
805 pub input_modalities: Vec<String>,
806 #[serde(default, skip_serializing_if = "Vec::is_empty")]
807 pub output_modalities: Vec<String>,
808 #[serde(default, skip_serializing_if = "Option::is_none")]
809 pub modality: Option<String>,
810 #[serde(default)]
811 pub tokenizer: String,
812 #[serde(default, skip_serializing_if = "Option::is_none")]
813 pub instruct_type: Option<String>,
814}
815
816#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
818pub struct Pricing {
819 pub prompt: String,
820 pub completion: String,
821}
822
823#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
825pub struct TopProvider {
826 #[serde(default, skip_serializing_if = "Option::is_none")]
827 pub context_length: Option<u32>,
828 #[serde(default, skip_serializing_if = "Option::is_none")]
829 pub max_completion_tokens: Option<u32>,
830 #[serde(default)]
831 pub is_moderated: bool,
832 #[serde(default, skip_serializing_if = "Option::is_none")]
833 pub max_thinking_tokens: Option<u32>,
834}
835
836#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
838pub struct ModelEndpoint {
839 pub provider: String,
840 pub model: String,
841 #[serde(default)]
842 pub down: bool,
843 pub source: String,
845}
846
847#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
849pub struct ModelEntry {
850 pub id: String,
851 #[serde(default)]
852 pub canonical_slug: String,
853 #[serde(default)]
854 pub name: String,
855 #[serde(default)]
856 pub created: i64,
857 #[serde(default)]
858 pub description: String,
859 #[serde(default, skip_serializing_if = "Option::is_none")]
860 pub context_length: Option<u32>,
861 #[serde(default, skip_serializing_if = "Option::is_none")]
862 pub architecture: Option<Architecture>,
863 #[serde(default, skip_serializing_if = "Option::is_none")]
864 pub pricing: Option<Pricing>,
865 #[serde(default, skip_serializing_if = "Option::is_none")]
866 pub top_provider: Option<TopProvider>,
867 #[serde(default, skip_serializing_if = "Vec::is_empty")]
868 pub supported_parameters: Vec<String>,
869 #[serde(default, skip_serializing_if = "Vec::is_empty")]
870 pub unsupported_parameters: Vec<String>,
871 #[serde(default, skip_serializing_if = "Option::is_none")]
873 pub default_parameters: Option<serde_json::Value>,
874 #[serde(default, skip_serializing_if = "Vec::is_empty")]
876 pub endpoints: Vec<ModelEndpoint>,
877}
878
879#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
881pub struct ListModelsResponse {
882 #[serde(default)]
883 pub data: Vec<ModelEntry>,
884}
885
886#[derive(Debug, Clone, Copy, PartialEq, Eq)]
888pub enum ModelType {
889 All,
890 Llm,
891 Tts,
892 Stt,
893 Embedding,
894}
895
896impl ModelType {
897 pub(crate) fn as_str(self) -> &'static str {
898 match self {
899 ModelType::All => "all",
900 ModelType::Llm => "llm",
901 ModelType::Tts => "tts",
902 ModelType::Stt => "stt",
903 ModelType::Embedding => "embedding",
904 }
905 }
906}
907
908#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
914pub struct BatchRequestItem {
915 pub custom_id: String,
916 pub body: ChatRequest,
917}
918
919#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
921pub struct BatchCreateRequest {
922 pub requests: Vec<BatchRequestItem>,
923}
924
925#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
927pub struct BatchCounts {
928 #[serde(default)]
929 pub total: u64,
930 #[serde(default)]
931 pub processing: u64,
932 #[serde(default)]
933 pub succeeded: u64,
934 #[serde(default)]
935 pub errored: u64,
936 #[serde(default)]
937 pub canceled: u64,
938 #[serde(default)]
939 pub expired: u64,
940}
941
942#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
944pub struct BatchHandle {
945 pub id: String,
946 pub status: BatchStatus,
947 #[serde(default)]
948 pub counts: BatchCounts,
949 #[serde(default, skip_serializing_if = "Option::is_none")]
950 pub created_at: Option<i64>,
951 #[serde(default, skip_serializing_if = "Option::is_none")]
952 pub expires_at: Option<i64>,
953 #[serde(default, skip_serializing_if = "Option::is_none")]
954 pub ended_at: Option<i64>,
955 #[serde(default, skip_serializing_if = "Option::is_none")]
956 pub endpoint: Option<String>,
957}
958
959#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
961pub struct BatchResponse {
962 pub status_code: u32,
963 pub body: ChatResponse,
964}
965
966#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
968pub struct BatchError {
969 pub code: String,
970 pub message: String,
971}
972
973#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
975pub struct BatchResultLine {
976 pub custom_id: String,
977 #[serde(default, skip_serializing_if = "Option::is_none")]
978 pub response: Option<BatchResponse>,
979 #[serde(default, skip_serializing_if = "Option::is_none")]
980 pub error: Option<BatchError>,
981}