pi-model-auto

Pi extension package that routes each turn to cheap or strong authenticated models.

Packages

Package details

extension

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:

  1. Run /model.
  2. Choose Pi Router (Auto).
  3. 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
  • @cheap asks for the lower edge of Low.
  • @strong asks for Ultra, falling back to the strongest available lower mode.
  • @model:provider/model-id uses that exact model.

The prefix is removed before the model sees your prompt.

Configure When Needed

Most people only need config for two reasons:

  1. Limit the pool to providers they trust.
  2. Tell the router what each model really costs them.

Config lives in either file:

  • ~/.pi/agent/model-router.json
  • .pi/model-router.json in 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.