Skip to content

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:

path: "/v1/chat/completions"

For embeddings traffic, the usual value is:

path: "/v1/embeddings"

match.headers

Exact-match header map.

Common examples:

  • x-lunargate-provider
  • x-lunargate-model
  • x-lunargate-complexity
  • x-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_completions to use the translated Chat Completions path
  • set responses to 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_id continuation 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 responses upstream 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:

  1. forced-provider or forced-route rules
  2. tool-aware rules
  3. complexity-tier rules
  4. generic default route

Request-side overrides that interact with routing

The runtime also understands request headers such as:

  • X-LunarGate-Provider
  • X-LunarGate-Model
  • X-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.provider exists in providers.
  • 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/auto technique when you want the gateway to decide tiers from one stable client model.