Skip to main content

floopy/resources/
files.rs

1use std::sync::Arc;
2
3use reqwest::multipart::{Form, Part};
4use reqwest::Method;
5
6use crate::constants::{file_by_id, file_content, ENDPOINT_FILES};
7use crate::error::Result;
8use crate::http::HttpTransport;
9use crate::options::RequestOptions;
10use crate::types::{FileList, FileListParams, FileObject, FileUploadParams};
11
12use super::require;
13
14/// Files API — upload/list/retrieve/delete files and download content.
15///
16/// v1 targets the `batch` purpose (JSONL input + output files). Select the
17/// upstream with [`RequestOptions::provider`] (the `floopy-provider`
18/// header).
19pub struct Files {
20    t: Arc<HttpTransport>,
21}
22
23impl Files {
24    pub(crate) fn new(t: Arc<HttpTransport>) -> Self {
25        Self { t }
26    }
27
28    /// Upload a file as `multipart/form-data`.
29    ///
30    /// # Errors
31    /// Returns an [`Error`](crate::Error) on a non-2xx response or transport
32    /// failure.
33    pub async fn upload(
34        &self,
35        params: FileUploadParams,
36        req: impl Into<Option<RequestOptions>>,
37    ) -> Result<FileObject> {
38        let filename = params.filename.unwrap_or_else(|| "file".to_owned());
39        let part = Part::bytes(params.file).file_name(filename);
40        let form = Form::new()
41            .text("purpose", params.purpose)
42            .part("file", part);
43        let (data, _) = self
44            .t
45            .request_multipart(ENDPOINT_FILES, form, req.into().as_ref())
46            .await?;
47        require(data)
48    }
49
50    /// List files, optionally filtered by purpose.
51    ///
52    /// # Errors
53    /// Returns an [`Error`](crate::Error) on a non-2xx response or transport
54    /// failure.
55    pub async fn list(
56        &self,
57        params: FileListParams,
58        req: impl Into<Option<RequestOptions>>,
59    ) -> Result<FileList> {
60        let (data, _) = self
61            .t
62            .request(
63                Method::GET,
64                ENDPOINT_FILES,
65                None,
66                &params.query(),
67                req.into().as_ref(),
68            )
69            .await?;
70        require(data)
71    }
72
73    /// Retrieve a single file's metadata.
74    ///
75    /// # Errors
76    /// Returns an [`Error`](crate::Error) on a non-2xx response or transport
77    /// failure.
78    pub async fn retrieve(
79        &self,
80        file_id: &str,
81        req: impl Into<Option<RequestOptions>>,
82    ) -> Result<FileObject> {
83        let (data, _) = self
84            .t
85            .request(
86                Method::GET,
87                &file_by_id(file_id),
88                None,
89                &[],
90                req.into().as_ref(),
91            )
92            .await?;
93        require(data)
94    }
95
96    /// Download raw file content (e.g. a batch output/error JSONL).
97    ///
98    /// # Errors
99    /// Returns an [`Error`](crate::Error) on a non-2xx response or transport
100    /// failure.
101    pub async fn content(
102        &self,
103        file_id: &str,
104        req: impl Into<Option<RequestOptions>>,
105    ) -> Result<Vec<u8>> {
106        let (bytes, _) = self
107            .t
108            .request_bytes(
109                Method::GET,
110                &file_content(file_id),
111                &[],
112                req.into().as_ref(),
113            )
114            .await?;
115        Ok(bytes)
116    }
117
118    /// Delete a file.
119    ///
120    /// # Errors
121    /// Returns an [`Error`](crate::Error) on a non-2xx response or transport
122    /// failure.
123    pub async fn delete(
124        &self,
125        file_id: &str,
126        req: impl Into<Option<RequestOptions>>,
127    ) -> Result<FileObject> {
128        let (data, _) = self
129            .t
130            .request(
131                Method::DELETE,
132                &file_by_id(file_id),
133                None,
134                &[],
135                req.into().as_ref(),
136            )
137            .await?;
138        require(data)
139    }
140}