pi-model-auto
Pi extension package that routes each turn to cheap or strong authenticated models.
Package details
Install pi-model-auto from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-model-auto- Package
pi-model-auto- Version
0.1.6- Published
- Jul 19, 2026
- Downloads
- 686/mo · 168/wk
- Author
- maynewong
- License
- MIT
- Types
- extension
- Size
- 203.7 KB
- Dependencies
- 0 dependencies · 2 peers
Pi manifest JSON
{
"extensions": [
"./src/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-model-auto
pi-model-auto adds one Pi model: Pi Router (Auto).
Choose it once with /model. After that, Pi keeps using the router, and the router chooses one of your authenticated models for each turn.
Start
Install from npm:
pi install npm:pi-model-auto
Or install from git (pin a release with @vX.Y.Z, or omit it to track the default branch):
pi install git:github.com/maynewong/pi-model-auto@v0.1.0
Update later with pi update --extensions.
To try it once without installing, point -e at a local checkout:
pi -e /path/to/pi-model-auto
Then:
- Run
/model. - Choose Pi Router (Auto).
- Use Pi normally.
No config is required at first. If the router says no authenticated models are available, run /login for the providers you want to use, then reload Pi.
Use
Check what the router sees:
/auto
Force one turn when you know what you want:
@cheap update the README
@strong debug this race condition
@model:anthropic/claude-3-5-sonnet-20241022 use Sonnet here
@cheapasks for the lower edge ofLow.@strongasks forUltra, falling back to the strongest available lower mode.@model:provider/model-iduses that exact model.
The prefix is removed before the model sees your prompt.
Configure When Needed
Most people only need config for two reasons:
- Limit the pool to providers they trust.
- Tell the router what each model really costs them.
Config lives in either file:
~/.pi/agent/model-router.json.pi/model-router.jsonin trusted projects
Project config overrides user config.
{
"router": {
"modelFilter": { "include": ["anthropic", "z-ai"], "exclude": [] },
"modelOverrides": {
"anthropic/claude-3-5-sonnet-20241022": { "costCoef": 0.05 },
"z-ai/glm-5.2": {
"costCoef": 0.4,
"costCoefHours": [{ "hours": [14, 18], "factor": 3 }]
}
}
}
}
Use provider/model ids exactly as they appear in your Pi registry. The names above are public examples.
costCoef
costCoef multiplies the benchmark cost:
< 1: cheaper for you than the benchmark, such as a subscription or discount.= 1: roughly benchmark cost.> 1: more expensive for you.
Avoid setting a limited subscription near zero unless you want it to win almost every turn.
costCoefHours
Use this when a model is more expensive during local hours:
"z-ai/glm-5.2": {
"costCoef": 0.4,
"costCoefHours": [{ "hours": [14, 18], "factor": 3 }]
}
This means 0.4 normally, 1.2 from 14:00 through 17:59. Windows are half-open [start, end). [22, 2] wraps across midnight.
How It Chooses
The router compares quality against your effective cost.
Quality comes from one benchmark table. Cost starts from the same table, then applies your costCoef and any active time window. When the table reports reasoning effort, the router treats (model, effort) as the measured operating point. The router keeps to the efficient frontier: a model-effort variant is only worth considering if no other available variant is both better and cheaper.
capabilitySource chooses the benchmark:
"ramp"(default): this package's Ramp SWE-Bench table, using coding-agent resolve rate and measured cost per task. It is a narrow coding-agent slice, not a general model score. The task family follows SWE-bench."aa": Artificial Analysis model data, using its Intelligence Index and blended price metrics.
The numeric tables live in src/canonical-models.ts. The two sources are not mixed.
With the Ramp source, the status label is an Amp-style capability mode derived only from solve rate: Ultra at 85% or above, High at 80–85%, Medium at 75–80%, and Low below 75%. Cost remains a separate routing axis and never determines this label.
Task hardness chooses the mode, while the continuous difficulty score sets a solve-rate floor inside that mode. The router picks the cheapest effective-cost model meeting the floor, then permits only affordable willingness upgrades inside the same mode. It never enters the next mode early. If the target mode has no models at all, it borrows the nearest stronger mode; if none exists, it uses the strongest model in the nearest lower mode. Models without capability-mode metadata retain Pareto routing.
Task difficulty is judged from language-neutral signals: context size, prompt length, and recent tool activity. In automatic routing, benchmark-backed effort is selected with the model and takes precedence over Pi's session default before the model's thinkingLevelMap is applied. Forced concrete-model routes still honor Pi's selected effort. When you know a task is harder than it looks, pin with @strong.
By default, the router also refreshes an LLM classifier in the background. The current turn uses the previous classification result, so time-to-first-token is not blocked. The classifier model is chosen from your authenticated, filtered auto pool by cheapest effective price, unless classifierModel pins a specific provider/model. The classifier receives the last user message plus small routing stats; it never uses a provider outside your configured pool.
Turn it off completely:
{
"router": {
"classifier": "off"
}
}
One user turn keeps one model, including tool-call continuations. Automatic routing also avoids quota-cooled plans and avoids switching away from a useful warm cache when the switch is not worth it.
Context Window and Compaction
Pi displays context usage and decides when to compact from the selected pi-router/auto model. The router keeps that virtual model's contextWindow synchronized with the concrete model selected for the current turn. It preselects the route before Pi's preflight compaction check, refreshes the value again when the provider request starts, and restores the most recent concrete model's window when resuming a session.
As a result, a turn routed to a 272K model displays and compacts against 272K; a later turn routed to a 1M model switches the same limits to 1M. The initial 1M registration is only a startup placeholder before a session target can be identified.
Settings
| setting | use |
|---|---|
capabilitySource |
Choose "ramp" or "aa". |
modelFilter |
Include or exclude providers/models by substring. |
models |
Pin the configured cheap or strong endpoint. |
modelOverrides |
Adjust cost or metadata for known/private/local models. |
willingness |
Control affordable same-mode upgrades under Ramp, or frontier climbing under AA. |
cacheAware |
Keep warm prompt caches when switching is not worth it. Enabled by default. |
quota |
Skip cooled-down plans after rate-limit headers or 429. Enabled by default. |
classifier |
Enable, tune, or disable the background LLM classifier. Use "off" to disable. |
classifierModel |
Pin the classifier to a specific provider/model in your pool. |
weights |
Language-neutral difficulty-scoring weights. Advanced. |
log |
Append routing decisions to .pi/router.log. |
Useful override fields:
| field | meaning |
|---|---|
costCoef |
Cost multiplier. |
costCoefHours |
Local-hour multipliers. |
canonical |
Name shown in /auto. |
costTier |
Cost-only classification: cheap, standard, premium, or unknown. |
capabilityMode |
Override the Ramp-style mode: low, medium, high, or ultra. |
profiles |
deep, fast, coder, balanced, vision, frontier. |
frontier |
Whether the model can appear in the strong frontier. |
benchmarkEffort |
Pi-normalized effort (minimal, low, medium, high, xhigh) backing a manual metric row. |
priceBlended, intelligence, scores, tps |
Raw metrics for models without benchmark data. |
Quota state is stored at ~/.pi/agent/quota-state.json. Providers without remaining-quota headers only cool down after a real 429.
Core API
Other Pi extensions can resolve a model without an ExtensionContext:
import { resolveRouteModel } from "pi-model-auto/core";
const selection = resolveRouteModel({
models: availableModels,
hint: "cheap", // cheap | strong | auto | provider/model
context,
});
The core API loads user-level model-router.json and quota state by default. It never reads project config because that requires a trust decision from the host. Pass cfg to supply an explicit configuration or filterQuota: false to disable persisted cooldown filtering.
Develop
npm run build
npm run typecheck
npm test
Maintainers: see RELEASING.md for the publish flow.