LLM Routing Layer — LiteLLM, OpenRouter, Portkey
Provider lock-in is expensive, and different tool-calling workloads suit different models. A routing gateway gives you one API surface plus retries, failover, cost tracking, and guardrails. Three archetypes dominate 2026: LiteLLM (open-source, self-hosted), OpenRouter (managed SaaS), and Portkey (production-grade, open-sourced in March 2026). This lesson names the decision criteria and walks through a stdlib routing gateway.
Learning Objectives
- Distinguish self-hosted, managed, and production-grade routing options.
- Implement a fallback chain that retries on provider failures in a defined priority order.
- Track per-request cost and token usage across providers.
- Decide between LiteLLM, OpenRouter, and Portkey for a given production constraint.
The Problem
Five scenarios where provider routing matters:
-
Cost. Claude Sonnet costs 3x what Haiku costs. Haiku is enough for a triage task, and Sonnet is worth it for synthesis. Route per request.
-
Failover. OpenAI has a bad hour and requests fail. You want automatic fallback to Anthropic without a redeploy.
-
Latency. A live chat UI needs a fast time-to-first-token. A batch summarizer does not. Route by latency SLA.
-
Compliance. EU users must stay in EU regions, so you route by region.
-
Experimentation. You A/B two models on the same workload and route by test bucket.
Hand-coding this per integration gets repetitive fast. A routing gateway gives you one OpenAI-compatible API and handles the rest.
The Concept
OpenAI-compatible proxy shape
Most clients already speak the OpenAI shape. The routing gateway exposes /v1/chat/completions, accepts the OpenAI schema, and internally proxies to Anthropic / Gemini / Cohere / Ollama / anything. The client does not need to know which provider answered.
Model aliases
Instead of claude-3-5-sonnet-20251022, your code says our_smart_model. The gateway maps aliases to real models. When Anthropic ships Claude 4, you change the alias server-side and your code stays as it was.
Fallback chains
primary: openai/gpt-4o
on 5xx: anthropic/claude-3-5-sonnet
on 5xx: google/gemini-1.5-pro
on 5xx: refuse
Gateways define this in config. Retries count against a budget so a fallback cascade does not blow up your cost.
Semantic caching
Identical or near-identical prompts hit a cache instead of the provider. Savings on repeated agent loops can be 30 to 60 percent. Keys are embedding-based, so near-identical prompts share a cache slot.
Guardrails
Gateway-level:
- PII redaction. Regex or ML-based pass before sending prompts.
- Policy violations. Reject prompts with prohibited content.
- Output filters. Scrub completions for leaks.
Portkey and Kong ship opinionated guardrails. LiteLLM leaves them optional.
Per-key rate limits
Give each team its own API key. Per-key budgets then stop one team from eating the shared quota, and most gateways support this.
Self-hosted vs managed trade-offs
| Factor | LiteLLM (self-hosted) | OpenRouter (managed) | Portkey (production) |
|---|---|---|---|
| Code | Open source, Python | Managed SaaS | Open source (Mar 2026) + managed |
| Setup | Deploy a proxy | Sign up | Either |
| Providers | 100+ | 300+ | 100+ |
| Billing | Your own keys | OpenRouter credits | Your own keys |
| Observability | OpenTelemetry | Dashboard | Full OTel + PII redaction |
| Best for | Teams that want full control | Rapid prototyping | Production with compliance |
LiteLLM wins when you have an SRE team and want data sovereignty. OpenRouter wins when you want a single subscription and no infrastructure. Portkey wins when you need guardrails and compliance out of the box.
Cost tracking
A request carries provider, model, input_tokens, and output_tokens. Multiply those by per-model, per-token prices from a pricing sheet the gateway maintains, then aggregate per user, per team, or per project.
MCP plus routing
A gateway can route both LLM calls and MCP sampling requests. When a sampling request's modelPreferences prefer a specific model, the gateway translates that to the right backend. This is where the MCP gateway from Phase 13 · 17 and a routing gateway sometimes merge into one service.
Routing strategies
- Static priority. First in list; fall back on error.
- Load balancing. Round-robin or weighted.
- Cost-aware. Pick the cheapest model meeting latency / quality.
- Latency-aware. Pick the fastest model in the last N minutes.
- Task-aware. Prompt classifier routes coding to one model, summarization to another.
Use It
code/main.py is a routing gateway in about 150 lines. It accepts OpenAI-shaped requests, dispatches to per-provider stubs, runs a priority fallback chain, tracks per-request cost, and redacts PII from the input before dispatch. Run it and you get four scenarios: a normal request, a primary-provider outage that triggers fallback, PII caught by redaction, and every provider in a chain down at once.
What to look at:
ROUTESmaps an alias to a priority-ordered list of concrete providers. An alias that is not in the dict is treated as a concrete model name.- The fallback loop catches
RuntimeErrorand moves to the next provider. The stub raises it to simulate a 5xx, and the loop does not inspect the status, so anyRuntimeErrortriggers a retry. - The cost tracker multiplies token usage by per-model rates. The rates in
PRICESare fake demo numbers, and token counts are estimated from string length. - The PII redactor scrubs SSN-shaped and 16-digit card-shaped patterns from the messages before forwarding.
Ship It
This lesson produces outputs/skill-routing-config-designer.md. Give it a workload profile (latency, cost, compliance) and it picks LiteLLM, OpenRouter, or Portkey and produces a routing config.
I'd run main.py before reading the concepts again. Scenario 2 marks openai/gpt-4o as down and the attempts list shows the chain walking to anthropic/claude-sonnet, with the cost computed at Sonnet's rate instead of GPT-4o's. Seeing that once makes fallback chains and cost attribution concrete.
The part I'd read twice is the except RuntimeError branch in route(). It retries on any RuntimeError, and the demo only ever raises one for a simulated outage, so it behaves like "retry on 5xx" here. A real gateway has to separate a provider outage from a bad request, because failing over on a 400 just sends the same bad prompt to three providers. The retry budget from the Concept section is also missing from this code. The chain is short enough that it doesn't matter in the demo, but it would matter with a longer chain and real prices.
My bet is that you'll end up picking between LiteLLM, OpenRouter, and Portkey on operational grounds (who runs the proxy, whose keys pay, how much compliance you need) more than on routing features. The toy gateway shows the routing part is small. The redactor here is two regexes, which is a demo and nothing you'd ship as your only PII control.
Exercises
-
Run
code/main.py. Trigger the outage scenario and confirm the fallback lands on the second provider and the cost is attributed to it. -
Add semantic caching: SHA256 of the prompt is a lookup key; cache hits return instantly. Measure cost savings on a repeated call.
-
Add a prompt classifier that routes "code ..." prompts to an alias favoring intelligence and "summarize ..." prompts to an alias favoring speed.
-
Design per-team budgets: each team has a monthly spend cap; gateway refuses requests once cap is hit. Pick an enforcement granularity (per-request or windowed).
-
Read LiteLLM, OpenRouter, and Portkey docs side by side. Name the one feature each ships that the other two do not.
Key Terms
| Term | What people say | What it actually means |
|---|---|---|
| Routing gateway | "LLM proxy" | One-API-surface layer in front of many providers |
| OpenAI-compatible | "Speaks the OpenAI schema" | Accepts /v1/chat/completions shape, translates to any backend |
| Model alias | "our_smart_model" | Name in your code that the gateway maps to a concrete model |
| Fallback chain | "Retry list" | Ordered list of providers attempted on failure |
| Semantic caching | "Prompt-embedding cache" | Key is embedding of the prompt; near-duplicates share a cache hit |
| Guardrails | "Input/output filters" | Redact PII, reject policy violations |
| Per-key rate limit | "Team budget" | Quota scoped to an API key |
| Cost tracking | "Per-request spend" | Aggregate token usage x price per model |
| LiteLLM | "The open proxy" | Self-hostable OSS routing gateway |
| OpenRouter | "The managed SaaS" | Hosted gateway with credit-based billing |
| Portkey | "The production option" | Open-source + managed with guardrails built in |
Further Reading
- LiteLLM docs: self-hosted routing gateway
- OpenRouter quickstart: managed routing SaaS
- Portkey docs: production routing with guardrails
- TrueFoundry, LiteLLM vs OpenRouter: decision guide
- Relayplane, LLM gateway comparison 2026: vendor survey
Code
- main.py (python, 147 lines)