1use serde::de::{self, Deserializer};
28use serde::ser::{SerializeSeq, Serializer};
29use serde::{Deserialize, Serialize};
30
31#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
38pub struct Usage {
39 #[serde(default)]
40 pub prompt_tokens: u32,
41 #[serde(default)]
42 pub completion_tokens: u32,
43 #[serde(default)]
44 pub total_tokens: u32,
45 #[serde(default, skip_serializing_if = "Option::is_none")]
46 pub cost_usd: Option<f64>,
47 #[serde(default, skip_serializing_if = "Option::is_none")]
50 pub prompt_tokens_details: Option<PromptTokensDetails>,
51 #[serde(default, skip_serializing_if = "Option::is_none")]
54 pub cache_creation_tokens: Option<u32>,
55}
56
57impl Usage {
58 pub fn cached_tokens(&self) -> u32 {
61 self.prompt_tokens_details
62 .as_ref()
63 .and_then(|d| d.cached_tokens)
64 .unwrap_or(0)
65 }
66
67 pub fn cache_writes(&self) -> u32 {
70 self.cache_creation_tokens.unwrap_or(0)
71 }
72}
73
74#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
77pub struct PromptTokensDetails {
78 #[serde(default, skip_serializing_if = "Option::is_none")]
79 pub cached_tokens: Option<u32>,
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
88#[serde(rename_all = "snake_case")]
89pub enum Role {
90 System,
91 User,
92 Assistant,
93 Tool,
94}
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
98#[serde(rename_all = "snake_case")]
99pub enum FinishReason {
100 Stop,
101 Length,
102 ToolCalls,
103 ContentFilter,
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
108#[serde(rename_all = "snake_case")]
109pub enum BatchStatus {
110 Validating,
111 InProgress,
112 Finalizing,
113 Completed,
114 Failed,
115 Expired,
116 Canceling,
117 Canceled,
118}
119
120#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
126pub struct ImageUrl {
127 pub url: String,
128 #[serde(default, skip_serializing_if = "Option::is_none")]
129 pub detail: Option<String>,
130}
131
132#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
138#[serde(tag = "type", rename_all = "snake_case")]
139pub enum ContentPart {
140 Text { text: String },
141 ImageUrl { image_url: ImageUrl },
142}
143
144impl ContentPart {
145 pub fn text(text: impl Into<String>) -> Self {
147 ContentPart::Text { text: text.into() }
148 }
149
150 pub fn image_url(url: impl Into<String>) -> Self {
152 ContentPart::ImageUrl {
153 image_url: ImageUrl {
154 url: url.into(),
155 detail: None,
156 },
157 }
158 }
159}
160
161#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
164#[serde(untagged)]
165pub enum Content {
166 Text(String),
167 Parts(Vec<ContentPart>),
168}
169
170impl From<String> for Content {
171 fn from(s: String) -> Self {
172 Content::Text(s)
173 }
174}
175
176impl From<&str> for Content {
177 fn from(s: &str) -> Self {
178 Content::Text(s.to_string())
179 }
180}
181
182impl From<Vec<ContentPart>> for Content {
183 fn from(p: Vec<ContentPart>) -> Self {
184 Content::Parts(p)
185 }
186}
187
188#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
194pub struct FunctionCall {
195 pub name: String,
196 pub arguments: String,
197}
198
199#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
201pub struct ToolCall {
202 pub id: String,
203 #[serde(rename = "type")]
204 pub kind: String,
205 pub function: FunctionCall,
206}
207
208#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
210pub struct FunctionCallDelta {
211 #[serde(default, skip_serializing_if = "Option::is_none")]
212 pub name: Option<String>,
213 #[serde(default, skip_serializing_if = "Option::is_none")]
214 pub arguments: Option<String>,
215}
216
217#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
219pub struct ToolCallDelta {
220 pub index: u32,
221 #[serde(default, skip_serializing_if = "Option::is_none")]
222 pub id: Option<String>,
223 #[serde(default, skip_serializing_if = "Option::is_none", rename = "type")]
224 pub kind: Option<String>,
225 #[serde(default, skip_serializing_if = "Option::is_none")]
226 pub function: Option<FunctionCallDelta>,
227}
228
229#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
246pub struct ReasoningDetail {
247 #[serde(rename = "type")]
248 pub kind: String,
249 #[serde(default, skip_serializing_if = "Option::is_none")]
250 pub text: Option<String>,
251 #[serde(default, skip_serializing_if = "Option::is_none")]
252 pub summary: Option<String>,
253 #[serde(default, skip_serializing_if = "Option::is_none")]
254 pub data: Option<String>,
255 #[serde(default, skip_serializing_if = "Option::is_none")]
256 pub signature: Option<String>,
257 #[serde(default, skip_serializing_if = "Option::is_none")]
258 pub id: Option<String>,
259 #[serde(default, skip_serializing_if = "Option::is_none")]
260 pub format: Option<String>,
261 #[serde(default, skip_serializing_if = "Option::is_none")]
262 pub index: Option<u32>,
263}
264
265impl ReasoningDetail {
266 pub fn is_hidden(&self) -> bool {
268 self.kind == "reasoning.encrypted" || (self.data.is_some() && self.text.is_none())
269 }
270
271 pub fn open_text(&self) -> Option<&str> {
274 self.text.as_deref().or(self.summary.as_deref())
275 }
276}
277
278#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
284pub struct ChatMessage {
285 pub role: Role,
286 #[serde(default, skip_serializing_if = "Option::is_none")]
289 pub content: Option<Content>,
290 #[serde(default, skip_serializing_if = "Option::is_none")]
291 pub name: Option<String>,
292 #[serde(default, skip_serializing_if = "Vec::is_empty")]
293 pub tool_calls: Vec<ToolCall>,
294 #[serde(default, skip_serializing_if = "Option::is_none")]
296 pub tool_call_id: Option<String>,
297 #[serde(default, skip_serializing_if = "Option::is_none")]
301 pub reasoning: Option<String>,
302 #[serde(default, skip_serializing_if = "Vec::is_empty")]
305 pub reasoning_details: Vec<ReasoningDetail>,
306}
307
308impl ChatMessage {
309 pub fn system(text: impl Into<String>) -> Self {
311 Self::with_text(Role::System, text)
312 }
313
314 pub fn user(text: impl Into<String>) -> Self {
316 Self::with_text(Role::User, text)
317 }
318
319 pub fn assistant(text: impl Into<String>) -> Self {
321 Self::with_text(Role::Assistant, text)
322 }
323
324 fn with_text(role: Role, text: impl Into<String>) -> Self {
325 ChatMessage {
326 role,
327 content: Some(Content::Text(text.into())),
328 name: None,
329 tool_calls: Vec::new(),
330 tool_call_id: None,
331 reasoning: None,
332 reasoning_details: Vec::new(),
333 }
334 }
335
336 pub fn text(&self) -> Option<&str> {
338 match &self.content {
339 Some(Content::Text(s)) => Some(s.as_str()),
340 _ => None,
341 }
342 }
343
344 pub fn reasoning_text(&self) -> Option<String> {
348 if let Some(r) = &self.reasoning {
349 return Some(r.clone());
350 }
351 let joined: String = self
352 .reasoning_details
353 .iter()
354 .filter_map(ReasoningDetail::open_text)
355 .collect();
356 (!joined.is_empty()).then_some(joined)
357 }
358}
359
360#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
366pub struct FunctionDef {
367 pub name: String,
368 #[serde(default, skip_serializing_if = "Option::is_none")]
369 pub description: Option<String>,
370 #[serde(default, skip_serializing_if = "Option::is_none")]
372 pub parameters: Option<serde_json::Value>,
373}
374
375#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
377pub struct ToolDef {
378 #[serde(rename = "type")]
379 pub kind: String,
380 pub function: FunctionDef,
381}
382
383impl ToolDef {
384 pub fn function(function: FunctionDef) -> Self {
386 ToolDef {
387 kind: "function".to_string(),
388 function,
389 }
390 }
391}
392
393#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
395pub struct FunctionName {
396 pub name: String,
397}
398
399#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
401pub struct NamedToolChoice {
402 #[serde(rename = "type")]
403 pub kind: String,
404 pub function: FunctionName,
405}
406
407#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
410#[serde(untagged)]
411pub enum ToolChoice {
412 Mode(String),
413 Named(NamedToolChoice),
414}
415
416impl ToolChoice {
417 pub fn mode(mode: impl Into<String>) -> Self {
419 ToolChoice::Mode(mode.into())
420 }
421
422 pub fn named(name: impl Into<String>) -> Self {
424 ToolChoice::Named(NamedToolChoice {
425 kind: "function".to_string(),
426 function: FunctionName { name: name.into() },
427 })
428 }
429}
430
431#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
433pub struct ResponseFormat {
434 #[serde(rename = "type")]
435 pub kind: String,
436 #[serde(default, skip_serializing_if = "Option::is_none")]
438 pub json_schema: Option<serde_json::Value>,
439}
440
441#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
448#[serde(untagged)]
449pub enum Stop {
450 One(String),
451 Many(Vec<String>),
452}
453
454impl Stop {
455 pub fn from_vec(mut v: Vec<String>) -> Option<Self> {
458 match v.len() {
459 0 => None,
460 1 => Some(Stop::One(v.pop().unwrap())),
461 _ => Some(Stop::Many(v)),
462 }
463 }
464}
465
466#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
468pub struct ChatRequest {
469 pub model: String,
470 pub messages: Vec<ChatMessage>,
471 #[serde(default, skip_serializing_if = "Option::is_none")]
472 pub stream: Option<bool>,
473 #[serde(default, skip_serializing_if = "Option::is_none")]
474 pub temperature: Option<f32>,
475 #[serde(default, skip_serializing_if = "Option::is_none")]
476 pub top_p: Option<f32>,
477 #[serde(default, skip_serializing_if = "Option::is_none")]
480 pub max_tokens: Option<u32>,
481 #[serde(default, skip_serializing_if = "Option::is_none")]
483 pub max_completion_tokens: Option<u32>,
484 #[serde(default, skip_serializing_if = "Option::is_none")]
485 pub stop: Option<Stop>,
486 #[serde(default, skip_serializing_if = "Option::is_none")]
487 pub n: Option<u32>,
488 #[serde(default, skip_serializing_if = "Option::is_none")]
489 pub seed: Option<i64>,
490 #[serde(default, skip_serializing_if = "Option::is_none")]
491 pub frequency_penalty: Option<f32>,
492 #[serde(default, skip_serializing_if = "Option::is_none")]
493 pub presence_penalty: Option<f32>,
494 #[serde(default, skip_serializing_if = "Vec::is_empty")]
495 pub tools: Vec<ToolDef>,
496 #[serde(default, skip_serializing_if = "Option::is_none")]
497 pub tool_choice: Option<ToolChoice>,
498 #[serde(default, skip_serializing_if = "Option::is_none")]
499 pub response_format: Option<ResponseFormat>,
500 #[serde(default, skip_serializing_if = "Option::is_none")]
502 pub reasoning_effort: Option<String>,
503 #[serde(default, flatten, skip_serializing_if = "Option::is_none")]
506 pub extra: Option<serde_json::Map<String, serde_json::Value>>,
507}
508
509impl ChatRequest {
510 pub fn new(model: impl Into<String>, messages: Vec<ChatMessage>) -> Self {
512 ChatRequest {
513 model: model.into(),
514 messages,
515 ..Default::default()
516 }
517 }
518}
519
520#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
526pub struct Choice {
527 #[serde(default)]
528 pub index: u32,
529 pub message: ChatMessage,
530 #[serde(default, skip_serializing_if = "Option::is_none")]
531 pub finish_reason: Option<FinishReason>,
532}
533
534#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
536pub struct ChatResponse {
537 pub id: String,
538 pub object: String,
539 pub created: i64,
540 pub model: String,
541 pub choices: Vec<Choice>,
542 #[serde(default, skip_serializing_if = "Option::is_none")]
543 pub usage: Option<Usage>,
544}
545
546impl ChatResponse {
547 pub fn first_text(&self) -> Option<&str> {
549 self.choices.first().and_then(|c| c.message.text())
550 }
551}
552
553#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
559pub struct Delta {
560 #[serde(default, skip_serializing_if = "Option::is_none")]
562 pub role: Option<Role>,
563 #[serde(default, skip_serializing_if = "Option::is_none")]
565 pub content: Option<String>,
566 #[serde(default, skip_serializing_if = "Vec::is_empty")]
567 pub tool_calls: Vec<ToolCallDelta>,
568 #[serde(default, skip_serializing_if = "Option::is_none")]
570 pub reasoning: Option<String>,
571 #[serde(default, skip_serializing_if = "Vec::is_empty")]
573 pub reasoning_details: Vec<ReasoningDetail>,
574}
575
576#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
578pub struct ChunkChoice {
579 #[serde(default)]
580 pub index: u32,
581 pub delta: Delta,
582 #[serde(default, skip_serializing_if = "Option::is_none")]
583 pub finish_reason: Option<FinishReason>,
584}
585
586#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
588pub struct ChatCompletionChunk {
589 pub id: String,
590 pub object: String,
591 pub created: i64,
592 pub model: String,
593 pub choices: Vec<ChunkChoice>,
594 #[serde(default, skip_serializing_if = "Option::is_none")]
596 pub usage: Option<Usage>,
597}
598
599impl ChatCompletionChunk {
600 pub fn first_delta_text(&self) -> Option<&str> {
602 self.choices
603 .first()
604 .and_then(|c| c.delta.content.as_deref())
605 }
606
607 pub fn first_delta_reasoning(&self) -> Option<&str> {
609 self.choices
610 .first()
611 .and_then(|c| c.delta.reasoning.as_deref())
612 }
613}
614
615#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
629pub struct ResponseFunctionCallOutputItem {
630 #[serde(default, skip_serializing_if = "Option::is_none")]
631 pub id: Option<String>,
632 pub call_id: String,
633 pub output: String,
634}
635
636#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
640pub struct ResponseFunctionCallItem {
641 #[serde(default, skip_serializing_if = "Option::is_none")]
642 pub id: Option<String>,
643 pub call_id: String,
644 pub name: String,
645 pub arguments: String,
646 #[serde(default, skip_serializing_if = "Option::is_none")]
647 pub status: Option<String>,
648}
649
650#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
653pub struct ResponseReasoningText {
654 pub text: String,
655}
656
657impl ResponseReasoningText {
658 pub fn new(text: impl Into<String>) -> Self {
660 ResponseReasoningText { text: text.into() }
661 }
662}
663
664#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
667pub struct ResponseReasoningItem {
668 #[serde(default, skip_serializing_if = "Option::is_none")]
669 pub id: Option<String>,
670 #[serde(
672 default,
673 skip_serializing_if = "Vec::is_empty",
674 serialize_with = "serialize_summary_texts"
675 )]
676 pub summary: Vec<ResponseReasoningText>,
677 #[serde(
679 default,
680 skip_serializing_if = "Vec::is_empty",
681 serialize_with = "serialize_content_texts"
682 )]
683 pub content: Vec<ResponseReasoningText>,
684 #[serde(default, skip_serializing_if = "Option::is_none")]
685 pub encrypted_content: Option<String>,
686}
687
688#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
692#[serde(tag = "type", rename_all = "snake_case")]
693pub enum ResponseContentPart {
694 InputText {
695 text: String,
696 },
697 InputImage {
698 image_url: String,
699 #[serde(default, skip_serializing_if = "Option::is_none")]
700 detail: Option<String>,
701 },
702 OutputText {
703 text: String,
704 #[serde(default)]
706 annotations: Vec<serde_json::Value>,
707 },
708}
709
710impl ResponseContentPart {
711 pub fn input_text(text: impl Into<String>) -> Self {
713 ResponseContentPart::InputText { text: text.into() }
714 }
715
716 pub fn input_image(image_url: impl Into<String>) -> Self {
718 ResponseContentPart::InputImage {
719 image_url: image_url.into(),
720 detail: None,
721 }
722 }
723
724 pub fn output_text(text: impl Into<String>) -> Self {
726 ResponseContentPart::OutputText {
727 text: text.into(),
728 annotations: Vec::new(),
729 }
730 }
731
732 pub fn text(&self) -> Option<&str> {
734 match self {
735 ResponseContentPart::InputText { text }
736 | ResponseContentPart::OutputText { text, .. } => Some(text.as_str()),
737 ResponseContentPart::InputImage { .. } => None,
738 }
739 }
740}
741
742#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
744#[serde(untagged)]
745pub enum ResponseContent {
746 Text(String),
747 Parts(Vec<ResponseContentPart>),
748}
749
750impl From<String> for ResponseContent {
751 fn from(s: String) -> Self {
752 ResponseContent::Text(s)
753 }
754}
755
756impl From<&str> for ResponseContent {
757 fn from(s: &str) -> Self {
758 ResponseContent::Text(s.to_string())
759 }
760}
761
762impl From<Vec<ResponseContentPart>> for ResponseContent {
763 fn from(p: Vec<ResponseContentPart>) -> Self {
764 ResponseContent::Parts(p)
765 }
766}
767
768#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
771pub struct ResponseMessageItem {
772 #[serde(default, skip_serializing_if = "Option::is_none")]
773 pub id: Option<String>,
774 pub role: String,
777 #[serde(default, skip_serializing_if = "Option::is_none")]
778 pub content: Option<ResponseContent>,
779 #[serde(default, skip_serializing_if = "Option::is_none")]
781 pub status: Option<String>,
782}
783
784impl ResponseMessageItem {
785 fn with_role(role: &str, content: impl Into<ResponseContent>) -> Self {
786 ResponseMessageItem {
787 id: None,
788 role: role.to_string(),
789 content: Some(content.into()),
790 status: None,
791 }
792 }
793
794 pub fn user(content: impl Into<ResponseContent>) -> Self {
796 Self::with_role("user", content)
797 }
798
799 pub fn system(content: impl Into<ResponseContent>) -> Self {
801 Self::with_role("system", content)
802 }
803
804 pub fn developer(content: impl Into<ResponseContent>) -> Self {
806 Self::with_role("developer", content)
807 }
808
809 pub fn assistant(content: impl Into<ResponseContent>) -> Self {
811 Self::with_role("assistant", content)
812 }
813}
814
815#[derive(Debug, Clone, PartialEq)]
820pub enum ResponseItem {
821 Message(ResponseMessageItem),
822 FunctionCall(ResponseFunctionCallItem),
823 FunctionCallOutput(ResponseFunctionCallOutputItem),
824 Reasoning(ResponseReasoningItem),
825 Other(serde_json::Value),
827}
828
829impl ResponseItem {
830 pub fn user(content: impl Into<ResponseContent>) -> Self {
832 ResponseItem::Message(ResponseMessageItem::user(content))
833 }
834
835 pub fn system(content: impl Into<ResponseContent>) -> Self {
837 ResponseItem::Message(ResponseMessageItem::system(content))
838 }
839
840 pub fn developer(content: impl Into<ResponseContent>) -> Self {
842 ResponseItem::Message(ResponseMessageItem::developer(content))
843 }
844
845 pub fn assistant(content: impl Into<ResponseContent>) -> Self {
847 ResponseItem::Message(ResponseMessageItem::assistant(content))
848 }
849
850 pub fn function_call(
852 call_id: impl Into<String>,
853 name: impl Into<String>,
854 arguments: impl Into<String>,
855 ) -> Self {
856 ResponseItem::FunctionCall(ResponseFunctionCallItem {
857 id: None,
858 call_id: call_id.into(),
859 name: name.into(),
860 arguments: arguments.into(),
861 status: None,
862 })
863 }
864
865 pub fn function_call_output(call_id: impl Into<String>, output: impl Into<String>) -> Self {
867 ResponseItem::FunctionCallOutput(ResponseFunctionCallOutputItem {
868 id: None,
869 call_id: call_id.into(),
870 output: output.into(),
871 })
872 }
873}
874
875#[derive(Serialize)]
878#[serde(tag = "type", rename_all = "snake_case")]
879enum TypedItemRef<'a> {
880 FunctionCall(&'a ResponseFunctionCallItem),
881 FunctionCallOutput(&'a ResponseFunctionCallOutputItem),
882 Reasoning(&'a ResponseReasoningItem),
883}
884
885impl Serialize for ResponseItem {
886 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
887 match self {
888 ResponseItem::Message(m) => m.serialize(serializer),
890 ResponseItem::FunctionCall(f) => TypedItemRef::FunctionCall(f).serialize(serializer),
891 ResponseItem::FunctionCallOutput(f) => {
892 TypedItemRef::FunctionCallOutput(f).serialize(serializer)
893 }
894 ResponseItem::Reasoning(r) => TypedItemRef::Reasoning(r).serialize(serializer),
895 ResponseItem::Other(v) => v.serialize(serializer),
896 }
897 }
898}
899
900impl<'de> Deserialize<'de> for ResponseItem {
901 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
902 where
903 D: Deserializer<'de>,
904 {
905 let value = serde_json::Value::deserialize(deserializer)?;
906 let ty = value
908 .get("type")
909 .and_then(serde_json::Value::as_str)
910 .map(str::to_owned);
911 let item = match ty.as_deref() {
912 None | Some("message") => {
913 ResponseItem::Message(serde_json::from_value(value).map_err(de::Error::custom)?)
914 }
915 Some("function_call") => {
916 ResponseItem::FunctionCall(serde_json::from_value(value).map_err(de::Error::custom)?)
917 }
918 Some("function_call_output") => ResponseItem::FunctionCallOutput(
919 serde_json::from_value(value).map_err(de::Error::custom)?,
920 ),
921 Some("reasoning") => {
922 ResponseItem::Reasoning(serde_json::from_value(value).map_err(de::Error::custom)?)
923 }
924 Some(_) => ResponseItem::Other(value),
926 };
927 Ok(item)
928 }
929}
930
931#[derive(Serialize)]
933struct TaggedReasoningText<'a> {
934 #[serde(rename = "type")]
935 kind: &'static str,
936 text: &'a str,
937}
938
939fn serialize_reasoning_texts<S: Serializer>(
940 texts: &[ResponseReasoningText],
941 kind: &'static str,
942 serializer: S,
943) -> Result<S::Ok, S::Error> {
944 let mut seq = serializer.serialize_seq(Some(texts.len()))?;
945 for t in texts {
946 seq.serialize_element(&TaggedReasoningText {
947 kind,
948 text: &t.text,
949 })?;
950 }
951 seq.end()
952}
953
954fn serialize_summary_texts<S: Serializer>(
955 texts: &[ResponseReasoningText],
956 serializer: S,
957) -> Result<S::Ok, S::Error> {
958 serialize_reasoning_texts(texts, "summary_text", serializer)
959}
960
961fn serialize_content_texts<S: Serializer>(
962 texts: &[ResponseReasoningText],
963 serializer: S,
964) -> Result<S::Ok, S::Error> {
965 serialize_reasoning_texts(texts, "reasoning_text", serializer)
966}
967
968#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
971pub struct ResponsesToolDef {
972 #[serde(rename = "type")]
973 pub kind: String,
974 pub name: String,
975 #[serde(default, skip_serializing_if = "Option::is_none")]
976 pub description: Option<String>,
977 #[serde(default, skip_serializing_if = "Option::is_none")]
979 pub parameters: Option<serde_json::Value>,
980 #[serde(default, skip_serializing_if = "Option::is_none")]
981 pub strict: Option<bool>,
982}
983
984impl ResponsesToolDef {
985 pub fn function(name: impl Into<String>) -> Self {
987 ResponsesToolDef {
988 kind: "function".to_string(),
989 name: name.into(),
990 description: None,
991 parameters: None,
992 strict: None,
993 }
994 }
995}
996
997#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
999pub struct ResponsesNamedToolChoice {
1000 #[serde(rename = "type")]
1001 pub kind: String,
1002 pub name: String,
1003}
1004
1005#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1008#[serde(untagged)]
1009pub enum ResponsesToolChoice {
1010 Mode(String),
1011 Named(ResponsesNamedToolChoice),
1012}
1013
1014impl ResponsesToolChoice {
1015 pub fn mode(mode: impl Into<String>) -> Self {
1017 ResponsesToolChoice::Mode(mode.into())
1018 }
1019
1020 pub fn named(name: impl Into<String>) -> Self {
1022 ResponsesToolChoice::Named(ResponsesNamedToolChoice {
1023 kind: "function".to_string(),
1024 name: name.into(),
1025 })
1026 }
1027}
1028
1029#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1031pub struct ResponsesReasoning {
1032 #[serde(default, skip_serializing_if = "Option::is_none")]
1033 pub effort: Option<String>,
1034 #[serde(default, skip_serializing_if = "Option::is_none")]
1035 pub summary: Option<String>,
1036}
1037
1038#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1041#[serde(untagged)]
1042pub enum ResponsesInput {
1043 Text(String),
1044 Items(Vec<ResponseItem>),
1045}
1046
1047impl From<String> for ResponsesInput {
1048 fn from(s: String) -> Self {
1049 ResponsesInput::Text(s)
1050 }
1051}
1052
1053impl From<&str> for ResponsesInput {
1054 fn from(s: &str) -> Self {
1055 ResponsesInput::Text(s.to_string())
1056 }
1057}
1058
1059impl From<Vec<ResponseItem>> for ResponsesInput {
1060 fn from(items: Vec<ResponseItem>) -> Self {
1061 ResponsesInput::Items(items)
1062 }
1063}
1064
1065#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1067pub struct ResponsesRequest {
1068 pub model: String,
1069 pub input: ResponsesInput,
1070 #[serde(default, skip_serializing_if = "Option::is_none")]
1072 pub instructions: Option<String>,
1073 #[serde(default, skip_serializing_if = "Option::is_none")]
1074 pub stream: Option<bool>,
1075 #[serde(default, skip_serializing_if = "Option::is_none")]
1076 pub temperature: Option<f32>,
1077 #[serde(default, skip_serializing_if = "Option::is_none")]
1078 pub top_p: Option<f32>,
1079 #[serde(default, skip_serializing_if = "Option::is_none")]
1080 pub max_output_tokens: Option<u32>,
1081 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1082 pub tools: Vec<ResponsesToolDef>,
1083 #[serde(default, skip_serializing_if = "Option::is_none")]
1084 pub tool_choice: Option<ResponsesToolChoice>,
1085 #[serde(default, skip_serializing_if = "Option::is_none")]
1086 pub reasoning: Option<ResponsesReasoning>,
1087 #[serde(default, skip_serializing_if = "Option::is_none")]
1089 pub store: Option<bool>,
1090 #[serde(default, flatten, skip_serializing_if = "Option::is_none")]
1093 pub extra: Option<serde_json::Map<String, serde_json::Value>>,
1094}
1095
1096impl ResponsesRequest {
1097 pub fn new(model: impl Into<String>, input: impl Into<ResponsesInput>) -> Self {
1099 ResponsesRequest {
1100 model: model.into(),
1101 input: input.into(),
1102 instructions: None,
1103 stream: None,
1104 temperature: None,
1105 top_p: None,
1106 max_output_tokens: None,
1107 tools: Vec::new(),
1108 tool_choice: None,
1109 reasoning: None,
1110 store: None,
1111 extra: None,
1112 }
1113 }
1114}
1115
1116#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1118pub struct ResponsesInputTokensDetails {
1119 #[serde(default, skip_serializing_if = "Option::is_none")]
1120 pub cached_tokens: Option<u32>,
1121}
1122
1123#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1125pub struct ResponsesOutputTokensDetails {
1126 #[serde(default, skip_serializing_if = "Option::is_none")]
1127 pub reasoning_tokens: Option<u32>,
1128}
1129
1130#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1133pub struct ResponsesUsage {
1134 #[serde(default)]
1135 pub input_tokens: u32,
1136 #[serde(default, skip_serializing_if = "Option::is_none")]
1137 pub input_tokens_details: Option<ResponsesInputTokensDetails>,
1138 #[serde(default)]
1139 pub output_tokens: u32,
1140 #[serde(default, skip_serializing_if = "Option::is_none")]
1141 pub output_tokens_details: Option<ResponsesOutputTokensDetails>,
1142 #[serde(default)]
1143 pub total_tokens: u32,
1144}
1145
1146impl ResponsesUsage {
1147 pub fn cached_tokens(&self) -> u32 {
1150 self.input_tokens_details
1151 .as_ref()
1152 .and_then(|d| d.cached_tokens)
1153 .unwrap_or(0)
1154 }
1155
1156 pub fn reasoning_tokens(&self) -> u32 {
1158 self.output_tokens_details
1159 .as_ref()
1160 .and_then(|d| d.reasoning_tokens)
1161 .unwrap_or(0)
1162 }
1163}
1164
1165#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1167pub struct ResponsesIncompleteDetails {
1168 pub reason: String,
1169}
1170
1171#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1175pub struct ResponsesError {
1176 pub message: String,
1177 #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
1178 pub kind: Option<String>,
1179 #[serde(default, skip_serializing_if = "Option::is_none")]
1180 pub code: Option<String>,
1181}
1182
1183#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1187pub struct ResponsesResponse {
1188 pub id: String,
1189 pub object: String,
1191 #[serde(default)]
1192 pub created_at: i64,
1193 pub status: String,
1195 #[serde(default, skip_serializing_if = "Option::is_none")]
1196 pub incomplete_details: Option<ResponsesIncompleteDetails>,
1197 #[serde(default, skip_serializing_if = "Option::is_none")]
1198 pub error: Option<ResponsesError>,
1199 #[serde(default)]
1200 pub model: String,
1201 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1202 pub output: Vec<ResponseItem>,
1203 #[serde(default, skip_serializing_if = "Option::is_none")]
1205 pub usage: Option<ResponsesUsage>,
1206 #[serde(default, skip_serializing_if = "Option::is_none")]
1208 pub store: Option<bool>,
1209 #[serde(default, skip_serializing_if = "Option::is_none")]
1210 pub instructions: Option<String>,
1211 #[serde(default, skip_serializing_if = "Option::is_none")]
1212 pub max_output_tokens: Option<u32>,
1213 #[serde(default, skip_serializing_if = "Option::is_none")]
1214 pub temperature: Option<f32>,
1215 #[serde(default, skip_serializing_if = "Option::is_none")]
1216 pub top_p: Option<f32>,
1217 #[serde(default, skip_serializing_if = "Option::is_none")]
1218 pub reasoning: Option<ResponsesReasoning>,
1219}
1220
1221impl ResponsesResponse {
1222 pub fn output_text(&self) -> String {
1225 let mut out = String::new();
1226 for item in &self.output {
1227 let ResponseItem::Message(msg) = item else {
1228 continue;
1229 };
1230 match &msg.content {
1231 Some(ResponseContent::Text(t)) => out.push_str(t),
1232 Some(ResponseContent::Parts(parts)) => {
1233 for part in parts {
1234 if let ResponseContentPart::OutputText { text, .. } = part {
1235 out.push_str(text);
1236 }
1237 }
1238 }
1239 None => {}
1240 }
1241 }
1242 out
1243 }
1244
1245 pub fn function_calls(&self) -> Vec<&ResponseFunctionCallItem> {
1247 self.output
1248 .iter()
1249 .filter_map(|item| match item {
1250 ResponseItem::FunctionCall(f) => Some(f),
1251 _ => None,
1252 })
1253 .collect()
1254 }
1255}
1256
1257#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1264pub struct ResponsesStreamEvent {
1265 #[serde(rename = "type")]
1267 pub kind: String,
1268 #[serde(default)]
1269 pub sequence_number: u64,
1270 #[serde(default, skip_serializing_if = "Option::is_none")]
1272 pub response: Option<ResponsesResponse>,
1273 #[serde(default, skip_serializing_if = "Option::is_none")]
1274 pub output_index: Option<u32>,
1275 #[serde(default, skip_serializing_if = "Option::is_none")]
1276 pub item_id: Option<String>,
1277 #[serde(default, skip_serializing_if = "Option::is_none")]
1278 pub content_index: Option<u32>,
1279 #[serde(default, skip_serializing_if = "Option::is_none")]
1281 pub item: Option<ResponseItem>,
1282 #[serde(default, skip_serializing_if = "Option::is_none")]
1284 pub part: Option<ResponseContentPart>,
1285 #[serde(default, skip_serializing_if = "Option::is_none")]
1287 pub delta: Option<String>,
1288 #[serde(default, skip_serializing_if = "Option::is_none")]
1290 pub text: Option<String>,
1291 #[serde(default, skip_serializing_if = "Option::is_none")]
1293 pub arguments: Option<String>,
1294 #[serde(default, skip_serializing_if = "Option::is_none")]
1296 pub message: Option<String>,
1297}
1298
1299impl ResponsesStreamEvent {
1300 pub fn is_terminal(&self) -> bool {
1303 matches!(
1304 self.kind.as_str(),
1305 "response.completed" | "response.incomplete" | "response.failed"
1306 )
1307 }
1308
1309 pub fn is_error(&self) -> bool {
1311 self.kind == "error"
1312 }
1313
1314 pub fn output_text_delta(&self) -> Option<&str> {
1317 (self.kind == "response.output_text.delta")
1318 .then_some(self.delta.as_deref())
1319 .flatten()
1320 }
1321
1322 pub fn terminal_response(&self) -> Option<&ResponsesResponse> {
1325 self.is_terminal().then_some(self.response.as_ref()).flatten()
1326 }
1327
1328 pub(crate) fn is_recognised(&self) -> bool {
1331 self.kind == "error" || self.kind.starts_with("response.")
1332 }
1333
1334 pub(crate) fn error_message(&self) -> String {
1336 self.message
1337 .clone()
1338 .or_else(|| {
1339 self.response
1340 .as_ref()
1341 .and_then(|r| r.error.as_ref().map(|e| e.message.clone()))
1342 })
1343 .unwrap_or_default()
1344 }
1345}
1346
1347#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1353#[serde(untagged)]
1354pub enum EmbeddingInput {
1355 One(String),
1356 Many(Vec<String>),
1357}
1358
1359impl From<String> for EmbeddingInput {
1360 fn from(s: String) -> Self {
1361 EmbeddingInput::One(s)
1362 }
1363}
1364
1365impl From<&str> for EmbeddingInput {
1366 fn from(s: &str) -> Self {
1367 EmbeddingInput::One(s.to_string())
1368 }
1369}
1370
1371impl From<Vec<String>> for EmbeddingInput {
1372 fn from(v: Vec<String>) -> Self {
1373 EmbeddingInput::Many(v)
1374 }
1375}
1376
1377#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1379pub struct EmbeddingRequest {
1380 pub model: String,
1381 pub input: EmbeddingInput,
1382 #[serde(default, skip_serializing_if = "Option::is_none")]
1383 pub dimensions: Option<u32>,
1384 #[serde(default, skip_serializing_if = "Option::is_none")]
1387 pub encoding_format: Option<String>,
1388 #[serde(default, skip_serializing_if = "Option::is_none")]
1390 pub extra: Option<serde_json::Value>,
1391}
1392
1393impl EmbeddingRequest {
1394 pub fn new(model: impl Into<String>, input: impl Into<EmbeddingInput>) -> Self {
1396 EmbeddingRequest {
1397 model: model.into(),
1398 input: input.into(),
1399 dimensions: None,
1400 encoding_format: None,
1401 extra: None,
1402 }
1403 }
1404}
1405
1406#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1409pub struct Embedding {
1410 pub object: String,
1411 #[serde(default)]
1412 pub index: u32,
1413 pub embedding: Vec<f32>,
1414}
1415
1416#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1418pub struct EmbeddingResponse {
1419 pub object: String,
1420 pub data: Vec<Embedding>,
1421 pub model: String,
1422 #[serde(default, skip_serializing_if = "Option::is_none")]
1423 pub usage: Option<Usage>,
1424}
1425
1426#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1434#[serde(untagged)]
1435pub enum RerankDocument {
1436 Text(String),
1437 Object(serde_json::Value),
1438}
1439
1440impl From<String> for RerankDocument {
1441 fn from(s: String) -> Self {
1442 RerankDocument::Text(s)
1443 }
1444}
1445
1446impl From<&str> for RerankDocument {
1447 fn from(s: &str) -> Self {
1448 RerankDocument::Text(s.to_string())
1449 }
1450}
1451
1452impl From<serde_json::Value> for RerankDocument {
1453 fn from(v: serde_json::Value) -> Self {
1454 RerankDocument::Object(v)
1455 }
1456}
1457
1458#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1460pub struct RerankRequest {
1461 pub model: String,
1462 pub query: String,
1463 pub documents: Vec<RerankDocument>,
1464 #[serde(default, skip_serializing_if = "Option::is_none")]
1465 pub top_n: Option<u32>,
1466 #[serde(default, skip_serializing_if = "Option::is_none")]
1467 pub return_documents: Option<bool>,
1468}
1469
1470impl RerankRequest {
1471 pub fn new(
1474 model: impl Into<String>,
1475 query: impl Into<String>,
1476 documents: impl IntoIterator<Item = impl Into<RerankDocument>>,
1477 ) -> Self {
1478 RerankRequest {
1479 model: model.into(),
1480 query: query.into(),
1481 documents: documents.into_iter().map(Into::into).collect(),
1482 top_n: None,
1483 return_documents: None,
1484 }
1485 }
1486}
1487
1488#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1490pub struct RerankResult {
1491 #[serde(default)]
1492 pub index: u32,
1493 pub relevance_score: f32,
1494 #[serde(default, skip_serializing_if = "Option::is_none")]
1497 pub document: Option<RerankDocument>,
1498}
1499
1500#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1502pub struct RerankResponse {
1503 pub model: String,
1504 pub results: Vec<RerankResult>,
1505 #[serde(default, skip_serializing_if = "Option::is_none")]
1506 pub usage: Option<Usage>,
1507}
1508
1509#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1515pub struct SpeechRequest {
1516 pub model: String,
1517 pub input: String,
1518 pub voice: String,
1519 #[serde(default, skip_serializing_if = "Option::is_none")]
1521 pub response_format: Option<String>,
1522 #[serde(default, skip_serializing_if = "Option::is_none")]
1523 pub speed: Option<f32>,
1524 #[serde(default, skip_serializing_if = "Option::is_none")]
1526 pub extra: Option<serde_json::Value>,
1527}
1528
1529impl SpeechRequest {
1530 pub fn new(
1532 model: impl Into<String>,
1533 input: impl Into<String>,
1534 voice: impl Into<String>,
1535 ) -> Self {
1536 SpeechRequest {
1537 model: model.into(),
1538 input: input.into(),
1539 voice: voice.into(),
1540 response_format: None,
1541 speed: None,
1542 extra: None,
1543 }
1544 }
1545}
1546
1547#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1549pub struct Voice {
1550 pub id: String,
1551 #[serde(default, skip_serializing_if = "Option::is_none")]
1552 pub name: Option<String>,
1553 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1554 pub languages: Vec<String>,
1555}
1556
1557#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1559pub struct VoicesResponse {
1560 pub model: String,
1561 pub voices: Vec<Voice>,
1562}
1563
1564#[derive(Debug, Clone, PartialEq, Default)]
1571pub struct TranscriptionRequest {
1572 pub model: String,
1573 pub language: Option<String>,
1575 pub prompt: Option<String>,
1577 pub response_format: Option<String>,
1579 pub temperature: Option<f32>,
1580}
1581
1582impl TranscriptionRequest {
1583 pub fn new(model: impl Into<String>) -> Self {
1585 TranscriptionRequest {
1586 model: model.into(),
1587 ..Default::default()
1588 }
1589 }
1590}
1591
1592#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1594pub struct TranscriptionResponse {
1595 pub text: String,
1596 #[serde(default, skip_serializing_if = "Option::is_none")]
1597 pub task: Option<String>,
1598 #[serde(default, skip_serializing_if = "Option::is_none")]
1599 pub language: Option<String>,
1600 #[serde(default, skip_serializing_if = "Option::is_none")]
1601 pub duration: Option<f32>,
1602 #[serde(default, skip_serializing_if = "Option::is_none")]
1603 pub usage: Option<Usage>,
1604}
1605
1606#[derive(Debug, Clone, PartialEq)]
1609pub enum Transcription {
1610 Json(TranscriptionResponse),
1611 Text(String),
1612}
1613
1614#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1620pub struct Architecture {
1621 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1622 pub input_modalities: Vec<String>,
1623 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1624 pub output_modalities: Vec<String>,
1625 #[serde(default, skip_serializing_if = "Option::is_none")]
1626 pub modality: Option<String>,
1627 #[serde(default)]
1628 pub tokenizer: String,
1629 #[serde(default, skip_serializing_if = "Option::is_none")]
1630 pub instruct_type: Option<String>,
1631}
1632
1633#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1635pub struct Pricing {
1636 pub prompt: String,
1637 pub completion: String,
1638}
1639
1640#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1642pub struct TopProvider {
1643 #[serde(default, skip_serializing_if = "Option::is_none")]
1644 pub context_length: Option<u32>,
1645 #[serde(default, skip_serializing_if = "Option::is_none")]
1646 pub max_completion_tokens: Option<u32>,
1647 #[serde(default)]
1648 pub is_moderated: bool,
1649 #[serde(default, skip_serializing_if = "Option::is_none")]
1650 pub max_thinking_tokens: Option<u32>,
1651}
1652
1653#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1655pub struct ModelEndpoint {
1656 pub provider: String,
1657 pub model: String,
1658 #[serde(default)]
1659 pub down: bool,
1660 pub source: String,
1662}
1663
1664#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1666pub struct ModelEntry {
1667 pub id: String,
1668 #[serde(default)]
1669 pub canonical_slug: String,
1670 #[serde(default)]
1671 pub name: String,
1672 #[serde(default)]
1673 pub created: i64,
1674 #[serde(default)]
1675 pub description: String,
1676 #[serde(default, skip_serializing_if = "Option::is_none")]
1677 pub context_length: Option<u32>,
1678 #[serde(default, skip_serializing_if = "Option::is_none")]
1679 pub architecture: Option<Architecture>,
1680 #[serde(default, skip_serializing_if = "Option::is_none")]
1681 pub pricing: Option<Pricing>,
1682 #[serde(default, skip_serializing_if = "Option::is_none")]
1683 pub top_provider: Option<TopProvider>,
1684 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1685 pub supported_parameters: Vec<String>,
1686 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1687 pub unsupported_parameters: Vec<String>,
1688 #[serde(default, skip_serializing_if = "Option::is_none")]
1690 pub default_parameters: Option<serde_json::Value>,
1691 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1693 pub endpoints: Vec<ModelEndpoint>,
1694}
1695
1696#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1698pub struct ListModelsResponse {
1699 #[serde(default)]
1700 pub data: Vec<ModelEntry>,
1701}
1702
1703#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1705pub enum ModelType {
1706 All,
1707 Llm,
1708 Tts,
1709 Stt,
1710 Embedding,
1711 Rerank,
1712}
1713
1714impl ModelType {
1715 pub(crate) fn as_str(self) -> &'static str {
1716 match self {
1717 ModelType::All => "all",
1718 ModelType::Llm => "llm",
1719 ModelType::Tts => "tts",
1720 ModelType::Stt => "stt",
1721 ModelType::Embedding => "embedding",
1722 ModelType::Rerank => "rerank",
1723 }
1724 }
1725}
1726
1727#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1733pub struct BatchRequestItem {
1734 pub custom_id: String,
1735 pub body: ChatRequest,
1736}
1737
1738#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1740pub struct BatchCreateRequest {
1741 pub requests: Vec<BatchRequestItem>,
1742}
1743
1744#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1746pub struct BatchCounts {
1747 #[serde(default)]
1748 pub total: u64,
1749 #[serde(default)]
1750 pub processing: u64,
1751 #[serde(default)]
1752 pub succeeded: u64,
1753 #[serde(default)]
1754 pub errored: u64,
1755 #[serde(default)]
1756 pub canceled: u64,
1757 #[serde(default)]
1758 pub expired: u64,
1759}
1760
1761#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1763pub struct BatchHandle {
1764 pub id: String,
1765 pub status: BatchStatus,
1766 #[serde(default)]
1767 pub counts: BatchCounts,
1768 #[serde(default, skip_serializing_if = "Option::is_none")]
1769 pub created_at: Option<i64>,
1770 #[serde(default, skip_serializing_if = "Option::is_none")]
1771 pub expires_at: Option<i64>,
1772 #[serde(default, skip_serializing_if = "Option::is_none")]
1773 pub ended_at: Option<i64>,
1774 #[serde(default, skip_serializing_if = "Option::is_none")]
1775 pub endpoint: Option<String>,
1776}
1777
1778#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1780pub struct BatchResponse {
1781 pub status_code: u32,
1782 pub body: ChatResponse,
1783}
1784
1785#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1787pub struct BatchError {
1788 pub code: String,
1789 pub message: String,
1790}
1791
1792#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1794pub struct BatchResultLine {
1795 pub custom_id: String,
1796 #[serde(default, skip_serializing_if = "Option::is_none")]
1797 pub response: Option<BatchResponse>,
1798 #[serde(default, skip_serializing_if = "Option::is_none")]
1799 pub error: Option<BatchError>,
1800}