This guide covers model routing and the route policy that decides which model a request for model: "auto" runs.
Phantom AI stores and enforces all of this on its side. pai sends the settings and prints what Phantom AI returns. Where this guide describes how Phantom AI applies a setting, it describes the server's code at the time of writing.
A request for model: "auto" needs a route policy on the key. Without one, Phantom AI refuses it. A policy names the models auto may run and the rules that pick one ( RoutePolicy, RouteRule):
| Field | Meaning | Phantom AI's default |
|---|---|---|
models |
the models auto may run; the first is the default |
required |
applies_to |
auto routes only requests for auto; all routes named models too |
auto |
rules |
checked in order; the first one that matches picks the model | none |
on_empty |
when credit is exhausted: stop refuses, cheapest runs the cheapest model in models |
stop |
fallback_on_error |
if the chosen model fails, try the other models | false |
stick_minutes |
keep a conversation on its model this many minutes after its last request; 0 turns it off |
5 |
stick_by_prompt |
recognise a conversation by its opening when it sends no x-phantom-session header |
true |
Each rule has one condition in if and a model in use:
| Condition | Matches when |
|---|---|
pace |
the plan's pace is on_pace or ahead |
budget_left_pct_below |
less than this percent of the cap is left |
days_left_below |
fewer than this many days are left in the period |
has_tools |
the request does (true) or doesn't (false) send tools |
input_tokens_over |
the prompt is estimated at more than this many tokens |
reasoning_requested |
the request does or doesn't ask for reasoning |
use names a model from models, or one of:
cheapest: the model in models with the lowest input plus output price,first: the first model in models,next: the model after the one requested in models, wrapping around; for auto, the second model.With no match, auto runs the first model, and a named model (under applies_to: "all") runs as asked.
pai route # show the policy
pai route set --models <model-a>,<model-b> # the first is the default
pai route set --on-empty cheapest --fallback # change two fields, keep the rest
pai route set --file policy.json # replace the whole policy
pai route rule add --if has_tools=true --use first --at 1 # add it first
pai route rule rm 2 # remove rule 2
pai route clear # remove the policy
route set with flags reads the current policy first. When one exists, pai sends only the fields you gave, and Phantom AI keeps the rest ( patchRoute). When none exists, pai sends the fields as a new policy ( putRoute), so the first route set needs --models. --file always replaces the whole policy.
--fallback and --stick-by-prompt turn a field on; follow them with false to turn it off.
route rule add needs a policy already. pai reads --if name=value into one condition: true and false become true and false, a number becomes a number, and anything else stays text ( parseCondition). --at n puts the rule at position n, counting from 1. pai then sends the whole new list of rules.
Phantom AI checks every policy before saving it: from one to ten models, up to 20 rules, one condition per rule, and a use that names one of models or cheapest, first or next.
pai route test # what "auto" would run now, and the rule that picked it
pai route test --model <id> # the same for a named model
route test runs Phantom AI's router against the key's current policy. Phantom AI calls no model and charges nothing ( testRoute). pai sends only the model name, so conditions about the request itself (has_tools, input_tokens_over, reasoning_requested) see an empty request. The MCP tool test_route does the same.
Switching models mid-conversation throws away the prompt cache, the discount a provider gives for repeating the start of a prompt. With stick_minutes above 0, Phantom AI keeps a conversation on the model it last ran unless the rules' pick costs less even against the cached price.
Phantom AI tells conversations apart by the x-phantom-session header your own inference requests send. Without the header, and with stick_by_prompt on, it uses a hash of the conversation's opening, held in memory. pai sends no x-phantom-session header: it makes no inference calls apart from verify. Add the header in the client your agent uses to call Phantom AI.
A request with the header x-phantom-route: off skips routing; a request for auto with that header is refused.
The MCP tools are get_route, set_route (pass policy: null to remove it, or merge: true to change some fields) and test_route.