Senior · Core path · 7 chapters
Multi-provider abstraction
Gateways, routers, or a hand-rolled adapter
A provider seam is valuable only when it preserves deliberate escape hatches; otherwise it turns vendor change into silent feature loss.
Read these first
What you will know
Put a seam between your code and a specific provider
A thin interface at the call site is enough; pushing every provider feature behind it creates an inflexible pseudo-standard.
Run the same request through two providers and document which hosted state, reasoning blocks, and provider-specific tools cannot be represented by the shared contract
A normalized response can look valid while silently discarding continuation handles, control signals, or tool semantics.
Swap providers for one feature without touching business logic
Provider selection and translation should stay at the integration boundary, leaving domain behavior independent of vendor names.
Measure the feature and quality loss caused by a lowest-common-denominator interface before adopting it
Flattening streaming events, tool calls, or structured outputs may lower capability without causing compile-time or obvious runtime failures.
Decide when a gateway is better than an in-process adapter
A gateway centralises keys, budgets, and logging for many services; an adapter is right for one.
Chapters
- 1
The provider seam
The boundary where code written in terms of your product ends and code written in terms of one vendor's API begins is the one place the question of which vendor gets answered — and how it is placed decides whether changing the answer is a config edit or a fortnight. This chapter contrasts a single Provider object selected by one environment variable with per-capability ports resolved at the composition root, gives a test for what belongs in a port, and separates independence of vendor names from independence of vendor behaviour.
- 2
The size of the shared contract
An interface that grew to fourteen methods because each vendor feature was given a shared equivalent charges rent on every capability it took responsibility for: adding one vendor's server-side search means editing the interface, all three implementations, the fake and both call sites. This chapter measures that cost, contrasts it with the ratified standards it resembles, and gives two questions that decide what belongs in a shared shape and what belongs inside one adapter.
- 3
Losses in a normalized response
Flattening a provider's reply to {text, tool_calls, usage} discards whatever has no field, and a field that does not exist cannot fail to be filled. Reasoning blocks vanish when the adapter rebuilds the next turn, a server-held conversation id is dropped so every turn resends the whole transcript, and tool-call semantics change meaning while keeping their names. This chapter traces each loss to the symptom it produces and the dashboard it produces it on, and explains why a shared conformance suite is green throughout.
- 4
Adapters and gateways
The translation code inside a gateway is the same code you would write in an adapter; what moving it out of your process changes is ownership — its own uptime, schema, release cadence and pager, and the ability to be down while every provider it fronts is healthy. This chapter prices what centralising keys, budgets, quota and audit logs actually buys against the number of services sharing them, works the availability arithmetic, and names the configuration that keeps a new failure domain from becoming an outage.
- 5
Hosted routers and ownership
A hosted router carries every cost of an out-of-process hop — its own uptime, schema and release cadence — on a service nobody at your company can page, patch or roll back, and it puts a second party between you and the vendor's error semantics, rate limits and quota accounting. This chapter works through what an on-call engineer can and cannot do when a router misbehaves, why the direct fallback decays unless it carries real traffic, and the workloads a router is genuinely the right answer for.
- 6
Measuring the capability loss
\"Supported\" in a compatibility matrix is a claim about a request being accepted, not about extended thinking surviving the round trip or a cache breakpoint landing where you placed it. This chapter separates four things the word can mean, then measures the difference: replay a few hundred production requests through both paths, aggregate the field-by-field diff, convert the lines that matter into currency, and run the evaluation set on each — a day of work that produces the number a compatibility matrix cannot.
- 7
Documented escape hatches
Adding a method to the shared interface and throwing NotImplemented in two implementations converts a compile-time fact into a production crash on the day a failover routes traffic to the wrong vendor. This chapter handles the vendor-specific capability by handing back the raw provider client as its own injected type, documented in an architecture decision record, shows how to price the swap that hatch commits you to, and gives the CI check that distinguishes a real hatch from a port that is missing a parameter.
External resources
- LLM Gateway: What It Is and How to Choose Oneopenrouter.ai
Defines the gateway's job: normalization, routing, failover, observability.
- OpenRouter vs LiteLLM: when to use one over the othermerge.dev
Self-hosted control versus managed breadth, stated as a decision rule.
- LLM Routers Compared: LiteLLM vs Portkey vs OpenRouterdevelopersdigest.tech
Feature-by-feature view before you commit to a router dependency.
- LLM Gateways Compared: LiteLLM vs OpenRouter vs Portkey vs RouteLLMwavect.io
Adds data-residency and governance angles that rule options out.
- One API to Rule Them All? LiteLLM in ProductionPyData
What a multi-provider gateway buys you and where the abstraction leaks