Skip to content

Commit e4d3635

Browse files
authored
feat: add container api (64bit#461)
* feat: containers api * feat: add examples/containers * updated readme * cargo fmt * clear docs on scope
1 parent c920edc commit e4d3635

File tree

10 files changed

+476
-10
lines changed

10 files changed

+476
-10
lines changed

async-openai/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
- [x] Chat
3030
- [x] Completions (Legacy)
3131
- [x] Conversations
32+
- [x] Containers | Container Files
3233
- [x] Embeddings
3334
- [x] Files
3435
- [x] Fine-Tuning
@@ -37,7 +38,7 @@
3738
- [x] Moderations
3839
- [x] Organizations | Administration (partially implemented)
3940
- [x] Realtime GA (partially implemented)
40-
- [x] Responses (partially implemented)
41+
- [x] Responses
4142
- [x] Uploads
4243
- [x] Videos
4344
- Bring your own custom types for Request or Response objects.
@@ -173,7 +174,7 @@ To maintain quality of the project, a minimum of the following is a must for cod
173174

174175
- **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.
175176
- **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.
176-
- **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.
177+
- **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.
177178
- **Consistency**: Keep code style consistent across all the "APIs" that library exposes; it creates a great developer experience.
178179

179180
This project adheres to [Rust Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct)

async-openai/src/client.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ use crate::{
1313
image::Images,
1414
moderation::Moderations,
1515
traits::AsyncTryFrom,
16-
Assistants, Audio, AuditLogs, Batches, Chat, Completions, Conversations, Embeddings,
17-
FineTuning, Invites, Models, Projects, Responses, Threads, Uploads, Users, VectorStores,
18-
Videos,
16+
Assistants, Audio, AuditLogs, Batches, Chat, Completions, Containers, Conversations,
17+
Embeddings, FineTuning, Invites, Models, Projects, Responses, Threads, Uploads, Users,
18+
VectorStores, Videos,
1919
};
2020

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

181+
/// To call [Containers] group related APIs using this client.
182+
pub fn containers(&self) -> Containers<'_, C> {
183+
Containers::new(self)
184+
}
185+
181186
pub fn config(&self) -> &C {
182187
&self.config
183188
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
use bytes::Bytes;
2+
use serde::Serialize;
3+
4+
use crate::{
5+
config::Config,
6+
error::OpenAIError,
7+
types::{
8+
ContainerFileListResource, ContainerFileResource, CreateContainerFileRequest,
9+
DeleteContainerFileResponse,
10+
},
11+
Client,
12+
};
13+
14+
/// Create and manage container files for use with the Code Interpreter tool.
15+
pub struct ContainerFiles<'c, C: Config> {
16+
client: &'c Client<C>,
17+
container_id: String,
18+
}
19+
20+
impl<'c, C: Config> ContainerFiles<'c, C> {
21+
pub fn new(client: &'c Client<C>, container_id: &str) -> Self {
22+
Self {
23+
client,
24+
container_id: container_id.to_string(),
25+
}
26+
}
27+
28+
/// Create a container file by uploading a raw file or by referencing an existing file ID.
29+
#[crate::byot(
30+
T0 = Clone,
31+
R = serde::de::DeserializeOwned,
32+
where_clause = "reqwest::multipart::Form: crate::traits::AsyncTryFrom<T0, Error = OpenAIError>",
33+
)]
34+
pub async fn create(
35+
&self,
36+
request: CreateContainerFileRequest,
37+
) -> Result<ContainerFileResource, OpenAIError> {
38+
self.client
39+
.post_form(&format!("/containers/{}/files", self.container_id), request)
40+
.await
41+
}
42+
43+
/// List container files.
44+
#[crate::byot(T0 = serde::Serialize, R = serde::de::DeserializeOwned)]
45+
pub async fn list<Q>(&self, query: &Q) -> Result<ContainerFileListResource, OpenAIError>
46+
where
47+
Q: Serialize + ?Sized,
48+
{
49+
self.client
50+
.get_with_query(&format!("/containers/{}/files", self.container_id), &query)
51+
.await
52+
}
53+
54+
/// Retrieve a container file.
55+
#[crate::byot(T0 = std::fmt::Display, R = serde::de::DeserializeOwned)]
56+
pub async fn retrieve(&self, file_id: &str) -> Result<ContainerFileResource, OpenAIError> {
57+
self.client
58+
.get(format!("/containers/{}/files/{file_id}", self.container_id).as_str())
59+
.await
60+
}
61+
62+
/// Delete a container file.
63+
#[crate::byot(T0 = std::fmt::Display, R = serde::de::DeserializeOwned)]
64+
pub async fn delete(&self, file_id: &str) -> Result<DeleteContainerFileResponse, OpenAIError> {
65+
self.client
66+
.delete(format!("/containers/{}/files/{file_id}", self.container_id).as_str())
67+
.await
68+
}
69+
70+
/// Returns the content of a container file.
71+
pub async fn content(&self, file_id: &str) -> Result<Bytes, OpenAIError> {
72+
self.client
73+
.get_raw(format!("/containers/{}/files/{file_id}/content", self.container_id).as_str())
74+
.await
75+
}
76+
}

