VSR Routing Headers
This page documents the public x-vsr-* headers emitted by vLLM Semantic Router for routing observability, replay correlation, and debugging.
Emission Rules
The router splits headers across two surfaces:
- Default surface — rides on every non-cache-hit response: the keystone headers (
x-vsr-schema-version,x-vsr-response-path), the final routing facts (x-vsr-selected-recipe,x-vsr-selected-decision,x-vsr-selected-confidence,x-vsr-selected-algorithm,x-vsr-selected-model) and thex-vsr-replay-identry point. The selected recipe also rides on recipe-scoped immediate responses such as cache hits. The client/upstream protocol markers ride here only on cross-protocol handling, andx-vsr-protocol-warningsonly when warnings exist. - Debug surface — the intermediate decision/classification details, the matched-signal headers, the tool-selection observability headers and the retention directive headers (
x-vsr-retention-*) are demoted off the default surface (#2205). They are emitted inline only when the request setsx-vsr-debug: true, and remain recoverable from the replay record viax-vsr-replay-id. The retention directive's runtime effects (cache write skip, TTL override, model-switch stay) are applied internally and unaffected by the header demotion.
Decision and matched-signal headers additionally require all of the following:
- The upstream response is successful (
2xx). - The response was not served from semantic cache.
- The router evaluated a routing decision or signal for the request.
Cache-hit responses can emit cache headers, but they do not re-run routing and therefore do not attach fresh matched-signal headers.
Request Headers
| Header | Direction | Description |
|---|---|---|
x-session-id | request | Stable client-provided session identifier for Chat Completions. Router Learning protection uses this, together with the configured conversation identity, to reason about stay-vs-switch decisions across turns. |
x-conversation-id | request | Stable client-provided conversation or agent-run identifier. Router Learning protection uses this by default when scope: conversation. |
x-vsr-skip-processing | request | Opts a request out of router processing when global.router.skip_processing.enabled is enabled. Use value true. |
x-vsr-debug | request | Opts the request into verbose/debug response headers — headers the contract otherwise omits or demotes to replay are emitted inline for that request. Use value true. |
Protocol And Replay Headers
| Header | Description |
|---|---|
x-vsr-client-protocol | Inbound protocol shape seen by the router, for example openai or anthropic. Emitted only on cross-protocol handling (client protocol differs from upstream), or when x-vsr-debug is set. |
x-vsr-upstream-protocol | Protocol shape sent to the selected upstream backend. Emitted only on cross-protocol handling, or when x-vsr-debug is set. |
x-vsr-protocol-warnings | Comma-separated protocol translation warnings encoded as severity;reason;field. Emitted only when warnings exist. |
x-vsr-replay-id | Opaque router replay record identifier for correlating a response with replay/Insights data. |
Response Warnings
| Header | Description |
|---|---|
x-vsr-response-warnings | Comma-separated response-quality warning codes for the completion, in fixed order: hallucination, unverified_factual, response_jailbreak. Emitted only when at least one applies. |
This header consolidates the v0.3 x-vsr-hallucination-detected, x-vsr-unverified-factual-response, and x-vsr-response-jailbreak-detected headers. The per-warning detail (hallucination spans, jailbreak type/confidence, fact-check verification context) is kept in the replay record rather than the response headers, recoverable via x-vsr-replay-id; the individual warning and detail headers are removed from the v0.4 contract.
Decision Headers
The final routing facts ride on the default surface; the intermediate details (including the Router Learning observability headers) are demoted to x-vsr-debug (#2205).
| Header | Surface | Description | Example |
|---|---|---|---|
x-vsr-selected-recipe | default | Routing isolation scope selected by an entrypoint or auto/looper alias. Omitted for concrete backend passthrough. | privacy-first |
x-vsr-selected-decision | default | Final decision selected by the decision engine. | formal_math_proof |
x-vsr-selected-confidence | default | Confidence score for the selected decision. | 0.9100 |
x-vsr-selected-algorithm | default | Model-selection algorithm used after the decision matched. | router_dc |
x-vsr-selected-model | default | Logical model alias selected by the router. | qwen/qwen3.5-rocm |
x-vsr-selected-category | debug | Domain/category classifier result when domain routing runs. | math |
x-vsr-selected-reasoning | debug | Reasoning mode selected for the request. | on |
x-vsr-selected-modality | debug | Modality result and optional method. | AR;classifier |
x-vsr-session-phase | debug | Protection trace phase from the selected routing policy. Detailed learning actions are exposed through the x-vsr-learning-* headers and Router Replay. | user_turn, tool_loop, provider_state |
x-vsr-learning-methods | debug | Router Learning methods summarized by this response. Full score/cache details live in Router Replay. | adaptation,protection |
x-vsr-learning-actions | debug | Method-keyed compact learning actions. | adaptation=propose_switch,protection=allow_switch |
x-vsr-learning-scopes | debug | Method-keyed identity scopes used by learning. | protection=conversation |
x-vsr-learning-reasons | debug | Method-keyed machine-readable reasons for actions. | adaptation=sampled_win,protection=switch_allowed |
x-vsr-injected-system-prompt | debug | Whether a system-prompt plugin injected text into the request. | true |
For UI display guidance, translate x-vsr-learning-actions into user-facing
phrases such as tool/protocol pinned, model switched, or learning bypassed.
Fresh conversation or session-start diagnostics are usually useful only in debug
views, where they should be shown as neutral status text rather than a primary
route state.
Matched Signal Headers
Matched signal headers contain comma-separated rule names. They are demoted to the x-vsr-debug surface (#2205) and omitted when the corresponding signal family did not match.
| Header | Signal family |
|---|---|
x-vsr-matched-keywords | keyword |
x-vsr-matched-embeddings | embedding |
x-vsr-matched-domains | domain |
x-vsr-matched-fact-check | fact_check |
x-vsr-matched-user-feedback | user_feedback |
x-vsr-matched-reask | reask |
x-vsr-matched-preference | preference |
x-vsr-matched-language | language |
x-vsr-matched-context | context |
x-vsr-context-token-count | Context token estimate used by context |
x-vsr-matched-structure | structure |
x-vsr-matched-complexity | complexity |
x-vsr-matched-modality | modality |
x-vsr-matched-authz | authz |
x-vsr-matched-jailbreak | jailbreak |
x-vsr-matched-pii | pii |
x-vsr-matched-kb | kb |
x-vsr-matched-conversation | conversation |
x-vsr-matched-event | event |
Projection Headers
| Header | Description |
|---|---|
x-vsr-matched-projections | Comma-separated projection mapping outputs that matched the request. |
Projection scores and full projection traces are stored in router replay records rather than expanded into response headers. Use x-vsr-replay-id to inspect those details in the dashboard or replay APIs.
Cache And Plugin Headers
x-vsr-cache-hit and x-vsr-fast-response mark how an immediate response was produced and ride on the default surface. The cache-similarity and tool-selection observability headers are demoted to the x-vsr-debug surface (#2205).
| Header | Surface | Description |
|---|---|---|
x-vsr-cache-hit | default | Response came from semantic cache. |
x-vsr-fast-response | default | Response was generated by the fast_response plugin without an upstream model call. |
x-vsr-cache-similarity | debug | Similarity score from the semantic-cache lookup. |
x-vsr-tools-strategy | debug | Semantic tool-selection retriever strategy used for the request. |
x-vsr-tools-confidence | debug | Highest tool-selection retriever similarity score. |
x-vsr-tools-latency-ms | debug | Tool-selection retriever latency in milliseconds. |
Example Response
Default surface — keystone headers, final routing facts and the replay-id entry point:
HTTP/1.1 200 OK
Content-Type: application/json
x-vsr-schema-version: 2
x-vsr-response-path: upstream
x-vsr-selected-recipe: default
x-vsr-selected-decision: critical_event_tool_session_route
x-vsr-selected-confidence: 1.0000
x-vsr-selected-algorithm: static
x-vsr-selected-model: anthropic/claude-opus-4.6
x-vsr-replay-id: replay_01J...
With x-vsr-debug: true on the request, the demoted intermediate details and matched signals are emitted inline as well:
HTTP/1.1 200 OK
Content-Type: application/json
x-vsr-schema-version: 2
x-vsr-response-path: upstream
x-vsr-selected-recipe: default
x-vsr-selected-decision: critical_event_tool_session_route
x-vsr-selected-confidence: 1.0000
x-vsr-selected-algorithm: static
x-vsr-selected-model: anthropic/claude-opus-4.6
x-vsr-session-phase: tool_loop
x-vsr-matched-conversation: active_tool_use
x-vsr-matched-event: critical_payment_event
x-vsr-matched-projections: verification_required
x-vsr-replay-id: replay_01J...
Notes
x-vsr-matched-projectionsis the v0.3 projection header. The old singular form is not part of the public v0.3 contract.- Recipe names scope local signal, projection, decision, cache, replay, metric, and learning/session identities. Use
x-vsr-selected-recipetogether with the local decision/signal names when correlating a response with Insights or metrics. eventis the public signal type used by decisions and DSL. Canonical YAML stores event rules underrouting.signals.events, matching other plural signal containers.- Router Learning uses router-owned online state internally. Users enable online model-choice learning through
global.router.learning.adaptation, enable stability protection throughglobal.router.learning.protection, pass stable identity headers, and optionally setrouting.decisions[].adaptations.mode, component modes, oradaptations.adaptation.candidate_set.scope: conversationprotects onex-conversation-id;scope: sessionprotects the broaderx-session-id. The oldrouting.decisions[].algorithm.session_awareshape is not part of the public contract.