An LLM Routing Gateway for Coding CLIs: Multi-Model API Routing in Practice

    A practical guide to putting an LLM routing gateway between an AI coding CLI and upstream model APIs. It covers the three-layer decoupled architecture, the key configuration fields (listen address, egress proxy, provider adapters, per-scenario routing), request rewriting and protocol adaptation challenges such as auth header injection, tool-call schema mapping, and SSE passthrough, a task-type routing policy matrix, a layered verification workflow using curl before attaching the CLI, a troubleshooting order, and security, cost, and observability baselines.

    1. Problem definition: why add a routing layer between a coding assistant and model services

    A command-line AI coding assistant (referred to below as a “coding CLI”) is essentially a terminal shell plus model calls: it reads repository context, assembles prompts, parses the structured instructions returned by the model, and then executes file reads/writes and command invocations. The actual reasoning comes from an upstream large model API. By default, these tools are tightly bound to a single vendor’s model service, which creates three practical pain points:

    1. Capability mismatch. Coding work spans very different subtasks. Completing a single line of code demands minimal latency; explaining a piece of legacy code needs stronger reasoning; refactoring across dozens of files is a classic long-context task. Forcing one model to serve all subtasks either wastes compute and spend on simple tasks or underperforms on complex ones.
    2. Uncontrolled cost. Heavy users can generate hundreds of requests per day, and long-context requests routinely reach tens of thousands of input tokens. Without tiered scheduling, the bill grows far faster than the improvement in results.
    3. A single point of availability failure. Once upstream rate limiting kicks in, a region becomes unreachable, or a model is retired, the entire development workflow stops immediately.

    The standard engineering answer is to introduce an LLM routing gateway (proxy) between the two sides: the coding CLI knows only one fixed local endpoint, while the gateway dispatches requests to different model services according to policy and handles authentication, protocol conversion, timeout control, and logging. Model selection then moves from “a setting baked into the tool” to “a runtime policy adjustable by the operations layer.”

    2. Architecture overview: three layers of decoupling

    1. +--------------------+ OpenAI/Anthropic-compatible protocol +------------------+
    2. | Coding CLI (client)| ----------------------------------------------> | LLM Routing Gateway|
    3. | reads repo / builds | <---------------------------------------------- | Local 127.0.0.1 |
    4. | tasks | Streaming responses (SSE) +---------+--------+
    5. +--------------------+ | protocol adapters / credential injection
    6. +----------------------+--------------------+
    7. v v v
    8. Lightweight chat model Flagship reasoning Long-context model
    9. service model service service

    Each layer needs a clear responsibility boundary:

    • Client layer: indifferent to who provides the model; it only points at the gateway address through an environment variable or configuration.
    • Gateway layer: owns routing decisions, credential custody, request/response body rewriting, stream forwarding, timeouts and retries, and access logs.
    • Model service layer: can be a public cloud model service or a self-hosted inference endpoint exposing a compatible API.

    The biggest benefit of this decoupling is that swapping the upstream does not affect the client: changing model vendors, canarying a new model, or failing over to a backup channel requires only a gateway configuration change and a gateway restart.

    3. Environment setup and dependency checklist

    • Runtime: an LTS release of a JavaScript runtime (v18 or later) is recommended, since the gateway and most coding CLIs rely on its built-in fetch, streaming reads, and ESM module support.
    • The coding CLI itself: install it globally through the system package manager or the runtime’s own package manager, and confirm that which (or where) resolves the executable.
    • Upstream credentials: create an API key in the console of the target model service, enabling only the models you need under the least-privilege principle. In enterprise environments, prefer a rotatable, auditable credential management solution over plaintext values written into a config file.
    • Network egress: if the upstream service is not directly reachable from your current network, configure a shared egress proxy. Here two concepts must be distinguished: the address and port the gateway listens on (for the CLI to connect to) and the proxy address and port the gateway uses when calling upstream (for the gateway’s outbound traffic). Confusing the two is the most common configuration error.

    4. Gateway configuration options, field by field

    Below is an illustrative configuration (field names and structure may be adjusted to match your gateway’s documentation; it is here to explain the design):

    1. {
    2. "server": {
    3. "host": "127.0.0.1",
    4. "port": 3456,
    5. "apiKey": "",
    6. "timeoutMs": 600000,
    7. "egressProxy": "http://127.0.0.1:7890"
    8. },
    9. "log": { "enabled": true, "level": "info", "redactHeaders": ["authorization", "x-api-key"] },
    10. "providers": [
    11. {
    12. "name": "fast",
    13. "baseUrl": "https://<model-service-endpoint>/v1",
    14. "apiKey": "<YOUR_FAST_MODEL_KEY>",
    15. "models": ["<vendor>-lite", "<vendor>-flash"],
    16. "adapter": "openai-compatible"
    17. },
    18. {
    19. "name": "reasoning",
    20. "baseUrl": "https://<model-service-endpoint>/v1",
    21. "apiKey": "<YOUR_REASONING_MODEL_KEY>",
    22. "models": ["<vendor>-pro", "<vendor>-reasoner"],
    23. "adapter": "openai-compatible"
    24. }
    25. ],
    26. "routing": {
    27. "default": "fast:<vendor>-flash",
    28. "background": "fast:<vendor>-flash",
    29. "reasoning": "reasoning:<vendor>-pro",
    30. "longContext": "reasoning:<vendor>-reasoner",
    31. "webSearch": "fast:<vendor>-flash"
    32. }
    33. }

    What the key fields do:

    Field Purpose Practical guidance
    server.host Gateway listen address Pin it to 127.0.0.1 so only the local machine can reach it
    server.port Gateway listen port Avoid common ports to prevent clashes with other local services
    server.apiKey Inbound authentication key for the gateway itself Can be empty for purely local use; must be set the moment it becomes reachable from the same network segment
    server.timeoutMs Upstream request timeout 5–10 minutes suits long-context tasks; completion-style tasks may get a shorter dedicated value
    server.egressProxy Gateway outbound proxy Affects only gateway-to-upstream traffic, not CLI-to-gateway traffic
    providers[].adapter Protocol adapter Determines how request bodies are rewritten; the core of the gateway
    routing.* Per-scenario model mapping Configure separately for the “latency-sensitive” and “quality-first” categories

    One point deserves emphasis: setting server.host to 0.0.0.0 exposes the gateway to the local network, and if no inbound key is configured, you have effectively opened a free entry point for others to spend your paid model quota. Do not do this without an explicit reverse proxy and authentication in front of it.

    5. Request rewriting and protocol adaptation: the real difficulty

    A coding CLI and the upstream model service usually do not speak the same protocol, so the gateway has to convert at several layers:

    1. Auth header injection. The client carries no upstream credentials; the gateway injects Authorization: Bearer ... or a vendor-specific header (such as x-goog-api-key or x-api-key) based on providers[].apiKey. Credentials exist only inside the gateway process, so they never leak through the repository or terminal history.
    2. URL and request body mapping. Vendors differ in endpoint paths, model field names (model vs. modelId), message structures (messages vs. contents), and where system prompts go (a top-level system field vs. a first message with role: system). The adapter must map both directions and watch for differences in the tool calling (function calling / tool use) format — this is where coding applications break most easily, and incompatible parameter schemas (JSON Schema) will make file edits fail outright.
    3. Streaming response forwarding. Coding CLIs depend on incremental output for real-time echo, so the gateway must pass SSE events through chunk by chunk rather than buffering the whole response in order to “parse complete JSON” — buffering ruins the interaction and can trigger client timeouts. It should also normalize the upstream incremental format into the event sequence the client expects, including the end-of-stream marker and token usage figures.
    4. Parameter trimming and substitution. When the target model does not support certain parameters (temperature ranges, thinking budgets, structured output modes), the gateway should proactively drop or substitute them instead of forwarding them unchanged and producing a 400 error.
    5. Retries and fallback. Use exponential backoff for 429/5xx responses; after repeated failures, fall back to an alternate provider and mark the model actually used in the response, which makes it much easier to explain differences in output quality.

    6. A routing policy matrix: pick models by task type, not by mood

    Scenario-based routing is the core value of this design. A workable mapping:

    • Default / background tasks: a lightweight, fast model. Used for low-value, high-frequency calls such as generating commit messages, summarizing diffs, and proposing names, keeping latency and unit price down.
    • Deep reasoning: a flagship or reasoning-capable model. Used for defect localization, algorithm design, and complex refactoring plans.
    • Long context: the model with the largest context window. Used for repository-wide retrieval-style Q&A and cross-file impact analysis; pair it with a context compression strategy (keep only relevant snippets and signature summaries) rather than blindly filling the window.
    • Web retrieval: a model or channel with search or tool-chain capability, used for time-sensitive questions such as confirming version behavior or the meaning of an error code.
    • Real-time completion scenarios: if the CLI supports a dedicated completion endpoint, give it its own lowest-latency channel isolated from the chat channel, so long-running tasks cannot exhaust the concurrency quota.

    Once the policy is in place, measure it: track call counts, average latency, average token consumption, and failure rate per channel. Without that data, whether “switching models pays off” is pure guesswork.

    7. Startup, integration, and verification

    Step 1: start the gateway in the background. Check the startup log to confirm the listen address, the loaded provider list, and whether the routing table parsed successfully. If the config file has JSON syntax errors (a stray comma, a trailing space), the gateway will usually fail fast at startup.

    Step 2: verify the gateway directly, bypassing the CLI. This is the step most often skipped and the most valuable one — treat the gateway as a standard compatible endpoint and send a single non-streaming request with a command-line tool or a minimal script:

    1. curl -s http://127.0.0.1:3456/v1/chat/completions \
    2. -H "Content-Type: application/json" \
    3. -d '{"model":"<vendor>-flash","messages":[{"role":"user","content":"Explain idempotency in one sentence."}]}'

    If this step fails, the problem is definitely in “gateway to upstream” (credentials, network, model name, protocol adaptation) and has nothing to do with the coding CLI. If it succeeds but the CLI fails, the problem is in “CLI to gateway” (endpoint address, environment variables, tool calling format).

    Step 3: start the coding CLI. Launching it through the wrapper command provided by the gateway is recommended, because the wrapper automatically injects the environment variables pointing at the gateway into the child process, avoiding manual mistakes:

    1. # Option 1: the gateway's own wrapper command (recommended, injects env vars automatically)
    2. model-gateway code
    3. # Option 2: export the environment variables manually, then start the CLI
    4. export ANTHROPIC_BASE_URL="http://127.0.0.1:3456"
    5. export ANTHROPIC_AUTH_TOKEN="dummy"
    6. claude

    Step 4: run one end-to-end closed loop. Have the CLI complete a full “read file -> modify code -> run tests” flow in a throwaway repository. Only when you reach tool calling and file writing have you truly validated the completeness of the adapter layer.

    8. Common failures and where to look first

    Symptom Likely cause What to do
    Connection refused Gateway not running, or the port/address does not match the CLI configuration Check the listen address and port
    401 / 403 Invalid upstream key, model not enabled, or auth headers not injected correctly Isolate it with curl straight against the upstream
    Request timeout Egress proxy not taking effect, or the timeout threshold is too low Separate the listen address from the egress proxy; raise the timeout for long tasks
    Model name not found Model identifiers in the config do not match what upstream actually offers Pull the upstream model list and compare
    Output stops, no increments The adapter is buffering the streaming response Switch to per-event passthrough
    Tool call fails, file unchanged Incompatible tool calling protocol or schema mapping Inspect the adapter’s tool use conversion logic
    Response truncated maxOutputTokens is too small Raise maxOutputTokens

    The troubleshooting principle is locate by layer: confirm network reachability first, then authentication, then protocol, and only then model parameters. Most complaints that “the model quality is bad” actually trace back to a request body that was rewritten incorrectly.

    9. Security, cost, and operations notes

    • Credential management: inject API keys through environment variables or a separate credential file, set the file permissions to current-user-read-only, and add it to the version control ignore list. Rotate on a schedule and revoke immediately on any sign of exposure.
    • Log redaction: gateway logs capture full requests and responses, which may contain source code snippets. In production, disable writing request bodies to disk, or at minimum redact headers such as authorization and x-api-key, and cap the log retention period.
    • Quotas and rate limits: set per-user/per-project QPS and daily token ceilings on the gateway to prevent runaway scripts from producing a surprise bill.
    • Observability: pipe request counts, token usage, time to first token (TTFT), and error rates into a log service and alerting; emit metrics per provider and per model, otherwise you cannot tell whether the routing policy is working.
    • Enterprise extension: when rolling this out to a team, consider upgrading the self-hosted gateway into the company’s unified model access layer, handling credential custody, quota allocation, audit trails, and multi-model canaries in one place. If you use hosted model services from a cloud provider, you can combine object storage to archive context snapshots, a message queue to buffer and drain peak async tasks, a container platform to run multiple gateway replicas, and a log/monitoring/alerting service for unified observability; refer to the official documentation of the chosen cloud platform for exact product selection and quota limits.

    10. Wrap-up

    Inserting a routing gateway between a coding assistant and a large model is essentially a separation of concerns: it splits “how the work gets done” (the CLI’s context assembly and tool execution) from “whose compute it runs on” (model selection, authentication, quotas). The rollout path boils down to four steps — deploy the gateway and pin it to the local machine, configure model mappings by task type, run layered verification with curl, and finally attach the CLI for an end-to-end closed loop. What truly determines the long-term experience is not which single model you connect, but whether the routing policy matches the characteristics of each task and whether your logging, quota, and security baselines are in place.