1use serde::{Deserialize, Serialize};
4
5use super::common::{Role, SortOrder};
6
7#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
9#[serde(rename_all = "snake_case")]
10#[non_exhaustive]
11pub enum RunStatus {
12 Queued,
13 InProgress,
14 RequiresAction,
15 Cancelling,
16 Cancelled,
17 Failed,
18 Completed,
19 Incomplete,
20 Expired,
21}
22
23#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
25#[serde(rename_all = "snake_case")]
26#[non_exhaustive]
27pub enum VectorStoreStatus {
28 Expired,
29 InProgress,
30 Completed,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
37#[serde(tag = "type")]
38#[non_exhaustive]
39pub enum BetaTool {
40 #[serde(rename = "code_interpreter")]
42 CodeInterpreter,
43 #[serde(rename = "file_search")]
45 FileSearch {
46 #[serde(skip_serializing_if = "Option::is_none")]
47 file_search: Option<FileSearchConfig>,
48 },
49 #[serde(rename = "function")]
51 Function { function: BetaFunctionDef },
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct FileSearchConfig {
57 #[serde(skip_serializing_if = "Option::is_none")]
59 pub max_num_results: Option<i64>,
60 #[serde(skip_serializing_if = "Option::is_none")]
62 pub ranking_options: Option<FileSearchRankingOptions>,
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct FileSearchRankingOptions {
68 pub score_threshold: f64,
70 #[serde(skip_serializing_if = "Option::is_none")]
72 pub ranker: Option<String>,
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize)]
77pub struct BetaFunctionDef {
78 pub name: String,
79 #[serde(skip_serializing_if = "Option::is_none")]
80 pub description: Option<String>,
81 #[serde(skip_serializing_if = "Option::is_none")]
82 pub parameters: Option<serde_json::Value>,
83}
84
85#[derive(Debug, Clone, Deserialize)]
87pub struct MessageAnnotation {
88 #[serde(rename = "type")]
90 pub type_: String,
91 #[serde(default)]
93 pub text: Option<String>,
94 #[serde(default)]
96 pub start_index: Option<i64>,
97 #[serde(default)]
99 pub end_index: Option<i64>,
100 #[serde(default)]
102 pub file_citation: Option<FileCitation>,
103 #[serde(default)]
105 pub file_path: Option<FilePath>,
106}
107
108#[derive(Debug, Clone, Deserialize)]
110pub struct FileCitation {
111 pub file_id: String,
112 #[serde(default)]
113 pub quote: Option<String>,
114}
115
116#[derive(Debug, Clone, Deserialize)]
118pub struct FilePath {
119 pub file_id: String,
120}
121
122#[derive(Debug, Clone, Serialize)]
126pub struct AssistantCreateRequest {
127 pub model: String,
128 #[serde(skip_serializing_if = "Option::is_none")]
129 pub name: Option<String>,
130 #[serde(skip_serializing_if = "Option::is_none")]
131 pub description: Option<String>,
132 #[serde(skip_serializing_if = "Option::is_none")]
133 pub instructions: Option<String>,
134 #[serde(skip_serializing_if = "Option::is_none")]
135 pub tools: Option<Vec<BetaTool>>,
136 #[serde(skip_serializing_if = "Option::is_none")]
137 pub metadata: Option<std::collections::HashMap<String, String>>,
138 #[serde(skip_serializing_if = "Option::is_none")]
139 pub temperature: Option<f64>,
140 #[serde(skip_serializing_if = "Option::is_none")]
141 pub top_p: Option<f64>,
142}
143
144impl AssistantCreateRequest {
145 pub fn new(model: impl Into<String>) -> Self {
146 Self {
147 model: model.into(),
148 name: None,
149 description: None,
150 instructions: None,
151 tools: None,
152 metadata: None,
153 temperature: None,
154 top_p: None,
155 }
156 }
157}
158
159#[derive(Debug, Clone, Deserialize)]
161pub struct Assistant {
162 pub id: String,
163 pub object: String,
164 pub created_at: i64,
165 pub model: String,
166 #[serde(default)]
167 pub name: Option<String>,
168 #[serde(default)]
169 pub description: Option<String>,
170 #[serde(default)]
171 pub instructions: Option<String>,
172 #[serde(default)]
173 pub tools: Vec<BetaTool>,
174 #[serde(default)]
175 pub metadata: Option<std::collections::HashMap<String, String>>,
176 #[serde(default)]
177 pub temperature: Option<f64>,
178 #[serde(default)]
179 pub top_p: Option<f64>,
180}
181
182#[derive(Debug, Clone, Deserialize)]
184pub struct AssistantList {
185 pub object: String,
186 pub data: Vec<Assistant>,
187 #[serde(default)]
189 pub has_more: Option<bool>,
190 #[serde(default)]
192 pub first_id: Option<String>,
193 #[serde(default)]
195 pub last_id: Option<String>,
196}
197
198#[derive(Debug, Clone, Deserialize)]
200pub struct AssistantDeleted {
201 pub id: String,
202 pub deleted: bool,
203 pub object: String,
204}
205
206#[derive(Debug, Clone, Default, Serialize)]
210pub struct ThreadCreateRequest {
211 #[serde(skip_serializing_if = "Option::is_none")]
212 pub messages: Option<Vec<ThreadMessage>>,
213 #[serde(skip_serializing_if = "Option::is_none")]
214 pub metadata: Option<std::collections::HashMap<String, String>>,
215}
216
217#[derive(Debug, Clone, Serialize)]
219pub struct ThreadMessage {
220 pub role: Role,
221 pub content: String,
222}
223
224#[derive(Debug, Clone, Deserialize)]
226pub struct Thread {
227 pub id: String,
228 pub object: String,
229 pub created_at: i64,
230 #[serde(default)]
231 pub metadata: Option<std::collections::HashMap<String, String>>,
232}
233
234#[derive(Debug, Clone, Deserialize)]
236pub struct ThreadDeleted {
237 pub id: String,
238 pub deleted: bool,
239 pub object: String,
240}
241
242#[derive(Debug, Clone, Deserialize)]
244pub struct Message {
245 pub id: String,
246 pub object: String,
247 pub created_at: i64,
248 pub thread_id: String,
249 pub role: Role,
250 pub content: Vec<MessageContent>,
251 #[serde(default)]
252 pub assistant_id: Option<String>,
253 #[serde(default)]
254 pub run_id: Option<String>,
255 #[serde(default)]
256 pub metadata: Option<std::collections::HashMap<String, String>>,
257}
258
259#[derive(Debug, Clone, Deserialize)]
261pub struct MessageContent {
262 #[serde(rename = "type")]
263 pub type_: String,
264 #[serde(default)]
265 pub text: Option<MessageText>,
266}
267
268#[derive(Debug, Clone, Deserialize)]
270pub struct MessageText {
271 pub value: String,
272 #[serde(default)]
273 pub annotations: Vec<MessageAnnotation>,
274}
275
276#[derive(Debug, Clone, Serialize)]
278pub struct MessageCreateRequest {
279 pub role: Role,
280 pub content: String,
281}
282
283#[derive(Debug, Clone, Deserialize)]
285pub struct MessageList {
286 pub object: String,
287 pub data: Vec<Message>,
288 #[serde(default)]
290 pub has_more: Option<bool>,
291 #[serde(default)]
293 pub first_id: Option<String>,
294 #[serde(default)]
296 pub last_id: Option<String>,
297}
298
299#[derive(Debug, Clone, Serialize)]
303pub struct RunCreateRequest {
304 pub assistant_id: String,
305 #[serde(skip_serializing_if = "Option::is_none")]
306 pub instructions: Option<String>,
307 #[serde(skip_serializing_if = "Option::is_none")]
308 pub tools: Option<Vec<BetaTool>>,
309 #[serde(skip_serializing_if = "Option::is_none")]
310 pub metadata: Option<std::collections::HashMap<String, String>>,
311 #[serde(skip_serializing_if = "Option::is_none")]
312 pub model: Option<String>,
313}
314
315impl RunCreateRequest {
316 pub fn new(assistant_id: impl Into<String>) -> Self {
317 Self {
318 assistant_id: assistant_id.into(),
319 instructions: None,
320 tools: None,
321 metadata: None,
322 model: None,
323 }
324 }
325}
326
327#[derive(Debug, Clone, Deserialize)]
329pub struct Run {
330 pub id: String,
331 pub object: String,
332 pub created_at: i64,
333 pub thread_id: String,
334 pub assistant_id: String,
335 pub status: RunStatus,
336 #[serde(default)]
337 pub model: Option<String>,
338 #[serde(default)]
339 pub instructions: Option<String>,
340 #[serde(default)]
341 pub tools: Vec<BetaTool>,
342 #[serde(default)]
343 pub started_at: Option<i64>,
344 #[serde(default)]
345 pub completed_at: Option<i64>,
346 #[serde(default)]
347 pub cancelled_at: Option<i64>,
348 #[serde(default)]
349 pub failed_at: Option<i64>,
350 #[serde(default)]
351 pub metadata: Option<std::collections::HashMap<String, String>>,
352}
353
354#[derive(Debug, Clone, Serialize)]
356pub struct SubmitToolOutputsRequest {
357 pub tool_outputs: Vec<ToolOutput>,
358}
359
360#[derive(Debug, Clone, Serialize)]
362pub struct ToolOutput {
363 pub tool_call_id: String,
364 pub output: String,
365}
366
367#[derive(Debug, Clone, Default, Serialize)]
371pub struct VectorStoreCreateRequest {
372 #[serde(skip_serializing_if = "Option::is_none")]
373 pub name: Option<String>,
374 #[serde(skip_serializing_if = "Option::is_none")]
375 pub file_ids: Option<Vec<String>>,
376 #[serde(skip_serializing_if = "Option::is_none")]
377 pub metadata: Option<std::collections::HashMap<String, String>>,
378}
379
380#[derive(Debug, Clone, Deserialize)]
382pub struct VectorStore {
383 pub id: String,
384 pub object: String,
385 pub created_at: i64,
386 #[serde(default)]
387 pub name: Option<String>,
388 pub status: VectorStoreStatus,
389 #[serde(default)]
390 pub file_counts: Option<VectorStoreFileCounts>,
391 #[serde(default)]
392 pub metadata: Option<std::collections::HashMap<String, String>>,
393}
394
395#[derive(Debug, Clone, Deserialize)]
397pub struct VectorStoreFileCounts {
398 pub in_progress: i64,
399 pub completed: i64,
400 pub failed: i64,
401 pub cancelled: i64,
402 pub total: i64,
403}
404
405#[derive(Debug, Clone, Deserialize)]
407pub struct VectorStoreList {
408 pub object: String,
409 pub data: Vec<VectorStore>,
410 #[serde(default)]
412 pub has_more: Option<bool>,
413 #[serde(default)]
415 pub first_id: Option<String>,
416 #[serde(default)]
418 pub last_id: Option<String>,
419}
420
421#[derive(Debug, Clone, Deserialize)]
423pub struct VectorStoreDeleted {
424 pub id: String,
425 pub deleted: bool,
426 pub object: String,
427}
428
429#[derive(Debug, Clone, Default)]
431pub struct AssistantListParams {
432 pub after: Option<String>,
434 pub before: Option<String>,
436 pub limit: Option<i64>,
438 pub order: Option<SortOrder>,
440}
441
442impl AssistantListParams {
443 pub fn new() -> Self {
444 Self::default()
445 }
446
447 pub fn after(mut self, after: impl Into<String>) -> Self {
448 self.after = Some(after.into());
449 self
450 }
451
452 pub fn before(mut self, before: impl Into<String>) -> Self {
453 self.before = Some(before.into());
454 self
455 }
456
457 pub fn limit(mut self, limit: i64) -> Self {
458 self.limit = Some(limit);
459 self
460 }
461
462 pub fn order(mut self, order: SortOrder) -> Self {
463 self.order = Some(order);
464 self
465 }
466
467 pub fn to_query(&self) -> Vec<(String, String)> {
469 let mut q = Vec::new();
470 if let Some(ref after) = self.after {
471 q.push(("after".into(), after.clone()));
472 }
473 if let Some(ref before) = self.before {
474 q.push(("before".into(), before.clone()));
475 }
476 if let Some(limit) = self.limit {
477 q.push(("limit".into(), limit.to_string()));
478 }
479 if let Some(ref order) = self.order {
480 q.push((
481 "order".into(),
482 serde_json::to_value(order)
483 .unwrap()
484 .as_str()
485 .unwrap()
486 .to_string(),
487 ));
488 }
489 q
490 }
491}
492
493#[derive(Debug, Clone, Default)]
495pub struct MessageListParams {
496 pub after: Option<String>,
498 pub before: Option<String>,
500 pub limit: Option<i64>,
502 pub order: Option<SortOrder>,
504 pub run_id: Option<String>,
506}
507
508impl MessageListParams {
509 pub fn new() -> Self {
510 Self::default()
511 }
512
513 pub fn after(mut self, after: impl Into<String>) -> Self {
514 self.after = Some(after.into());
515 self
516 }
517
518 pub fn before(mut self, before: impl Into<String>) -> Self {
519 self.before = Some(before.into());
520 self
521 }
522
523 pub fn limit(mut self, limit: i64) -> Self {
524 self.limit = Some(limit);
525 self
526 }
527
528 pub fn order(mut self, order: SortOrder) -> Self {
529 self.order = Some(order);
530 self
531 }
532
533 pub fn run_id(mut self, run_id: impl Into<String>) -> Self {
534 self.run_id = Some(run_id.into());
535 self
536 }
537
538 pub fn to_query(&self) -> Vec<(String, String)> {
540 let mut q = Vec::new();
541 if let Some(ref after) = self.after {
542 q.push(("after".into(), after.clone()));
543 }
544 if let Some(ref before) = self.before {
545 q.push(("before".into(), before.clone()));
546 }
547 if let Some(limit) = self.limit {
548 q.push(("limit".into(), limit.to_string()));
549 }
550 if let Some(ref order) = self.order {
551 q.push((
552 "order".into(),
553 serde_json::to_value(order)
554 .unwrap()
555 .as_str()
556 .unwrap()
557 .to_string(),
558 ));
559 }
560 if let Some(ref run_id) = self.run_id {
561 q.push(("run_id".into(), run_id.clone()));
562 }
563 q
564 }
565}
566
567#[derive(Debug, Clone, Default)]
569pub struct VectorStoreListParams {
570 pub after: Option<String>,
572 pub before: Option<String>,
574 pub limit: Option<i64>,
576 pub order: Option<SortOrder>,
578}
579
580impl VectorStoreListParams {
581 pub fn new() -> Self {
582 Self::default()
583 }
584
585 pub fn after(mut self, after: impl Into<String>) -> Self {
586 self.after = Some(after.into());
587 self
588 }
589
590 pub fn before(mut self, before: impl Into<String>) -> Self {
591 self.before = Some(before.into());
592 self
593 }
594
595 pub fn limit(mut self, limit: i64) -> Self {
596 self.limit = Some(limit);
597 self
598 }
599
600 pub fn order(mut self, order: SortOrder) -> Self {
601 self.order = Some(order);
602 self
603 }
604
605 pub fn to_query(&self) -> Vec<(String, String)> {
607 let mut q = Vec::new();
608 if let Some(ref after) = self.after {
609 q.push(("after".into(), after.clone()));
610 }
611 if let Some(ref before) = self.before {
612 q.push(("before".into(), before.clone()));
613 }
614 if let Some(limit) = self.limit {
615 q.push(("limit".into(), limit.to_string()));
616 }
617 if let Some(ref order) = self.order {
618 q.push((
619 "order".into(),
620 serde_json::to_value(order)
621 .unwrap()
622 .as_str()
623 .unwrap()
624 .to_string(),
625 ));
626 }
627 q
628 }
629}
630
631#[derive(Debug, Clone, Default)]
633pub struct RunListParams {
634 pub after: Option<String>,
636 pub before: Option<String>,
638 pub limit: Option<i64>,
640 pub order: Option<SortOrder>,
642}
643
644impl RunListParams {
645 pub fn new() -> Self {
646 Self::default()
647 }
648
649 pub fn after(mut self, after: impl Into<String>) -> Self {
650 self.after = Some(after.into());
651 self
652 }
653
654 pub fn before(mut self, before: impl Into<String>) -> Self {
655 self.before = Some(before.into());
656 self
657 }
658
659 pub fn limit(mut self, limit: i64) -> Self {
660 self.limit = Some(limit);
661 self
662 }
663
664 pub fn order(mut self, order: SortOrder) -> Self {
665 self.order = Some(order);
666 self
667 }
668
669 pub fn to_query(&self) -> Vec<(String, String)> {
671 let mut q = Vec::new();
672 if let Some(ref after) = self.after {
673 q.push(("after".into(), after.clone()));
674 }
675 if let Some(ref before) = self.before {
676 q.push(("before".into(), before.clone()));
677 }
678 if let Some(limit) = self.limit {
679 q.push(("limit".into(), limit.to_string()));
680 }
681 if let Some(ref order) = self.order {
682 q.push((
683 "order".into(),
684 serde_json::to_value(order)
685 .unwrap()
686 .as_str()
687 .unwrap()
688 .to_string(),
689 ));
690 }
691 q
692 }
693}
694
695#[cfg(test)]
696mod tests {
697 use super::*;
698
699 #[test]
700 fn test_serialize_assistant_create() {
701 let req = AssistantCreateRequest::new("gpt-4o");
702 let json = serde_json::to_value(&req).unwrap();
703 assert_eq!(json["model"], "gpt-4o");
704 }
705
706 #[test]
707 fn test_serialize_assistant_with_tools() {
708 let mut req = AssistantCreateRequest::new("gpt-4o");
709 req.tools = Some(vec![
710 BetaTool::CodeInterpreter,
711 BetaTool::FileSearch { file_search: None },
712 BetaTool::Function {
713 function: BetaFunctionDef {
714 name: "get_weather".into(),
715 description: Some("Get weather".into()),
716 parameters: Some(serde_json::json!({"type": "object"})),
717 },
718 },
719 ]);
720 let json = serde_json::to_value(&req).unwrap();
721 let tools = json["tools"].as_array().unwrap();
722 assert_eq!(tools.len(), 3);
723 assert_eq!(tools[0]["type"], "code_interpreter");
724 assert_eq!(tools[1]["type"], "file_search");
725 assert_eq!(tools[2]["type"], "function");
726 assert_eq!(tools[2]["function"]["name"], "get_weather");
727 }
728
729 #[test]
730 fn test_deserialize_assistant() {
731 let json = r#"{
732 "id": "asst_abc123",
733 "object": "assistant",
734 "created_at": 1699009709,
735 "model": "gpt-4o",
736 "tools": [{"type": "code_interpreter"}]
737 }"#;
738 let asst: Assistant = serde_json::from_str(json).unwrap();
739 assert_eq!(asst.id, "asst_abc123");
740 assert_eq!(asst.tools.len(), 1);
741 assert!(matches!(asst.tools[0], BetaTool::CodeInterpreter));
742 }
743
744 #[test]
745 fn test_deserialize_assistant_with_function_tool() {
746 let json = r#"{
747 "id": "asst_abc123",
748 "object": "assistant",
749 "created_at": 1699009709,
750 "model": "gpt-4o",
751 "tools": [{
752 "type": "function",
753 "function": {
754 "name": "get_weather",
755 "description": "Get current weather",
756 "parameters": {"type": "object", "properties": {"city": {"type": "string"}}}
757 }
758 }]
759 }"#;
760 let asst: Assistant = serde_json::from_str(json).unwrap();
761 match &asst.tools[0] {
762 BetaTool::Function { function } => {
763 assert_eq!(function.name, "get_weather");
764 }
765 _ => panic!("expected function tool"),
766 }
767 }
768
769 #[test]
770 fn test_deserialize_thread() {
771 let json = r#"{
772 "id": "thread_abc123",
773 "object": "thread",
774 "created_at": 1699012949
775 }"#;
776 let thread: Thread = serde_json::from_str(json).unwrap();
777 assert_eq!(thread.id, "thread_abc123");
778 }
779
780 #[test]
781 fn test_deserialize_run() {
782 let json = r#"{
783 "id": "run_abc123",
784 "object": "thread.run",
785 "created_at": 1699012949,
786 "thread_id": "thread_abc123",
787 "assistant_id": "asst_abc123",
788 "status": "completed",
789 "tools": []
790 }"#;
791 let run: Run = serde_json::from_str(json).unwrap();
792 assert_eq!(run.status, RunStatus::Completed);
793 }
794
795 #[test]
796 fn test_deserialize_run_with_tools() {
797 let json = r#"{
798 "id": "run_abc123",
799 "object": "thread.run",
800 "created_at": 1699012949,
801 "thread_id": "thread_abc123",
802 "assistant_id": "asst_abc123",
803 "status": "completed",
804 "tools": [
805 {"type": "code_interpreter"},
806 {"type": "file_search", "file_search": {"max_num_results": 10}}
807 ]
808 }"#;
809 let run: Run = serde_json::from_str(json).unwrap();
810 assert_eq!(run.tools.len(), 2);
811 match &run.tools[1] {
812 BetaTool::FileSearch { file_search } => {
813 assert_eq!(file_search.as_ref().unwrap().max_num_results, Some(10));
814 }
815 _ => panic!("expected file_search tool"),
816 }
817 }
818
819 #[test]
820 fn test_deserialize_message_with_annotations() {
821 let json = r#"{
822 "id": "msg_abc123",
823 "object": "thread.message",
824 "created_at": 1699012949,
825 "thread_id": "thread_abc123",
826 "role": "assistant",
827 "content": [{
828 "type": "text",
829 "text": {
830 "value": "See file [1].",
831 "annotations": [{
832 "type": "file_citation",
833 "text": "[1]",
834 "start_index": 9,
835 "end_index": 12,
836 "file_citation": {
837 "file_id": "file-abc123",
838 "quote": "relevant text"
839 }
840 }]
841 }
842 }]
843 }"#;
844 let msg: Message = serde_json::from_str(json).unwrap();
845 let text = msg.content[0].text.as_ref().unwrap();
846 assert_eq!(text.annotations.len(), 1);
847 assert_eq!(text.annotations[0].type_, "file_citation");
848 let citation = text.annotations[0].file_citation.as_ref().unwrap();
849 assert_eq!(citation.file_id, "file-abc123");
850 }
851
852 #[test]
853 fn test_deserialize_vector_store() {
854 let json = r#"{
855 "id": "vs_abc123",
856 "object": "vector_store",
857 "created_at": 1699012949,
858 "name": "My Store",
859 "status": "completed",
860 "file_counts": {
861 "in_progress": 0,
862 "completed": 5,
863 "failed": 0,
864 "cancelled": 0,
865 "total": 5
866 }
867 }"#;
868 let vs: VectorStore = serde_json::from_str(json).unwrap();
869 assert_eq!(vs.id, "vs_abc123");
870 assert_eq!(vs.file_counts.unwrap().completed, 5);
871 }
872}