One SDK, Multiple LLMs: How to Talk to GPT, Claude, and Gemini the Same Way
The Pain Everyone Recognizes
You build a feature with OpenAI. Then someone asks to support Claude. Then Gemini. Suddenly you have three API clients, three different request formats, three sets of error codes, and a routing layer that looks like a switch-case from 2008.
What a Unified SDK Actually Does
The idea is straightforward: one interface, multiple providers. You call chat(model="gpt-4o", messages=[...]) or chat(model="claude-sonnet-4", messages=[...]) and the SDK handles translation under the hood. The request and response shapes stay consistent; the provider-specific details get abstracted.
What to Normalize
Not everything maps cleanly. The basic chat completion — system prompt, user message, assistant response — works across all providers. Tool calling, streaming, and vision inputs mostly normalize with a bit of translation. But features that are unique to one provider (like OpenAI's structured outputs or Anthropic's extended thinking) need either a fallback or a provider-specific escape hatch.
Model Routing, Not Model Lock-in
The real benefit isn't convenience — it's routing. You can send simple tasks to a cheaper model, complex reasoning to a stronger one, and fall back to another provider when one has an outage. Without a unified layer, that logic gets scattered across your codebase.
Implementation Notes
Keep the abstraction thin. A wrapper that exposes 90% of the common interface is useful; one that tries to hide every provider difference becomes a maintenance nightmare. Let developers drop down to raw provider calls when they need to.
Test against all providers on every change. The one that looks fine in the OpenAI docs might break when Claude returns a slightly different response shape.

