Presets
A preset is a named, reusable routing configuration you declare once in routeplane.yaml and invoke inline by putting @<name> in the model field. Where a model variant only narrows which providers are eligible, a preset can also substitute the base model, prepend a system prompt, and set default generation params — all behind a single short token.
Like a variant, the token lives in the model string itself, so it needs no body fields and no SDK — it works the same on the OpenAI, Anthropic, and Google surfaces. A request that uses @fast looks exactly like any other request; the preset is resolved server-side before routing.
Declaring a preset
Section titled “Declaring a preset”Presets are configuration, not API objects. Declare them under presets: in routeplane.yaml:
presets: fast: model: openai/gpt-5-mini system_prompt: Be terse. params: temperature: 0.1 routing: only: [openai]
variants: free: routing: require_tags: [free][!IMPORTANT] There is no management API and no console UI for presets. Earlier drafts of this page documented
POST /v1/namespaces/{nsid}/routing-presets, a full CRUD surface,routing_preset:read/routing_preset:writescopes, and a Settings → Routing Presets screen. None of that exists — no such route, scope, or handler is implemented. Presets live in the config file and change when you edit it.
Because presets are config, there is also no enable/disable toggle: remove or comment out a preset to retire it.
Invoking a preset
Section titled “Invoking a preset”Put @<name> where you would normally put a model id. The grammar is @<name>[:<variant>]:
model value |
Resolves to |
|---|---|
@fast |
The preset fast; its model: and overrides apply. |
@fast:free |
The preset fast, with the declared :free variant refining its routing filters. |
anthropic/claude-sonnet-4.6 |
Itself — a bare id with no leading @ is untouched. Presets are purely additive. |
There is no inline base-model form. A model of @fast/openai/gpt-5 is not a preset plus an override — the entire string after @ is read as the preset name, so it resolves as the unknown preset fast/openai/gpt-5 and fails 400. Because model is the request’s only preset token, a preset without its own model: cannot be invoked successfully.
curl http://127.0.0.1:4356/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "@fast", "messages": [{"role": "user", "content": "Summarize this in one line."}] }'What a preset can set
Section titled “What a preset can set”The config parser accepts every field as optional, but a usable preset needs model:. Invoking one without it fails 400 because no base model exists.
| Field | Effect |
|---|---|
model |
The base model to route to (e.g. openai/gpt-5-mini). Required for a preset that can be invoked. |
system_prompt |
A system prompt applied when the request doesn’t already set one. |
params |
Default generation params (temperature, max_tokens, top_p, …), merged in for keys the request didn’t set. |
routing.require_tags |
Only providers carrying all these tags stay eligible. |
routing.only |
A provider allow-list. Routing is restricted to these provider names. |
routing.ignore |
A provider deny-list. These providers are dropped from the chain. |
routing.sort |
Chain ordering — accepted values alphabetical, latency, cost. Only alphabetical does anything; see below. |
Ordering is not implemented
Section titled “Ordering is not implemented”routing.sort is parsed and accepted on a preset exactly as on a variant, and exactly as there, only one of its values has an effect:
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. No pricing-driven ordering exists; same fall-through.
There is no balanced value and no throughput value — neither is accepted, and nothing measures a blend of cost, latency, throughput, or uptime. Setting sort: latency or sort: cost in a preset is byte-identical to leaving it unset.
That is a design position, not a gap in the roadmap: only negative evidence — an Influence breaker reacting to a target that actually failed — is allowed to disturb the priority you declared. See Provider Selection for the full reasoning.
How preset values compose with the request
Section titled “How preset values compose with the request”A preset supplies the base model plus request defaults:
- Base model — the preset’s
model:is the base. The@name[:variant]token has no inline override form, so every usable preset must declare one. - Routing filters — a
:variantsuffix is applied after the preset, and the two are additive: the union of bothrequire_tagslists applies, and likewise foronlyandignore. A variant’ssortoverrides the preset’s — though nosortvalue changes routing today. - System prompt — the preset’s
system_promptis applied only if the request didn’t send one. An explicit system message always wins. - Params — preset params are merged key-by-key, and only for keys the request omitted. A
temperaturein the request body beats the preset’s.
Presets never change authorization
Section titled “Presets never change authorization”Resolution happens before policy enforcement, and a preset can only ever narrow what a key could already do — never widen it:
- Guardrail model allow/deny lists and BYOK rules judge the resolved base model, so a preset that substitutes
openai/gpt-5is checked exactly as if you had asked foropenai/gpt-5directly. A preset can’t smuggle a request past a model denylist. routing.only/routing.ignore/routing.require_tagscan only remove providers from the eligible set — they can never add a provider the request wasn’t already allowed to reach.- Billing is unchanged — you pay the selected provider’s rate for the resolved base model.
Errors
Section titled “Errors”| Condition | Result |
|---|---|
@name is not declared under presets: |
400 (distinct from an unknown-model 404) |
The preset has no model: |
400 |
| The resolved model name is empty | 400 |
| The routing filters leave no eligible providers | 404 — the model is unroutable, with a “did you mean” suggestion when one is close |
An unknown @preset is a hard 400, but an unknown :variant is not an error at all: it is simply not stripped, so the suffix stays in the model id and is looked up literally.
Presets vs. model variants
Section titled “Presets vs. model variants”The two features overlap deliberately — reach for whichever fits:
- A model variant (
openai/gpt-4o:free) carries arouting:block and nothing else: it narrows the eligible providers for a single request. - A preset (
@fast) captures a base model, a prompt, params, and provider filters once, so callers invoke a tested configuration by name instead of repeating it.
They compose — @fast:free applies the preset, then adds the variant’s filters on top.