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
5 changes: 3 additions & 2 deletions async-openai/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
- [x] Chat
- [x] Completions (Legacy)
- [x] Conversations
- [x] Containers | Container Files
- [x] Embeddings
- [x] Files
- [x] Fine-Tuning
Expand All @@ -37,7 +38,7 @@
- [x] Moderations
- [x] Organizations | Administration (partially implemented)
- [x] Realtime GA (partially implemented)
- [x] Responses (partially implemented)
- [x] Responses
- [x] Uploads
- [x] Videos
- Bring your own custom types for Request or Response objects.
Expand Down Expand Up @@ -173,7 +174,7 @@ To maintain quality of the project, a minimum of the following is a must for cod

- **Names & Documentation**: All struct names, field names and doc comments are from OpenAPI spec. Nested objects in spec without names leaves room for making appropriate name.
- **Tested**: For changes supporting test(s) and/or example is required. Existing examples, doc tests, unit tests, and integration tests should be made to work with the changes if applicable.
- **Scope**: Keep scope limited to APIs available in official documents such as [API Reference](https://platform.openai.com/docs/api-reference) or [OpenAPI spec](https://github.com/openai/openai-openapi/). Other LLMs or AI Providers offer OpenAI-compatible APIs, yet they may not always have full parity. In such cases, the OpenAI spec takes precedence.
- **Scope**: Keep scope limited to APIs available in official documents such as [API Reference](https://platform.openai.com/docs/api-reference) or [OpenAPI spec](https://github.com/openai/openai-openapi/). Other LLMs or AI Providers offer OpenAI-compatible APIs, yet they may not always have full parity - for those use `byot` feature.
- **Consistency**: Keep code style consistent across all the "APIs" that library exposes; it creates a great developer experience.

This project adheres to [Rust Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct)
Expand Down
11 changes: 8 additions & 3 deletions async-openai/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ use crate::{
image::Images,
moderation::Moderations,
traits::AsyncTryFrom,
Assistants, Audio, AuditLogs, Batches, Chat, Completions, Conversations, Embeddings,
FineTuning, Invites, Models, Projects, Responses, Threads, Uploads, Users, VectorStores,
Videos,
Assistants, Audio, AuditLogs, Batches, Chat, Completions, Containers, Conversations,
Embeddings, FineTuning, Invites, Models, Projects, Responses, Threads, Uploads, Users,
VectorStores, Videos,
};

#[derive(Debug, Clone, Default)]
Expand Down Expand Up @@ -178,6 +178,11 @@ impl<C: Config> Client<C> {
Conversations::new(self)
}

/// To call [Containers] group related APIs using this client.
pub fn containers(&self) -> Containers<'_, C> {
Containers::new(self)
}

pub fn config(&self) -> &C {
&self.config
}
Expand Down
76 changes: 76 additions & 0 deletions async-openai/src/container_files.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use bytes::Bytes;
use serde::Serialize;

use crate::{
config::Config,
error::OpenAIError,
types::{
ContainerFileListResource, ContainerFileResource, CreateContainerFileRequest,
DeleteContainerFileResponse,
},
Client,
};

/// Create and manage container files for use with the Code Interpreter tool.
pub struct ContainerFiles<'c, C: Config> {
client: &'c Client<C>,
container_id: String,
}

impl<'c, C: Config> ContainerFiles<'c, C> {
pub fn new(client: &'c Client<C>, container_id: &str) -> Self {
Self {
client,
container_id: container_id.to_string(),
}
}

/// Create a container file by uploading a raw file or by referencing an existing file ID.
#[crate::byot(
T0 = Clone,
R = serde::de::DeserializeOwned,
where_clause = "reqwest::multipart::Form: crate::traits::AsyncTryFrom<T0, Error = OpenAIError>",
)]
pub async fn create(
&self,
request: CreateContainerFileRequest,
) -> Result<ContainerFileResource, OpenAIError> {
self.client
.post_form(&format!("/containers/{}/files", self.container_id), request)
.await
}

/// List container files.
#[crate::byot(T0 = serde::Serialize, R = serde::de::DeserializeOwned)]
pub async fn list<Q>(&self, query: &Q) -> Result<ContainerFileListResource, OpenAIError>
where
Q: Serialize + ?Sized,
{
self.client
.get_with_query(&format!("/containers/{}/files", self.container_id), &query)
.await
}

