Last updated 2026-05-17
Comparison vs alternatives
llmrouter is one of several reasonable answers to
“how do I call an LLM from Go.” This page is an honest
comparison against the alternatives, with explicit guidance on when
to pick each one. We are biased — but the goal here is to make
sure you do not adopt this library for a problem it was not built
to solve.
The short version: if you only ever call one vendor and want every
feature that vendor exposes, use that vendor’s official Go
SDK. If you want a Python LiteLLM equivalent with typed wrappers
around embeddings and tool use across ten providers,
mozilla-ai/any-llm-go is closer to what you want.
llmrouter exists for the specific case where you want
a single OpenAI-shaped surface plus byte-level passthrough across
OpenAI, Anthropic, and any OpenAI-compatible endpoint.
At a glance
The table below summarises the dimensions that usually decide the answer. “Byte passthrough” means: can you forward an upstream chunk to a downstream consumer without re-marshaling it. “Azure / Bedrock / Vertex” refers to the three managed cloud LLM endpoints that need different auth, body shape, and URL structure from the vendor’s own API.
llmrouter | any-llm-go | Per-vendor SDKs | Direct HTTP | |
|---|---|---|---|---|
| Providers shipped today | OpenAI, Anthropic, any OpenAI-compat | ~10 (OpenAI, Anthropic, Gemini, Mistral, etc.) | One per package | Whatever you write |
| Byte passthrough | Yes (Chunk.Raw, ChatRequest.Raw) | No (parsed to typed shapes) | N/A | Yes (you have the bytes) |
| Azure / Bedrock / Vertex | Planned v0.2 | Partial | Yes, per vendor | Hand-rolled |
Streaming with ctx cancel | Yes; propagates to HTTP | Yes | Varies; usually yes | You write it |
| License | Apache-2.0 | Apache-2.0 | Apache-2.0 / MIT | N/A |
| Pre-1.0 API churn | Yes (v0.1.x) | Yes (pre-1.0) | Mostly stable | N/A |
| Dependency footprint | stdlib + google/uuid | Several | One per vendor SDK | stdlib only |
| Embeddings / audio / vision API | Not yet (roadmap v0.3 / v0.4) | Partial | Yes (full vendor surface) | Hand-rolled |
vs. per-vendor SDKs (openai-go, anthropic-sdk-go, google.golang.org/genai)
Each major vendor publishes an official Go SDK. They are good libraries — stable, idiomatic, fully covering that vendor’s feature surface (tool calls, vision, audio, embeddings, assistants, batch APIs, etc.) the day each feature ships. If you only ever call one vendor, you should almost certainly use that vendor’s SDK.
Pros of per-vendor SDKs.
- Stable, post-1.0, officially supported.
- Complete coverage of that vendor’s API surface, including features that have not landed in any cross-vendor abstraction yet.
- Vendor-specific niceties — e.g.
openai-go’s strongly typed tool-call helpers,anthropic-sdk-go’s helpers for the prompt-caching beta header. - Vendor support channels actually know the SDK.
Cons.
- One SDK per provider. Your application code branches on which provider you’re calling — different types, different request shape, different stream events, different error wrappers.
- Hard to abstract away if you want to A/B test models across vendors or fail over from one to another.
- No native concept of “same API for OpenAI-compatible third parties.”
- No byte-level passthrough — these SDKs deserialize the wire format into their own types; if you want to proxy bytes you have to use the HTTP transport directly.
Pick the vendor SDK when:
- Your code only ever calls one provider, and you have no plan to add another.
- You need a feature that
llmrouterdoes not model yet (embeddings, audio, vision, assistants, batch). - You want the most stable possible API surface.
Pick llmrouter when:
- You call more than one vendor, and you don’t want to write provider-specific request building and stream parsing twice.
- You want to support “bring your own OpenAI-compatible endpoint” (OpenRouter, Together, Groq, vLLM, Ollama) without adding a new SDK per vendor.
- You want byte-identical pass-through for proxy/gateway use cases.
mozilla-ai/any-llm-go
mozilla-ai/any-llm-go
is the closest neighbour to llmrouter in the Go
ecosystem: a single API surface across multiple LLM providers,
streaming-first, channel-based. It supports around ten providers
out of the box including OpenAI, Anthropic, Gemini, Mistral, and
several others.
If you want a Go equivalent of Python’s LiteLLM
that wraps the most providers and gives you typed access to tool
use and (in some cases) embeddings,
mozilla-ai/any-llm-go ships more provider coverage
today than llmrouter does.
Pros of any-llm-go.
- Broader provider catalogue out of the box.
- Typed wrappers around features beyond chat (where available).
- Backed by Mozilla AI, which gives it some institutional momentum.
Cons / gaps.
- No first-class Azure OpenAI Service, AWS Bedrock, or Google Vertex AI provider — the three managed cloud endpoints that most enterprise deployments end up using.
- No byte passthrough: requests and responses are deserialized into the library’s own typed shapes. You cannot run an
any-llm-go-fronted proxy that is byte-identical to the upstream OpenAI wire format. - Also pre-1.0; expect API changes.
- OpenAI is just one of many providers, not the canonical reference shape — so the request and response types are
any-llm-go’s own design, not OpenAI’s.
Pick any-llm-go when:
- You need a provider
llmrouterdoes not ship yet (e.g. Gemini direct, Mistral, Cohere) and waiting for v0.2 / v0.3 is not viable. - You don’t care about byte-level passthrough and are happy consuming a library-specific typed surface.
- You want a closer Go analogue to Python’s LiteLLM in shape.
Pick llmrouter when:
- You specifically want the OpenAI request and response shape, not a third-library shape. Your existing OpenAI SDK code drops in.
- You are building a proxy, gateway, or telemetry layer that needs to forward bytes unchanged.
- You need Azure / Bedrock / Vertex on the roadmap with a single API surface, not three separate vendor SDKs.
LiteLLM and OpenLLM (Python, for reference)
LiteLLM and OpenLLM are the canonical Python answers to “one client, many LLMs.” They are excellent — LiteLLM in particular is the de facto standard if you are building in Python and want the OpenAI request shape against any provider, plus features like routing, budget caps, caching, and key rotation.
They are listed here only so you do not waste time evaluating
them as Go options. If your codebase is Python, use LiteLLM. If
it is Go, your choice is between llmrouter, per-vendor
SDKs, any-llm-go, or direct HTTP.
Note that LiteLLM bundles a lot of gateway features
(rate limiting, budget caps, caching, vector store integration)
that llmrouter deliberately does not — see
Out of
scope on the roadmap page. Those belong in the gateway layer
you build on top of a library like llmrouter,
not in the library itself.
Direct HTTP (net/http + bufio.Scanner)
Every option above is a wrapper on top of net/http.
You can skip them all and write your own client against each
vendor’s REST endpoint. People do this — sometimes for good
reasons.
Pros of direct HTTP.
- Zero dependencies beyond the standard library.
- Full control over retry, backoff, connection pooling, observability hooks, header injection, request mutation.
- No version churn imposed by a third-party library.
- If you only ever build the one client for the one path you care about, the total code can be smaller than any wrapper’s API surface.
Cons.
- You re-implement Server-Sent Events parsing per vendor. OpenAI uses standard SSE with
data:lines and a sentinel[DONE]. Anthropic uses SSE with namedevent:types (message_start,content_block_delta,message_delta,message_stop). Each one is correct, none of them are the same. - You re-implement the error envelope per vendor. OpenAI uses
{ "error": { "type": "...", "message": "..." } }. Anthropic uses{ "type": "error", "error": { "type": "...", "message": "..." } }. Vertex returns Google-Cloud-shaped errors. Bedrock returns SigV4-style errors. - You re-implement the request body per vendor — including, for Anthropic, the lifted
systemfield and thecontentblock shape. - You write context-to-HTTP cancellation wiring, single-consumer channel discipline, and the producer-goroutine lifecycle every time.
- You re-do all of it for every new provider you add.
Pick direct HTTP when:
- You only ever call one endpoint, and the wrapper’s convenience does not pay for the dependency.
- You have a hard constraint against third-party dependencies.
- You want full control over the wire format because you’re doing something unusual (e.g. testing, fuzzing, debugging upstream behaviour).
Pick llmrouter when:
- You would otherwise write the same SSE parser, error wrapper, and producer goroutine twice — once per provider — and you’d like that code to be the same one library’s problem instead.
When llmrouter is the right choice
The library was built around four motivating use cases. If your project looks like one of these, llmrouter is probably the best fit:
- OpenAI-shaped gateway for multiple upstreams.
You expose an OpenAI-shaped HTTP API to your callers, but
internally you route to OpenAI, Anthropic, OpenRouter, or a
self-hosted vLLM. With
llmrouteryour handler does not branch on provider: it builds a singleChatRequest, picks a provider, and streams the result back. ForwardingChunk.Rawdownstream keeps the response byte-identical to upstream OpenAI for callers that have strict expectations of the SSE format. - Bring your own OpenAI-compatible endpoint.
Your users want to point your product at OpenRouter, Together,
Groq, vLLM, Ollama, LM Studio, or a private model behind their
firewall. With
llmrouterthis is oneWithBaseURLaway — no new SDK per endpoint. - Multi-vendor application code. Your application calls both OpenAI and Anthropic from the same code path (model A/B tests, failover, “cheap fast model then expensive smart model” cascades). You don’t want provider-specific branches inside your business logic; you want the provider abstracted behind one interface.
- Production streaming with proper cancellation.
You run a long-lived server that opens streams to upstream
LLMs. You need
context.Contextcancellation that reaches the in-flight HTTP request (so a client hang-up actually kills the upstream connection, freeing budget). The stream semantics inllmrouterare designed for this.
When llmrouter is the wrong choice (honest weaknesses)
The library is pre-1.0 and intentionally narrow. We’d rather you know about the gaps than discover them after a migration.
- Pre-1.0 API churn. We expect minor breakages between minor versions (v0.1 → v0.2 → v0.3) before we freeze the API at v1.0. If you need a library you can bump without reading changelogs, wait until v1.0 — or pin a minor and absorb a migration each time you upgrade.
- Missing providers. v0.1 ships OpenAI and
Anthropic. Azure OpenAI Service, AWS Bedrock, and Google Vertex
AI are planned for v0.2 but not shipped yet. Direct Gemini,
Mistral, Cohere are not on the near-term roadmap — for those,
mozilla-ai/any-llm-goships today. - No embeddings API. The library is focused on
streaming chat completions.
Embed()is on the v0.3 roadmap. If you need embeddings now, use the vendor SDK. - No audio / TTS / STT API. Whisper, GPT-4o audio, and Vertex audio are v0.4 material. Today you would call them with the vendor SDK directly.
- No vision provider abstraction yet. You
can send vision content today via
ChatRequest.Rawbyte passthrough, but there is no typed helper forimage_url/image bytescontent blocks. That lands in v0.3. - No tool-call typed surface yet. Same as
vision: you can pass tool definitions and consume tool calls
via byte passthrough, but the typed
ChatRequest.Tools/Choice.Message.ToolCallsshape lands in v0.3. - No gateway features. Rate limiting, budget caps, response caching, key rotation, vector store integration, persistent conversation state — these are all intentionally out of scope. They belong in a gateway layer you build on top of the library. If you want them bundled, LiteLLM (Python) is closer to that shape.
Decision tree
A short, honest version of the above:
- Only ever calling one vendor, want the full vendor surface? → that vendor’s official Go SDK.
- Need embeddings, audio, or assistants APIs today?
→ the vendor SDK (or
any-llm-gofor embeddings coverage). - Need Gemini / Mistral / Cohere / a long tail of
providers now? →
any-llm-go. - Calling OpenAI + Anthropic + OpenAI-compatible
endpoints from one codebase, want OpenAI shape, want byte
passthrough, can live with v0.x churn? →
llmrouter. - Building a proxy / gateway that forwards SSE
byte-identical to upstream? →
llmrouter(this is the headline use case). - Allergic to dependencies and only need one
provider? → direct
net/http.
If we got something wrong
Comparisons go stale. If something on this page is out of date, or you think we’ve mis-characterised an alternative, please open an issue at github.com/elloloop/llmrouter/issues with the corrected facts. We’d rather have an accurate comparison page than a flattering one.