rtb-chat¶
A single chat client across providers. genai
covers the mainstream (OpenAI, Gemini, Ollama, OpenAI-compatible);
the Claude backend additionally drops down to direct reqwest
calls against the Anthropic Messages API for features genai does
not surface — prompt caching, extended thinking, citations.
Formerly
rtb-ai. Renamed at extraction for GTB-family name convergence — the Go counterpart lives at chat.go.phpboyscout.uk. Only the crate identity changed; the API is unchanged.
Part of the phpboyscout Rust toolkit; extracted from — and battle-tested by — rust-tool-base.
Public API¶
use rtb_chat::{AiClient, ChatRequest, Config, Message, Provider};
# async fn run() -> Result<(), rtb_chat::AiError> {
let client = AiClient::new(Config {
provider: Provider::Claude,
model: "claude-opus-4-7".into(),
..Config::default()
})?;
let reply = client.chat(ChatRequest {
messages: vec![Message::user("Summarise this changelog in one line.")],
cache_control: true,
..ChatRequest::default()
}).await?;
# Ok(())
# }
Config and ChatRequest are plain structs with public fields and a
Default impl — set what you need, spread the rest. Provider is
#[non_exhaustive].
| Item | Purpose |
|---|---|
AiClient |
new / chat / chat_stream / chat_structured. |
Config, Provider |
Provider selection, model, endpoint, auth. |
Message, Role, ContentBlock, Citation, Usage |
Request/response message model. |
ChatRequest, ChatResponse, ChatStream, ChatStreamEvent |
Call shapes — one-shot, streaming, and structured. |
ThinkingMode |
Extended-thinking control on the Claude backend. |
AiError |
thiserror + miette::Diagnostic error enum. |
validate_base_url |
Endpoint allowlist guard (see below). |
Full API reference: docs.rs/rtb-chat.
Structured output¶
chat_structured::<T>() takes any T: serde::Deserialize +
schemars::JsonSchema. The generated JSON Schema is sent with the
request and the response is validated with jsonschema before
deserialising — a malformed model response surfaces as an
AiError, never a partial T.
Endpoint safety¶
Every Config::base_url passes validate_base_url: non-HTTPS
schemes, URLs carrying userinfo (user:pass@host), and placeholder
hosts (example.com and subdomains) are rejected. Tests against a
wiremock/httpmock server set Config::allow_insecure_base_url,
which is #[serde(skip)] so config files can't downgrade HTTPS
enforcement. Each successful AiClient::new logs the endpoint
hostname at INFO — never the path or query.
Model defaults¶
New AI code defaults to Claude 4.7 models with prompt caching at every stable point (system prompt, tools, static context). Migration mapping: Opus 4.6 → Opus 4.7, Sonnet 4.5 → Sonnet 4.6, Haiku 4.5 (current).
Secrets¶
API keys cross the boundary as secrecy::SecretString and are
resolved through the precedence chain documented in
configure credentials.
Never log the exposed form.
Design record¶
The authoritative contract is the crate's v0.1 spec (written under the former name), retained in the rust-tool-base spec series.