A Creology runtime · Rust

One Rust API for every model.

chat-rs is the interaction layer between your app and any language model. Streaming, multimodal content, tools, structured output, and multi-provider routing, behind one type-safe API across OpenAI, Claude, Gemini, Ollama, and more.

Get startedView on GitHub
Copy
use chat_rs::{ChatBuilder, StreamEvent, ollama::OllamaBuilder, parts, types::messages};
use futures::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // A local model, pulled and run on your machine. No API keys.
    let client = OllamaBuilder::new().with_model("llama3.2").pull().await?.build();
    let mut chat = ChatBuilder::new().with_model(client).build();

    let mut messages = messages::from_user(parts!["Explain ownership in one line."]);

    // Interaction is a typed event stream. Consume it as it arrives.
    let mut stream = chat.stream(&mut messages).await.map_err(|e| e.err)?;
    while let Some(event) = stream.next().await {
        if let Ok(StreamEvent::TextChunk(text)) = event {
            print!("{text}");
        }
    }
    Ok(())
}

01Everything you need to talk to models

From a one-line completion to streaming, tools, and typed output, chat-rs hands you the whole interaction surface and keeps it identical across every provider you reach for.

Providers

One API, every provider

OpenAI, Claude, Gemini, Ollama, DeepSeek, OpenRouter and more. Change one builder; your call sites never move.

Type-state

The compiler checks your wiring

Missing configuration is a compile error, not a 2am panic. The builder won't let you call what you didn't set up.

Responses

One response shape, always

complete, resume, and stream return the same content and metadata. Switch modes without a rewrite.

Tools

Tools your model can call

Annotate an async fn with #[tool]. The loop runs it, feeds the result back, and keeps going.

Human in the loop

Approve before it acts

Pause the loop on a tool call, let a human approve, reject, or edit it, then resume right where it left off.

Structured

Typed output, every time

Constrain any model to a Rust type and get it back deserialized. No parsing, no guesswork.

Embeddings

Embeddings, first-class

Vectors come from the same providers through one embed call, ready for search and retrieval.

ImagesBeta

Generate images, too

Ask a capable model for pictures, not just words. Image parts come back alongside the text.

Routing

Route and recover

Send each request to the right model by cost or capability, with automatic fallback when one is down.

02How the runtime works

Under the hood, chat-rs runs the reason-and-act loop for you: it calls the model, runs the tools it asks for, feeds the results back, and repeats until the model is done. You write a provider; the loop takes care of the rest.

  • 01

    Plug in any provider

    A provider is just one trait. Bring a hosted API, a local model, or your own gateway. It doesn't have to speak Chat Completions or the Responses API.

  • 02

    The loop is handled for you

    Streaming, retries, structured output, and human-in-the-loop pauses all come built in, so anything you plug in gets them for free.

  • 03

    Native and custom tools, together

    Providers can expose their own native tools, and your #[tool] functions run right alongside them. Put a few providers behind a router and send each request wherever it fits.

provider.rs
Copy
use async_trait::async_trait;use chat_rs::{    ChatFailure, ChatResponse, CompletionProvider, Messages,    types::{options::ChatOptions, tools::ToolDeclarations},};// A provider implements one trait. Bring a hosted API, a local model,// or your own gateway. It need not speak Completions or Responses.struct MyProvider;#[async_trait]impl CompletionProvider for MyProvider {    async fn complete(        &mut self,        messages: &mut Messages,        tools: Option<&dyn ToolDeclarations>,        options: Option<&ChatOptions>,        schema: Option<&schemars::Schema>,    ) -> Result<ChatResponse, ChatFailure> {        // Call your model, then hand back a ChatResponse. chat-rs runs the        // reason-and-act loop, tools, and retries for you.        todo!()    }}

03Small on purpose

chat-rs is the runtime, not the framework. You get a rock-solid interaction layer; agents, workflows, and memory stay yours to build on top, however you like, with whatever architecture fits.

In the box

  • +Messages & multimodal content
  • +Streaming & duplex input
  • +Tools & human-in-the-loop
  • +Structured output & embeddings
  • +Provider routing & fallback
  • +Pluggable HTTP / WebSocket transport

Bring your own

  • ·Agents & planners
  • ·Workflows & DAGs
  • ·Memory & vector stores
  • ·Business logic
  • ·Application state

Start here

Ship your first completion in five minutes.

Add the crate, pick a provider, and call complete. Swap in streaming, tools, or routing whenever you're ready. The API stays the same.