async-openai/src/containers.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
use serde::Serialize;
2+
3+
use crate::{
4+
config::Config,
5+
container_files::ContainerFiles,
6+
error::OpenAIError,
7+
types::{
8+
ContainerListResource, ContainerResource, CreateContainerRequest, DeleteContainerResponse,
9+
},
10+
Client,
11+
};
12+
13+
pub struct Containers<'c, C: Config> {
14+
client: &'c Client<C>,
15+
}
16+
17+
impl<'c, C: Config> Containers<'c, C> {
18+
pub fn new(client: &'c Client<C>) -> Self {
19+
Self { client }
20+
}
21+
22+
/// [ContainerFiles] API group
23+
pub fn files(&self, container_id: &str) -> ContainerFiles<'_, C> {
24+
ContainerFiles::new(self.client, container_id)
25+
}
26+
27+
/// Create a container.
28+
#[crate::byot(T0 = serde::Serialize, R = serde::de::DeserializeOwned)]
29+
pub async fn create(
30+
&self,
31+
request: CreateContainerRequest,
32+
) -> Result<ContainerResource, OpenAIError> {
33+
self.client.post("/containers", request).await
34+
}
35+
36+
/// List containers.
37+
#[crate::byot(T0 = serde::Serialize, R = serde::de::DeserializeOwned)]
38+
pub async fn list<Q>(&self, query: &Q) -> Result<ContainerListResource, OpenAIError>
39+
where
40+
Q: Serialize + ?Sized,
41+
{
42+
self.client.get_with_query("/containers", &query).await
43+
}
44+
45+
/// Retrieve a container.
46+
#[crate::byot(T0 = std::fmt::Display, R = serde::de::DeserializeOwned)]
47+
pub async fn retrieve(&self, container_id: &str) -> Result<ContainerResource, OpenAIError> {
48+
self.client
49+
.get(format!("/containers/{container_id}").as_str())
50+
.await
51+
}
52+
53+
/// Delete a container.
54+
#[crate::byot(T0 = std::fmt::Display, R = serde::de::DeserializeOwned)]
55+
pub async fn delete(&self, container_id: &str) -> Result<DeleteContainerResponse, OpenAIError> {
56+
self.client
57+
.delete(format!("/containers/{container_id}").as_str())
58+
.await
59+
}
60+
}

