Release note: v0.5, the decision model is first-class
The Router already answered most routing questions with one model family, the System One decision models. Its configuration still described an older system, in which each signal had its own small specialist model or an embedding-similarity trick. v0.5 makes the decision model first-class:
- Signals have three layers: the
systemoneprimitive for questions you write; eleven application signals, each compiled into onesystemonequestion; and seven deterministic heuristics. - Zero-shot everywhere. Every System One model answers every question it
supports zero-shot; trained adapters and calibrated thresholds are
per-family optimizations. Models differ in the question types they answer:
Decision 1.0, 2.0 and 3.0 return no spans, and vela3-fast answers no score
question. The configuration fails to load when a question resolves to a
deployment that cannot answer its type: a rule with
span_requiredorquestion_type_unsupported, and a judge, thesystemoneselector or an escalate check with a message naming the deployment and the types it answers. - Similarity has one
matchblock, with an optional embedding or BM25recallstage and an optional System Onejudgestage. Every judge has a hard cap on its candidates, and overflow is truncated, never an error. - Embeddings are for recall only, and start lazily. No decision path uses embeddings.
- One way to call System One: a standard request, or a batch of named standard requests.
- Algorithms are selectors and executors, and plugins only act, naming the signals they act on.
- The configuration has one structure, at schema
v0.5. - The response headers are a public set, with one gated
x-vsr-trace.
The new System One chapter explains the model behind all of it.
Breaking changes
This is one breaking release. Run vllm-sr config migrate once, then review
the notes it prints; the migration guide
lists every removed form, its load error and its rewrite.
- Config schema v0.5.
version: v0.3fails at load withinvalid_value, and migrate writesversion: v0.5. There is no top-levelrouting: routing lives in flatrecipes, model facts onproviders.models[], and default plugins inglobal.plugins. - Removed forms fail at load with one line, and migrate rewrites them.
The only alias is
decision, which loads assystemonewith a warning. - Removed signal types:
classifiers,preferences,embeddingsandkb(systemonequestions; migrate drops akbrule, which never matches, and names its question),reasksandtopic_continuity(turn_relation),eventsandstructure(keywords), andactions(asystemonequestion).global.model_catalog.modules.*is removed; a module's threshold is a rule'spredicate. - Removed algorithms:
prompt,router_dc,knn,kmeans,svmandmlpbecomesystemone;automix,hybridandlatency_awarebecomemulti_factor; the Looperconfidencealgorithm and the nativecascadebecomeescalate. The ML selectors' training pipeline and the Dashboard ML pipeline are removed. - Removed plugins:
tool_selection(thetoolsplugin'sselect),rag,memoryandcontext_compression(thecontextplugin),hallucinationandresponse_jailbreak(response_guard),shadow_dispatch(router_replay.shadow), and the cache aliases (response_cache). - Prompt compression is the
contextplugin'scompress.signals.global.model_catalog.modulesis removed:prompt_compressioncompresses the text the routing signals read fromglobal.pluginsor a recipe's plugins, never a decision's, and acompressblock with onlysignalsleaves the request whole. Migrate moves it. - The Router's embeddings API and
embeddings.multimodalare removed:global.services.api.embeddings,POST /api/v1/diagnostics/embeddings,/api/v1/diagnostics/similarityand/api/v1/diagnostics/similarity/batch, andglobal.model_catalog.embeddings.multimodal; migrate drops both. A decision about an image asks asystemonequestion that reads it (input.image). The model runtime's worker/v1/embeddingsstays. global.model_catalog.embeddings.defaultnames a deployment,embeddings.default: {deployment: vela-embedding}, the waysystem.decision_modeldoes. A bare name fails withinvalid_value, and migrate rewrites it.- The previous release's model-resource forms no longer load:
external[]models, binding objects (contract,adapter,head,mapping_path,operating_point), bindings for one rule,question_typesandserved_nameon deployments,classifyandhttpdeployments,candle,ortandopenvinoproviders, and a backend'sbase_url,api_key_env,auth_header,auth_prefixandextra_headers. A binding names its deployment, and a backend connects withendpoint,authandheaders; migrate rewrites them. A backend's inlineapi_keystill loads, but not together withauth.api_key_env. vllm-sr config migrateneeds a Router (a binary or an image), and--offlineis removed. It takes a configuration in the layout before config v0.3 to v0.5 in one run.- Looper names and endpoint:
global.integrations.looper.*.model_namesis refused at load; migrate writes the names as an entrypoint of a recipe it writes, the default recipe with only its decisions of that algorithm.global.integrations.looper.endpointis refused, and migrate drops it: the Router makes a Looper's model calls in process. The Dashboard drops the External Models card and its Looper card edits only the timeout and headers; the operator refusesexternal_models. - Model output facts are
capabilitiesandmodalities. The modelmodalitykeyword (ar,diffusion,omni), image routes onDIFFUSIONorBOTH, theimage_genplugin and audio embedding rules are removed; migrate rewrites what it can, drops a route no decision executes, and names the rest. - The System One batch form is
statesonly. A batch body carries named standard requests understatesand no top-levelstate,questions,imagesorvideos; a body that mixes the two forms, or an emptystates, is refused withinvalid_request. A batch's response has no top-levelanswers, and itsusagecounts every entry. See the API. - Model runtime contract 4.0.0. A Router of this release refuses a model runtime of any other contract; a Router and its runtime come from one release.
- One System One call per request stage, for remote deployments too. The
Router no longer splits a stage's questions to a remote System One API into
calls of at most eight texts. A System One API you run must accept a batch
with as many entries as the stage asks about, up to
judge.max_candidatesper judge. - The Kubernetes operator's
SemanticRouterresource follows the new structure.spec.config.routingis refused; its fields arespec.config.recipes(each recipe's signals, projections, decisions, model bindings and plugins), withspec.config.entrypoints,spec.config.pluginsandspec.config.models(the facts ofproviders.models[]). IntelligentRouteresources that use the removed signal types need a hand rewrite.vllm-sr config migraterewrites configuration files, not cluster resources: rewriteeventsandstructuresignals askeywordsrules, and the other removed types as the migration guide shows, before you upgrade the CRDs.- The response headers are a public set and one trace, with no aliases
for the old names (see Headers). A custom gateway that routes on
x-selected-modelmust matchx-vsr-route-model, and clients that read the matched-signal or decision headers read thex-vsr-tracemembers instead.
Behavior to expect
jailbreakandpiirules whose model gives no answer do not match. The defaulton_unknownisno_match, as it was: a guard whose decision model is down lets traffic through.on_unknown: matchis the explicit opt-in to fail closed, and migrate writes it for theon_error: blockof earlier releases. Text the model could not read still counts as an attack or as personal data.- Thresholds were re-fitted where the old value belonged to another
model. A rule without a
predicatetakes the answering model's calibrated threshold, or 0.5 with anuncalibrated_presetwarning, when it is a domain, user feedback, modality or PII rule or reads its preset's positive option (FACT_CHECK_NEEDED,unsafe,refusal,jailbreak). Any other rule without one matches on the model's own selection, and a hallucination rule on the model's own span thresholds. - A
response_guardthat can refuse or rewrite an answer holds a streamed one. The Router buffers the stream of a decision whose guard blocks or writes notes until the guard has read the whole answer, then sends its events or the refusal as the protocol's stream error. Time to first token grows only for those decisions, and a held answer is bounded at 64 MiB. A guard that only annotates or records keeps true streaming. - Span-needing rules must resolve to a span-capable deployment, or the
configuration fails with
span_required. The default decision model, vela3-fast, returns spans. - RAG relevance and tool selection are judged by the decision model, and
semantic cache hits are confirmed by its
same_requestquestion. The response cache's semantic match defaults to embedding recall (top_k: 3,min_similarity: 0.8) confirmed bysame_request(min_score: 0.8);mode: exactis unchanged. - Every
match.judgetakesmax_candidates. A choice judge reads up to 255 candidates (the default); a judge asked about each candidate reads 64 by default. More candidates never fail a request: the judge reads the first by recall rank, or the most recent without recall, records amatch.judge.truncatedspan event and countsvsr_match_judge_truncated_total. A tool catalog of more than 255 tools is cut to its first 255, or recalled withbm25. - RAG and memory sources may judge without recall, and a RAG source over
the in-memory vector store may recall with
bm25. Context compression judges each segment against the latest user message, by default the 64 thatbm25ranks first. - The embedding model is for recall only and starts lazily. The
pre-declared
vela-embeddingstarts only when the response cache, memory, a vector store or a RAG source recalls with it. A remoteopenai_embeddingsdeployment declarescapabilities.dimensionsandcapabilities.modalities, and the Router refuses at load a recall deployment that does not embed text or a dimension the index does not have. - Prompt cache affinity moved into
multi_factor. With thehybridselector gone, the weightedmulti_factorobjective favors the candidate that served a session's earlier turns (a later turn, carried history, aprevious_response_idor aprompt_cache_key), as a bounded tie-breaker. - Decisions declare their output with
output: {kind, action}. An image decision answers a plain-text request with a generated image on the Responses API: the Router adds the hostedimage_generationoperation and selects only a model that can generate it. The load fails withoutput_capability_missingwhen a model does not declare what the output needs, and withoutput_not_executablefor what this release cannot execute (image edits, audio, video) or an API that cannot carry it. A Chat Completions or Messages client of an image decision is refused withunsupported_capability. - The Router has an Images API entry.
POST /v1/images/generationsis routed on its prompt and answered only by a decision whose output is an image; any other request there gets400 image_route_requiredbefore a model is called.api_format: imagesreaches OpenAI-compatible image-generation backends. systemonequestions withinput.image: trueroute on what a request's images show, answered by a decision model that reads images.- Identity headers are trusted only where configured. A standalone Router
drops client-sent identity headers unless a listener sets
identity.trust_headers: true; policies that read the gateway's identity (authz signals, rate limits by user or group, per-user keys, andglobal.router.headers.debug: authenticated) fail to load in standalone mode without an identity source. Debug headers reach only the callersglobal.router.headers.debugallows. - Delegated credentials are an auth mode of every upstream. An upstream's
authis a static key (api_key_env,api_key_file, or a backend'sapi_key),credential: delegated, or neither, for provider backends, decision-model and remote embedding deployments,escalatejudges andapi_format: systemonetargets alike.global.services.authz.delegated_credentialnames the header the authenticator writes the user's credential to (defaultx-vsr-delegated-credential). A call made for a client request carries that request's credential, or the request is answered401before the call; a call the Router makes on its own behalf never does, and a delegated upstream refuses it.backend_refs[].trusted_gatewayandglobal.services.authz.gateway_credentialare removed: the Router and migrate refuse them and name the new form. See Security Hardening.
Headers
Every response of the routing pipeline carries one public set, on cache hits,
fast responses, blocks, Looper answers and routing errors as well as upstream
answers: x-vsr-schema-version (now 3), x-vsr-config-version,
x-vsr-response-path, x-vsr-selected-recipe, x-vsr-selected-decision,
x-vsr-selected-model, x-vsr-replay-id, x-vsr-warnings and the configured
cost (x-vsr-cost, x-vsr-cost-currency). Everything else the Router reports
rides in x-vsr-trace, an RFC 8941 dictionary of at most 4 KB, and only for a
caller global.router.headers.debug allows: past 4 KB the lowest-priority
members are left out and truncated ends it. The renames keep no aliases.
| Old | New |
|---|---|
x-vsr-matched-*, x-vsr-context-token-count | The trace's signals member |
| Decision details, retention, prompt cache, Looper, protocol and effective-token headers, cache similarity | Trace members (ranking, confidence, algorithm, cache_similarity, ...) |
x-vsr-selected-modality | The trace's modality member, the selected decision's output kinds |
x-vsr-protocol-warnings, x-vsr-response-warnings | x-vsr-warnings, an RFC 8941 list of codes |
x-vsr-cache-hit, x-vsr-fast-response | x-vsr-response-path: cache or fast_response |
x-selected-model (the Envoy route key) | x-vsr-route-model (internal); custom gateway routes must match it |
x-disable-router-memory | x-vsr-memory: off |
x-sr-bench-* request headers, and the benchmark receipt headers | x-vsr-bench-*; the receipts are trace members, so sr-bench needs debug access |
| Retired Looper hop headers; the unused KV transfer status | Removed |
x-session-id, x-claude-code-session-id | x-vsr-session-id; global.router.headers.session_id_headers lists the client names also accepted, these two by default |
The Router drops its other x-vsr-* headers when a client sends them, and
every x-vsr-* header a provider returns. The one it reads from the
authenticator is x-vsr-delegated-credential, trusted like the identity
headers when global.services.authz.delegated_credential is enabled; no
upstream receives the header, and only a delegated upstream its value.
The response headers reference lists every header the registry defines.
Removed test profiles
The end-to-end profiles of removed backends and of the Vela 1.0 encoders,
which cannot answer System One questions, are deleted:
local-classifier-backend, category-remote-backend,
complexity-remote-backend and preference-external-routing (remote
classifier backends); ml-model-selection (the trained selectors);
multimodal-routing (embedding image anchors) and vela-omni (audio
routing); vela-halu and vela-shield (Vela 1.0 Halu and Shield). The
baseline profiles run on a deterministic fake System One backend, and image
routing is tested again on systemone input.image.
Measurements
The measurements behind the zero-shot presets (harm categories, RAG and cache judges, tool selection, the turn relation and complexity questions, the trained-ID fix and output intent) run on public data and land in the calibration tables and this note as their results are published; they are not gates for the release.
Known limits
- Multimodal output is phase 1: image generation, and text with an image
from one omni model. Image edits, speech and audio (phase 2) and video
(phase 3) fail at load with
output_not_executableuntil their codecs land. - No decision model reads audio yet, so
input.audiois refused on every deployment;input.videoneeds a model that declares it. - Remote decision models have no calibrated thresholds; their presets use
0.5 until a rule writes its own
predicate. - Calibrated thresholds exist only for vela3-fast and Vela 2.0; the
other families use 0.5 with the
uncalibrated_presetwarning.