routing¶
The routing section decides which provider/model handles a request.
Top-level fields¶
| Field | Type | Default | Notes |
|---|---|---|---|
default_strategy |
string | round-robin |
supported values: weighted, round-robin, random |
routes |
list | required in practice | ordered route list evaluated from top to bottom |
Route shape¶
routing:
default_strategy: "weighted"
routes:
- name: "default"
match:
path: "/v1/chat/completions"
headers:
x-lunargate-provider: "openai"
targets:
- provider: openai
model: gpt-5.2
weight: 100
fallback:
- provider: anthropic
model: claude-sonnet-4-6
weight: 100
If you want separate policies for chat and embeddings, use separate match.path rules:
routing:
routes:
- name: "chat-default"
match:
path: "/v1/chat/completions"
targets:
- provider: openai
model: gpt-5.2
weight: 100
- name: "embeddings-default"
match:
path: "/v1/embeddings"
targets:
- provider: ollama
model: nomic-embed-text-v2-moe
weight: 100
Route fields¶
name¶
Human-readable identifier. It is returned in X-LunarGate-Route and can be useful in logs and observability.
match.path¶
"*"matches everything- any other value is treated as a prefix match
For chat traffic, the usual value is:
For embeddings traffic, the usual value is:
match.headers¶
Exact-match header map.
Common examples:
x-lunargate-providerx-lunargate-modelx-lunargate-complexityx-lunargate-requires-tools
Any valid request-header name may be used here; matching is case-insensitive and values are compared exactly. Headers named only in routing config are read into a route-only view. They are not added to Dashboard collector tags or provider forwarding, so a private tenant or policy header used for selection is not exported as telemetry by that fact alone.
Gateway-derived values such as the effective request type, selected model hints, and other canonical routing metadata take precedence over a raw caller value with the same name.
targets¶
Each target needs:
| Field | Notes |
|---|---|
provider |
provider ID from the providers section |
model |
upstream model name for that provider |
weight |
required positive integer used by weighted; the total weight of one target list must fit the platform integer range |
upstream_request_type |
upstream protocol: chat_completions (default) or responses |
fallback¶
Ordered list of backup targets used by the fallback executor if the primary path fails.
Choosing the upstream protocol¶
The public client endpoint and the upstream provider protocol are separate choices. upstream_request_type controls what LunarGate sends to one target:
- omit it or set
chat_completionsto use the translated Chat Completions path - set
responsesto use the provider's native Responses path
routing:
routes:
- name: "native-responses"
match:
path: "/v1/responses"
targets:
- provider: openai
model: gpt-5.2
weight: 100
upstream_request_type: responses
A target with upstream_request_type: responses is required whenever a client
request must use the provider's native Responses transport, including native
Response creation or continuation, background execution, hosted tools,
Responses utility operations, and a Response associated with a native
Conversation. Native Conversations CRUD and item endpoints select and retain a
provider account through capabilities.conversations; they do not use ordinary
route-target selection. Native Response lifecycle follows its retained binding
or an explicitly selected capable provider. The corresponding optional features
must also be enabled under providers.<id>.capabilities.
Response and Conversation bindings deliberately retain different routing information:
- a native Response binding retains its exact route, provider, effective model,
and upstream protocol;
previous_response_idcontinuation and lifecycle operations stay pinned to that target - a native Conversation binding retains its owning provider account, but not a
route or model; a later Response associated with that Conversation pins the
owner provider and the
responsesupstream protocol while still honoring the current request's route and model selection
The first route that matches a native-Conversation Response must therefore
contain an eligible target for the owning provider with
upstream_request_type: responses. If it does not, LunarGate returns status 400; the error has type: invalid_request_error, param: conversation, and code: unsupported_feature. The failure happens before an upstream call. LunarGate does not fall through to a later route or send the Conversation through Chat Completions translation.
Stateful objects stay pinned to their creating provider. Retrieval, mutation, cancellation, and continuation do not move to another route or fallback target. After a provider account or endpoint changes during hot reload, LunarGate rejects follow-up operations for objects created under the old account identity. If distinct provider accounts return the same native object ID, implicit follow-up calls fail with provider_binding_conflict; an explicit X-LunarGate-Provider can target a known owner for recovery. See Stateful ownership conflicts and recovery.
Translated targets are intentionally fail-closed: when a request contains a field LunarGate cannot represent faithfully in the selected upstream protocol, the request returns a structured compatibility error instead of silently dropping the field.
Supported balancing strategies¶
weighted¶
Chooses between eligible targets based on weight.
round-robin¶
Cycles through eligible targets evenly.
random¶
Selects a random eligible target.
Unknown strategies are rejected during startup or hot reload. Route target and fallback lists are also rejected when a weight is missing, non-positive, or their sum would overflow.
Route order matters¶
Routes are checked from top to bottom.
The first route whose path and headers match is authoritative. If a requested provider, model, or trusted stateful protocol constraint has no eligible target inside that route, LunarGate returns an error immediately instead of searching later matching routes. This keeps a specific policy from being bypassed by a broader rule farther down the list.
Recommended order:
- forced-provider or forced-route rules
- tool-aware rules
- complexity-tier rules
- generic default route
Request-side overrides that interact with routing¶
The runtime also understands request headers such as:
X-LunarGate-ProviderX-LunarGate-ModelX-LunarGate-Route
Those headers do not replace routing config, but they can narrow the set of eligible targets or force a specific named route.
Practical guidance¶
- Keep at least one generic fallback route at the bottom.
- Make sure every
target.providerexists inproviders. - Use header-based routing for team, environment, capability, or complexity decisions.
- Use separate path matches when chat and embeddings should go to different upstream models or providers.
- Use the
lunargate/autotechnique when you want the gateway to decide tiers from one stable client model.