llmrust

English | 中文版
Call multiple LLM APIs with one unified Rust interface.
A high-performance, type-safe Rust library for calling multiple LLM providers through a unified interface. Inspired by Python's LiteLLM, but built for Rust's performance and safety guarantees.
Why llmrust? — Differentiation & Advantages
Rust already has several good multi-provider LLM crates (e.g. genai, rig, llm/rllm). Some are broader than us — genai supports many more providers, rig is a full agent framework. llmrust deliberately stays focused and wins on three things they don't all do:
1. Built-in dual-protocol proxy (OpenAI and Anthropic)
With features = ["proxy"], llmrust runs as a translating API gateway that speaks both wire protocols at the same time:
POST /v1/chat/completions— OpenAI Chat Completions protocolPOST /v1/messages— Anthropic Messages protocol
Any client SDK — one that only speaks OpenAI or one that only speaks Anthropic (e.g. tools built for Claude) — can point at llmrust and reach any registered backend (OpenAI, Anthropic, Gemini, DeepSeek, Moonshot, OpenRouter, Ollama) through automatic format conversion. Bearer-token auth, CORS, health checks, and graceful shutdown are included. Most competing crates are client libraries only; the few that ship a server usually expose the OpenAI format alone.
2. Cross-provider logprobs, normalized
llmrust normalizes token log-probabilities — including each position's top-N alternatives — into a single ChatResponse.logprobs shape across OpenAI-compatible providers and Google Gemini (whose native logprobsResult is reshaped to match). That gives you one uniform surface for evaluation, confidence scoring, and re-ranking, instead of per-provider special-casing.
3. A lean, correct, type-safe core
default = [] — nothing is enabled unless you ask for it, the dependency tree stays small, and the scope is intentionally narrow (no embedding / vector-store / agent-framework bloat). You get native-protocol support for Anthropic and Gemini (not just OpenAI-compatible shims), full compile-time type safety, structured tracing that never logs secrets or prompt content, and built-in retry + router failover. When you want a clean, predictable multi-provider call layer rather than a heavyweight framework, that's the niche llmrust fills.
Honest scope: llmrust is young. If you need the widest provider catalog or a batteries-included agent/RAG framework today,
genaiorrigmay fit better. llmrust's bet is the three areas above.
Features
- Unified API — One interface for OpenAI, Anthropic, DeepSeek, Google Gemini, Ollama, and more
- Streaming support — First-class async streaming for all providers
- Dual-protocol proxy — Serve any backend over both the OpenAI and Anthropic APIs (
proxyfeature) - Normalized logprobs — Uniform token log-probabilities across OpenAI-compatible providers and Gemini
- Type-safe — Full compile-time guarantees with serde and thiserror
- High performance — Built on reqwest + tokio, minimal overhead
- Zero runtime dependencies — Single binary, no Python/Node required
Installation
Add llmrust to your Cargo.toml:
[]
= "0.1"
Or use cargo add:
Feature Flags
| Feature | Default | Description |
|---|---|---|
| (none) | ✅ | LLM client — all providers + streaming; tool calling on OpenAI-compatible, Anthropic, and Gemini providers (chat + streaming); JSON mode on OpenAI-compatible and Gemini providers |
proxy |
❌ | Built-in dual-protocol HTTP proxy — exposes any backend over both OpenAI (/v1/chat/completions) and Anthropic (/v1/messages) APIs (adds axum) |
Enable the proxy server:
[]
= { = "0.1", = ["proxy"] }
Or:
Quick Start
use LmrsClient;
async
Supported Providers
| Provider | Models | Streaming | Tool calling | Status |
|---|---|---|---|---|
| OpenAI | gpt-4o, gpt-4o-mini, o1-preview | ✅ | ✅ | Stable |
| DeepSeek | deepseek-chat, deepseek-coder | ✅ | ✅ | Stable |
| Moonshot / Kimi | moonshot-v1-8k, kimi-latest | ✅ | ✅ | Stable |
| OpenRouter | any model via OpenRouter | ✅ | ✅ | Stable |
| Anthropic | claude-3.5-sonnet, claude-3-opus | ✅ | ✅ | Stable |
| Google Gemini | gemini-2.0-flash, gemini-1.5-pro | ✅ | ✅ | Stable |
| Ollama | llama3.2, qwen2.5, any local model | ✅ | ➖ | Stable (chat) |
Feature support notes
- Tool calling / function calling is supported on the OpenAI-compatible providers (OpenAI, DeepSeek, Moonshot, OpenRouter) and natively on Anthropic and Gemini, on both the non-streaming
chatpath and the streamingstreampath (streamed tool calls are reconstructed and surfaced asStreamChunk.tool_callson the terminal chunk).- The OpenAI-compatible proxy accepts both modern
tools/tool_choiceand legacyfunctions/function_callrequest fields, normalizing them to llmrust's unified tool model.- JSON mode / structured outputs are wired through the OpenAI-compatible providers and mapped to Gemini's
responseMimeType/responseSchema.- Sampling and request metadata parameters beyond
temperature/max_tokens/top_p(e.g.stop,seed,presence_penalty,frequency_penalty,logprobs,n,response_format,parallel_tool_calls,service_tier,store,metadata,user) are sent to OpenAI-compatible providers and mapped where Gemini has native equivalents. Anthropic and Ollama currently honor a smaller provider-native subset.- Non-streaming
logprobsresponses are normalized intoChatResponse.logprobsfor OpenAI-compatible providers and Gemini.- Gemini image inputs: Gemini currently supports image inputs only when the image is provided as a
data:URL. Remotehttp(s)image URLs are skipped with a warning in v0.1.0; convert remote images to data URLs before sending them.
n / Multiple Completions
llmrust currently returns a single completion. Direct provider calls may forward n to OpenAI-compatible upstream APIs and will emit a warning when n > 1.
The OpenAI-compatible proxy rejects n values other than 1 (or absent) because upstream APIs may bill for multiple completions while llmrust only returns the first one.
Usage Examples
Basic Chat
use LmrsClient;
async
Streaming
use LmrsClient;
use StreamExt;
async
Tool Calling
Tool calling works on OpenAI-compatible providers as well as natively on Anthropic and Gemini, on both the non-streaming
chatpath and the streamingstreampath. Provide tool definitions on the request, then feed the returnedtool_callsresults back astoolmessages for the next turn.
use ;
use json;
let tools = vec!;
let request = from_messages
.with_tools
.with_tool_choice;
let response = llm.chat_with.await?;
if let Some = &response.tool_calls
Advanced Configuration
use ;
async
JSON Mode & Sampling Parameters
JSON mode and the extended sampling parameters below are applied on OpenAI-compatible providers (OpenAI, DeepSeek, Moonshot, OpenRouter) and mapped where Gemini has native equivalents.
use ChatRequest;
let request = new
.with_json_mode
.with_seed
.with_temperature;
HTTP Proxy Server
Run a local dual-protocol API gateway that speaks both the OpenAI and Anthropic wire formats (requires features = ["proxy"]):
# Optional: enable bearer-token auth
Call it with the OpenAI Chat Completions API:
The same server also exposes the Anthropic Messages API, so Anthropic-only clients (such as tools built for Claude) work unchanged — even when the backend is OpenAI:
Security note: Without
LLMRUST_PROXY_KEYset, the proxy has no authentication. Run it on localhost or behind a reverse proxy. WithLLMRUST_PROXY_KEYset, every request must include anAuthorization: Bearer <key>header; the token is compared in constant time.The proxy follows OpenAI chat-completions request conventions, including
stopas either a string or an array, and returns JSON error bodies for malformed requests. Stream errors are surfaced to clients as error events rather than being silently converted to successful completions. Streaming responses use OpenAI-style SSE chunks, including a single initialassistantrole delta. Whenstream_options.include_usageistrue, usage-only chunks use emptychoices.
Proxy model names
The proxy routes requests by the model string. Use the same provider-prefixed model names as the client API, for example:
openai/gpt-4oanthropic/claude-3-5-sonnet-latestgemini/gemini-1.5-proollama/llama3.2
The prefix selects the provider. The remaining model name is sent to the provider after llmrust resolves the route.
Logging
llmrust emits structured tracing events for provider registration, request lifecycle (including the convenience chat / stream APIs), proxy requests, retries, router failover, and upstream API errors. As a library, it does not install a global subscriber; your application decides how logs are collected.
[]
= { = "0.3", = ["env-filter"] }
fmt
.with_env_filter
.init;
llmrust tracing events do not include API keys, prompt content, response text, request bodies, tool arguments, image data, or full URLs. When helpful, logs use counts and lengths such as message_count, tool_count, data_len, or url_len. Operational metadata like provider, model, HTTP status, retry attempt, and router group are always recorded.
Provider Configuration
OpenAI
llm.set_openai.await;
// Custom base URL (Azure, local proxy, etc.)
llm.set_openai_compatible.await;
Anthropic
llm.set_anthropic.await;
DeepSeek
llm.set_deepseek.await;
Google Gemini
llm.set_google.await;
Ollama (Local Models)
llm.set_ollama.await; // Default: http://localhost:11434
llm.set_ollama.await;
Moonshot / Kimi
llm.set_moonshot.await;
OpenRouter
llm.set_openrouter.await;
Error Handling
use ;
match llm.chat.await
Performance
We have not yet published formal benchmarks. The library adds a thin async layer on top of reqwest, so overhead versus calling the HTTP API directly should be a few hundred microseconds per request at most.
Comparison
vs. Python LiteLLM
| Feature | Python LiteLLM | llmrust |
|---|---|---|
| Startup time | ~hundreds ms (interpreter) | compiled binary, near-instant |
| Memory | Python runtime dependent | single static binary |
| Concurrency | asyncio | tokio (native) |
| Deployment | Python + venv | Single binary |
| Type safety | Runtime | Compile-time |
| Providers | 100+ | 7 (growing) |
vs. other Rust crates
| llmrust | genai | rig | llm / rllm | |
|---|---|---|---|---|
| Focus | Lean unified client + proxy | Broad unified client | Agent framework | Client + extras (TTS/STT/vision) |
| Providers | 7 (growing) | 25+ | several | many |
| Built-in proxy server | ✅ OpenAI + Anthropic | ➖ | ➖ | OpenAI REST only |
| Normalized cross-provider logprobs | ✅ (incl. Gemini) | ➖ | ➖ | ➖ |
| Default dependencies | minimal (default = []) |
moderate | heavy (framework) | moderate+ |
| Extra scope | none | client only | agents / RAG | embeddings / vision / audio |
Provider counts and feature sets for other crates move fast — treat this as a positioning sketch, not a benchmark. Pick the tool that matches your needs.
Roadmap
- Core providers (OpenAI, Anthropic, DeepSeek)
- Streaming support
- Google Gemini, Ollama, Moonshot, OpenRouter
- HTTP proxy server (OpenAI + Anthropic protocols)
- Retry logic
- Tool-use / Function calling (OpenAI-compatible, Anthropic, Gemini; non-streaming)
- JSON mode & sampling parameters (OpenAI-compatible providers)
- Streaming tool calls (reconstruct tool calls from streamed chunks)
- Embeddings API
- Batch API
- Rate limiting
- More providers (Cohere, Mistral, Groq, etc.)
License
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contributing
Contributions welcome! Please open an issue or PR.
RUSTDOCFLAGS="-D warnings"