/// Retrieve a container file.
#[crate::byot(T0 = std::fmt::Display, R = serde::de::DeserializeOwned)]
pub async fn retrieve(&self, file_id: &str) -> Result<ContainerFileResource, OpenAIError> {
self.client
.get(format!("/containers/{}/files/{file_id}", self.container_id).as_str())
.await
}

/// Delete a container file.
#[crate::byot(T0 = std::fmt::Display, R = serde::de::DeserializeOwned)]
pub async fn delete(&self, file_id: &str) -> Result<DeleteContainerFileResponse, OpenAIError> {
self.client
.delete(format!("/containers/{}/files/{file_id}", self.container_id).as_str())
.await
}

/// Returns the content of a container file.
pub async fn content(&self, file_id: &str) -> Result<Bytes, OpenAIError> {
self.client
.get_raw(format!("/containers/{}/files/{file_id}/content", self.container_id).as_str())
.await
}
}
60 changes: 60 additions & 0 deletions async-openai/src/containers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use serde::Serialize;

use crate::{
config::Config,
container_files::ContainerFiles,
error::OpenAIError,
types::{
ContainerListResource, ContainerResource, CreateContainerRequest, DeleteContainerResponse,
},
Client,
};

pub struct Containers<'c, C: Config> {
client: &'c Client<C>,
}

impl<'c, C: Config> Containers<'c, C> {
pub fn new(client: &'c Client<C>) -> Self {
Self { client }
}

/// [ContainerFiles] API group
pub fn files(&self, container_id: &str) -> ContainerFiles<'_, C> {
ContainerFiles::new(self.client, container_id)
}

/// Create a container.
#[crate::byot(T0 = serde::Serialize, R = serde::de::DeserializeOwned)]
pub async fn create(
&self,
request: CreateContainerRequest,
) -> Result<ContainerResource, OpenAIError> {
self.client.post("/containers", request).await
}

/// List containers.
#[crate::byot(T0 = serde::Serialize, R = serde::de::DeserializeOwned)]
pub async fn list<Q>(&self, query: &Q) -> Result<ContainerListResource, OpenAIError>
where
Q: Serialize + ?Sized,
{
self.client.get_with_query("/containers", &query).await
}

/// Retrieve a container.
#[crate::byot(T0 = std::fmt::Display, R = serde::de::DeserializeOwned)]
pub async fn retrieve(&self, container_id: &str) -> Result<ContainerResource, OpenAIError> {
self.client
.get(format!("/containers/{container_id}").as_str())
.await
}

/// Delete a container.
#[crate::byot(T0 = std::fmt::Display, R = serde::de::DeserializeOwned)]
pub async fn delete(&self, container_id: &str) -> Result<DeleteContainerResponse, OpenAIError> {
self.client
.delete(format!("/containers/{container_id}").as_str())
.await
}
}
4 changes: 4 additions & 0 deletions async-openai/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ mod chat;
mod client;
mod completion;
pub mod config;
mod container_files;
mod containers;
mod conversation_items;
mod conversations;
mod download;
Expand Down Expand Up @@ -185,6 +187,8 @@ pub use batches::Batches;
pub use chat::Chat;
pub use client::Client;
pub use completion::Completions;
pub use container_files::ContainerFiles;
pub use containers::Containers;
pub use conversation_items::ConversationItems;
pub use conversations::Conversations;
pub use embedding::Embeddings;
Expand Down
176 changes: 176 additions & 0 deletions async-openai/src/types/containers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
use derive_builder::Builder;
use serde::{Deserialize, Serialize};

use crate::error::OpenAIError;

use super::InputSource;

#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
pub struct ContainerResource {
/// Unique identifier for the container.
pub id: String,
/// The type of this object.
pub object: String,
/// Name of the container.
pub name: String,
/// Unix timestamp (in seconds) when the container was created.
pub created_at: u32,
/// Status of the container (e.g., active, deleted).
pub status: String,
/// The container will expire after this time period. The anchor is the reference point for the expiration.
/// The minutes is the number of minutes after the anchor before the container expires.
#[serde(skip_serializing_if = "Option::is_none")]
pub expires_after: Option<ContainerExpiresAfter>,
/// Unix timestamp (in seconds) when the container was last active.
#[serde(skip_serializing_if = "Option::is_none")]
pub last_active_at: Option<u32>,
}

