Model Routing
Why use model routing
Section titled “Why use model routing”Model routing chooses a model and reasoning effort for each workflow run based on its task. It can use less expensive models for routine work and stronger models for harder tasks, without pinning a model that may be retired. A fixed model is useful when a task needs a specific capability or more consistent behavior.
Get started
Section titled “Get started”Set model: router to route the main agent:
---on: issues: types: [opened]permissions: contents: read issues: read copilot-requests: writeengine: copilotmodel: routersafe-outputs: add-comment:---
Find the likely cause of issue #${{ github.event.issue.number }} and comment with a suggested fix.The router classifies each task as economy, balanced, or robust, then chooses the cheapest model and effort likely to succeed at that level. If classification is unavailable or unknown, it uses balanced. The example uses GitHub Copilot inference and needs Copilot authentication. Routing also needs the AWF firewall, enabled by default.
Router values can also be set in engine.model or an imported workflow. engine.model takes precedence over the top-level model, and a workflow’s own model takes precedence over an imported one.
Setting the level yourself
Section titled “Setting the level yourself”Use a router level when the workflow’s tasks have a known risk or complexity:
| Value | Success target | Use it for |
|---|---|---|
router/economy | Lowest | Routine, low-risk tasks such as triage, labeling, and summaries. |
router/balanced | Medium | General-purpose work. |
router/robust | Highest | Tasks where a failed run is costly, such as opening pull requests. |
These are estimated success rates from benchmarks, not guarantees. The router may choose below the target if no candidate meets it or if a choice just below it costs much less. model: router chooses a level for each run instead.
Choosing the level at run time
Section titled “Choosing the level at run time”The model can be an expression that resolves to a router value when the run starts, so a dispatch input, a pre-activation step, or an experiment can choose the level:
---on: workflow_dispatch: inputs: level: type: stringengine: copilotmodel: ${{ inputs.level || 'router/balanced' }}---The activation job resolves the expression once, and later jobs use its result, so the expression can read pre-activation step outputs. A router value routes the run at that level; any other value, such as a model name, runs that model without routing, as before. An empty value uses the model variables and the engine’s default without routing, so write a default into the expression if you want one. A value that looks like a router value but isn’t one, such as router/fast, Router/Economy, or router/economy?effort=high, stops the run before the agent starts.
This applies when the whole model is one expression, such as ${{ inputs.level }} but not ${{ inputs.model }}?effort=high, that reads only inputs, vars, run-wide github properties such as github.event or github.run_attempt, experiments, or pre-activation step outputs, and the workflow would otherwise be eligible for default routing. Other expressions, such as ones that read an agent step’s outputs or github.job, or call hashFiles(), run without routing as before. Pi isn’t supported yet. Threat detection never receives the router value: when the run is routed, it uses its own default model. Router values in the GH_AW_MODEL_AGENT_<ENGINE> and GH_AW_DEFAULT_MODEL_<ENGINE> variables are still not accepted.
Default routing for workflows without a model
Section titled “Default routing for workflows without a model”Eligible workflows without a model already behave like model: router. For Copilot, Claude, and Codex, a nonempty GH_AW_MODEL_AGENT_<ENGINE> or GH_AW_DEFAULT_MODEL_<ENGINE> repository or organization variable overrides this default at run time; the agent variable wins. Pi has no such variables. Set engine.model-routing: false to opt out without pinning a model, or set a specific model.
Default routing eligibility
- The engine is
copilot,claude,codex, orpi, using GitHub Copilot inference. Claude and Codex requiremodel-provider: github;copilot-requests: writealone doesn’t switch providers. - Neither the workflow nor its imports set a top-level
model,engine.model(including aliases,auto, and expressions), explicit routing settings,engine.fallback-models, or anengine.envmodel variable such asCOPILOT_MODELorGH_AW_DEFAULT_MODEL_COPILOT. - The workflow uses a routing-capable AWF version and images and doesn’t otherwise customize model selection or inference, for example through a custom engine command, model or effort arguments in
engine.args, a custom agent, sub-agent models, a custom API target, or custom sandbox images.
The default applies only to the main agent, not threat detection, evals, or sub-agents. If eligibility isn’t met or is uncertain, compilation preserves the previous behavior. When it applies, compilation prints an informational notice. Explicit router values aren’t bypassed by model variables.
Limiting which models it can pick
Section titled “Limiting which models it can pick”By default, the router considers supported models available to your organization’s Copilot plan or native provider account. A model policy limits every model request in the run, including sub-agents and built-in agents; the firewall rejects requests outside it. Use model names or * patterns in models.allowed and models.blocked.
For example, route among GPT models only:
engine: copilotmodel: routermodels: allowed: ["gpt-*"]Or exclude Opus models while targeting robust results:
engine: copilotmodel: router/robustmodels: blocked: ["claude-opus-*"]The router only considers models with benchmark data in the AWF release pinned by gh-aw. Run gh aw upgrade when gh-aw updates AWF to make newly supported models candidates. Retired models and models unavailable to your account are skipped. Patterns keep the policy open to new models; a shortlist instead restricts the main agent’s router choices.
Engines and providers
Section titled “Engines and providers”Claude and Codex route either on their native provider or on GitHub Copilot, but only within their own model family:
| Engine | Provider | Setting | Candidate models |
|---|---|---|---|
copilot | GitHub Copilot | None | Any |
pi | GitHub Copilot | None | Any |
claude | Anthropic (ANTHROPIC_API_KEY) | None, or model-provider: anthropic | claude-* only |
claude | GitHub Copilot | model-provider: github | claude-* only |
codex | OpenAI (CODEX_API_KEY or OPENAI_API_KEY) | None, or model-provider: openai | gpt-* only |
codex | GitHub Copilot | model-provider: github | gpt-* only |
For native Anthropic routing:
engine: claudemodel: router/economyFor native OpenAI routing:
engine: codexmodel: routerFor Claude on GitHub Copilot:
engine: id: claude model-provider: githubmodel: router/balancedNative Anthropic and OpenAI routing
Section titled “Native Anthropic and OpenAI routing”Native inference uses the workflow’s provider API key in the firewall’s API proxy and is billed by that provider, not the Copilot plan. AWF still accounts for the run’s AI Credits. Native model names follow the provider’s naming, such as Anthropic’s claude-sonnet-5-5 rather than Copilot’s claude-sonnet-5.5.
Native routing requires:
- The provider’s own HTTPS endpoint (
https://api.anthropic.comorhttps://api.openai.com), without a customengine.api-target, base URL,sandbox.agent.targetsgateway, orengine.authfor another cloud such as Azure OpenAI. - A provider model list that includes the candidate, firewall metadata for its API and supported reasoning efforts, and router benchmark data. The policy and any shortlist must also allow it. Models without the required metadata are skipped.
- The AWF firewall and a routing-capable version; if
sandbox.agent.versionis pinned, use v0.28.33 or later. Organizations with approved registries can replace the router image usingsandbox.agent.images.router; see custom infrastructure images.
Routing doesn’t switch providers or credentials, and fails rather than falling back to another provider. Native routing must be explicitly configured; the no-model default applies only to GitHub Copilot inference.
Sub-agents
Section titled “Sub-agents”Routing chooses the main agent’s model only. Declared sub-agents use their declared models, and built-in agents such as explore use their defaults, subject to the model policy. The compiler warns if a declared sub-agent model is outside the policy. gh aw audit reports sub-agent requests as deviations from the selected route, which is expected.
| Engine | Sub-agent models |
|---|---|
| Copilot in CLI mode (default) | Same family as the main agent’s selected model, such as gpt-* or claude-* |
Copilot in SDK mode (engine.copilot-sdk: true) | Any family |
| pi | Any family |
| Claude, Codex | The engine’s own family |
In Copilot CLI mode, a sub-agent from a different family fails (copilot-cli#5103), and the run logs a startup warning. Restrict candidates to the sub-agents’ family or use SDK mode. Here SDK mode allows a Claude sub-agent regardless of the main agent’s selected family:
---on: workflow_dispatchpermissions: contents: read copilot-requests: writeengine: id: copilot copilot-sdk: truemodel: router---
Review the open issues and ask the `triager` agent to suggest a label for each one.
## agent: `triager`---description: Suggests one label for an issuemodel: claude-haiku-4.5---Reply with the single best label for the issue you are given.Check which model was used
Section titled “Check which model was used”The agent step’s log shows the selection, for example:
inference routing: mode=awf-routed model=gpt-5.6-sol effort=highgh aw audit <run-id> shows the requested value, such as router/economy, next to the model the router picked, and includes a Model Routing section with the task classification, the selected model and effort, the ranked alternatives, and what the classifier, the selected model, and any other models cost. gh aw logs aggregates routing across runs, showing which model each kind of task received and what it cost. See Audit and Cost Management.
Advanced: engine model routing
Section titled “Advanced: engine model routing”Use engine.model-routing for a candidate shortlist or a speed-oriented goal. The following router values are equivalent to blocks with goal: cost:
| Router value | Equivalent block |
|---|---|
router | model-routing: {goal: cost, mode: auto} |
router/economy | model-routing: {goal: cost, mode: economy} |
router/balanced | model-routing: {goal: cost, mode: balanced} |
router/robust | model-routing: {goal: cost, mode: robust} |
Both goal and mode are required. goal: cost-speed favors the fastest model and effort meeting the target, even when a slower choice costs less:
engine: id: copilot model-routing: goal: cost-speed mode: autoAn explicit block overrides a fixed engine.model or effort setting and the compiler warns. Remove the fixed setting when enabling routing.
A shortlist of models
Section titled “A shortlist of models”allowed-models limits the router’s main-agent candidates, not other requests. It accepts exact model names and model aliases, but not patterns:
engine: id: copilot model-routing: goal: cost mode: auto allowed-models: [gpt-5.4-mini, gpt-5.6-sol, small, sonnet]The compiler expands aliases to patterns for the routing provider and narrows them to the engine’s family. Exact names stay exact, including a bundled model ID that is also an alias; a matching provider prefix also makes a name exact. An incompatible provider, unknown entry, or alias with no models in the engine’s family fails compilation. Exact names aren’t checked against current availability. If no shortlisted model is available at run time, routing fails. See the model alias specification for expansion and provider rules.
The model policy also narrows a shortlist; it cannot be overridden by one:
models: allowed: ["gpt-*"]engine: id: copilot model-routing: goal: cost mode: balanced allowed-models: [gpt-5.4-mini, gpt-5.6-sol, claude-sonnet-5]The compiler warns that claude-sonnet-5 is excluded; if every entry is excluded, compilation fails. Alias patterns wholly excluded by the policy are dropped or narrowed, while partial overlaps remain subject to the firewall’s per-model policy. Rules match case-insensitively; GitHub Copilot provider prefixes are equivalent, while native routing uses bare rules or its own provider’s prefix. Expressions such as ${{ vars.ROUTING_MODEL }} are allowed in a shortlist only without a model policy; a literal prefix for native routing must name its native provider.
Shared routing settings
Section titled “Shared routing settings”A shared file can set a router value or a routing block. For example, .github/workflows/shared/routing.md can contain:
---engine: id: copilot model-routing: goal: cost mode: auto allowed-models: [gpt-5.4-mini, gpt-5.6-sol]---An importing workflow uses those settings. It must omit engine or use its object form, such as engine: { id: copilot }. If it also enables routing for the same engine, its goal and mode apply. If any routing configuration for that engine omits allowed-models, there is no shortlist; otherwise the lists are combined. For a shared router value instead, set model: router/economy in the shared file; importing workflows can override it with their own model.
Router value restrictions
Section titled “Router value restrictions”Router values are not model names. Unknown or differently cased values such as router/fast or router/Economy, and parameters such as router/economy?effort=high, are rejected. The router chooses effort; pin a model to set it. Router values can’t appear in model policies, fallbacks, model aliases, sub-agents, threat detection, or evals, and can’t be combined with a routing block or engine.fallback-models. Write the value in the workflow, or in a model expression as described in Choosing the level at run time. Otherwise, a variable or expression that resolves to a router value at run time stops the run before the agent starts.
Known limitations
Section titled “Known limitations”The classifier can select a different model or effort for the same task on another run. Routing doesn’t consider the run’s AI Credits budget (max-ai-credits, 1,000 by default); robust runs can consume more credits. See Cap AI Credits per Run.
Before the main agent starts, a small candidate model classifies the interpolated prompt as data, and the router ranks candidates and efforts using benchmark results. Classification typically costs a small fraction of an AI Credit. Routing stays within the candidates regardless of task text and fails if it can’t choose a model; it doesn’t fall back to a fixed model.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause and fix |
|---|---|
| Compilation rejects the provider or native endpoint | Use GitHub Copilot inference, or the engine’s own native provider and HTTPS endpoint; remove custom targets. See Engines and providers. |
| Compilation rejects a shortlist entry or excludes all candidates | Use exact names or aliases from the error, not patterns; check the engine family and policy. See A shortlist of models. |
| Compilation reports multiple engine fields after an import | Omit engine in the importing workflow or use engine: { id: copilot }. |
The agent step fails and gh aw audit reports a routing failure | No candidate may be available, the router may have failed (exit code 78), or the engine may not support the selection. Check the audit’s Model Routing details. |
| A sub-agent fails with HTTP 400 in Copilot CLI mode | Its family differs from the selected main model. See Sub-agents. |
gh aw audit reports deviated requests | Sub-agents or built-in agents may use another model; this is expected and isn’t a policy violation. |
| A workflow without a model is routed and you want a fixed model | Set a model, an applicable model variable, or engine.model-routing: false; see default routing. |
gh aw audit reports degraded classification | Classification failed; model: router uses balanced in this case. |