rpi-ai
The unified, provider-agnostic LLM layer: message and content types, the
Provider trait, streaming events, and concrete provider implementations.
Every other crate in the family talks in these types. Nothing above this layer
knows whether it is talking to Anthropic, an OpenAI-compatible gateway, or the
deterministic in-process faux provider.
Install
[]
# Types + the faux provider only. No HTTP stack, no network at build time.
= "0.3"
[]
# Adds the real HTTP providers (Anthropic Messages, OpenAI-compatible Chat
# Completions / Responses, and OpenAI-compatible gateways).
= { = "0.3", = ["providers"] }
| Feature | Default | What it enables |
|---|---|---|
providers |
off | HTTP providers, reqwest + eventsource-stream, images, http |
smoke |
off | providers plus a live network smoke test, gated at runtime on ANTHROPIC_API_KEY |
Core builds stay network-free on purpose: a test double is not a special case, it is the default.
What lives where
types—Message,Content,AssistantMessage,AssistantMessageEvent,Context,Tool,Usage. Serialized in the provider wire shape.model—Modeland the streaming-protocol compatibility structs.schema— tool-parameter schema generation plus the coercion that recovers JSON an LLM emitted slightly wrong.event_stream— the producer/consumer streaming queue behindAssistantMessageEventStream.provider— theProvidertrait seam,ProviderHooks,SimpleStreamOptions, and the optionalDeferredProvidercapability for long-poll APIs.providers— concrete implementations:faux(always),anthropic,openai_completions,openai_responses,openrouter,deepseek,llama_cpp,proxy(behindproviders).
The Provider trait
use Model;
use SimpleStreamOptions;
use Context;
use AssistantMessageEventStream;
stream_simple returns the consumer end of a stream whose producer is already
running, and reports failures as Error events rather than Err. That keeps
the harness and the agent loop uniform across providers: one code path handles
a network failure, a malformed frame, and a cancelled request.
Testing without a network
The faux provider scripts exact turns, so a test pins the model's behaviour
rather than mocking it:
use ;
use Provider;
let provider = new;
let model = provider.default_model.clone;
A tool call followed by a text turn is enough to exercise the whole
tool → result → follow-up cycle offline. See
examples/tools.
Related crates
| Crate | Role |
|---|---|
rpi-agent |
The agent loop built on this layer |
rpi-harness |
Sessions, persistence, compaction |
rpi-cli |
The rpi terminal agent |
Documentation
- Crate docs: https://docs.rs/rpi-ai
- Architecture: docs/architecture.md