chat-rs
Capabilities

Transports

Swap the HTTP layer for WebSocket, your own protocol, or anything else, without touching provider code.

A transport is how a provider actually moves bytes. By default chat-rs speaks HTTP over reqwest, but the transport is pluggable. Every provider builder is generic over T: Transport, so you can swap in WebSocket, wire up something like gRPC, or write your own, and none of your provider or call-site code changes.

The default

Providers default to ReqwestTransport (HTTP), so most of the time you never think about transports at all.

Built-in transports

TransportFeatureUse
ReqwestTransportreqwest-transportHTTP, the default
WsTransporttungsteniteblocking WebSocket
AsyncWsTransporttokio-tungsteniteasync WebSocket

Swap one in with .with_transport(...) on the provider builder. For example, OpenAI over an async WebSocket (enable the tokio-tungstenite feature):

websocket.rs
use chat_rs::{ChatBuilder, openai::OpenAIBuilder, transport::AsyncWsTransport};

let ws = AsyncWsTransport::new().with_message_type("response.create");

let client = OpenAIBuilder::new()
    .with_model("gpt-4o-mini")
    .with_transport(ws)
    .build();

let chat = ChatBuilder::new().with_model(client).build();

with_transport changes the builder's transport type parameter, so everything downstream stays the same. The chat, the loop, tools, and streaming all behave identically; only the wire underneath is different.

Write your own

A transport implements two methods: send for a single request and response, and stream for an event stream.

use std::future::Future;
use chat_rs::transport::{EventStream, Request, Response, Transport, TransportError};

pub trait Transport: Send + Sync {
    fn send(
        &self,
        req: Request,
    ) -> impl Future<Output = Result<Response, TransportError>> + Send;

    fn stream(
        &self,
        req: Request,
    ) -> impl Future<Output = Result<EventStream, TransportError>> + Send;
}

A Request carries headers and a body (Vec<u8>), and a Response carries headers and a body. send delivers one request and returns the full response; stream returns a stream of events (an HTTP transport parses SSE, a WebSocket transport yields frames). Implement those two methods on your type and hand it to .with_transport(...).

This is the path for a custom protocol, a gRPC backend over Tonic, a mock transport for tests, or a proxy that adds signing or logging. The provider does not know or care which one it is talking to.

On this page