Rust SDK Setup
Add the Rust port of @baleybots/core to your Rust project — async,
streaming, and type-safe over the same three-tier model as the TypeScript
and Swift SDKs.
The Rust SDK currently lives in a private mirror repo. You need read
access to baleybots/baleybots-rust on GitHub. Reach out in the org if
you don't have it yet.
Requirements
-
Rust 1.80+ (2021 edition)
-
A Tokio runtime — the SDK is async and built on
tokio+reqwest -
An API key for whichever provider you use, in the environment:
Provider Cargo feature Env var Anthropic (default) ANTHROPIC_API_KEYOpenAI openaiOPENAI_API_KEYGoogle (Gemini) googleGEMINI_API_KEYOllama ollamaOLLAMA_HOST(local needs no key)
Anthropic builds by default; the other three providers are additive Cargo features — turn on only what you use.
Add the package
The crate ships from the baleybots-rust mirror, synced from this
monorepo. Depend on it by git URL:
[dependencies]
# Anthropic only (default):
baleybots = { git = "https://github.com/baleybots/baleybots-rust", branch = "main" }
# …or with extra providers:
# baleybots = { git = "https://github.com/baleybots/baleybots-rust", branch = "main", features = ["openai", "google", "ollama"] }
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
futures = "0.3" # only if you consume streams
Pin to a tag once a release is cut (tag = "rust-v0.1.0") instead of
tracking branch = "main".
Authenticating Cargo to a private repo
Cargo uses the same credentials as git clone against github.com.
Pick whichever you already have set up:
-
HTTPS (credential helper) — tell Cargo to shell out to the system
git, so it reuses your credential helper:# ~/.cargo/config.toml[net]git-fetch-with-cli = trueor per-command:
CARGO_NET_GIT_FETCH_WITH_CLI=true cargo build. -
SSH (recommended for dev) — change the URL to
ssh://git@github.com/baleybots/baleybots-rustand Cargo uses your SSH agent, no extra config.
Rule of thumb: if you can git clone the mirror, cargo build will
pull it.
Hello, world
#[tokio::main]
async fn main() -> Result<(), baleybots::Error> {
// Tier 1 — one-liner. `None` model = resolve from the environment.
let answer = baleybots::generate("Capital of France?", None, None).await?;
println!("{answer}");
Ok(())
}
export ANTHROPIC_API_KEY=sk-ant-… and cargo run.
What's in the package
Three composable tiers, all built on one Processable primitive:
| Tier | Entry point | When to use |
|---|---|---|
| Tier 1 | generate / stream | One-line completions, prototyping |
| Tier 2 | ModelClient | Custom loops, parallel fan-out, your own retry policy |
| Tier 3 | Baleybot | Goal-driven agent with tool loop, multi-turn history, typed structured output |
use baleybots::{AnthropicModel, Baleybot, Model, ModelClient};
let model = Model::Anthropic(AnthropicModel::ClaudeHaiku45);
// Tier 2 — one round-trip, you own the loop.
let client = ModelClient::new(model.clone())?;
let haiku = client.generate("Write a haiku about ownership.", None).await?;
// Tier 3 — a goal-driven agent with a tool loop.
let bot = Baleybot::builder("researcher", "Answer precisely; use tools.", model).build()?;
let reply = bot.process("Summarize the borrow checker in one line.".to_string()).await?;
Plus building blocks that span all three:
Tool+TypedTool— local Rust functions as model tools (one trait; derive a typed one for schema-aware decoding)HttpTool— remote tools (REST endpoints) via the sameTooltrait, no separate protocolprocess_typed::<T>— typed structured output; the answer decodes straight into yourDeserializetype- Multimodal —
image_path/file_pathbuilders; each provider renders only the media kinds (and image subtypes) its API accepts and skips the rest
Streaming + drop-based cancellation
Streaming is Stream<Item = Result<_, Error>>, never callbacks — and the
work runs inside the stream, so dropping the consumer cancels the
in-flight request. No cancel token to thread:
use baleybots::stream;
use futures::StreamExt;
let mut s = stream("Name three primary colors.", None, None);
while let Some(token) = s.next().await {
print!("{}", token?);
}
// Drop `s` early — break out of the loop — and the HTTP request,
// the SSE parse, and any running tool futures all cancel with it.
This is the piece Rust does uniquely well: cancellation is the language's
Drop, not bookkeeping you maintain.
Connecting to a proxy server
Routing through a @baleybots/proxy-server deployment instead of calling
the provider directly:
use baleybots::{AnthropicModel, Model, ModelClient, Url};
let client = ModelClient::with_proxy(
Model::Anthropic(AnthropicModel::ClaudeHaiku45),
Url::parse("https://your-proxy.example.com").unwrap(),
)?;
let answer = client.generate("Capital of France?", None).await?;
No API key needed on the device — the proxy injects its own credentials.
What's in v0.1
The Rust port is feature-complete across the matrix:
- All four providers — Anthropic, OpenAI, Google (Gemini), Ollama — across all three tiers, with model discovery
- Multimodal input — images and files, capability-gated per provider
(and Ollama gates per-model via
/api/show) - Typed structured output — native JSON modes where available, forced tool-calling on Anthropic, identical surface either way
- Prompt caching, remote tools (
HttpTool), drop-cancellable streaming throughout
APIs may still shift before 0.1. No todo!(), no stubs.
Source + contributing
- Rust SDK source: github.com/baleybots/baleybots-rust
- Development happens in the monorepo: PRs go to
baleybots/baleybotsunderrust/. The mirror repo is a derived artifact synced from monorepomain. - Project guide:
rust/AGENTS.md(named invariants, shape rules) - Release procedure:
rust/docs/RELEASE.md