Skip to content

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 :discount unless you wrote one.

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.

Terminal window
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.

routing.sort is parsed and accepted, but only one of its values does anything:

  • alphabetical — the default, and the only order Routeplane actually produces.
  • latencynot implemented. No metrics source exists. It falls through to the same ordering as alphabetical.
  • costnot implemented. Same: no pricing-driven ordering exists. It falls through to the same ordering as alphabetical.

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.

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-5 stays intact, so the provider:model routing form still works.
  • openai/gpt-4o:fast, with no fast variant declared, is looked up as the literal model id openai/gpt-4o:fast — which normally means a 404, not a silent reroute.

An unknown @preset, by contrast, is a hard 400.

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.

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:free is judged exactly as anthropic/claude-sonnet-4.6. A suffix can never bypass a policy.
  • only / ignore / require_tags remove 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.

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.