/// Expiration policy for containers.
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
pub struct ContainerExpiresAfter {
/// Time anchor for the expiration time. Currently only 'last_active_at' is supported.
pub anchor: ContainerExpiresAfterAnchor,
pub minutes: u32,
}

/// Anchor for container expiration.
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum ContainerExpiresAfterAnchor {
LastActiveAt,
}

/// Request to create a container.
/// openapi spec type: CreateContainerBody
#[derive(Debug, Default, Clone, Builder, PartialEq, Serialize)]
#[builder(name = "CreateContainerRequestArgs")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "OpenAIError"))]
pub struct CreateContainerRequest {
/// Name of the container to create.
pub name: String,
/// IDs of files to copy to the container.
#[serde(skip_serializing_if = "Option::is_none")]
pub file_ids: Option<Vec<String>>,
/// Container expiration time in minutes relative to the 'anchor' time.
#[serde(skip_serializing_if = "Option::is_none")]
pub expires_after: Option<ContainerExpiresAfter>,
}

/// Response when listing containers.
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
pub struct ContainerListResource {
/// The type of object returned, must be 'list'.
pub object: String,
/// A list of containers.
pub data: Vec<ContainerResource>,
/// The ID of the first container in the list.
pub first_id: Option<String>,
/// The ID of the last container in the list.
pub last_id: Option<String>,
/// Whether there are more containers available.
pub has_more: bool,
}

/// Response when deleting a container.
#[derive(Debug, Deserialize, Clone, PartialEq, Serialize)]
pub struct DeleteContainerResponse {
pub id: String,
pub object: String,
pub deleted: bool,
}

/// Query parameters for listing containers.
#[derive(Debug, Serialize, Default, Clone, Builder, PartialEq)]
#[builder(name = "ListContainersQueryArgs")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "OpenAIError"))]
pub struct ListContainersQuery {
/// A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<u32>,
/// Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.
#[serde(skip_serializing_if = "Option::is_none")]
pub order: Option<String>,
/// A cursor for use in pagination. `after` is an object ID that defines your place in the list.
#[serde(skip_serializing_if = "Option::is_none")]
pub after: Option<String>,
}

// Container File types

/// The container file object represents a file in a container.
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
pub struct ContainerFileResource {
/// Unique identifier for the file.
pub id: String,
/// The type of this object (`container.file`).
pub object: String,
/// The container this file belongs to.
pub container_id: String,
/// Unix timestamp (in seconds) when the file was created.
pub created_at: u32,
/// Size of the file in bytes.
pub bytes: u32,
/// Path of the file in the container.
pub path: String,
/// Source of the file (e.g., `user`, `assistant`).
pub source: String,
}

/// Request to create a container file.
/// openapi spec type: CreateContainerFileBody
#[derive(Debug, Default, Clone, PartialEq)]
pub struct CreateContainerFileRequest {
/// The File object (not file name) to be uploaded.
pub file: Option<InputSource>,
/// Name of the file to create.
pub file_id: Option<String>,
}

/// Response when listing container files.
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
pub struct ContainerFileListResource {
/// The type of object returned, must be 'list'.
pub object: String,
/// A list of container files.
pub data: Vec<ContainerFileResource>,
/// The ID of the first file in the list.
pub first_id: Option<String>,
/// The ID of the last file in the list.
pub last_id: Option<String>,
/// Whether there are more files available.
pub has_more: bool,
}

/// Response when deleting a container file.
#[derive(Debug, Deserialize, Clone, PartialEq, Serialize)]
pub struct DeleteContainerFileResponse {
pub id: String,
pub object: String,
pub deleted: bool,
}

/// Query parameters for listing container files.
#[derive(Debug, Serialize, Default, Clone, Builder, PartialEq)]
#[builder(name = "ListContainerFilesQueryArgs")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "OpenAIError"))]
pub struct ListContainerFilesQuery {
/// A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<u32>,
/// Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.
#[serde(skip_serializing_if = "Option::is_none")]
pub order: Option<String>,
/// A cursor for use in pagination. `after` is an object ID that defines your place in the list.
#[serde(skip_serializing_if = "Option::is_none")]
pub after: Option<String>,
}
Loading
Loading