Providers
A provider is one upstream endpoint plus its credentials. Providers are independent of the model names clients see — you can add several providers serving the same model and let fallback and priority decide who answers.

Adding a provider
Providers → Add Provider. Fields:
| Field | Notes |
|---|---|
| Name | Unique identifier, referenced by model mappings. Not the vendor name — e.g. openai-prod |
| Type | One of the 23 built-in adapter types (table below) |
| API key | Required for most types; not required (and not used) by ollama, vllm, sglang |
| Base URL | Pre-filled per type. Required for openai-compatible. Trailing slashes are trimmed |
| Icon URL | Optional logo shown next to the provider in the console; must be a public URL (https:/http:) — private/loopback addresses are rejected by the SSRF guard |
| Gemini Interactions | gemini type only: use Google's newer Interactions API instead of generateContent (metadata.api_variant). The provider list shows an Interactions badge |
| API version | Not in the console dialog; set via API/DB only. Not applied by any adapter in v0.2.3 |
| Timeout | Not in the console dialog; set via API/DB only. Not applied in v0.2.3 — upstream calls use a fixed 600 s read timeout (10 s connect) |
| Custom headers | Extra headers sent upstream (e.g. tenant or organization headers). These win over any other source for the same header name |
| App Attribution | openrouter type only: application URL (HTTP-Referer), name, marketplace categories and ranking visibility. Unset values fall back to crediting the LLM Proxy project |
| Endpoint base URLs | Per-endpoint overrides; the URL is used as-is, without appending the endpoint path |
| Native web search | Set when the upstream provides its own web-search tool; the proxy then passes web_search tools through instead of intercepting them |
| Priority | Not shown in the console; provider-level priority exists only via API/DB and is ignored by selection — fallback order comes from each model mapping's priority (see Models & Pricing) |
Provider API keys are masked in the console — the edit dialog shows only a masked, disabled field. The only way to read a key back is the audited POST /api/config/providers/{name}/api-key/reveal endpoint. Keys are encrypted at rest with Fernet (key derived from ENCRYPTION_KEY); rotating that environment variable makes stored keys undecryptable.
Provider types
| Type | Upstream wire protocol | Default base URL |
|---|---|---|
openai | Chat Completions + Responses | https://api.openai.com/v1 |
anthropic | Anthropic Messages (x-api-key, anthropic-version: 2023-06-01) | https://api.anthropic.com |
openai-compatible | Chat Completions (generic; base URL required) | — |
gemini | Google generateContent (x-goog-api-key) | https://generativelanguage.googleapis.com/v1beta |
ollama | Native Ollama (/api/chat, /api/tags) | http://localhost:11434 |
vllm | Chat Completions | http://localhost:8000/v1 |
sglang | Chat Completions | http://localhost:30000/v1 |
deepseek | Chat Completions + native Anthropic/Responses | https://api.deepseek.com/v1 |
xai | Chat Completions + native Responses | https://api.x.ai/v1 |
zai | Chat Completions (GLM layout) | https://api.z.ai/api/paas/v4 |
zai-coding | Chat Completions + native Anthropic/Responses | https://api.z.ai/api/coding/paas/v4 |
zhipu | Chat Completions + native Anthropic | https://open.bigmodel.cn/api/paas/v4 |
zhipu-coding | Chat Completions + native Anthropic/Responses | https://open.bigmodel.cn/api/coding/paas/v4 |
moonshot | Chat Completions + native Anthropic | https://api.moonshot.ai/v1 |
kimi-code | Chat Completions + native Anthropic | https://api.kimi.com/coding/v1 |
minimax | Chat Completions + native Anthropic/Responses | https://api.minimax.io/v1 |
qwen | Chat Completions + native Anthropic/Responses | https://dashscope.aliyuncs.com/compatible-mode/v1 |
qwen-intl | Same as qwen (international keys are region-bound) | https://dashscope-intl.aliyuncs.com/compatible-mode/v1 |
openrouter | Chat Completions + native Responses | https://openrouter.ai/api/v1 |
chutes | Chat Completions | https://llm.chutes.ai/v1 |
mistral | Chat Completions | https://api.mistral.ai/v1 |
nanogpt | Chat Completions | https://nano-gpt.com/api/v1 |
opencode-zen | Per-model dialect: Chat Completions / Anthropic / Responses / Google / System One (see below) | https://opencode.ai/zen/v1 |
opencode-go | Same gateway, subscription lane (own model catalog) | https://opencode.ai/zen/go/v1 |
Native protocol means the proxy can forward a request verbatim to that upstream when the client speaks the same protocol (Anthropic clients hitting a native-Anthropic provider, Responses clients hitting a native-Responses provider), preserving fields the translation layer would otherwise normalize. vllm/sglang opt in per provider via provider metadata native_passthrough: true.
Cost reporting: openrouter and nanogpt return their own cost figures, which the proxy records instead of computing them from token prices.
OpenCode (opencode-zen / opencode-go)
The OpenCode gateway fronts model families whose upstreams speak different wire protocols. Which dialect a request takes is a property of the model, resolved per request from, in order: a learned correction (below), the models.dev catalog (the OpenCode team's own per-model provider.npm data, fetched with a 6h TTL cache — new model families route correctly with no code change), then model-id prefix heuristics (Chat Completions by default). Claude models are served on Anthropic Messages, GPT/Grok on OpenAI Responses, Gemini on generateContent, Jev on System One (/v1/systemone endpoint only — chat requests for Jev models are rejected with a pointer there), and everything else on Chat Completions.
- Types = lanes:
opencode-zenis the Console pay-as-you-go lane (https://opencode.ai/zen/v1),opencode-gothe subscription lane (https://opencode.ai/zen/go/v1) — the two lanes serve different model catalogs (the same id can even speak a different dialect on each; the adapter reads the lane's own models.dev entry). Both keep every dialect on one flat root under the base URL. The Console lane's dialect-prefixed paths (https://opencode.ai/inference/v1) are reachable by settingbase_urlto it. - Headers:
Authorization: Bearereverywhere, plus the dialect's auth header where the gateway reads one (x-api-keyon the Anthropic route,x-goog-api-keyon the Google route). The OpenCode CLI's client-identity headers (User-Agent: opencode/...,x-opencode-client,x-opencode-project, per-requestx-opencode-session/x-opencode-request) are sent by default — the free-tier models are rejected with403 FreeTierErrorwithout them. Any of these can be replaced via Custom headers. - Self-healing: when the gateway rejects a model with a model-flavored 400/404, the request is retried on the remaining dialects (Chat Completions first) and the working dialect is remembered for the process lifetime.
- Model list: the gateway's
/v1/modelsanswers with bare ids; display names, context windows and pricing are enriched from the models.dev catalog. Set provider metadataopencode_catalog_urlto a mirror to pin the catalog, or to an empty value to disable it (heuristics then carry the routing).
OpenRouter specifics
OpenRouter is a router rather than a single upstream, so the adapter keeps a few of its request shapes intact:
- Responses and Messages passthrough.
/v1/responsesand/v1/messagesclients are forwarded verbatim, soreasoning.mode/reasoning.context,thinkingbudgets,context_management,providerrouting preferences,plugins,modelsfallbacks andusageaccounting all reach OpenRouter. An Anthropic-protocol client avoids the double translation (Messages → Chat Completions → Messages) that used to drop them. Chat Completions clients get the equivalent fields through the request builder. Set provider metadatanative_passthrough: falseto fall back to the Chat Completions translation (e.g. if the routed model rejects a Responses-only item type). - Unified
reasoningobject. The proxy sendsreasoning: {effort, mode, context, summary, max_tokens, exclude, enabled}instead of a barereasoning_effort, becausemode: "pro"(and the other Responses-only fields) have no top-level equivalent. OpenRouter only honoursmode: "pro"when it can route to OpenAI or Azure, and only for GPT-5.6 and newer. - App attribution. The App Attribution fields in the provider dialog identify your deployment on OpenRouter's rankings: the application URL (
HTTP-Referer), display name (X-OpenRouter-Title), marketplace categories and whether a newly created app is listed publicly. Unset fields fall back to crediting the LLM Proxy project; headers set explicitly under Custom Headers win over both. Values are validated as printable ASCII (they become raw HTTP headers), the URL must be an absolute http(s) URL, and at most two categories are accepted. - Forwarded client headers. Per-request OpenRouter control headers (
X-OpenRouter-Metadata,X-OpenRouter-Cache,X-OpenRouter-Cache-TTL,X-OpenRouter-Cache-Clear) are forwarded from the client, as arex-session-id(sticky routing and prompt-cache affinity on chat; on the embeddings/image/audio endpoints it is the only way to attach a session, used there for usage grouping) andx-anthropic-beta(Claude beta opt-ins such asstructured-outputs-2025-11-13, without which OpenRouter stripsstrictfrom tool definitions). Client Codex / Claude Code fingerprint headers are not forwarded to a router. - Per-endpoint passthrough fields. Each endpoint keeps the fields OpenRouter documents beyond the OpenAI schema: chat (
provider,models,plugins,session_id,trace,preset,structured_outputs,cache_control,min_p,top_k,top_a,repetition_penalty), embeddings (input_type,session_id,trace,provider,user), image generation (resolution,aspect_ratio,seed,input_references,provider), TTS (input_references,provider) and STT (provider). Chat also forwards the legacyroute,transforms,usageandinclude_reasoningfields for clients that still send them — upstream deprecates them in favour ofproviders.sort.partition, thecontext-compressionplugin,stream_options.include_usageandreasoning.excluderespectively. Image requests are normalized to the Image API's own schema (output_format,background,output_compression;response_formatandstyleare dropped), and TTS/STTresponse_formatis validated against the formats OpenRouter accepts (mp3/pcmandjson/verbose_json) instead of surfacing an upstream 400. - Response metadata.
X-Generation-Id(usable withGET /api/v1/generation?id=) and theX-OpenRouter-Cache-*headers are forwarded to the client;cost,cost_detailsandis_byokfromusagefeed the cost record. - Model picker. The provider's catalog is fetched with
output_modalities=all— upstream defaults totext, which would hide the image, video, audio and embedding models this adapter also serves — and keeps the catalog'scontext_length, modalities,supported_parametersand pricing, so the picker can show which models are non-text and what they cost. - Audio. Speech (
mp3/pcm) and transcription (json/verbose_json) formats are checked at the proxy boundary rather than surfaced as an upstream 400. OpenRouter has no audio translation endpoint, so/v1/audio/translationsis rejected locally instead of 404-ing upstream. - Model variants.
:free,:nitro,:floor,:online,:thinkingand the*-provariants are passed through as plain model strings, but model lookup is an exact name match — register each variant you want to expose as its own model, or route to it with a model alias.
Qwen (Model Studio / DashScope) specifics
Two provider types cover the region-bound DashScope domains: qwen (Beijing, dashscope.aliyuncs.com) and qwen-intl (Singapore, dashscope-intl.aliyuncs.com). API keys are region-bound, so an international key only works with qwen-intl and vice versa. Business-space dedicated domains (https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/…) work through the provider Base URL field.
- Native protocols. Anthropic Messages (
{host}/apps/anthropic/v1/messages) and OpenAI Responses ({base_url}/responses) are forwarded verbatim, so Claude Code request shapes,cache_controlbreakpoints and Responses items reach DashScope unchanged. The Anthropic endpoint exposes only/v1/messages— there is no model-list endpoint, so clients that probe/v1/modelssee a 404 from upstream. - Chat vendor fields. Unrecognized chat fields pass through to DashScope, so
enable_thinking,thinking_budget,enable_search,search_options,tool_streamandenable_code_interpreterwork from a client'sextra_body. - Images. Wan and Qwen-Image both run through the DashScope async task API. DashScope's image extension fields are accepted and forwarded, flat on the body:
image(URL / base64 / array, turning the call into image-to-image),negative_prompt,seed,prompt_extend,prompt_extend_mode,enable_thinking,watermark, and on editsbbox_list,color_palette,enable_sequential,thinking_mode. There is no mask concept — passbbox_listinstead. - Embeddings.
text-embedding-v1..v4andqwen3.7-text-embeddingride{base_url}/embeddings.dimensionsis validated against DashScope's documented value set, and per-request row caps (20 forqwen3.7-text-embedding, 10 for v3/v4, 25 for v1/v2) are enforced at the proxy. - No audio. DashScope has no OpenAI-compatible audio surface. Speech synthesis (Qwen-TTS, CosyVoice) uses the native
/api/v1/services/aigc/multimodal-generation/generationendpoint, and OpenAI-compatible ASR (qwen3-asr-flash) is/chat/completionswithinput_audiocontent parts./v1/audio/*against a qwen provider is rejected with a400. - Web search. DashScope's Responses API has its own
web_search/web_extractor/code_interpretertools. Set provider metadatanative_web_search: trueto letweb_searchtools reach it; otherwise the proxy intercepts them and runs its own web search.
Verifying a provider
GET /api/config/providers/{name}/modelsqueries the upstream model list — this doubles as the effective connectivity check. The console calls it from the provider-model picker in the model editor (the ↻ button in the picker refreshes the list).ollama,vllm, andsglangneed no API key for this; everything else does.- Once a model mapping exists, send a test request from the Chat playground or with
curl, then confirm the row in Logs.
Known limitations (v0.2.3)
Documented deliberately so you do not chase them:
- Provider
timeout,api_version, andrate_limitare not applied by the request path. Upstream calls use a fixed 600 s read timeout; rate limiting is global/per-key, not per provider. - Provider
prioritydoes not affect selection — set priority on each model → provider mapping instead. - Provider
enableddoes not filter traffic; remove the model mappings (or raise another provider's mapping priority) to take a provider out of rotation. - No vertex/region/project fields for Gemini; no OAuth flows. Upstreams that need client fingerprint headers work via native passthrough only.
- No per-provider health checks; the circuit breaker is the only automatic protection.
Related
- Models & Pricing — map client-facing names onto this provider
- Server Settings — retries, fallback, circuit breaker