1use bytes::Bytes;
2use serde::Serialize;
34use crate::{
5 config::Config,
6 error::OpenAIError,
7 types::{CreateFileRequest, DeleteFileResponse, ListFilesResponse, OpenAIFile},
8 Client,
9};
1011/// Files are used to upload documents that can be used with features like Assistants and Fine-tuning.
12pub struct Files<'c, C: Config> {
13 client: &'c Client<C>,
14}
1516impl<'c, C: Config> Files<'c, C> {
17pub fn new(client: &'c Client<C>) -> Self {
18Self { client }
19 }
2021/// Upload a file that can be used across various endpoints. Individual files can be up to 512 MB, and the size of all files uploaded by one organization can be up to 100 GB.
22 ///
23 /// The Assistants API supports files up to 2 million tokens and of specific file types. See the [Assistants Tools guide](https://platform.openai.com/docs/assistants/tools) for details.
24 ///
25 /// The Fine-tuning API only supports `.jsonl` files. The input also has certain required formats for fine-tuning [chat](https://platform.openai.com/docs/api-reference/fine-tuning/chat-input) or [completions](https://platform.openai.com/docs/api-reference/fine-tuning/completions-input) models.
26 ///
27 ///The Batch API only supports `.jsonl` files up to 100 MB in size. The input also has a specific required [format](https://platform.openai.com/docs/api-reference/batch/request-input).
28 ///
29 /// Please [contact us](https://help.openai.com/) if you need to increase these storage limits.
30#[crate::byot(
31 T0 = Clone,
32 R = serde::de::DeserializeOwned,
33 where_clause = "reqwest::multipart::Form: crate::traits::AsyncTryFrom<T0, Error = OpenAIError>",
34 )]
35pub async fn create(&self, request: CreateFileRequest) -> Result<OpenAIFile, OpenAIError> {
36self.client.post_form("/files", request).await
37}
3839/// Returns a list of files that belong to the user's organization.
40#[crate::byot(T0 = serde::Serialize, R = serde::de::DeserializeOwned)]
41pub async fn list<Q>(&self, query: &Q) -> Result<ListFilesResponse, OpenAIError>
42where
43Q: Serialize + ?Sized,
44 {
45self.client.get_with_query("/files", &query).await
46}
4748/// Returns information about a specific file.
49#[crate::byot(T0 = std::fmt::Display, R = serde::de::DeserializeOwned)]
50pub async fn retrieve(&self, file_id: &str) -> Result<OpenAIFile, OpenAIError> {
51self.client.get(format!("/files/{file_id}").as_str()).await
52}
5354/// Delete a file.
55#[crate::byot(T0 = std::fmt::Display, R = serde::de::DeserializeOwned)]
56pub async fn delete(&self, file_id: &str) -> Result<DeleteFileResponse, OpenAIError> {
57self.client
58 .delete(format!("/files/{file_id}").as_str())
59 .await
60}
6162/// Returns the contents of the specified file
63pub async fn content(&self, file_id: &str) -> Result<Bytes, OpenAIError> {
64self.client
65 .get_raw(format!("/files/{file_id}/content").as_str())
66 .await
67}
68}
6970#[cfg(test)]
71mod tests {
72use crate::{
73 types::{CreateFileRequestArgs, FilePurpose},
74 Client,
75 };
7677#[tokio::test]
78async fn test_file_mod() {
79let test_file_path = "/tmp/test.jsonl";
80let contents = concat!(
81"{\"prompt\": \"<prompt text>\", \"completion\": \"<ideal generated text>\"}\n", // \n is to make it valid jsonl
82"{\"prompt\": \"<prompt text>\", \"completion\": \"<ideal generated text>\"}"
83);
8485 tokio::fs::write(test_file_path, contents).await.unwrap();
8687let client = Client::new();
8889let request = CreateFileRequestArgs::default()
90 .file(test_file_path)
91 .purpose(FilePurpose::FineTune)
92 .build()
93 .unwrap();
9495let openai_file = client.files().create(request).await.unwrap();
9697assert_eq!(openai_file.bytes, 135);
98assert_eq!(openai_file.filename, "test.jsonl");
99//assert_eq!(openai_file.purpose, "fine-tune");
100101 //assert_eq!(openai_file.status, Some("processed".to_owned())); // uploaded or processed
102let query = [("purpose", "fine-tune")];
103104let list_files = client.files().list(&query).await.unwrap();
105106assert_eq!(list_files.data.into_iter().last().unwrap(), openai_file);
107108let retrieved_file = client.files().retrieve(&openai_file.id).await.unwrap();
109110assert_eq!(openai_file.created_at, retrieved_file.created_at);
111assert_eq!(openai_file.bytes, retrieved_file.bytes);
112assert_eq!(openai_file.filename, retrieved_file.filename);
113assert_eq!(openai_file.purpose, retrieved_file.purpose);
114115/*
116 // "To help mitigate abuse, downloading of fine-tune training files is disabled for free accounts."
117 let retrieved_contents = client.files().retrieve_content(&openai_file.id)
118 .await
119 .unwrap();
120121 assert_eq!(contents, retrieved_contents);
122 */
123124 // Sleep to prevent "File is still processing. Check back later."
125tokio::time::sleep(std::time::Duration::from_secs(15)).await;
126let delete_response = client.files().delete(&openai_file.id).await.unwrap();
127128assert_eq!(openai_file.id, delete_response.id);
129assert!(delete_response.deleted);
130 }
131}