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
2 changes: 1 addition & 1 deletion async-openai/MIDDLEWARE.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,6 @@ Tower's `BoxError` converts into `OpenAIError::Boxed`, which is useful for gener

## Bring Your Own Types Interaction

With the `byot` feature, generated `*_byot` methods keep the same minimal trait bounds with or without middleware. JSON request bodies are serialized before they enter the replayable middleware request factory; multipart request bodies use the client-level replay bounds required by form handling.
With the `byot` feature, generated `*_byot` methods keep the same minimal trait bounds with or without middleware. JSON request bodies are serialized before they enter the replayable middleware request factory.


89 changes: 40 additions & 49 deletions async-openai/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,18 @@

## Overview

`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.
`async-openai` is an unofficial Rust library for OpenAI, based on [OpenAI OpenAPI spec](https://github.com/openai/openai-openapi).
- Requests are retried with exponential backoff when [rate limited](https://platform.openai.com/docs/guides/rate-limits).
- Ergonomic builder pattern for all request objects.
- SSE streaming.
- Granular feature flags to enable any types or apis.
- WASM.
- Middleware support with [tower](https://crates.io/crates/tower) ecosystem.

**+ OpenAI compatible providers**
- Bring your own custom types for Request or Response objects.
- Customize path, query and headers per request or for all requests.
- Microsoft Azure OpenAI Service.

<details>
<summary>Feature Flags</summary>
Expand All @@ -41,17 +52,6 @@

</details>


- Bring your own custom types for Request or Response objects.
- Requests are retried with exponential backoff when [rate limited](https://platform.openai.com/docs/guides/rate-limits).
- Ergonomic builder pattern for all request objects.
- SSE streaming.
- Customize path, query and headers per request or for all requests.
- Granular feature flags to enable any types or apis.
- Microsoft Azure OpenAI Service.
- WASM.
- Middleware support with [tower](https://crates.io/crates/tower) ecosystem.

## Usage

The library reads [API key](https://platform.openai.com/account/api-keys) from the environment variable `OPENAI_API_KEY`.
Expand Down Expand Up @@ -116,7 +116,11 @@ async fn main() -> Result<(), Box<dyn Error>> {
<sub>Scaled up for README, actual size 256x256</sub>
</div>

## Bring Your Own Types
## OpenAI Compatible Providers

Even though the scope of the crate is official OpenAI APIs, it is very configurable to work with compatible providers.

### 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.

Expand All @@ -142,17 +146,17 @@ 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` (for example with `#[serde(flatten)]`).
- When shape of request/response in OpenAI-compatible APIs don't exactly match OpenAI.
- Extend existing types in this crate with new fields like `extra_body` (with serde flatten)
- To avoid typing verbose types.
- To escape deserialization errors.
- To escape deserialization errors on expected type and actual response mismatch.

`*_byot` methods require same trait bounds as regular methods.

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

### References: Borrow Instead of Move
#### References: Borrow Instead of Move

With `byot` use reference to request types

