Skip to main content

async_openai/
lib.rs

1//! Rust library for OpenAI
2//!
3//! ## Creating client
4//!
5//! ```
6//! use async_openai::{Client, config::OpenAIConfig};
7//!
8//! // Create a OpenAI client with api key from env var OPENAI_API_KEY and default base url.
9//! let client = Client::new();
10//!
11//! // Above is shortcut for
12//! let config = OpenAIConfig::default();
13//! let client = Client::with_config(config);
14//!
15//! // OR use API key from different source and a non default organization
16//! let api_key = "sk-..."; // This secret could be from a file, or environment variable.
17//! let config = OpenAIConfig::new()
18//!     .with_api_key(api_key)
19//!     .with_org_id("the-continental");
20//!
21//! let client = Client::with_config(config);
22//!
23//! // Use custom reqwest client
24//! let http_client = reqwest::ClientBuilder::new().user_agent("async-openai").build().unwrap();
25//! let client = Client::new().with_http_client(http_client);
26//! ```
27//!
28//!
29//! ## Making requests
30//!
31//!```
32//!# tokio_test::block_on(async {
33//! use async_openai::{Client, types::responses::{CreateResponseArgs}};
34//!
35//! // Create client
36//! let client = Client::new();
37//!
38//! // Create request using builder pattern
39//! // Every request struct has companion builder struct with same name + Args suffix
40//! let request = CreateResponseArgs::default()
41//!     .model("gpt-5-mini")
42//!     .input("tell me the recipe of pav bhaji")
43//!     .max_output_tokens(512u32)
44//!     .build()?;
45//!
46//! // Call API
47//! let response = client
48//!     .responses()      // Get the API "group" (responses, images, etc.) from the client
49//!     .create(request)  // Make the API call in that "group"
50//!     .await?;
51//!
52//! println!("{:?}", response.output_text());
53//! # Ok::<(), Box<dyn std::error::Error>>(())
54//! # });
55//!```
56//!
57//! ## OpenAI Compatible Providers
58//!
59//! Even though the scope of the crate is official OpenAI APIs, it is very configurable to work with compatible providers.
60//!
61//! ### Bring Your Own Types
62//!
63//! To use custom types for inputs and outputs, enable `byot` feature which provides additional generic methods with same name and `_byot` suffix.
64//! This feature is available on methods whose return type is not `Bytes`
65//!
66//!```
67//!# #[cfg(feature = "byot")]
68//!# tokio_test::block_on(async {
69//! use async_openai::Client;
70//! use serde_json::{Value, json};
71//!
72//! let client = Client::new();
73//!
74//! let response: Value = client
75//!        .chat()
76//!        .create_byot(json!({
77//!            "messages": [
78//!                {
79//!                    "role": "developer",
80//!                    "content": "You are a helpful assistant"
81//!                },
82//!                {
83//!                    "role": "user",
84//!                    "content": "What do you think about life?"
85//!                }
86//!            ],
87//!            "model": "gpt-4o",
88//!            "store": false
89//!        }))
90//!        .await?;
91//!
92//!  if let Some(content) = response["choices"][0]["message"]["content"].as_str() {
93//!     println!("{}", content);
94//!  }
95//! # Ok::<(), Box<dyn std::error::Error>>(())
96//! # });
97//!```
98//!
99//! **References: Borrow Instead of Move**
100//!
101//! With `byot` use reference to request types
102//!
103//! ```
104//! # #[cfg(feature = "byot")]
105//! # tokio_test::block_on(async {
106//! # use async_openai::{Client, types::responses::{CreateResponse, Response}};
107//! # let client = Client::new();
108//! # let request = CreateResponse::default();
109//! let response: Response = client
110//!   .responses()
111//!   .create_byot(&request).await?;
112//! # Ok::<(), Box<dyn std::error::Error>>(())
113//! # });
114//! ```
115//!
116//! ### Configurable Requests
117//! Configure path, headers, and query parameters for a HTTP request.
118//!
119//! **Request Options**
120//!
121//! 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.
122//!
123//! For demonstration:
124//! ```
125//! # tokio_test::block_on(async {
126//! # use async_openai::Client;
127//! # use async_openai::traits::RequestOptionsBuilder;
128//! # let client = Client::new();
129//! client
130//!   .chat()
131//!   // override default path
132//!   .path("/v1/messages")
133//!   // query can be a struct or a map too - additive
134//!   .query(&[("limit", "10")])?
135//!   // header for unique id for this API request - additive
136//!   .header("x-request-id", "id123")?
137//!   .list()
138//!   .await?;
139//! # Ok::<(), Box<dyn std::error::Error>>(())
140//! # });
141//! ```
142//!
143//! **Modifying all Requests**
144//!
145//! Use `Config`, `OpenAIConfig` etc. for configuring url, headers or query parameters globally for all requests.
146//!
147//!
148//! ### Dynamic Dispatch
149//!
150//! This allows you to use same code (say a `fn`) to call APIs on different OpenAI-compatible providers.
151//!
152//! Create a client with `Box` or `Arc` wrapped configuration.
153//!
154//! For example:
155//! ```
156//! use async_openai::{Client, config::{Config, OpenAIConfig}};
157//!
158//! // Use `Box` or `std::sync::Arc` to wrap the config
159//! let config = Box::new(OpenAIConfig::default()) as Box<dyn Config>;
160//! // create client
161//! let client: Client<Box<dyn Config>> = Client::with_config(config);
162//!
163//! // A function can now accept a `&Client<Box<dyn Config>>` parameter
164//! // which can invoke any openai compatible api
165//! fn chat_completion(client: &Client<Box<dyn Config>>) {
166//!     todo!()
167//! }
168//! ```
169//!
170//! ### Microsoft Azure
171//!
172//! ```
173//! use async_openai::{Client, config::AzureConfig};
174//!
175//! let config = AzureConfig::new()
176//!     .with_api_base("https://my-resource-name.openai.azure.com")
177//!     .with_api_version("2023-03-15-preview")
178//!     .with_deployment_id("deployment-id")
179//!     .with_api_key("...");
180//!
181//! let client = Client::with_config(config);
182//!
183//!
184//! ```
185//!
186//!
187//! ## Rust Types
188//!
189//! To only use Rust types from the crate - use feature flag `types`.
190//!
191//! There are granular feature flags like `response-types`, `chat-completion-types`, etc.
192//!
193//! These granular types are enabled when the corresponding API feature is enabled - for example `responses` will enable `response-types`.
194//!
195//! ## WASM
196//! WASM is supported for all APIs.
197//! 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).
198//!
199//!
200//! ## Middleware
201//!
202//! Middleware is supported via Tower ecosystem. See [`middleware`] for more detail.
203//!
204//! ## Examples
205//! For full working examples for all supported features see [examples](https://github.com/64bit/async-openai/tree/main/examples) directory in the repository.
206//!
207//! ## OpenAPI
208//!
209//! Enable `openapi` together with `chat-completion-types` and/or `completion-types`
210//! to use utoipa 5's `ToSchema` with those request, response, streaming chat, and
211//! nested types. Other API families are not yet covered.
212//!
213//! For example, compose an upstream request with your own fields:
214//!
215//! ```
216//! # #[cfg(all(feature = "openapi", feature = "chat-completion-types"))]
217//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
218//! use async_openai::types::chat::CreateChatCompletionRequest;
219//! use serde::{Deserialize, Serialize};
220//! use utoipa::{OpenApi, ToSchema};
221//!
222//! #[derive(Serialize, Deserialize, ToSchema)]
223//! struct ExtendedChatRequest {
224//!     #[serde(flatten)]
225//!     request: CreateChatCompletionRequest,
226//!     application_tag: Option<String>,
227//! }
228//!
229//! #[derive(OpenApi)]
230//! #[openapi(components(schemas(ExtendedChatRequest)))]
231//! struct ApiDoc;
232//!
233//! let json = ApiDoc::openapi().to_pretty_json()?;
234//! # assert!(json.contains("async_openai.CreateChatCompletionRequest"));
235//! # Ok(())
236//! # }
237//! # #[cfg(not(all(feature = "openapi", feature = "chat-completion-types")))]
238//! # fn main() {}
239//! ```
240//!
241//! OpenAPI component names use a flat `async_openai.` prefix, for example
242//! `async_openai.ImageDetail`, to distinguish them from downstream types. These
243//! names are document identifiers, not Rust module paths; imports remain under
244//! `async_openai::types::chat` or `async_openai::types::completions`. Shared types
245//! keep one component name regardless of which public module re-exports them.
246//!
247//! Request schemas include documented OpenAI numeric ranges for sampling parameters,
248//! counts, and logit biases. These are schema constraints only: they do not add
249//! validation to Serde, builders, or HTTP requests. The schemas do not express every
250//! server rule, including model-dependent limits and relationships between fields.
251//!
252//! Downstream servers can use [`utoipa::Modify`](https://docs.rs/utoipa/5/utoipa/trait.Modify.html)
253//! or edit the generated document to replace or remove constraints. For example,
254//! changing the `n` property's `maximum` from 128 to 255 widens its documented range;
255//! clearing `minimum` and `maximum` removes the explicit bounds. This does not
256//! widen the Rust field's `u8` range. Edits to a shared component affect all its
257//! references; clone and rename it for endpoint-specific changes. Adding an
258//! `allOf` constraint can narrow an inherited range, but cannot widen it.
259//! Schema generation does not enable the HTTP client.
260//!
261#![cfg_attr(docsrs, feature(doc_cfg))]
262
263#[cfg(all(feature = "_api", feature = "byot"))]
264#[allow(unused_imports)]
265pub(crate) use async_openai_macros::byot;
266
267#[cfg(all(feature = "_api", not(feature = "byot")))]
268#[allow(unused_imports)]
269pub(crate) use async_openai_macros::byot_passthrough as byot;
270
271// #[cfg(all(not(feature = "_api"), not(feature = "byot")))]
272// #[macro_export]
273// macro_rules! byot {
274//     ($($tt:tt)*) => {
275//         $($tt)*
276//     };
277// }
278
279#[cfg(feature = "administration")]
280mod admin;
281#[cfg(feature = "audio")]
282mod audio;
283#[cfg(feature = "batch")]
284mod batches;
285#[cfg(feature = "chat-completion")]
286mod chat;
287#[cfg(feature = "chatkit")]
288mod chatkit;
289#[cfg(feature = "_api")]
290mod client;
291#[cfg(feature = "completions")]
292mod completion;
293#[cfg(feature = "_api")]
294pub mod config;
295#[cfg(feature = "container")]
296mod containers;
297#[cfg(feature = "image")]
298mod download;
299#[cfg(feature = "embedding")]
300mod embedding;
301pub mod error;
302#[cfg(feature = "evals")]
303mod evals;
304#[cfg(feature = "_api")]
305mod executor;
306#[cfg(feature = "file")]
307mod file;
308#[cfg(feature = "finetuning")]
309mod fine_tuning;
310#[cfg(feature = "image")]
311mod image;
312#[cfg(feature = "_api")]
313mod impls;
314#[cfg(feature = "middleware")]
315pub mod middleware;
316#[cfg(feature = "model")]
317mod model;
318#[cfg(feature = "moderation")]
319mod moderation;
320#[cfg(feature = "realtime")]
321mod realtime;
322#[cfg(feature = "_api")]
323mod request_options;
324#[cfg(feature = "responses")]
325mod responses;
326#[cfg(feature = "_api")]
327#[allow(dead_code)]
328#[path = "middleware/retry/mod.rs"]
329mod retry;
330#[cfg(feature = "skill")]
331mod skills;
332#[cfg(feature = "_api")]
333pub mod traits;
334pub mod types;
335#[cfg(feature = "upload")]
336mod uploads;
337#[cfg(any(
338    feature = "content-provenance-checks",
339    feature = "audio",
340    feature = "file",
341    feature = "upload",
342    feature = "image",
343    feature = "video",
344    feature = "container",
345    feature = "skill"
346))]
347mod util;
348#[cfg(feature = "vectorstore")]
349mod vectorstores;
350#[cfg(feature = "video")]
351mod video;
352#[cfg(feature = "webhook")]
353pub mod webhooks;
354
355// admin::* would be good - however its expanded here so that docs.rs shows the feature flags
356#[cfg(feature = "administration")]
357pub use admin::{
358    Admin, AdminAPIKeys, AuditLogs, Certificates, GroupRoles, GroupUsers, Groups, Invites,
359    OrganizationDataRetentions, OrganizationSpendAlerts, OrganizationSpendLimit, ProjectAPIKeys,
360    ProjectCertificates, ProjectDataRetentions, ProjectGroupRoles, ProjectGroups,
361    ProjectHostedToolPermission, ProjectModelPermission, ProjectRateLimits, ProjectRoles,
362    ProjectServiceAccounts, ProjectSpendAlerts, ProjectSpendLimit, ProjectUserRoles, ProjectUsers,
363    Projects, Roles, Usage, UserRoles, Users,
364};
365#[cfg(feature = "audio")]
366pub use audio::{Audio, Speech, Transcriptions, Translations};
367#[cfg(feature = "batch")]
368pub use batches::Batches;
369#[cfg(feature = "chat-completion")]
370pub use chat::Chat;
371#[cfg(feature = "chatkit")]
372pub use chatkit::Chatkit;
373#[cfg(feature = "_api")]
374pub use client::Client;
375#[cfg(feature = "completions")]
376pub use completion::Completions;
377#[cfg(feature = "container")]
378pub use containers::{ContainerFiles, Containers};
379#[cfg(feature = "embedding")]
380pub use embedding::Embeddings;
381#[cfg(feature = "evals")]
382pub use evals::{EvalRunOutputItems, EvalRuns, Evals};
383#[cfg(feature = "file")]
384pub use file::Files;
385#[cfg(feature = "finetuning")]
386pub use fine_tuning::FineTuning;
387#[cfg(feature = "image")]
388pub use image::Images;
389#[cfg(feature = "model")]
390pub use model::Models;
391#[cfg(feature = "moderation")]
392pub use moderation::Moderations;
393#[cfg(feature = "realtime")]
394pub use realtime::{Realtime, RealtimeTranslations};
395#[cfg(feature = "_api")]
396pub use request_options::RequestOptions;
397#[cfg(feature = "responses")]
398pub use responses::{ConversationItems, Conversations, Responses};
399#[cfg(feature = "skill")]
400pub use skills::{SkillVersions, Skills};
401#[cfg(feature = "upload")]
402pub use uploads::Uploads;
403#[cfg(feature = "vectorstore")]
404pub use vectorstores::{VectorStoreFileBatches, VectorStoreFiles, VectorStores};
405#[cfg(feature = "video")]
406#[allow(deprecated)]
407pub use video::Videos;
408
409#[cfg(feature = "safety")]
410mod safety;
411#[cfg(feature = "safety")]
412pub use safety::*;
413
414#[cfg(feature = "content-provenance-checks")]
415mod content_provenance_checks;
416#[cfg(feature = "content-provenance-checks")]
417pub use content_provenance_checks::ContentProvenanceChecks;