Skip to content

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.

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:write scopes, 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.

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.

Terminal window
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."}]
}'

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.

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.
  • latencynot implemented. No metrics source exists; it falls through to the same ordering as alphabetical.
  • costnot 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 :variant suffix is applied after the preset, and the two are additive: the union of both require_tags lists applies, and likewise for only and ignore. A variant’s sort overrides the preset’s — though no sort value changes routing today.
  • System prompt — the preset’s system_prompt is 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 temperature in the request body beats the preset’s.

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-5 is checked exactly as if you had asked for openai/gpt-5 directly. A preset can’t smuggle a request past a model denylist.
  • routing.only / routing.ignore / routing.require_tags can 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.
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.

The two features overlap deliberately — reach for whichever fits:

  • A model variant (openai/gpt-4o:free) carries a routing: 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.