Expand All @@ -165,52 +169,31 @@ let response: Response = client
Visit [examples/borrow-instead-of-move](https://github.com/64bit/async-openai/tree/main/examples/borrow-instead-of-move) to learn more.


## Rust Types

To only use Rust types from the crate - disable default features and use feature flag `types`.

There are granular feature flags like `response-types`, `chat-completion-types`, etc.

These granular types are enabled when the corresponding API feature is enabled - for example `responses` will enable `response-types`.
### Configurable Requests

## Configurable Requests
Configure path, headers, and query parameters for a HTTP request.

### Individual Request
Certain individual APIs that need additional query or header parameters - these can be provided by chaining `.query()`, `.header()`, `.headers()` on the API group.
#### Request Options
Use `path()`, `.query()`, `.header()`, `.headers()` on the API group. Path overrides the default path but all other methods are additive - adds to existing query or headers.

For example:
For demonstration:
```rust
client.
.chat()
// query can be a struct or a map too.
// override default path
.path("/v1/messages")
// query can be a struct or a map too - additive
.query(&[("limit", "10")])?
// header for demo
.header("key", "value")?
// header for unique id for this API request - additive
.header("x-request-id", "id123")?
.list()
.await?
```

### All Requests
#### Modifying all Requests

Use `Config`, `OpenAIConfig` etc. for configuring url, headers or query parameters globally for all requests.

## OpenAI-compatible Providers

Even though the scope of the crate is official OpenAI APIs, it is very configurable to work with compatible providers.

### Configurable Path

In addition to `.query()`, `.header()`, `.headers()` a path for individual request can be changed by using `.path()`, method on the API group.

For example:

```rust
client
.chat()
.path("/v1/messages")?
.create(request)
.await?
```

### Dynamic Dispatch

Expand All @@ -235,6 +218,14 @@ fn chat_completion(client: &Client<Box<dyn Config>>) {
}
```

## Rust Types

To only use Rust types from the crate - disable default features and use feature flag `types`.

There are granular feature flags like `response-types`, `chat-completion-types`, etc.

These granular types are enabled when the corresponding API feature is enabled - for example `responses` will enable `response-types`.

## Webhooks

Support for webhook includes event types, signature verification, and building webhook events from payloads.
Expand Down
4 changes: 1 addition & 3 deletions async-openai/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -401,9 +401,7 @@ impl<C: Config> Client<C> {
I: Serialize,
{
// JSON bodies are materialized once so the base BYOT path can keep
// accepting borrowed inputs. Middleware-enabled BYOT still adds owned
// replay bounds in the macro, but the core client does not force those
// bounds onto non-middleware users.
// accepting borrowed inputs.
let request = Bytes::from(serde_json::to_vec(&request).map_err(|error| {
OpenAIError::InvalidArgument(format!("failed to serialize request: {error}"))
})?);
Expand Down
83 changes: 31 additions & 52 deletions async-openai/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@
//! # });
//!```
//!
//! ## Bring Your Own Types
//! ## OpenAI Compatible Providers
//!
//! Even though the scope of the crate is official OpenAI APIs, it is very configurable to work with compatible providers.
//!
//! ### Bring Your Own Types
//!
//! To use custom types for inputs and outputs, enable `byot` feature which provides additional generic methods with same name and `_byot` suffix.
//! This feature is available on methods whose return type is not `Bytes`
Expand Down Expand Up @@ -109,75 +113,39 @@
//! # });
//! ```
//!
//! ## Rust Types
//!
//! To only use Rust types from the crate - use feature flag `types`.
//!
//! There are granular feature flags like `response-types`, `chat-completion-types`, etc.
//! ### Configurable Requests
//! Configure path, headers, and query parameters for a HTTP request.
//!
//! These granular types are enabled when the corresponding API feature is enabled - for example `responses` will enable `response-types`.
//! **Request Options**
//!
//! ## WASM
//! WASM is supported for all APIs.
//! See [examples/wasm-responses](https://github.com/64bit/async-openai/tree/main/examples/wasm-responses) or [examples/tower-wasm](https://github.com/64bit/async-openai/tree/main/examples/tower-wasm).
//! Use `path()`, `.query()`, `.header()`, `.headers()` on the API group. Path overrides the default path but all other methods are additive - adds to existing query or headers.
//!
//! ## Configurable Requests
//!
//! **Individual Request**
//!
//! Certain individual APIs that need additional query or header parameters - these can be provided by chaining `.query()`, `.header()`, `.headers()` on the API group.
//!
//! For example:
//! For demonstration:
//! ```
//! # tokio_test::block_on(async {
//! # use async_openai::Client;
//! # use async_openai::traits::RequestOptionsBuilder;
//! # let client = Client::new();
//! client
//! .chat()
//! // query can be a struct or a map too.
//! // override default path
//! .path("/v1/messages")
//! // query can be a struct or a map too - additive
//! .query(&[("limit", "10")])?
//! // header for demo
//! .header("key", "value")?
//! // header for unique id for this API request - additive
//! .header("x-request-id", "id123")?
//! .list()
//! .await?;
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! # });
//! ```
//!
//! **All Requests**
//! **Modifying all Requests**
//!
//! Use `Config`, `OpenAIConfig` etc. for configuring url, headers or query parameters globally for all requests.
//!
//! ## OpenAI-compatible Providers
//!
//! Even though the scope of the crate is official OpenAI APIs, it is very configurable to work with compatible providers.
//!
//! **Configurable Path**
//!
//! In addition to `.query()`, `.header()`, `.headers()` a path for individual request can be changed by using `.path()`, method on the API group.
//!
//! For example:
//! ```
//! # tokio_test::block_on(async {
//! # use async_openai::{Client, types::chat::CreateChatCompletionRequestArgs};
//! # use async_openai::traits::RequestOptionsBuilder;
//! # let client = Client::new();
//! # let request = CreateChatCompletionRequestArgs::default()
//! # .model("gpt-4")
//! # .messages([])
//! # .build()
//! # .unwrap();
//! client
//! .chat()
//! .path("/v1/messages")?
//! .create(request)
//! .await?;
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! # });
//! ```
//!
//! **Dynamic Dispatch**
//! ### Dynamic Dispatch
//!
//! This allows you to use same code (say a `fn`) to call APIs on different OpenAI-compatible providers.
//!
Expand All @@ -199,7 +167,7 @@
//! }
//! ```
//!
//! ## Microsoft Azure
//! ### Microsoft Azure
//!
//! ```
//! use async_openai::{Client, config::AzureConfig};
Expand All @@ -212,12 +180,23 @@
//!
//! let client = Client::with_config(config);
//!
//! // Note that `async-openai` only implements OpenAI spec
//! // and doesn't maintain parity with the spec of Azure OpenAI service.
//!
//! ```
//!
//!
//! ## Rust Types
//!
//! To only use Rust types from the crate - use feature flag `types`.
//!
//! There are granular feature flags like `response-types`, `chat-completion-types`, etc.
//!
//! These granular types are enabled when the corresponding API feature is enabled - for example `responses` will enable `response-types`.
//!
//! ## WASM
//! WASM is supported for all APIs.
//! See [examples/wasm-responses](https://github.com/64bit/async-openai/tree/main/examples/wasm-responses) or [examples/tower-wasm](https://github.com/64bit/async-openai/tree/main/examples/tower-wasm).
//!
//!
//! ## Middleware
//!
//! Middleware is supported via Tower ecosystem. See [`middleware`] for more detail.
Expand Down
3 changes: 1 addition & 2 deletions async-openai/src/middleware/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,7 @@
//!
//! With the `byot` feature, generated `*_byot` methods keep the same minimal
//! trait bounds with or without middleware. JSON request bodies are serialized
//! before they enter the replayable middleware request factory; multipart
//! request bodies use the client-level replay bounds required by form handling.
//! before they enter the replayable middleware request factory.

/// Retry layers and policies for middleware.
pub mod retry {
Expand Down
Loading