Skip to content

Commit

Permalink
feat: support upload api
Browse files Browse the repository at this point in the history
  • Loading branch information
YanceyOfficial committed Oct 21, 2024
1 parent 7640aa4 commit 818c9e4
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 1 deletion.
2 changes: 1 addition & 1 deletion rs_openai/src/apis/files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ impl<'a> Files<'a> {
/// # Path parameters
///
/// - `file_id` - The ID of the file to use for this request
pub async fn delete(&self, file_id: &str) -> OpenAIResponse<files::DeleteFileResponse> {
pub async fn add_upload(&self, file_id: &str) -> OpenAIResponse<files::DeleteFileResponse> {
self.openai.delete(&format!("/files/{file_id}"), &()).await
}

Expand Down
1 change: 1 addition & 0 deletions rs_openai/src/apis/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ pub mod fine_tunes;
pub mod images;
pub mod models;
pub mod moderations;
pub mod uploads;
92 changes: 92 additions & 0 deletions rs_openai/src/apis/uploads.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//! Create large uploads of API requests for asynchronous processing.
//! The Upload API returns completions within 24 hours for a 50% discount. Related guide: [Upload](https://platform.openai.com/docs/guides/upload)
use crate::client::OpenAI;
use crate::interfaces::uploads;
use crate::shared::response_wrapper::OpenAIResponse;
use reqwest::multipart::Form;

pub struct Upload<'a> {
openai: &'a OpenAI,
}

impl<'a> Upload<'a> {
pub fn new(openai: &'a OpenAI) -> Self {
Self { openai }
}

/// Creates an intermediate [Upload](https://platform.openai.com/docs/api-reference/uploads/object) object that you can add Parts to. Currently,
/// an Upload can accept at most 8 GB in total and expires after an hour after you create it.
///
/// Once you complete the Upload, we will create a [File](https://platform.openai.com/docs/api-reference/files/object) object that contains all the parts you uploaded.
/// This File is usable in the rest of our platform as a regular File object.
///
/// For certain `purposes`, the correct `mime_type` must be specified. Please refer to documentation for the supported MIME types for your use case:
///
/// - [Assistants](https://platform.openai.com/docs/assistants/tools/file-search/supported-files)
///
/// For guidance on the proper filename extensions for each purpose, please follow the documentation on [creating a File](https://platform.openai.com/docs/api-reference/files/create).
pub async fn upload(
&self,
req: &uploads::UploadFileRequest,
) -> OpenAIResponse<uploads::UploadFileResponse> {
let file_part = reqwest::multipart::Part::stream(req.file.buffer.clone())
.file_name(req.file.filename.clone())
.mime_str("application/octet-stream")
.unwrap();

let form = Form::new()
.part("file", file_part)
.text("purpose", req.purpose.to_string());

self.openai.post_form("/uploads", form).await
}

/// Adds a [Part](https://platform.openai.com/docs/api-reference/uploads/part-object) to an [Upload](https://platform.openai.com/docs/api-reference/uploads/object) object. A Part represents a chunk of bytes from the file you are trying to upload.
///
/// Each Part can be at most 64 MB, and you can add Parts until you hit the Upload maximum of 8 GB.
///
/// It is possible to add multiple Parts in parallel. You can decide the intended order of the Parts when you [complete the Upload](https://platform.openai.com/docs/api-reference/uploads/complete).
pub async fn add_upload_part(
&self,
upload_id: &str,
req: &uploads::AddUploadPartRequest,
) -> OpenAIResponse<uploads::AddUploadPartResponse> {
let file_part = reqwest::multipart::Part::stream(req.data.buffer.clone())
.file_name(req.data.filename.clone())
.mime_str("application/octet-stream")
.unwrap();
let form = Form::new().part("data", file_part);

self.openai
.post_form(&format!("/uploads/{upload_id}/parts"), form)
.await
}

/// Completes the [Upload](https://platform.openai.com/docs/api-reference/uploads/object).
///
/// Within the returned Upload object, there is a nested [File](https://platform.openai.com/docs/api-reference/files/object) object that is ready to use in the rest of the platform.
///
/// You can specify the order of the Parts by passing in an ordered list of the Part IDs.
///
/// The number of bytes uploaded upon completion must match the number of bytes initially specified when creating the Upload object. No Parts may be added after an Upload is completed.
pub async fn complete_upload(
&self,
upload_id: &str,
req: &uploads::CompleteUploadRequest,
) -> OpenAIResponse<uploads::CompleteUploadResponse> {
self.openai
.post(&format!("/uploads/{upload_id}/complete"), req)
.await
}

/// Cancels the Upload. No Parts may be added after an Upload is cancelled.
pub async fn cancel_upload(
&self,
upload_id: &str,
) -> OpenAIResponse<uploads::UploadFileResponse> {
self.openai
.post(&format!("/uploads/{upload_id}/cancel"), &())
.await
}
}
1 change: 1 addition & 0 deletions rs_openai/src/interfaces/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ pub mod fine_tunes;
pub mod images;
pub mod models;
pub mod moderations;
pub mod uploads;
90 changes: 90 additions & 0 deletions rs_openai/src/interfaces/uploads.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
use crate::shared::{response_wrapper::OpenAIError, types::FileMeta};
use derive_builder::Builder;
use serde::{Deserialize, Serialize};

#[derive(Builder, Clone, Debug, Default, Serialize)]
#[builder(name = "UploadFileRequestBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "OpenAIError"))]
pub struct UploadFileRequest {
/// The File object (not file name) to be uploaded.
pub file: FileMeta,

/// The intended purpose of the uploaded file.
/// Use "assistants" for [Assistants](https://platform.openai.com/docs/api-reference/assistants) and [Message](https://platform.openai.com/docs/api-reference/messages) files,
/// "vision" for Assistants image file inputs, "batch" for [Batch API](https://platform.openai.com/docs/guides/batch), and "fine-tune" for [Fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning).
pub purpose: String,
}

#[derive(Debug, Deserialize, Clone, Serialize)]
pub struct UploadFileResponse {
id: String,
object: String,
bytes: u64,
created_at: i64,
filename: String,
purpose: String,
status: String,
expires_at: i64,
}

#[derive(Builder, Clone, Debug, Default, Serialize)]
#[builder(name = "AddUploadPartRequestBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "OpenAIError"))]
pub struct AddUploadPartRequest {
/// The chunk of bytes for this Part.
pub data: FileMeta,
}

/// The upload [Part](https://platform.openai.com/docs/api-reference/uploads/part-object) object.
#[derive(Debug, Deserialize, Clone, Serialize)]
pub struct AddUploadPartResponse {
id: String,
object: String,
created_at: i64,
upload_id: String,
}

#[derive(Builder, Clone, Debug, Default, Serialize)]
#[builder(name = "CompleteUploadBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "OpenAIError"))]
pub struct CompleteUploadRequest {
/// The ordered list of Part IDs.
pub part_ids: Vec<String>,

/// The optional md5 checksum for the file contents to verify if the bytes uploaded matches what you expect.
#[serde(skip_serializing_if = "Option::is_none")]
pub md5: Option<String>,
}

#[derive(Debug, Deserialize, Clone, Serialize)]
pub struct CompleteUploadFile {
id: String,
object: String,
bytes: u64,
created_at: i64,
filename: String,
purpose: String,
}

#[derive(Debug, Deserialize, Clone, Serialize)]
pub struct CompleteUploadResponse {
id: String,
object: String,
bytes: u64,
created_at: i64,
filename: String,
purpose: String,
status: String,
expires_at: i64,
file: CompleteUploadFile,
}

0 comments on commit 818c9e4

Please sign in to comment.