ModelMux — case-study key art

Architflix Original

ModelMux

2026AI / MLCase study

One OpenAI-compatible API in front of many LLM providers. Every model name is a fallback chain, so a rate-limited or dead provider is skipped before your client sees a byte. Adds about 0.14 ms at the median and recovered every recoverable request across five fault scenarios.

Overview

ModelMux is a single-binary Go gateway by Archit Jain that puts one OpenAI-compatible API in front of many LLM providers, with fallback chains, circuit breakers, exact and semantic caching, and per-tenant keys, rate limits and budgets, adding about 0.14 ms at the median.

Season 1 · The Build

5 episodes

One OpenAI-compatible API in front of many LLM providers. Every model name is a fallback chain, so a rate-limited or dead provider is skipped before your client sees a byte. Adds about 0.14 ms at the median and recovered every recoverable request across five fault scenarios.

Built with Go, Prometheus, Docker, k6, Ollama.

GoPrometheusDockerk6Ollama

Any OpenAI SDK can point at ModelMux. A model name resolves to an ordered chain of providers (OpenAI-compatible APIs such as OpenAI, Groq, Gemini, Ollama and vLLM, plus two-way Anthropic translation including streaming and tool calls), and a request moves down the chain on rate limits, server errors and timeouts. Streaming is the hard case: a stream commits to a provider only when its first chunk arrives, so failures before the first byte fall back invisibly, while a failure mid-stream ends with an in-band error and is never cached. A per-provider circuit breaker stops sending traffic to a dead provider, and only a designated probe request can close it again. Per-tenant API keys (stored hashed), model allow-lists, rate limits and monthly token budgets reserved up front control admission, and every admitted request emits a usage event and Prometheus metrics. An exact cache answers repeated requests, and an opt-in semantic cache answers paraphrases, guarded by an entity check because embeddings treat "hotels in Udaipur" and "hotels in Munnar" as near-identical. The benchmarks are honest about that trade-off: the semantic cache was tuned on one set of question pairs and evaluated on a held-out set, where it safely served 14.4% of paraphrases at a 0.4% wrong-answer rate, so it ships off by default. Against a 50 ms stub, the gateway adds 0.14 ms at the median and 0.58 ms at p99 at about 2,000 requests per second; in all five recoverable fault scenarios every recoverable request was recovered, and with the breaker on, a dead provider received only 23-32 of 2,000 requests. Built by Archit with Claude as the AI coding assistant.

  • OpenAI-compatible endpoint in front of OpenAI, Groq, Gemini, Ollama and vLLM, plus two-way Anthropic translation with streaming and tool calls
  • Fallback chains with a first-byte timeout; streams commit to a provider only after the first chunk
  • Per-provider circuit breaker that only a designated probe can close; client disconnects don't count as failures
  • Exact cache plus an opt-in semantic cache with an entity guard against swapped names and places
  • Hashed per-tenant keys, model allow-lists, rate limits and monthly token budgets reserved up front
  • Usage events, Prometheus metrics, and a playground with simulated provider outages
  • A single Go binary on net/http with no framework; Docker image and a Render blueprint

Every project hits walls. Here's what nearly broke ModelMux — and how it shipped anyway.

  • Streaming fallback: retry only before the first chunk, end mid-stream failures with an in-band error, never cache a broken stream
  • Semantic caching is risky: embeddings rate swapped entities as near-duplicates, so a lexical guard was added, and re-evaluating on held-out pairs cut the headline from 21.4% to about 14% of paraphrases
  • Circuit-breaker correctness under concurrency: one probe at a time, and client cancellations kept out of the failure count
  • Translating the Anthropic API both ways: system-prompt placement, merging tool results, and re-cutting stream events
  • Making cache lookups fast enough to skip an approximate index: unrolling the dot product cut a 2,000-entry scan from 2.55 ms to 0.99 ms

Every build leaves residue. The takeaways from ModelMux that carry forward.

  • A gateway's real job is failure handling; the happy path is just a proxy
  • Evaluate caches on held-out data: the honest number (about 14%) was a third lower than the first one, which is why the feature ships off by default
  • Measure overhead against a stub, not a real provider, or network noise swamps the result
  • Known limits: all state is in memory (not shared across replicas), token counts are estimated when providers don't report them, and the numbers come from one machine