llm-dialect
= "0.1.3" # MSRV 1.88
Pure sans-I/O LLM dialect translation: Anthropic Messages, OpenAI Chat Completions, and OpenAI Responses wire formats ↔ a canonical request/response model, plus SSE framer state machines for streamed turns. No async runtime, no HTTP client/server types in the API surface, no wall clock, no randomness — id minting and timestamps are supplied by the caller.
The mapping is the translation core of an LLM gateway (a LiteLLM-proxy-class
service): parse whatever dialect your client speaks into one ItemRequest,
translate to the ChatRequest your engine consumes, render the engine's
canonical chunks back to the caller's dialect.
What it gives an embedder
Put an Anthropic /v1/messages (or OpenAI Responses) surface in front of any
chat/completions-class backend without rewriting the translation yourself:
use ;
// 1. parse an Anthropic Messages wire body into the canonical model
let items: ItemRequest = from_anthropic?;
// 2. flatten to the internal chat request your engine speaks
let req: ChatRequest = items_to_chat_request?;
// 3. per-chunk: canonical chunks back to Anthropic SSE frames
let mut st = with_stop_sequences;
for chunk in engine_chunks
// one guaranteed terminal pair (message_delta + message_stop)
for in finalize_stream
Enable the axum feature for the shared SSE pump and per-dialect
*_stream_response shells (anthropic_stream_response(&stream, …) -> Response),
if you're already an axum service and want drop-in handlers.
Crate shape
| module | contents |
|---|---|
items |
the canonical request model (ItemRequest, Item, ContentItem, …) |
canonical |
the flattened ChatRequest/ChatResponse/CanonChunk the engine speaks |
dialect/{anthropic,openai_chat,openai_responses} |
wire parsers (*/req.rs), response renderers (*/out.rs), SSE framers (*/stream.rs) |
dialect::deflate |
ItemRequest → ChatRequest |
dialect::sse (feature axum) |
the pump that drives a framer over a stream |
error |
the shared ProxyError and its HTTP error envelope |
See ARCHITECTURE.md for the module dataflow, the purity boundary and how it's enforced, and the cross-dialect invariants (usage arithmetic, thinking, tool-call linkage, streaming terminal frames).
Guarantees the crate holds
The dialect translation files are pure data transformation. A lint in the
integration tests enforces no tokio/reqwest/axum/sqlx/rand/
clock calls outside the sanctioned shell/factory regions, so the boundary
above is enforced rather than aspirational.
License
MIT.