chat-core 0.4.0

Core library for chat-rs
Documentation

chat-core

Core traits, types, and engine for chat-rs — the foundation every provider crate builds on. Exposes the Chat engine, ChatBuilder, the CompletionProvider / StreamProvider / EmbeddingsProvider traits, the Transport trait (with built-in HTTP/SSE and WebSocket impls), and the core message/tool/response types.

You usually don't depend on this crate directly — instead, depend on a provider crate (e.g. chat-openai, chat-gemini, chat-completions) which brings chat-core in transitively. Bring chat-core in explicitly when you want the engine types in your code without pulling the umbrella chat-rs crate.

Install

[dependencies]
chat-core = "0.4.0"

What's in here

  • builder::ChatBuilder — the type-state builder that wires a provider into a Chat engine
  • chat::Chat — the engine; orchestrates the call loop, tool calls, retries, HITL, structured output
  • traitsCompletionProvider, StreamProvider, EmbeddingsProvider, ChatProvider
  • transportTransport trait + three built-in impls (feature-gated): ReqwestTransport (HTTP/SSE, default), AsyncWsTransport (tokio-tungstenite), WsTransport (tungstenite)
  • typesMessages, Content, Parts, Tool, ChatOptions, ChatResponse, StreamEvent, Metadata, etc.
  • errorChatError, ChatFailure

What's new in 0.4

  • Bidirectional streaming, redesigned. Chat<CP, InputStreamed>::stream(&mut messages) now returns a ChatStream instead of taking a caller-supplied input stream. It is the output stream you iterate with .next(), and it carries an input side you push to with .send() — the inverse of .next(), one verb for every input. split() peels it into independent (InputStream, OutputStream) halves (the InputStream is Clone + Send + 'static, so it drops into a task and clones into multiple producers); cancel() tears the exchange down. Builder transition: ChatBuilder::with_input_stream() (no longer generic).

    Pushed input rides as PartEnum (audio = File, text = Text, tool result = Tool), mapped caller-side before send. It coalesces into the trailing user turn via Messages::push and restarts the provider stream — the same interrupt-and-restart pattern HITL uses, now push-driven. Completed tool work survives interrupts (tools run between steps, never mid-stream); only the in-flight partial generation is discarded.

What's new in 0.3

  • StreamEvent::Structured(Value) — providers can yield complete structured objects mid-stream (each event is a whole serde_json::Value, not a fragment). The engine accumulates them into ChatResponse.content.parts as PartEnum::Structured so non-streaming consumers see them too. Drop-in for robotics consumers that produce a stream of typed action steps.

Both are additive over Chat<CP, Unstructured>::stream, which is unchanged.

Feature Flags

Feature What it enables
reqwest-transport HTTP/SSE transport via reqwest (default for most providers)
tokio-tungstenite Async WebSocket transport
tungstenite Sync WebSocket transport bridged via spawn_blocking
stream StreamProvider trait + streaming machinery
testing Test helpers for provider crates

Writing a Custom Provider

Implement CompletionProvider (and optionally StreamProvider, EmbeddingsProvider) on your client struct. Use the Transport abstraction so users can swap reqwest for their own HTTP/WS client. See providers/AGENTS.md for the conventions every existing provider follows.