-
Notifications
You must be signed in to change notification settings - Fork 0
Model Selection
Conductor picks a model the way a spot fleet picks an instance: you say what is acceptable, in what order, and what should happen when nothing acceptable is available. It then resolves that against what your box can actually run.
This matters because a config is portable and a model list is not. A pack that
hardcodes claude-opus-5 is broken for someone running codex; a pack that asks
for "a strong reviewer model" runs for both.
See also: Model discovery (where the available list comes from) and Runtimes.
| Where | What it says |
|---|---|
runtimes.<name>.models |
what this runtime may run, prefers, and defaults to |
models: (top level) |
named fleets — reusable acceptable-model lists |
model: on a step |
which fleet/model this particular step needs |
runtimes:
paseo:
models:
default: claude-opus-5 # optional
prefer: [claude-opus-5, gpt-5.6-sol] # ranking among acceptable models
allow: ["claude-opus-*", "gpt-5.6-*"] # a ceiling on what may ever runEvery field is optional, and so is the whole block. Omit it and the runtime is fully automatic: its roster is discovered, nothing is restricted, and its default is a bare launch.
-
default:— the model to pass when nothing else resolves one. Its absence is meaningful: no default means bare launch. -
prefer:— ranks acceptable models when a fleet offers a choice. The pack proposes; the consumer disposes.prefer:also decides the effective default whendefault:is unset and several models are available. -
allow:— filters the discovered roster. A model outside it can never run on this runtime, whatever a fleet asks for.
models:
reviewer:
any: ["claude-opus-*", "gpt-5.6-*", "gemini-3.*-pro"]
required: true # nothing available -> HARD ERROR (don't downgrade a reviewer)
summarizer:
any: ["claude-haiku-*", "*-mini", "gemini-2.5-flash*"]
required: false # nothing available -> bare launch-
any:is ordered, best-first. Literals and wildcards. -
required:decides the fallback posture.truerefuses to run rather than quietly using something weaker;falsefalls through to a bare launch.
Patterns are globbed against the discovered roster, so a wildcard can never conjure a model you cannot run.
- Forms:
claude-opus-*,gpt-5.6-*,"*-mini","*pro*", and the full wildcard"*". -
Quote any pattern that starts with
*. A bare*is a YAML alias indicator and will not parse as you meant. Write"*","*-mini","*pro*". - Literals keep their listed priority. A wildcard expands in place,
newest-first as the catalog reports, de-duped against literals already
listed. Then
prefer:re-ranks the result. - A pattern that matches nothing contributes nothing — resolution falls through
to the next entry, then to
required:/bare launch.
steps:
- { id: architecture, type: agent, model: reviewer }
- { id: security, type: agent, model: ["claude-opus-*", "gpt-5.6-*"] }
- { id: audit, type: agent, model: { any: ["claude-opus-*"], required: true } }
- { id: summarize, type: agent, model: gemini-2.5-flash }
- { id: lint, type: agent, model: "*" }-
string — map-key-wins: if it names a key in
models:it is a fleet reference, otherwise it is a model id or a wildcard. -
array — an inline fleet, sugar for
{ any: [...], required: false }. -
object — the full
{ any, required }.
A step may also pin where it runs with runtime: <name>; otherwise
resolution picks the runtime that offers the winning model.
For each model: reference, in order:
- a consumer override (a pack instance's
models:overlay) — use it; - otherwise intersect the fleet's
any:(wildcards expanded) with the union of the configured runtimes' allow-filtered rosters, rank byprefer:, and run the winner on the runtime that offers it; - otherwise, if
required: false, bare launch; - otherwise (
required: true, nothing matched) a hard error naming the fleet and acceptable-vs-available.
Ties between runtimes offering the same model are broken by default: true,
then by name.
A step with no model: at all takes the runtime's models.default:, then
its first available prefer: entry, then a bare launch.
Bare launch is a first-class outcome, not a failure. Conductor dispatches
with no --model and the runtime uses its own built-in default. It happens
when:
- the runtime declares no
models.default:and the step names no model; - a fleet with
required: falsematches nothing available; - the runtime cannot enumerate its models at all — it still works, you just
cannot
prefer:/allow:-filter it.
model: "*" is not bare launch. "*" resolves to a concrete model
through prefer:; bare launch passes no model at all.
Bare launch needs the runtime to have a default. It leans entirely on the
runtime's own built-in default model/provider — so a runtime that has none
rejects the launch. The paseo runtime is the common case: with no provider
configured, paseo run with no model fails, and conductor surfaces it as
paseo could not choose a model to run this agent — set
model:on the step, ormodels.default:on the paseo runtime.
(The raw paseo error is MISSING_PROVIDER; conductor translates it, since it has
no "providers" concept of its own.) Set model: on the step or models.default:
on the runtime to give it a concrete pick. This can't be caught at config-load
time — a bare launch is deliberately valid, and conductor can't know paseo's
provider state ahead of the run — but the bare-launch decision does emit a
notice naming this risk, visible in run logs and conductor validate.
An exact pin survives an un-enumerable runtime. If you write
model: some-private-model and no configured runtime can enumerate, conductor
passes it through. It is not the authority on what exists when it cannot see.
If a runtime did enumerate and does not offer the model, the ladder is
followed instead (and you get a notice saying so).
required: true is only enforced against a real answer. Conductor errors
when discovery ran and found nothing acceptable. When discovery is unavailable
— no network, no credentials — it has learned nothing, not that a model is
gone, so it does not fail the load. Hard-failing there would crash-loop an
auto-updating fleet on the first network blip.
Resolution picks the best roster match — but a provider can refuse a model at RUN time for reasons no roster shows: the API gates a newly released model behind a minimum client version ("400 … does not support this model; version X or newer is required"), a model is deprecated or removed, or a plan doesn't include it. Conductor classifies those replies as a model refusal (never a schema/output failure — the provider's own error line goes to the journal), marks the (model) pair unsupported, and immediately re-resolves down the fleet to the newest model that actually runs. A fleet where every candidate is refused fails the step with an error naming each refusal.
Marks expire after ~6 hours, so the fleet re-tries the newest model on its own
— update the client (e.g. claude CLI) and conductor climbs back to the top
candidate automatically; if nothing changed, one cheap probe re-marks it.
Nothing to configure: the fleet's order IS the fallback order.
Setup
The model
- Connectors
- Workflows
- Reuse
- Settings-and-Templating
- Packs
- Verbs
- Code-Steps
- Stores
- Runtimes
- Model-Selection
- Model-Discovery
- Steps
- Decide-Steps
- Grouping
- Memory
- Binary-Data
- Agent-Skill
- Policy
- Gates
- Teams
- Outcomes
- Cost-Accounting
- Secrets
- Hosts
- Isolation
- Trust-and-Isolation
Connectors
Operations
- One-Shot
- Callable-Service
- Runs
- Hand-offs
- Notifications
- Migration
- Controllers (legacy name → Runtimes)