Model Variants
A model variant is a named routing modifier you declare once in routeplane.yaml and then apply inline by appending :<name> to a model id. It refines which providers are eligible for that one request.
The suffix is part of the model string itself, so it needs no body fields and no SDK — it works the same on the OpenAI, Anthropic, and Google surfaces.
[!IMPORTANT] Variants are operator-defined. Routeplane ships no built-in variant names. A suffix only means something if you declared it under
variants:in your config; there is no:cost,:latency,:throughput,:balanced, or:discountunless you wrote one.
Declaring a variant
Section titled “Declaring a variant”variants: free: routing: require_tags: [free] local: routing: only: [ollama, lmstudio] no-thirdparty: routing: ignore: [some-reseller]A variant carries a routing: block and nothing else — it cannot substitute the base model, set a system prompt, or set params. (A preset can.)
routing key |
Effect |
|---|---|
require_tags |
Only providers carrying all these tags stay eligible. |
only |
Restrict the chain to exactly these provider names. |
ignore |
Drop these providers from the chain. |
sort |
Chain ordering. Accepted values are alphabetical, latency, cost — but see Ordering is not implemented below. |
All three filters are additive when a preset and a variant are combined: the union of both require_tags lists applies, and so on.
Quick example
Section titled “Quick example”curl http://127.0.0.1:4356/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "anthropic/claude-sonnet-4.6:free", "messages": [{"role": "user", "content": "Translate to French: Hello."}] }'The suffix behaves identically on /v1/messages (Anthropic) and /v1beta/models/{model}:generateContent (Google) — it travels with the model id.
Ordering is not implemented
Section titled “Ordering is not implemented”routing.sort is parsed and accepted, but only one of its values does anything:
alphabetical— the default, and the only order Routeplane actually produces.latency— not implemented. No metrics source exists. It falls through to the same ordering asalphabetical.cost— not implemented. Same: no pricing-driven ordering exists. It falls through to the same ordering asalphabetical.
Setting sort: latency or sort: cost today changes nothing about which provider is tried first. It is byte-identical to leaving it unset. Nothing in Routeplane measures time-to-first-token, output tokens per second, or per-request cost for the purpose of ranking providers, and there is no rolling telemetry window feeding routing.
This is not an oversight waiting on a sprint — see Why there is no scoring.
Unknown suffixes are left alone
Section titled “Unknown suffixes are left alone”A trailing :segment is stripped as a variant only if it names a variant you declared. Anything else is left in the model id verbatim:
openai:gpt-5stays intact, so theprovider:modelrouting form still works.openai/gpt-4o:fast, with nofastvariant declared, is looked up as the literal model idopenai/gpt-4o:fast— which normally means a404, not a silent reroute.
An unknown @preset, by contrast, is a hard 400.
Why there is no scoring
Section titled “Why there is no scoring”Routeplane deliberately does not rank providers by predicted cost, latency, or throughput. The ordering you get is the priority you declared — provider class and explicit priority, described in Provider Selection — and the only thing permitted to disturb it is negative evidence: the Influence circuit breaker deprioritizes a target that has actually just failed, and restores it after a successful probe.
Filtering is an observation about eligibility; scoring is a prediction about the future. A latency ranker would reshuffle a deterministic chain on the strength of a belief, fight the operator’s declared priority, and make two identical requests route differently. SortOrder::Latency exists in the source as a dead stub and stays dead on purpose.
So: variants filter, they do not score.
Variants never change authorization
Section titled “Variants never change authorization”A variant can only ever narrow the eligible provider set — never widen it:
- Guardrail model allowlists/denylists judge the resolved base model, so
anthropic/claude-sonnet-4.6:freeis judged exactly asanthropic/claude-sonnet-4.6. A suffix can never bypass a policy. only/ignore/require_tagsremove providers from the chain; they cannot add one the request could not already reach.- Billing is unchanged — you pay the selected provider’s rate for the base model.
Relationship to provider.sort
Section titled “Relationship to provider.sort”There is no provider request-body field. Older drafts of these docs described a provider.sort body knob; it was never implemented and is not accepted. Routing preferences are declared in routeplane.yaml and applied inline via a :variant or @preset token in the model string.