Model routing
modelRouting is an opt-in, deterministic policy that sends different orchestration calls to different models without touching your team or agent configuration. The usual shape is “flagship model plans, a cheap model runs the leaf work”:
import type { ModelRoutingPolicy } from '@open-multi-agent/core'
const modelRouting: ModelRoutingPolicy = { rules: [ // Flagship model decomposes the goal and synthesizes the final answer. { match: { phase: 'coordinator' }, route: { model: 'claude-opus-4-7' } }, { match: { phase: 'synthesis' }, route: { model: 'claude-opus-4-7' } }, // Everything that has no dependents runs on a cheap model. { match: { leaf: true }, route: { model: 'claude-haiku-4-5' } }, ],}
const result = await orchestrator.runTeam(team, goal, { modelRouting })runTasks takes the same option:
const result = await orchestrator.runTasks(team, tasks, { modelRouting })A rule is { match, route }. Rules are evaluated in array order and the first match wins, so put your most specific rules first. A call that matches no rule keeps the model it would have used anyway.
Match dimensions
Section titled “Match dimensions”A rule matches only when every field set in match agrees with the call. An empty match: {} matches everything (useful as a final catch-all).
| Field | Matches when | Available on |
|---|---|---|
phase | the call is for this orchestration phase (coordinator, synthesis, short-circuit, worker, delegated) | every call |
agent | the running agent’s name equals this | every call |
taskRole | the task’s role equals this | worker / delegated calls (role comes from an explicit task or the coordinator’s JSON) |
taskPriority | the task’s priority equals this (low | normal | high | critical) | worker / delegated calls |
leaf | the task has no dependents in the execution graph | worker / delegated calls |
hasDependencies | the task’s dependsOn is non-empty | worker / delegated calls |
taskRole, taskPriority, leaf, and hasDependencies are task-scoped, so they only ever match worker and delegated calls. The coordinator, synthesis, and short-circuit phases have no task attached, and a rule that sets a task field will simply never match them.
Route config
Section titled “Route config”The route is the override applied when the rule matches. Only model is required; the rest fall back to the agent’s config, then the orchestrator defaults.
| Field | Meaning |
|---|---|
model | Model for the matched call. Required. |
provider | Provider override (anthropic, openai, gemini, …). Defaults to the agent’s or default provider. |
baseURL | Base URL for OpenAI-compatible or self-hosted endpoints. |
apiKey | API key for the matched call. |
region | AWS region, for Bedrock routes. |
Which calls are routed
Section titled “Which calls are routed”A single policy covers all five orchestration phases:
coordinator— the goal-decomposition call inrunTeam.synthesis— the final answer-assembly call inrunTeam.worker— a task agent running an assigned task (runTeamandrunTasks).delegated— an agent reached throughdelegate_to_agent.short-circuit— the single-agent pathrunTeamtakes when a goal is simple enough to skip decomposition.
Opt-in and non-mutation
Section titled “Opt-in and non-mutation”Two guarantees keep routing safe to leave configured:
- Opt-in. Omit
modelRoutingand model selection is byte-identical to before, on bothrunTeamandrunTasks. - No mutation. A matched route builds an ephemeral effective config for that one call. Your
Team, itsAgentConfigs, and the orchestrator defaults are never modified, so the same team can run with different policies across calls.
Cost-tiered example
Section titled “Cost-tiered example”examples/patterns/cost-tiered-pipeline.ts runs the same four-stage pipeline twice (all-flagship vs. a tiered mix) and prints the per-model token and USD breakdown, so you can see what a routing policy saves before adopting one.