Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion async-openai/src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ impl<'c, C: Config> Files<'c, C> {
#[cfg(test)]
mod tests {
use crate::{
types::{CreateFileRequestArgs, FilePurpose},
types::{CreateFileRequestArgs, FilePurpose, FileExpiresAfter, FileExpiresAfterAnchor},
Client,
};

Expand All @@ -89,6 +89,7 @@ mod tests {
let request = CreateFileRequestArgs::default()
.file(test_file_path)
.purpose(FilePurpose::FineTune)
.expires_after(FileExpiresAfter{ anchor: FileExpiresAfterAnchor::CreatedAt, seconds: 3600 })
.build()
.unwrap();

Expand All @@ -111,6 +112,7 @@ mod tests {
assert_eq!(openai_file.bytes, retrieved_file.bytes);
assert_eq!(openai_file.filename, retrieved_file.filename);
assert_eq!(openai_file.purpose, retrieved_file.purpose);
assert_eq!(openai_file.expires_at, retrieved_file.expires_at);

/*
// "To help mitigate abuse, downloading of fine-tune training files is disabled for free accounts."
Expand Down
21 changes: 21 additions & 0 deletions async-openai/src/types/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,22 @@ pub enum FilePurpose {
Vision,
}

#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)]
pub enum FileExpiresAfterAnchor {
#[default]
#[serde(rename = "created_at")]
CreatedAt
}

#[derive(Debug, Default, Clone, PartialEq)]
pub struct FileExpiresAfter {
/// Anchor timestamp after which the expiration policy applies. Supported anchors: `created_at`.
pub anchor: FileExpiresAfterAnchor,

/// The number of seconds after the anchor time that the file will expire. Must be between 3600 (1 hour) and 2592000 (30 days).
pub seconds: u32,
}

#[derive(Debug, Default, Clone, Builder, PartialEq)]
#[builder(name = "CreateFileRequestArgs")]
#[builder(pattern = "mutable")]
Expand All @@ -33,6 +49,9 @@ pub struct CreateFileRequest {
///
/// 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: FilePurpose,

/// The expiration policy for a file. By default, files with `purpose=batch` expire after 30 days and all other files are persisted until they are manually deleted.
pub expires_after: Option<FileExpiresAfter>,
}

#[derive(Debug, Deserialize, Clone, PartialEq, Serialize)]
Expand Down Expand Up @@ -77,6 +96,8 @@ pub struct OpenAIFile {
pub bytes: u32,
/// The Unix timestamp (in seconds) for when the file was created.
pub created_at: u32,
/// The Unix timestamp (in seconds) for when the file will expire.
pub expires_at: Option<u32>,
/// The name of the file.
pub filename: String,
/// The intended purpose of the file. Supported values are `assistants`, `assistants_output`, `batch`, `batch_output`, `fine-tune`, `fine-tune-results` and `vision`.
Expand Down
21 changes: 19 additions & 2 deletions async-openai/src/types/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use super::{
CreateSpeechResponse, CreateTranscriptionRequest, CreateTranslationRequest, DallE2ImageSize,
EmbeddingInput, FileInput, FilePurpose, FunctionName, Image, ImageInput, ImageModel,
ImageResponseFormat, ImageSize, ImageUrl, ImagesResponse, ModerationInput, Prompt, Role, Stop,
TimestampGranularity,
TimestampGranularity, FileExpiresAfterAnchor
};

/// for `impl_from!(T, Enum)`, implements
Expand Down Expand Up @@ -278,6 +278,18 @@ impl Display for FilePurpose {
}
}

impl Display for FileExpiresAfterAnchor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::CreatedAt => "created_at",
}
)
}
}

impl ImagesResponse {
/// Save each image in a dedicated Tokio task and return paths to saved files.
/// For [ResponseFormat::Url] each file is downloaded in dedicated Tokio task.
Expand Down Expand Up @@ -970,9 +982,14 @@ impl AsyncTryFrom<CreateFileRequest> for reqwest::multipart::Form {

async fn try_from(request: CreateFileRequest) -> Result<Self, Self::Error> {
let file_part = create_file_part(request.file.source).await?;
let form = reqwest::multipart::Form::new()
let mut form = reqwest::multipart::Form::new()
.part("file", file_part)
.text("purpose", request.purpose.to_string());

if let Some(expires_after) = request.expires_after {
form = form.text("expires_after[anchor]", expires_after.anchor.to_string())
.text("expires_after[seconds]", expires_after.seconds.to_string());
}
Ok(form)
}
}
Expand Down
1 change: 1 addition & 0 deletions async-openai/src/vector_store_files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ mod tests {
String::from(":3").into_bytes(),
),
purpose: FilePurpose::Assistants,
expires_after: None,
})
.await?;

Expand Down