Skip to main content

openai_oxide/resources/
files.rs

1// Files resource — client.files().create() / list() / retrieve() / delete() / content()
2
3use crate::client::OpenAI;
4use crate::error::OpenAIError;
5use crate::types::file::{FileDeleted, FileList, FileObject, FileUploadParams};
6
7/// Access file endpoints.
8pub struct Files<'a> {
9    client: &'a OpenAI,
10}
11
12impl<'a> Files<'a> {
13    pub(crate) fn new(client: &'a OpenAI) -> Self {
14        Self { client }
15    }
16
17    /// Upload a file.
18    ///
19    /// `POST /files`
20    pub async fn create(&self, params: FileUploadParams) -> Result<FileObject, OpenAIError> {
21        let form = reqwest::multipart::Form::new()
22            .part(
23                "file",
24                reqwest::multipart::Part::bytes(params.file).file_name(params.filename),
25            )
26            .text("purpose", params.purpose);
27
28        self.client.post_multipart("/files", form).await
29    }
30
31    /// List files.
32    ///
33    /// `GET /files`
34    pub async fn list(&self) -> Result<FileList, OpenAIError> {
35        self.client.get("/files").await
36    }
37
38    /// Retrieve a file by ID.
39    ///
40    /// `GET /files/{file_id}`
41    pub async fn retrieve(&self, file_id: &str) -> Result<FileObject, OpenAIError> {
42        self.client.get(&format!("/files/{file_id}")).await
43    }
44
45    /// Delete a file.
46    ///
47    /// `DELETE /files/{file_id}`
48    pub async fn delete(&self, file_id: &str) -> Result<FileDeleted, OpenAIError> {
49        self.client.delete(&format!("/files/{file_id}")).await
50    }
51
52    /// Retrieve file content as bytes.
53    ///
54    /// `GET /files/{file_id}/content`
55    pub async fn content(&self, file_id: &str) -> Result<bytes::Bytes, OpenAIError> {
56        self.client
57            .get_raw(&format!("/files/{file_id}/content"))
58            .await
59    }
60}
61
62#[cfg(test)]
63mod tests {
64    use crate::OpenAI;
65    use crate::config::ClientConfig;
66    use crate::types::file::FileUploadParams;
67
68    #[tokio::test]
69    async fn test_files_create() {
70        let mut server = mockito::Server::new_async().await;
71        let mock = server
72            .mock("POST", "/files")
73            .match_header("authorization", "Bearer sk-test")
74            .with_status(200)
75            .with_header("content-type", "application/json")
76            .with_body(
77                r#"{
78                    "id": "file-abc123",
79                    "object": "file",
80                    "bytes": 120000,
81                    "created_at": 1677610602,
82                    "filename": "data.jsonl",
83                    "purpose": "fine-tune",
84                    "status": "uploaded"
85                }"#,
86            )
87            .create_async()
88            .await;
89
90        let client = OpenAI::with_config(ClientConfig::new("sk-test").base_url(server.url()));
91        let params = FileUploadParams::new(b"test data".to_vec(), "data.jsonl", "fine-tune");
92
93        let response = client.files().create(params).await.unwrap();
94        assert_eq!(response.id, "file-abc123");
95        assert_eq!(response.purpose, "fine-tune");
96        mock.assert_async().await;
97    }
98
99    #[tokio::test]
100    async fn test_files_list() {
101        let mut server = mockito::Server::new_async().await;
102        let mock = server
103            .mock("GET", "/files")
104            .with_status(200)
105            .with_header("content-type", "application/json")
106            .with_body(
107                r#"{
108                    "object": "list",
109                    "data": [{
110                        "id": "file-abc123",
111                        "object": "file",
112                        "bytes": 120000,
113                        "created_at": 1677610602,
114                        "filename": "data.jsonl",
115                        "purpose": "fine-tune",
116                        "status": "processed"
117                    }]
118                }"#,
119            )
120            .create_async()
121            .await;
122
123        let client = OpenAI::with_config(ClientConfig::new("sk-test").base_url(server.url()));
124        let response = client.files().list().await.unwrap();
125        assert_eq!(response.data.len(), 1);
126        mock.assert_async().await;
127    }
128
129    #[tokio::test]
130    async fn test_files_retrieve() {
131        let mut server = mockito::Server::new_async().await;
132        let mock = server
133            .mock("GET", "/files/file-abc123")
134            .with_status(200)
135            .with_header("content-type", "application/json")
136            .with_body(
137                r#"{
138                    "id": "file-abc123",
139                    "object": "file",
140                    "bytes": 120000,
141                    "created_at": 1677610602,
142                    "filename": "data.jsonl",
143                    "purpose": "fine-tune",
144                    "status": "processed"
145                }"#,
146            )
147            .create_async()
148            .await;
149
150        let client = OpenAI::with_config(ClientConfig::new("sk-test").base_url(server.url()));
151        let file = client.files().retrieve("file-abc123").await.unwrap();
152        assert_eq!(file.id, "file-abc123");
153        mock.assert_async().await;
154    }
155
156    #[tokio::test]
157    async fn test_files_delete() {
158        let mut server = mockito::Server::new_async().await;
159        let mock = server
160            .mock("DELETE", "/files/file-abc123")
161            .with_status(200)
162            .with_header("content-type", "application/json")
163            .with_body(r#"{"id": "file-abc123", "object": "file", "deleted": true}"#)
164            .create_async()
165            .await;
166
167        let client = OpenAI::with_config(ClientConfig::new("sk-test").base_url(server.url()));
168        let resp = client.files().delete("file-abc123").await.unwrap();
169        assert!(resp.deleted);
170        mock.assert_async().await;
171    }
172
173    #[tokio::test]
174    async fn test_files_content() {
175        let mut server = mockito::Server::new_async().await;
176        let content_bytes = b"line1\nline2\nline3";
177        let mock = server
178            .mock("GET", "/files/file-abc123/content")
179            .with_status(200)
180            .with_header("content-type", "application/octet-stream")
181            .with_body(content_bytes)
182            .create_async()
183            .await;
184
185        let client = OpenAI::with_config(ClientConfig::new("sk-test").base_url(server.url()));
186        let response = client.files().content("file-abc123").await.unwrap();
187        assert_eq!(response.as_ref(), content_bytes);
188        mock.assert_async().await;
189    }
190}