Hacker News
July 20, 20266 min read
Self-hosted LLM gateway + model router. One static Go binary that speaks both the OpenAI and Anthropic API dialects inbound , routes across any provider outbound (bring your own keys), with routing policies from static aliases to a learned smart tier β gated by an eval harness that ships in the box. Zero telemetry β not even opt-in pings. Apache-2.0.
any OpenAI SDK ββ ββ OpenAI / Anthropic / Gemini Claude Code βββββ€β relay (one binary, :4000) β ββ Groq / DeepSeek / Mistral / xAI / β¦ plain curl ββββββ route Β· failover Β· log Β· cache ββ your local Ollama 60-second quickstart # 1. install (or download a release binary; a tested Dockerfile is included to build yourself) brew install llmrelay/tap/relay # 2. keys you already have, zero config export GEMINI_API_KEY=... # and/or OPENAI_API_KEY, ANTHROPIC_API_KEY, GROQ_API_KEY, ... relay serve # 3. point anything at it curl http://localhost:4000/v1/chat/completions -H " Content-Type: application/json " \ -d ' {"model":"gemini/gemini-3.1-flash-lite","messages":[{"role":"user","content":"hello"}]} ' export ANTHROPIC_BASE_URL=http://localhost:4000 # Claude Code now runs through relay relay init scaffolds a full relay.yaml β including where to get every provider's key. See examples/ for Claude Code, Cursor, SDK snippets, and the sensitive-local / bulk-cheap alias recipe.
relay LiteLLM OpenRouter RouteLLM What it is self-hosted gateway + router Python proxy/SDK gateway hosted aggregator API research routing framework Deploy one static binary / distroless image Python service + deps nothing (their cloud) Python library Inbound dialects OpenAI and Anthropic, full cross-translation OpenAI (+ passthroughs) OpenAI-compatible n/a Learned routing tiered, local, trained on your logs, eval-gated manual routing strategies their routing the prior artΒΉ Eval harness in the box yes ( relay eval , committed sets, live-judge) no no offline research harness Your prompts transit a third party never (self-hosted; remote-embedder routing requires explicit opt-in) self-hosted yes β that's the product n/a Telemetry none, ever none per their docs hosted service n/a Ecosystem breadth 4 native + 12 presets, adapter guide broadest provider matrix, teams/budgets/virtual keys very broad n/a License / runtime Apache-2.0, Go mixed (OSS + enterprise), Python proprietary service Apache-2.0, Python ΒΉ relay's tier-2 KNN is the same family as RouteLLM (Ong et al.), run locally over your own traffic; graph routers ( GraphRouter , ICLR 2025) are roadmap. LiteLLM and OpenRouter are good products with different trade-offs β pick the row that matters to you.
Inbound Status OpenAI Chat Completions ( /v1/chat/completions ) full, incl. streaming, tools, vision OpenAI Responses API ( /v1/responses ) not yet β tracked v1.1 fast-follow Anthropic Messages ( /v1/messages , count_tokens ) full, incl. streaming, tools Embeddings ( /v1/embeddings ) OpenAI dialect; providers without an embeddings API answer an honest 404 /v1/models , /metrics (Prometheus), /dashboard , /v1/feedback yes Performance Gateway overhead measured against a loopback mock upstream ( go test -run TestOverheadBudget ./internal/server/ β the budget is a hard CI gate, methodology in DESIGN.md Β§11 ; Windows 11 / Go 1.25, 2026-07-18):
Provider latency dominates end-to-end time; relay's job is to stay invisible.
Static routes and aliases with four policies (fallback / cheapest / fastest / weighted), plus reliability underneath every chain: retries with jittered backoff, API-key pools with rate-limit cooldowns, circuit breakers, and pre-first-token streaming failover.
relay ships a difficulty-based smart router (easy traffic β your cheap chain, hard β your frontier chain) with an eval harness β and the harness's own verdict is that you should beat our baseline on your traffic before trusting it , so smart routing is off by default:
relay eval # dry-run: the committed sets, your candidates relay eval --live-judge --dry-run # real completions, judge-scored; prints spend first What our harness measured on the held-out set v2, live-judged β real completions from each routed model, quality scored by claude-opus-4-8 (2026-07-20; 49 prompts, valid N = 49 for every policy; completions capped at 700 output tokens; mid band is claude-sonnet-5 because Gemini's free tier caps gemini-3.5-flash at 20 requests/day; 6 of 147 judge replies buried the verdict number in commentary and were re-parsed deterministically from the preserved raw replies rather than scored 0 β the corrections log inside the verdict JSON records each one):
Values are shown rounded. Gate decisions use the unrounded values from the committed verdict asset (a delta of exactly β0.0200 would pass β the rule is inclusive; tier 1's unrounded delta is β0.020408β¦, exactly β1/49, strictly below it). A CI test asserts this table matches the asset at full precision.
On this 49-prompt, 700-output-token, single-judge evaluation, the static-cheap baseline received a higher judged score than the configured frontier baseline. That result is specific to the tested models, prompts, token cap, and judge β it is not a general model-quality claim. What it does establish is the bar relay holds itself to: routing cleverness must earn its keep against strong dumb baselines, measured on your traffic . Tier 1 failed that bar on held-out data; tier 2 passed within tolerance but stays opt-in for v0.1.0; no smart tier is on by default.
(History: on the v1 set tier 1 had "passed" synthetic at β0.017 β in-sample flattery, since its weights were calibrated on v1. On v2 synthetic labels it failed at β0.071. v1 is the calibration set; v2 live-judged is the standing verdict. Full tables: assets/eval/ .)
Enabling it is one config block β with an explicit tier, because nothing routes your traffic by silent default:
providers : gemini : api_key : ["${GEMINI_API_KEY}"] anthropic : api_key : ["${ANTHROPIC_API_KEY}"] ollama : type : ollama routing : default : smart smart : easy : gemini/gemini-3.1-flash-lite hard : anthropic/claude-sonnet-5 embeddings : ollama/nomic-embed-text # selects the knn tier (documented path) # tier: lexical # experimental: failed the held-out gate Tier 2 (KNN) gets better on YOUR traffic via relay train β implicit signals, optional replay+judge (always estimates spend and asks first), and POST /v1/feedback scores grow its reference set; then measure your own crossover with relay eval --refs ~/.relay/smart_refs.json . Every smart decision logs its evidence ( knn: 5 neighbors [seed-math-03 d=0.75 sim=0.95; β¦] ) β "the model felt like it" is not an accepted routing reason. A remote embedder requires allow_remote_embeddings: true ; routing never silently ships prompts anywhere.
relay compare --models gemini/gemini-3.1-flash-lite,anthropic/claude-haiku-4-5,groq/llama-3.3-70b \ --html compare.html " Explain CRDTs to a backend engineer in five sentences. " One table (and a shareable HTML report): output, cost, latency, and TTFT side by side, through the same adapters that serve your traffic. docs/models-landscape.md is the dated "which model for what" companion.
Every request logs to local SQLite with privacy tiers ( off by default β metadata only; embeddings stores query vectors, never text; full is an explicit choice). /dashboard shows spend by day, latency percentiles, and recent routing decisions with human-readable reasons. /metrics is Prometheus. Models missing from the pricing registry surface as unpriced β never a silent $0.
Loopback by default; a non-loopback bind without inbound API keys refuses to start . No telemetry. See SECURITY.md .
CONTRIBUTING.md β the adapter-authoring guide is there; an OpenAI-compatible provider is a one-entry preset. Design changes get argued in DESIGN.md Β§0 first; it's how the doc stays true.
Read what's here, then head to the original whenever you're ready - never required.
Continue Reading on Hacker NewsGoogle Developers July 21, 2026