Skip to main content

rai_sdk/
lib.rs

1//! A unified Rust SDK for backend AI workflows across OpenAI, Anthropic, and
2//! OpenRouter.
3//!
4//! `rai-sdk` wraps three provider APIs behind one typed client so switching
5//! models does not mean rewriting request-building, streaming, or tool-calling
6//! code.
7//!
8//! # Capabilities
9//!
10//! - **Typed providers and models** — construct models with [`Model::gpt4o_mini`],
11//!   [`Model::claude_sonnet_46`], or [`Model::openrouter_auto`], or pass any
12//!   provider model ID directly.
13//! - **Typestate request builders** — [`RequestBuilder::generate`] only exists
14//!   once a prompt and a model are present, so incomplete requests fail to
15//!   compile rather than at runtime.
16//! - **Structured output** — derive [`JsonSchema`] and call
17//!   [`RequestBuilder::generate_structured`] to validate the response against a
18//!   generated schema and deserialize it into your own type.
19//! - **Tool calling** — register typed async tools with [`Tool`];
20//!   [`RequestBuilder::generate`] runs the tool loop, feeding results back until
21//!   the model produces a final answer.
22//! - **Streaming** — consume raw provider events, or use
23//!   [`RequestBuilder::stream_accumulated`] to stream internally and return a
24//!   complete [`Response`].
25//! - **Proxyable streams** — [`RequestBuilder::stream_wire_events`] yields
26//!   serializable [`WireStreamEvent`]s so a server can re-emit a generation to
27//!   its own clients over SSE, and [`StreamAccumulator`] reassembles them on the
28//!   far end. See the [`wire`] module.
29//! - **Retries** — transient rate-limit, timeout, and HTTP failures are retried
30//!   with configurable exponential backoff and jitter via [`RetryConfig`].
31//! - **Multimodal prompts** — build prompts from text, image, audio, video, and
32//!   file [`ContentBlock`]s. Provider support varies.
33//! - **Local and self-hosted models** — point a client at any endpoint speaking
34//!   the OpenAI Chat Completions format with
35//!   [`ClientBuilder::openai_compatible_base_url`] (or
36//!   [`ClientBuilder::ollama`]) and name models with
37//!   [`Model::openai_compatible`]. See the `provider::openai_compatible`
38//!   module, which the `openai` feature gates.
39//!
40//! # Quickstart
41//!
42//! ```no_run
43//! use rai_sdk::{ClientBuilder, Model};
44//!
45//! # async fn run() -> Result<(), Box<dyn std::error::Error>> {
46//! let client = ClientBuilder::new()
47//!     .from_env()
48//!     .model(Model::gpt4o_mini())
49//!     .build()?;
50//!
51//! let response = client
52//!     .request()
53//!     .prompt("Explain Rust ownership in two sentences.")
54//!     .generate()
55//!     .await?;
56//!
57//! println!("{}", response.text());
58//! # Ok(())
59//! # }
60//! ```
61//!
62//! # Configuration
63//!
64//! [`ClientBuilder::from_env`] reads `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, and
65//! `OPENROUTER_API_KEY`, along with optional base-URL, timeout, and retry
66//! overrides. Everything can also be set explicitly on [`ClientBuilder`] or
67//! [`Config`], and explicit values take precedence over the environment.
68//!
69//! # Cargo features
70//!
71//! The `openai`, `anthropic`, and `openrouter` features are all enabled by
72//! default and gate the corresponding provider support. Disable the defaults to
73//! compile against only the providers you use.
74//!
75//! `openai` additionally gates the OpenAI-compatible provider, which reuses
76//! that module's request builder and stream parser rather than duplicating
77//! them. A build that talks only to local models therefore enables `openai`
78//! and nothing else.
79//!
80//! Enabling a provider also requires at least one TLS backend:
81//!
82//! - `rustls-tls` (default) needs no system OpenSSL, but builds `aws-lc-rs`,
83//!   which requires cmake and a C compiler.
84//! - `native-tls` uses the platform TLS stack instead, avoiding that build
85//!   requirement.
86//!
87//! Because a TLS backend is part of the default feature set, disabling default
88//! features means re-enabling one explicitly:
89//!
90//! ```toml
91//! rai-sdk = { version = "0.1", default-features = false, features = ["anthropic", "native-tls"] }
92//! ```
93//!
94//! Cargo features are additive, so dependency feature unification can enable
95//! both backends. That configuration is supported and uses rustls; select only
96//! `native-tls` as shown above to avoid compiling `aws-lc-rs`.
97//!
98//! # Further reading
99//!
100//! The [guide](https://rmagatti.github.io/rai-sdk/) covers each capability in
101//! task-oriented chapters. Its examples are compile-checked against this crate,
102//! so they stay in sync with the API you see here.
103#![cfg_attr(docsrs, feature(doc_cfg))]
104
105// Catch a provider without a TLS backend at compile time. A featureless build
106// is valid because it cannot make provider requests and is still useful to
107// consumers that only need the crate's shared data types.
108#[cfg(all(
109    any(feature = "openai", feature = "anthropic", feature = "openrouter"),
110    not(any(feature = "rustls-tls", feature = "native-tls"))
111))]
112compile_error!(
113    "rai-sdk has a provider enabled but no TLS backend. \
114     This usually means `default-features = false` was set without re-enabling one. \
115     Add `rustls-tls` (the default), or `native-tls` if you cannot build aws-lc-rs, \
116     which requires cmake and a C compiler."
117);
118
119// Compile-check every Rust snippet in the mdBook guide as a doctest, so the
120// published guide cannot drift away from the real API. This module only exists
121// while rustdoc is collecting doctests, so it adds nothing to the built crate or
122// to the rendered documentation.
123#[cfg(doctest)]
124mod guide {
125    macro_rules! chapter {
126        ($name:ident, $path:literal) => {
127            #[doc = include_str!($path)]
128            pub struct $name;
129        };
130    }
131
132    chapter!(Introduction, "../docs/src/introduction.md");
133    chapter!(Installation, "../docs/src/installation.md");
134    chapter!(Quickstart, "../docs/src/quickstart.md");
135    chapter!(Configuration, "../docs/src/configuration.md");
136    chapter!(ProvidersAndModels, "../docs/src/providers-and-models.md");
137    chapter!(StructuredOutput, "../docs/src/structured-output.md");
138    chapter!(ToolCalling, "../docs/src/tool-calling.md");
139    chapter!(Streaming, "../docs/src/streaming.md");
140    chapter!(MultimodalPrompts, "../docs/src/multimodal-prompts.md");
141    chapter!(RetriesAndErrors, "../docs/src/retries-and-errors.md");
142    chapter!(Examples, "../docs/src/examples.md");
143    chapter!(Contributing, "../docs/src/contributing.md");
144}
145
146pub mod client;
147pub mod config;
148pub mod error;
149pub mod generation;
150pub mod message;
151pub mod model;
152pub mod provider;
153pub mod retry;
154pub mod tool;
155pub mod wire;
156
157pub use client::{Client, ClientBuilder, RequestBuilder};
158pub use config::{Config, EndpointCapabilities};
159pub use error::{Capability, Error, ProviderKind, Result, ToolArgumentIssue};
160pub use generation::GenerationConfig;
161pub use message::{
162    ContentBlock, ImageSource, Message, Prompt, Response, Role, StreamChunk, StructuredOutput,
163    ToolCall, ToolDefinition, Usage,
164};
165pub use model::{AnthropicModel, Model, OpenAICompatibleModel, OpenAIModel, OpenRouterModel};
166pub use retry::RetryConfig;
167pub use schemars::{self, JsonSchema};
168pub use tool::{Tool, ToolContext};
169pub use wire::{
170    StreamAccumulator, WIRE_PROTOCOL_VERSION, WireError, WireErrorKind, WireStreamEvent,
171};