Introduction

The lowest-overhead
LLM router.

One OpenAI-compatible endpoint in front of everything you serve. Written in Rust for teams putting real traffic through more than one model or node — 0.76 µs of work per request, and no I/O on the request path at all.

openai/gpt-5 · 38 ms
anthropic/claude-sonnet · 44 ms
local/qwen-* · 21 ms
Run it
bash
docker run ghcr.io/azrtydxb/fastllm-proxy:v0.2.0 --help
Per-request work
0.76 µs
No I/O on the request path
Throughput vs LiteLLM
~15×
Mock upstream, GPU removed
p99 TTFT
766 ms
Against 2921 ms at 32 streams
Screens in the binary
16
Seventeen under the operator

What it is

Nothing on the path does I/O

RBAC, per-model grants, rate limits and budgets are integer comparisons against a snapshot already flattened in memory. A test in the repo fails the build if anything I/O-shaped lands there.

Nothing on the path parses

An upstream's frames reach your client exactly as they arrived — never deserialised, never re-encoded, never buffered. Cost does not grow with how much your users read.

Routing knows what your engine knows

A shared prefix goes back to the node already holding its KV cache, unless that node is meaningfully hotter than the least-loaded one. Round-robin makes every request pay full prefill.

Highly available on purpose

A proxy that loses its control plane keeps serving from its last-known-good snapshot. Health is per replica, never merged, and SIGHUP swaps the routing table without touching in-flight generations.

The numbers, and their conditions

Measured against LiteLLM on the same cluster, same backends, interleaved A/B runs — with the GPU removed, so the gateway is the only thing being measured.

Requests per second higher is better
fastllm
635/s
LiteLLM
36/s
Median time to first token lower is better
fastllm
8–46 ms
LiteLLM
87–1313 ms
Inter-token jitter, real vLLM std. deviation
fastllm
−15 to −25%
LiteLLM
baseline

With real GPUs, aggregate throughput is a wash — both gateways saturate the same hardware. What survives contact with real silicon is steadiness: p99 TTFT of 766 ms against 2921 ms at 32 concurrent streams, and inter-token jitter 15–25% lower at every concurrency level.

Every number, its conditions, and what was not measured →

What you get

Cache-affinity routing
A shared prefix returns to the node holding its KV cache. least-loaded, round-robin and lowest-latency are selectable.
Virtual models
One client-facing name, ordered rules, weighted and ordered targets — so a rule is both a traffic split and a failover chain.
Rule-based routing
Match on principal, role, prompt size, requested generation, streaming, headers, budget consumption, in-flight count or time of day.
Semantic routing
A ~115 µs static-embedding tier decides most prompts; a transformer loads only if a rule asks for one.
RBAC with real keys
Principals, roles, per-model grants. Keys SHA-256 hashed, passwords Argon2id — deliberately different.
Usage accounting
Every attributable request, priced at the price in force when it ran, in integer micro-units.
MCP and A2A gateways
Every tool server and agent behind one address, namespaced, with grants that are deliberately not implied by model:invoke.
80 providers
Anything OpenAI-shaped is a row in a table. Anthropic and Gemini in their own wire format, translated both ways.

The full list, with its measured trade-offs and honest limits →

Routing you can inspect before you trust it

Dry-run answers which rule would decide and what the chain resolves to, without dispatching anything — because a routing table you cannot interrogate is a routing table you find out about in production.

The Virtual models screen, showing a rule's conditions and weighted targets alongside a dry-run panel

History, not just a live view

Requests stacked as served / upstream errors / refusals-by-kind — because a caller stopped by a budget and a backend that fell over need different people to do different things. A gap in the latency line is a bucket with nothing to measure, never zero.

The traffic drill-down: 1h to 30d ranges, pan controls, filters by model and principal, and stacked charts for requests, latency and tokens

Already running LiteLLM?

fastllm-proxy import --config litellm_config.yaml --database-url postgres://...

Models, backends, keys and each key's per-model grants come across. Idempotent — re-importing an edited file converges rather than duplicating, and grants removed from the file are revoked. Your existing keys keep working against the same models they already had.

Start here

Deploying to Kubernetes: the Helm chart, or the worked manifests for one real cluster. Everything else — troubleshooting, security, the CLI, the API, the changelog — is in the sidebar.

Apache-2.0 source v0.2.0