Skip to content

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.