1use serde::{Deserialize, Serialize};
4
5use super::common::SortOrder;
6
7#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
9#[non_exhaustive]
10pub enum FilePurpose {
11 #[serde(rename = "assistants")]
12 Assistants,
13 #[serde(rename = "assistants_output")]
14 AssistantsOutput,
15 #[serde(rename = "batch")]
16 Batch,
17 #[serde(rename = "batch_output")]
18 BatchOutput,
19 #[serde(rename = "fine-tune")]
20 FineTune,
21 #[serde(rename = "fine-tune-results")]
22 FineTuneResults,
23 #[serde(rename = "vision")]
24 Vision,
25 #[serde(rename = "user_data")]
26 UserData,
27}
28
29#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
31#[serde(rename_all = "snake_case")]
32#[non_exhaustive]
33pub enum FileStatus {
34 Uploaded,
35 Processed,
36 Error,
37}
38
39#[derive(Debug, Clone, Deserialize)]
41pub struct FileObject {
42 pub id: String,
43 pub bytes: i64,
44 pub created_at: i64,
45 pub filename: String,
46 pub object: String,
47 pub purpose: FilePurpose,
48 pub status: FileStatus,
49 #[serde(default)]
50 pub status_details: Option<String>,
51 #[serde(default)]
52 pub expires_at: Option<i64>,
53}
54
55#[derive(Debug, Clone, Deserialize)]
57pub struct FileList {
58 pub object: String,
59 pub data: Vec<FileObject>,
60 #[serde(default)]
62 pub has_more: Option<bool>,
63 #[serde(default)]
65 pub first_id: Option<String>,
66 #[serde(default)]
68 pub last_id: Option<String>,
69}
70
71#[derive(Debug, Clone, Deserialize)]
73pub struct FileDeleted {
74 pub id: String,
75 pub deleted: bool,
76 pub object: String,
77}
78
79#[derive(Debug)]
81pub struct FileUploadParams {
82 pub file: Vec<u8>,
83 pub filename: String,
84 pub purpose: FilePurpose,
85}
86
87impl FileUploadParams {
88 pub fn new(file: Vec<u8>, filename: impl Into<String>, purpose: FilePurpose) -> Self {
89 Self {
90 file,
91 filename: filename.into(),
92 purpose,
93 }
94 }
95}
96
97#[derive(Debug, Clone, Default)]
99pub struct FileListParams {
100 pub after: Option<String>,
102 pub limit: Option<i64>,
104 pub order: Option<SortOrder>,
106 pub purpose: Option<FilePurpose>,
108}
109
110impl FileListParams {
111 pub fn new() -> Self {
112 Self::default()
113 }
114
115 pub fn after(mut self, after: impl Into<String>) -> Self {
116 self.after = Some(after.into());
117 self
118 }
119
120 pub fn limit(mut self, limit: i64) -> Self {
121 self.limit = Some(limit);
122 self
123 }
124
125 pub fn order(mut self, order: SortOrder) -> Self {
126 self.order = Some(order);
127 self
128 }
129
130 pub fn purpose(mut self, purpose: FilePurpose) -> Self {
131 self.purpose = Some(purpose);
132 self
133 }
134
135 pub fn to_query(&self) -> Vec<(String, String)> {
137 let mut q = Vec::new();
138 if let Some(ref after) = self.after {
139 q.push(("after".into(), after.clone()));
140 }
141 if let Some(limit) = self.limit {
142 q.push(("limit".into(), limit.to_string()));
143 }
144 if let Some(ref order) = self.order {
145 q.push((
146 "order".into(),
147 serde_json::to_value(order)
148 .unwrap()
149 .as_str()
150 .unwrap()
151 .to_string(),
152 ));
153 }
154 if let Some(ref purpose) = self.purpose {
155 q.push((
156 "purpose".into(),
157 serde_json::to_value(purpose)
158 .unwrap()
159 .as_str()
160 .unwrap()
161 .to_string(),
162 ));
163 }
164 q
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[test]
173 fn test_deserialize_file_object() {
174 let json = r#"{
175 "id": "file-abc123",
176 "object": "file",
177 "bytes": 120000,
178 "created_at": 1677610602,
179 "filename": "data.jsonl",
180 "purpose": "fine-tune",
181 "status": "processed"
182 }"#;
183 let file: FileObject = serde_json::from_str(json).unwrap();
184 assert_eq!(file.id, "file-abc123");
185 assert_eq!(file.bytes, 120000);
186 assert_eq!(file.purpose, FilePurpose::FineTune);
187 assert_eq!(file.status, FileStatus::Processed);
188 }
189
190 #[test]
191 fn test_deserialize_file_list_with_pagination() {
192 let json = r#"{
193 "object": "list",
194 "data": [{
195 "id": "file-abc123",
196 "object": "file",
197 "bytes": 120000,
198 "created_at": 1677610602,
199 "filename": "data.jsonl",
200 "purpose": "fine-tune",
201 "status": "processed"
202 }],
203 "has_more": true,
204 "first_id": "file-abc123",
205 "last_id": "file-abc123"
206 }"#;
207 let list: FileList = serde_json::from_str(json).unwrap();
208 assert_eq!(list.data.len(), 1);
209 assert_eq!(list.has_more, Some(true));
210 assert_eq!(list.first_id.as_deref(), Some("file-abc123"));
211 assert_eq!(list.last_id.as_deref(), Some("file-abc123"));
212 }
213
214 #[test]
215 fn test_file_list_params_to_query() {
216 let params = FileListParams::new()
217 .after("file-cursor")
218 .limit(10)
219 .order(crate::types::common::SortOrder::Desc)
220 .purpose(FilePurpose::FineTune);
221 let query = params.to_query();
222 assert!(query.contains(&("after".into(), "file-cursor".into())));
223 assert!(query.contains(&("limit".into(), "10".into())));
224 assert!(query.contains(&("order".into(), "desc".into())));
225 assert!(query.contains(&("purpose".into(), "fine-tune".into())));
226 }
227
228 #[test]
229 fn test_deserialize_file_deleted() {
230 let json = r#"{"id": "file-abc123", "object": "file", "deleted": true}"#;
231 let deleted: FileDeleted = serde_json::from_str(json).unwrap();
232 assert!(deleted.deleted);
233 }
234}