xai-grok 0.1.0

High-level async client for the xAI Grok API (chat, streaming, tool calling, vision, structured outputs), built on the stream-rs streaming toolkit.
Documentation

xai-grok

CI crates.io docs.rs License: MIT

High-level async Rust client for the xAI Grok API, built on the stream-rs streaming toolkit.

xAI hosts an OpenAI-compatible REST API at https://api.x.ai/v1. This crate speaks that wire format and uses stream-rs for the part most LLM clients get subtly wrong: byte-accurate Server-Sent Events parsing of streamed completions. Chunk boundaries, multi-line data: fields, partial UTF-8, and interleaved tool-call deltas are handled correctly no matter how the bytes are framed on the socket — a property covered by offline tests that split events mid-stream.

Capabilities

Feature API
Chat (buffered) [GrokClient::chat]
Realtime streaming [GrokClient::chat_stream] → Stream<Item = Result<ChatChunk>>
Streaming + assembly [GrokClient::chat_stream_collect] (live token callback + folded result)
Tool calling [Tool] / [ToolCall] / [ToolChoice]
Vision multimodal [Content::Parts] with image URLs / data: URIs
Structured outputs [ResponseFormat::json_schema]

Embeddings: xAI does not currently expose a public /v1/embeddings REST endpoint (embeddings exist only server-side inside the Collections API), so this crate intentionally does not ship an embeddings method rather than invent one.

Install

[dependencies]
xai-grok = "0.1"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
futures-util = "0.3"

TLS backend is selectable: rustls (default, pure-Rust, no OpenSSL) or native-tls.

Quick start

use xai_grok::{GrokClient, ChatRequest, Message};

# async fn run() -> xai_grok::Result<()> {
let client = GrokClient::from_env()?;          // reads XAI_API_KEY
let resp = client.chat(ChatRequest::new("grok-4.3", vec![
    Message::system("You are concise."),
    Message::user("Say hello."),
])).await?;
println!("{}", resp.content().unwrap_or_default());
# Ok(())
# }

Streaming

use futures_util::StreamExt;
use xai_grok::{GrokClient, ChatRequest, Message};

# async fn run() -> xai_grok::Result<()> {
let client = GrokClient::from_env()?;
let mut stream = client.chat_stream(
    ChatRequest::new("grok-4.3", vec![Message::user("Tell a short story.")]),
).await?;

while let Some(chunk) = stream.next().await {
    for choice in &chunk?.choices {
        if let Some(text) = &choice.delta.content {
            print!("{text}");
        }
    }
}
# Ok(())
# }

Examples

All four are runnable with a live key (export XAI_API_KEY=xai-...):

cargo run -p xai-grok --example grok_tool_calling
cargo run -p xai-grok --example grok_structured_outputs
cargo run -p xai-grok --example grok_realtime_streaming
cargo run -p xai-grok --example grok_vision

Why build on stream-rs?

A streaming LLM response is an SSE body whose TCP reads have no relationship to event or token boundaries. The offline test survives_chunk_boundary_mid_event feeds the same logical stream split inside a JSON string and across the \n\n terminator, and asserts the assembled output is identical — exactly the guarantee stream-rs is designed and fuzzed for.

License

MIT