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
84 changes: 45 additions & 39 deletions async-openai/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,23 @@

## Overview

`async-openai` is an unofficial Rust library for OpenAI.

- It's based on [OpenAI OpenAPI spec](https://github.com/openai/openai-openapi)
- Current features:
- [x] Administration (partially implemented)
- [x] Assistants (beta)
- [x] Audio
- [x] Batch
- [x] Chat
- [x] ChatKit (beta)
- [x] Completions (legacy)
- [x] Conversations
- [x] Containers
- [x] Embeddings
- [x] Evals
- [x] Files
- [x] Fine-Tuning
- [x] Images
- [x] Models
- [x] Moderations
- [x] Realtime (partially implemented)
- [x] Responses
- [x] Uploads
- [x] Vector Stores
- [x] Videos
- [x] Webhooks
`async-openai` is an unofficial Rust library for OpenAI, based on [OpenAI OpenAPI spec](https://github.com/openai/openai-openapi). It implements all APIs from the spec:

| Features | APIs |
|---|---|
| **Responses API** | Responses, Conversations, Streaming events |
| **Webhooks** | Webhook Events |
| **Platform APIs** | Audio, Audio Streaming, Videos, Images, Image Streaming, Embeddings, Evals, Fine-tuning, Graders, Batch, Files, Uploads, Models, Moderations |
| **Vector stores** | Vector stores, Vector store files, Vector store file batches |
| **ChatKit** <sub>(Beta)</sub> | ChatKit |
| **Containers** | Containers, Container Files |
| **Realtime** | Realtime Calls, Client secrets, Client events, Server events |
| **Chat Completions** | Chat Completions, Streaming |
| **Assistants** <sub>(Beta)</sub> | Assistants, Threads, Messages, Runs, Run steps, Streaming |
| **Administration** | Administration, Admin API Keys, Invites, Users, Projects, Project users, Project service accounts, Project API keys, Project rate limits, Audit logs, Usage, Certificates |
| **Legacy** | Completions |

Features that makes `async-openai` unique:
- Bring your own custom types for Request or Response objects.
- SSE streaming on available APIs
- Requests (except SSE streaming) including form submissions are retried with exponential backoff when [rate limited](https://platform.openai.com/docs/guides/rate-limits).
Expand All @@ -68,9 +59,9 @@ $Env:OPENAI_API_KEY='sk-...'
- Visit [examples](https://github.com/64bit/async-openai/tree/main/examples) directory on how to use `async-openai`.
- Visit [docs.rs/async-openai](https://docs.rs/async-openai) for docs.

## Realtime API
## Realtime

Only types for Realtime API are implemented, and can be enabled with feature flag `realtime`.
Realtime types and APIs can be enabled with feature flag `realtime`.

## Webhooks

Expand Down Expand Up @@ -122,7 +113,11 @@ async fn main() -> Result<(), Box<dyn Error>> {

## Bring Your Own Types

Enable methods whose input and outputs are generics with `byot` feature. It creates a new method with same name and `_byot` suffix.
Enable methods whose input and outputs are generics with `byot` feature. It creates a new method with same name and `_byot` suffix.

`byot` requires trait bounds:
- a request type (`fn` input parameter) needs to implement `serde::Serialize` or `std::fmt::Display` trait
- a response type (`fn` ouput parameter) needs to implement `serde::de::DeserializeOwned` trait.

For example, to use `serde_json::Value` as request and response type:
```rust
Expand All @@ -147,27 +142,35 @@ let response: Value = client

This can be useful in many scenarios:
- To use this library with other OpenAI compatible APIs whose types don't exactly match OpenAI.
- Extend existing types in this crate with new fields with `serde`.
- Extend existing types in this crate with new fields with `serde` (for example with `#[serde(flatten)]`).
- To avoid verbose types.
- To escape deserialization errors.

Visit [examples/bring-your-own-type](https://github.com/64bit/async-openai/tree/main/examples/bring-your-own-type)
directory to learn more.

## Dynamic Dispatch for Different Providers
## Dynamic Dispatch for OpenAI-compatible Providers

This allows you to use same code (say a `fn`) to call APIs on different OpenAI-compatible providers.

For any struct that implements `Config` trait, you can wrap it in a smart pointer and cast the pointer to `dyn Config`
trait object, then your client can accept any wrapped configuration type.
For any struct that implements `Config` trait, wrap it in a smart pointer and cast the pointer to `dyn Config`
trait object, then create a client with `Box` or `Arc` wrapped configuration.

For example,
For example:

```rust
use async_openai::{Client, config::Config, config::OpenAIConfig};
use async_openai::{Client, config::{Config, OpenAIConfig}};

// Use `Box` or `std::sync::Arc` to wrap the config
let config = Box::new(OpenAIConfig::default()) as Box<dyn Config>;
// create client
let client: Client<Box<dyn Config>> = Client::with_config(config);

let openai_config = OpenAIConfig::default();
// You can use `std::sync::Arc` to wrap the config as well
let config = Box::new(openai_config) as Box<dyn Config>;
let client: Client<Box<dyn Config> > = Client::with_config(config);
// A function can now accept a `&Client<Box<dyn Config>>` parameter
// which can invoke any openai compatible api
fn chat_completion(client: &Client<Box<dyn Config>>) {
todo!()
}
```

## Contributing
Expand All @@ -187,8 +190,11 @@ To maintain quality of the project, a minimum of the following is a must for cod

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

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in async-openai by you, shall be licensed as MIT, without any additional terms or conditions.

## Complimentary Crates
- [async-openai-wasm](https://github.com/ifsheldon/async-openai-wasm) provides WASM support.
- [openai-func-enums](https://github.com/frankfralick/openai-func-enums) macros for working with function/tool calls.

## License

Expand Down
46 changes: 46 additions & 0 deletions async-openai/src/admin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use crate::{
admin_api_keys::AdminAPIKeys, audit_logs::AuditLogs, certificates::Certificates,
config::Config, invites::Invites, projects::Projects, users::Users, Client,
};

/// Admin group for all administration APIs.
/// This groups together admin API keys, invites, users, projects, audit logs, and certificates.
pub struct Admin<'c, C: Config> {
client: &'c Client<C>,
}

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

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

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

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

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

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

/// To call [Certificates] group related APIs using this client.
pub fn certificates(&self) -> Certificates<'_, C> {
Certificates::new(self.client)
}
}
60 changes: 60 additions & 0 deletions async-openai/src/admin_api_keys.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use serde::Serialize;

use crate::{
config::Config,
error::OpenAIError,
types::admin::api_keys::{
AdminApiKey, AdminApiKeyDeleteResponse, ApiKeyList, CreateAdminApiKeyRequest,
},
Client,
};

/// Admin API keys enable Organization Owners to programmatically manage various aspects of their
/// organization, including users, projects, and API keys. These keys provide administrative capabilities,
/// allowing you to automate organization management tasks.
pub struct AdminAPIKeys<'c, C: Config> {
client: &'c Client<C>,
}

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

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

/// Create an organization admin API key.
pub async fn create(
&self,
request: CreateAdminApiKeyRequest,
) -> Result<AdminApiKey, OpenAIError> {
self.client
.post("/organization/admin_api_keys", request)
.await
}

/// Retrieve a single organization API key.
#[crate::byot(T0 = std::fmt::Display, R = serde::de::DeserializeOwned)]
pub async fn retrieve(&self, key_id: &str) -> Result<AdminApiKey, OpenAIError> {
self.client
.get(format!("/organization/admin_api_keys/{key_id}").as_str())
.await
}

/// Delete an organization admin API key.
#[crate::byot(T0 = std::fmt::Display, R = serde::de::DeserializeOwned)]
pub async fn delete(&self, key_id: &str) -> Result<AdminApiKeyDeleteResponse, OpenAIError> {
self.client
.delete(format!("/organization/admin_api_keys/{key_id}").as_str())
.await
}
}
2 changes: 1 addition & 1 deletion async-openai/src/assistants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use serde::Serialize;
use crate::{
config::Config,
error::OpenAIError,
types::{
types::assistants::{
AssistantObject, CreateAssistantRequest, DeleteAssistantResponse, ListAssistantsResponse,
ModifyAssistantRequest,
},
Expand Down
4 changes: 3 additions & 1 deletion async-openai/src/audit_logs.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use serde::Serialize;

use crate::{config::Config, error::OpenAIError, types::ListAuditLogsResponse, Client};
use crate::{
config::Config, error::OpenAIError, types::admin::audit_logs::ListAuditLogsResponse, Client,
};

/// Logs of user actions and configuration changes within this organization.
/// To log events, you must activate logging in the [Organization Settings](https://platform.openai.com/settings/organization/general).
Expand Down
124 changes: 124 additions & 0 deletions async-openai/src/certificates.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
use serde::Serialize;

use crate::{
config::Config,
error::OpenAIError,
types::admin::certificates::{
Certificate, DeleteCertificateResponse, ListCertificatesResponse, ModifyCertificateRequest,
ToggleCertificatesRequest, UploadCertificateRequest,
},
Client,
};

/// Certificates enable Mutual TLS (mTLS) authentication for your organization.
/// Manage certificates at the organization level.
pub struct Certificates<'c, C: Config> {
client: &'c Client<C>,
}

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

// Organization-level certificate operations

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

/// Upload a certificate to the organization.
/// This does not automatically activate the certificate.
pub async fn upload_organization(
&self,
request: UploadCertificateRequest,
) -> Result<Certificate, OpenAIError> {
self.client
.post("/organization/certificates", request)
.await
}

/// Activate certificates for the organization.
/// You can atomically and idempotently activate up to 10 certificates at a time.
pub async fn activate_organization(
&self,
request: ToggleCertificatesRequest,
) -> Result<ListCertificatesResponse, OpenAIError> {
self.client
.post("/organization/certificates/activate", request)
.await
}

/// Deactivate certificates for the organization.
/// You can atomically and idempotently deactivate up to 10 certificates at a time.
pub async fn deactivate_organization(
&self,
request: ToggleCertificatesRequest,
) -> Result<ListCertificatesResponse, OpenAIError> {
self.client
.post("/organization/certificates/deactivate", request)
.await
}

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

/// Retrieve a single certificate with optional include parameters.
pub async fn retrieve_with_query<Q>(
&self,
certificate_id: &str,
query: &Q,
) -> Result<Certificate, OpenAIError>
where
Q: Serialize + ?Sized,
{
self.client
.get_with_query(
format!("/organization/certificates/{certificate_id}").as_str(),
query,
)
.await
}

/// Modify a certificate. Note that only the name can be modified.
#[crate::byot(T0 = std::fmt::Display, T1 = serde::Serialize, R = serde::de::DeserializeOwned)]
pub async fn modify(
&self,
certificate_id: &str,
request: ModifyCertificateRequest,
) -> Result<Certificate, OpenAIError> {
self.client
.post(
format!("/organization/certificates/{certificate_id}").as_str(),
request,
)
.await
}

/// Delete a certificate from the organization.
/// The certificate must be inactive for the organization and all projects.
#[crate::byot(T0 = std::fmt::Display, R = serde::de::DeserializeOwned)]
pub async fn delete(
&self,
certificate_id: &str,
) -> Result<DeleteCertificateResponse, OpenAIError> {
self.client
.delete(format!("/organization/certificates/{certificate_id}").as_str())
.await
}
}
Loading
Loading