Lesson 1.2
One API, many providers
Every provider speaks a slightly different dialect. The gateway gives you one shape and translates.
Same idea, three different dialects.
Every model provider wants the same basic thing from you: a model name, some messages, a cap on how long the answer can run. But each one asks for it in its own words. OpenAI, Anthropic, and Google all accept "a chat request," yet the address you post to and the field names you use are different in each.
That difference is small until it costs you. If your app speaks OpenAI's exact shape and you later want to send some traffic to Anthropic (cheaper, or up when OpenAI is down), you cannot just point at a new URL. The body is wrong. You would rewrite every call site to match the new dialect, then rewrite them again for the next provider.
Speaking each provider's raw shape directly couples your app to that provider. Switching providers turns into rewriting your app.
One shape in, the right dialect out.
The gateway fixes this by exposing one schema to your app, and translating it to whatever the chosen provider expects. Your app only ever learns one shape. Commonly that shape is the OpenAI chat format, since most tools already speak it, but the point is that there is a single agreed shape, whatever it is.
The piece that does the rewriting is the adapter (a translation layer). One adapter per provider. It knows that "the system prompt" lives in the messages list for one provider and in a top-level field for another, and it moves the values into the right slots before the call goes out.
The gatekeeper monk speaks one language at the gate. Behind the gate, he knows how to address each room the way that room expects. The traveler never has to learn three sets of manners.
Pick a provider, watch the same request get remapped
What your app always sends (canonical)
system: "You are concise."
messages: [ user: "Hello" ]
max_tokens: 256
Sent to OpenAI
One API in, many dialects out. The adapter is what lets you switch providers without touching your app.
The same request, mapped by hand.
Start from one logical request your app sends: { model, messages, system, max_tokens }. Here is where each piece lands in each provider's request. Read it as "this field becomes that."
| Canonical field | OpenAI | Anthropic | |
|---|---|---|---|
| endpoint | /v1/chat/completions | /v1/messages | :generateContent |
| model | model: in body | model: in body | in the URL path |
| system prompt | role:"system" in messages | system: top-level field | systemInstruction |
| max tokens | max_tokens (optional) | max_tokens (required) | maxOutputTokens |
Notice the two things that bite. The system prompt lives in a different place in all three. And Anthropic requires a max-tokens value where OpenAI treats it as optional, so the adapter has to supply a sensible default rather than leave it out. Translation is not just renaming keys; it is also filling gaps each dialect insists on.
Why this shape of design pays off.
Once the adapter exists, the provider becomes a setting, not a rewrite. Send 90 percent of traffic to one provider and 10 percent to another to compare them. Fail over to a second provider when the first is down (a later unit). Drop a new provider in by writing one more adapter, with no change to any app that calls the gateway.
The cost is real but bounded: you maintain one adapter per provider, and you keep them honest as providers change their APIs. That is far less work than the alternative, which is every app carrying the knowledge of every provider's dialect.
Key takeaways
Providers accept the same idea in different dialects: different endpoints and different field names for model, system prompt, and max tokens.
The gateway exposes one schema and uses an adapter per provider to translate that one shape into each provider's request.
With adapters in place the provider is a setting you can switch, not a rewrite, which is what makes routing and fallback possible later.