async-openai/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ mod chat;
148148
mod client;
149149
mod completion;
150150
pub mod config;
151+
mod container_files;
152+
mod containers;
151153
mod conversation_items;
152154
mod conversations;
153155
mod download;
@@ -185,6 +187,8 @@ pub use batches::Batches;
185187
pub use chat::Chat;
186188
pub use client::Client;
187189
pub use completion::Completions;
190+
pub use container_files::ContainerFiles;
191+
pub use containers::Containers;
188192
pub use conversation_items::ConversationItems;
189193
pub use conversations::Conversations;
190194
pub use embedding::Embeddings;
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
use derive_builder::Builder;
2+
use serde::{Deserialize, Serialize};
3+
4+
use crate::error::OpenAIError;
5+
6+
use super::InputSource;
7+
8+
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
9+
pub struct ContainerResource {
10+
/// Unique identifier for the container.
11+
pub id: String,
12+
/// The type of this object.
13+
pub object: String,
14+
/// Name of the container.
15+
pub name: String,
16+
/// Unix timestamp (in seconds) when the container was created.
17+
pub created_at: u32,
18+
/// Status of the container (e.g., active, deleted).
19+
pub status: String,
20+
/// The container will expire after this time period. The anchor is the reference point for the expiration.
21+
/// The minutes is the number of minutes after the anchor before the container expires.
22+
#[serde(skip_serializing_if = "Option::is_none")]
23+
pub expires_after: Option<ContainerExpiresAfter>,
24+
/// Unix timestamp (in seconds) when the container was last active.
25+
#[serde(skip_serializing_if = "Option::is_none")]
26+
pub last_active_at: Option<u32>,
27+
}
28+
29+
/// Expiration policy for containers.
30+
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
31+
pub struct ContainerExpiresAfter {
32+
/// Time anchor for the expiration time. Currently only 'last_active_at' is supported.
33+
pub anchor: ContainerExpiresAfterAnchor,
34+
pub minutes: u32,
35+
}
36+
37+
/// Anchor for container expiration.
38+
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
39+
#[serde(rename_all = "snake_case")]
40+
pub enum ContainerExpiresAfterAnchor {
41+
LastActiveAt,
42+
}
43+
44+
/// Request to create a container.
45+
/// openapi spec type: CreateContainerBody
46+
#[derive(Debug, Default, Clone, Builder, PartialEq, Serialize)]
47+
#[builder(name = "CreateContainerRequestArgs")]
48+
#[builder(pattern = "mutable")]
49+
#[builder(setter(into, strip_option), default)]
50+
#[builder(derive(Debug))]
51+
#[builder(build_fn(error = "OpenAIError"))]
52+
pub struct CreateContainerRequest {
53+
/// Name of the container to create.
54+
pub name: String,
55+
/// IDs of files to copy to the container.
56+
#[serde(skip_serializing_if = "Option::is_none")]
57+
pub file_ids: Option<Vec<String>>,
58+
/// Container expiration time in minutes relative to the 'anchor' time.
59+
#[serde(skip_serializing_if = "Option::is_none")]
60+
pub expires_after: Option<ContainerExpiresAfter>,
61+
}
62+
63+
/// Response when listing containers.
64+
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
65+
pub struct ContainerListResource {
66+
/// The type of object returned, must be 'list'.
67+
pub object: String,
68+
/// A list of containers.
69+
pub data: Vec<ContainerResource>,
70+
/// The ID of the first container in the list.
71+
pub first_id: Option<String>,
72+
/// The ID of the last container in the list.
73+
pub last_id: Option<String>,
74+
/// Whether there are more containers available.
75+
pub has_more: bool,
76+
}
77+
78+
/// Response when deleting a container.
79+
#[derive(Debug, Deserialize, Clone, PartialEq, Serialize)]
80+
pub struct DeleteContainerResponse {
81+
pub id: String,
82+
pub object: String,
83+
pub deleted: bool,
84+
}
85+
86+
/// Query parameters for listing containers.
87+
#[derive(Debug, Serialize, Default, Clone, Builder, PartialEq)]
88+
#[builder(name = "ListContainersQueryArgs")]
89+
#[builder(pattern = "mutable")]
90+
#[builder(setter(into, strip_option), default)]
91+
#[builder(derive(Debug))]
92+
#[builder(build_fn(error = "OpenAIError"))]
93+
pub struct ListContainersQuery {
94+
/// A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.
95+
#[serde(skip_serializing_if = "Option::is_none")]
96+
pub limit: Option<u32>,
97+
/// Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.
98+
#[serde(skip_serializing_if = "Option::is_none")]
99+
pub order: Option<String>,
100+
/// A cursor for use in pagination. `after` is an object ID that defines your place in the list.
101+
#[serde(skip_serializing_if = "Option::is_none")]
102+
pub after: Option<String>,
103+
}
104+
105+
// Container File types
106+
107+
/// The container file object represents a file in a container.
108+
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
109+
pub struct ContainerFileResource {
110+
/// Unique identifier for the file.
111+
pub id: String,
112+
/// The type of this object (`container.file`).
113+
pub object: String,
114+
/// The container this file belongs to.
115+
pub container_id: String,
116+
/// Unix timestamp (in seconds) when the file was created.
117+
pub created_at: u32,
118+
/// Size of the file in bytes.
119+
pub bytes: u32,
120+
/// Path of the file in the container.
121+
pub path: String,
122+
/// Source of the file (e.g., `user`, `assistant`).
123+
pub source: String,
124+
}
125+
126+
/// Request to create a container file.
127+
/// openapi spec type: CreateContainerFileBody
128+
#[derive(Debug, Default, Clone, PartialEq)]
129+
pub struct CreateContainerFileRequest {
130+
/// The File object (not file name) to be uploaded.
131+
pub file: Option<InputSource>,
132+
/// Name of the file to create.
133+
pub file_id: Option<String>,
134+
}
135+
136+
/// Response when listing container files.
137+
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
138+
pub struct ContainerFileListResource {
139+
/// The type of object returned, must be 'list'.
140+
pub object: String,
141+
/// A list of container files.
142+
pub data: Vec<ContainerFileResource>,
143+
/// The ID of the first file in the list.
144+
pub first_id: Option<String>,
145+
/// The ID of the last file in the list.
146+
pub last_id: Option<String>,
147+
/// Whether there are more files available.
148+
pub has_more: bool,
149+
}
150+
151+
/// Response when deleting a container file.
152+
#[derive(Debug, Deserialize, Clone, PartialEq, Serialize)]
153+
pub struct DeleteContainerFileResponse {
154+
pub id: String,
155+
pub object: String,
156+
pub deleted: bool,
157+
}
158+
159+
/// Query parameters for listing container files.
160+
#[derive(Debug, Serialize, Default, Clone, Builder, PartialEq)]
161+
#[builder(name = "ListContainerFilesQueryArgs")]
162+
#[builder(pattern = "mutable")]
163+
#[builder(setter(into, strip_option), default)]
164+
#[builder(derive(Debug))]
165+
#[builder(build_fn(error = "OpenAIError"))]
166+
pub struct ListContainerFilesQuery {
167+
/// A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.
168+
#[serde(skip_serializing_if = "Option::is_none")]
169+
pub limit: Option<u32>,
170+
/// Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.
171+
#[serde(skip_serializing_if = "Option::is_none")]
172+
pub order: Option<String>,
173+
/// A cursor for use in pagination. `after` is an object ID that defines your place in the list.
174+
#[serde(skip_serializing_if = "Option::is_none")]
175+
pub after: Option<String>,
176+
}

0 commit comments

Comments
 (0)