pi-star-router
StarRouter is a focused model-routing extension for Pi. It chooses the best available model and thinking level for each prompt using a deterministic heuristic prompt profiler, Artificial Analysis benchmark data, provider-scoped model pools, strict model i
Package details
Install pi-star-router from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-star-router- Package
pi-star-router- Version
1.2.1- Published
- Jul 28, 2026
- Downloads
- 548/mo · 187/wk
- Author
- ketchh
- License
- MIT
- Types
- extension
- Size
- 403.2 KB
- Dependencies
- 0 dependencies · 3 peers
Pi manifest JSON
{
"extensions": [
"./index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
StarRouter for Pi
StarRouter 1.2.1 is a local, deterministic model-routing extension for Pi. Before a turn, it profiles the prompt, compares benchmark-backed candidates inside one configured provider, and chooses a model plus supported thinking level. It asks for confirmation by default and can abstain when evidence is weak.
Version 1.2.0 adds redacted in-memory decision traces, benchmark attribution, /router explain, and explicit offline replay. The routing kernel and its 80 golden prompt expectations remain unchanged from 1.1.1.
There is no remote classifier and no cross-provider fallback. Artificial Analysis data supplies model measurements; prompt text is never sent there.
Requirements and installation
- Node.js
>=22.19.0 - Pi
>=0.82.1(verified with0.82.1)
pi install npm:pi-star-router
Then reload Pi and opt in:
/reload
/router settings
/router on
For a one-off run:
pi -e npm:pi-star-router
Routing is off by default.
Command surface
| Command | Behavior |
|---|---|
/router status |
Reports enabled/disabled state and, when enabled, the latest decision. |
/router explain |
Formats the latest in-memory decision or routing-attempt trace without exposing the raw prompt. |
/router replay export [bundle.json] |
Explicitly exports the latest replay capture to a private, bounded JSON bundle. |
/router replay verify <bundle.json> |
Re-runs the frozen deterministic route kernel offline and reports route/ranking/score deltas. |
/router on |
Enables routing for subsequent turns in the active session branch. |
/router off |
Disables routing and removes the ambient decision widget. |
/router refresh |
Forces an Artificial Analysis dataset refresh. |
/router settings |
Opens transactional settings in TUI mode. |
Runtime pipeline
- Local prompt profile. English/Italian dictionaries and structural signals detect mechanical transforms, simple output, coding, debugging, architecture, security, research, agentic/tool work, structured output, long context, and vision. Mixed intents are additive: formatting language cannot erase a debugging or security signal.
- Task requirements. The profile sets benchmark weights, context/vision needs, cost and speed sensitivity, a routing tier, and a target thinking level.
- Provider scope. Only currently available text models from
strategy.routingProviderare eligible. An unavailable configured provider produces no silent fallback to another provider. - Candidate generation. Provider filters are applied, then candidates are created only for thinking variants supported by Pi/model metadata.
- Identity match. A Pi model may borrow an Artificial Analysis row only after vendor, family/subfamily, generation/version, moving-alias, and reasoning-class checks. Host-compatible rows are
host-verified. Because AA may not benchmark an aggregator itself, a small explicit allowlist (including OpenRouter) may use an otherwise identity-compatible row asmodel-only; this never relaxes identity gates. - Normalization and ranking. Qualified host/deployment identity is preserved, and true duplicates select one coherent source row rather than combining the best field from each row. Host-verified evidence can contribute AA price, output speed, latency, and context fallback. Token burn is retained as diagnostic evidence but does not affect ranking. Model-only evidence contributes benchmark/model quality only; economics/context must come from Pi metadata and host performance is unavailable. Latency uses end-to-end evidence pool-wide when any candidate has it; TTFT is used only when the entire pool lacks end-to-end measurements.
balancedranks by composite;quality,cheapest, andfastestrank lexicographically by their named primary metric with deterministic tie-breakers. Cheapest/fastest abstain when their primary evidence is absent. - Safety gates. Vision, context capacity, task thinking floors/caps, minimum AA identity match, and a relative quality floor are hard constraints. Standard weak Pareto filtering removes routes that are no better on any observed dimension. Low overall confidence causes abstention unless a trustworthy current route can be kept.
- Hysteresis. The current route is retained when its objective score is within
preferCurrentWithinof the winner, reducing model flapping. - Confirmation and explanation. Auto-accept is off by default. Decisions keep the deterministic recommendation and its basis separate from the applied route and its origin, so a manual alternative cannot be reported as the algorithmic winner. A compact widget records both identities when they differ.
- Observability without authority. The
1.2.0layer records a bounded, redacted trace only after the deterministic kernel has decided. Ranking and benchmark attribution share one numerical implementation; detailed attribution is materialized only for the top three candidates. Trace assembly, formatting, or replay-capture failure cannot invalidate an already applied route.
See docs/algorithm.md for the ranking invariants and limitations.
Route confirmation and runtime modes
TUI
The confirmation overlay focuses the current recommendation. Controls are:
↑ / ↓ move focus
Enter confirm the focused route
Esc cancel and keep the current route
There is no separate Space-to-mark step. The layout degrades to a compact view on small terminals. The decision widget above the editor is capped at four lines.
RPC
Route confirmation uses Pi's serializable ctx.ui.select() surface. Decision widgets are plain text lines rather than TUI component factories, so RPC clients receive useful rationale.
Print/headless
/router status, /router explain, and replay results print plain text. Transactional settings require TUI and direct users to edit JSON otherwise. If confirmation is required but no interactive UI exists, StarRouter conservatively keeps the current route. Set global ui.autoAcceptRouting only when unattended switching is intentional.
Explainability and offline replay
After a routing attempt, inspect the latest activity with:
/router explain
Successful decisions include the redacted profile, provider scope, recommendation basis, application origin, selected and runner-up routes, benchmark coverage, per-metric contributions, missing metrics, and score breakdown. Skipped attempts explain outcomes such as exact follow-up retention, empty input, unavailable data, or no trustworthy route. TUI, RPC, and print modes consume the same plain-text formatter.
Traces are memory-only diagnostics. They contain prompt character count, image presence, and derived profile evidence, but no raw prompt, prompt hash, image content, conversation content, response, or credential. They are cleared when the active session branch is restored or changed and are not added to Pi session entries.
Replay export is separate and always explicit:
/router replay export # private default path
/router replay export ./decision.json # explicit cwd-relative path
/router replay verify ./decision.json # no network or profiler rerun
The default directory is ~/.pi/agent/extensions/star-router/replays/. A replay freezes the derived profile, route-relevant strategy, model registry snapshot, validated AA snapshot, expected route, ranking, and scores. Verification re-runs only the deterministic route kernel; it deliberately does not re-profile the original prompt. JSON is schema-versioned, bounded to 16 MiB, structurally validated, work-limited, and written atomically with file mode 0600; newly created parent directories request mode 0700 on supported Unix filesystems. Its SHA-256 value detects accidental or adversarial input mutation but is not a signature and does not establish provenance. Treat replay files as potentially sensitive model/configuration metadata even though prompt text and secrets are excluded.
Transactional settings and filters
Open /router settings in TUI mode. All edits stay in an in-memory draft until:
Ctrl+S save the selected global or project draft
Esc cancel without writing
Switching the save target keeps independent global and project drafts. In project scope, global-only controls are visibly inherited and read-only.
The model-filter screen is also transactional:
Enter toggle the focused family/model
Ctrl+S apply the filter draft to the settings draft
Esc cancel filter edits
Ctrl+Shift+S save a named preset without applying the draft
Built-in presets (benchmark-safe, frontier-safe, coding-safe, budget-safe) are templates, not locks. Configuration and saved-preset reads are byte/count/string bounded; malformed, oversized, or prototype-sensitive project JSON is ignored rather than merged.
Configuration and trust boundary
Configuration is loaded from:
- global:
~/.pi/agent/model-router.json - project:
.pi/model-router.json
model-router.json.example is a complete global example. It includes privileged controls and data-source settings. model-router.project.json.example contains only project-safe fields.
Global configuration can control
enabledstrategy.routingProviderui.autoAcceptRouting- all
dataSourcefields, including endpoint and optional API-key environment variable - all project-safe fields below
Project configuration can control only
strategy.objectivestrategy.qualityFloorstrategy.preferCurrentWithinstrategy.minAaMatchstrategy.minRouteConfidenceui.showAdvancedSettingsfiltersmodelOverrides
Project attempts to set enabled, strategy.routingProvider, ui.autoAcceptRouting, or dataSource are ignored. This prevents a checked-out repository from enabling routing, changing providers, auto-accepting switches, redirecting benchmark traffic, or selecting secret headers.
Dataset, network, and cache behavior
StarRouter's only core network dependency is benchmark metadata:
- it requests the configured Artificial Analysis API path;
- if API parsing/fetching fails, it can parse the configured Artificial Analysis page fallback;
- a validated cache is stored atomically at
~/.pi/agent/cache/star-router-public.json; - a fresh validated cache is preferred, and a stale validated cache may be used if both network paths fail; failure to persist an already validated network snapshot is non-fatal for the current run;
/router refreshbypasses a fresh disk cache and reports whether it reached the network or retained a validated stale fallback;- responses, caches, model/profile counts, and external strings are bounded and validated before routing;
- unknown optional AA prompt buckets are ignored while recognized routing profiles remain strict;
- if no trustworthy dataset is available, routing is skipped and the current route is kept.
Fetches are single-flight and bounded by requestTimeoutMs. Dataset/cache rows are validated before use, and obsolete generations cannot overwrite a newer cache. Qualified host labels such as Google (AI Studio), Google (Vertex), and DeepInfra (Turbo) remain distinct. Deployment grouping includes hostApiId, but that field can never certify host economics; host proof uses exact normalized hostLabel/hostSlug aliases. A direct-provider host mismatch is rejected. For the explicit aggregator allowlist only, a hosted mismatch becomes degraded model-only evidence: AA host price/speed/E2E/TTFT/token-burn/context fallback are suppressed, confidence is reduced, and rationale says so. Unhosted page-scrape rows use the same model-only scope. Secret headers are attached only to the official https://artificialanalysis.ai origin; a custom global endpoint does not receive them.
Artificial Analysis is an external source whose page/API schema and measurements may change. /router refresh is therefore an operational refresh, not a guarantee that every provider model will match.
Privacy and persisted state
- Prompt profiling runs locally and deterministically.
- No prompt text, images, Pi conversation content, provider tokens, or model responses are sent to Artificial Analysis.
- There are zero classifier-model calls.
- Session entries persist only enabled state and compact route-decision summaries; recommendation basis and application origin are separate, and cancellation persists a
nullclear marker so an old widget is not resurrected. 1.2.0decision/attempt traces remain in memory only. Replay files exist only after/router replay export, exclude raw prompts and prompt hashes, and can reveal route, model, benchmark, and safe strategy metadata.- The public dataset cache contains benchmark/model metadata, not prompts.
- StarRouter reads provider availability/auth status from Pi but does not log or persist provider secrets.
See SECURITY.md for the full trust model and reporting path.
Reproducible validation
The default suite is deliberately offline. Its preload denies Fetch/WebSocket and the standard in-process Node HTTP(S), HTTP/2, TCP, TLS, UDP, and DNS entry points unless a test replaces fetch() with a local deterministic stub. This is a regression guard, not an operating-system sandbox: it intentionally does not claim to contain malicious child processes or native code.
npm test # alias for test:offline
npm run test:offline # 149 deterministic offline tests
npm run golden # 80/80 checked-in golden prompts
npm run typecheck
npm run smoke
npm run perf:check # offline replay/trace p95 budgets
npm audit --omit=dev --audit-level=low # shipped StarRouter surface
npm pack --dry-run --json
The 1.2.1 release gate targets 149 offline tests, 80/80 golden prompts, strict TypeScript, release smoke checks, offline observability performance budgets, production-surface audit, package-content inspection, and Pi 0.82.1 compatibility CI. Version 1.2.0 retained the earlier Pi 0.80.6–0.80.10 compatibility matrix.
As of 2026-07-27, the production-surface audit reports zero vulnerabilities. A full development-tree npm audit reports one upstream denial-of-service advisory inside the npm-shrinkwrapped Pi 0.82.1 CLI development dependency (brace-expansion@5.0.7, high). It is not shipped in the StarRouter tarball and StarRouter does not call it, but local/CI Pi tooling still contains it. The protobufjs advisory inherited by the previous Pi baseline is resolved in 0.82.1. The remaining upstream risk is documented rather than hidden by a lockfile-only substitution that would not change the installed shrinkwrapped code.
perf:check executes 180 network-disabled observations across the nine replay fixtures and enforces conservative defaults of 75 ms replay-verify p95 and 5 ms trace validate/format p95. On the 2026-07-27 Pi 0.82.1 release workstation it observed 0.918 ms and 0.269 ms respectively. A same-process paired kernel benchmark executed 47,250 routes per implementation against baseline 3c4c147: median chooseRoute time was 0.0475 ms at baseline and 0.0509 ms in this tree (+0.0034 ms, +7.2%). Detailed top-three attribution is deliberately populated after chooseRoute, so observability failure cannot suppress a valid route. These values are machine-specific release evidence, not universal latency promises.
Live OpenRouter catalog observations are opt-in and are not a PR gate:
npm run test:live # 8 tests, one shared request per external source/file
npm run test:all # offline first, then live
The live suite makes two external requests (one shared OpenRouter request and one shared Artificial Analysis request) to check current catalog/schema shape, representative IDs, parseable prices, and model-only aggregator compatibility. It intentionally makes no universal savings claim. To evaluate cost for your workload, record the observation timestamp, exact provider/model IDs, input/output/cache token counts, provider fees, and the baseline selection policy; then rerun the comparison when catalog prices change.
Troubleshooting
Router status → disabled
Run /router on, or save enabled: true in the global file. Project config cannot enable routing.
Configured provider shows unavailable
Authenticate/configure that provider in Pi or change the global routing provider. StarRouter will not borrow another provider silently.
No trustworthy route
The provider may have no benchmarkable available model, an identity/host gate may reject the match, or confidence may be below the configured floor. Keep the current route, inspect filters/overrides, and run /router refresh.
Dataset refresh fails
Check connectivity, the configured timeout, and the Artificial Analysis endpoint. A validated stale cache is used when available; otherwise routing abstains.
Settings unavailable in RPC/print
Edit the appropriate JSON file. Keep privileged fields in the global file.
A model name does not match AA
Add a narrow modelOverrides slug pin after verifying creator, exact version, evidence scope, and reasoning class. The pin resolves alias naming only; contradictory or missing fixed identity evidence is still rejected. A versioned moving alias can be pinned explicitly, with reduced confidence. For aggregator model-only evidence, AA host economics remain suppressed even when pinned.
Repository map
index.ts Pi lifecycle, commands, runtime routing
src/router-core.ts config trust boundary, AA ingestion, ranking
src/decision-trace.ts versioned redacted trace and attribution contracts
src/route-replay.ts explicit bounded replay export and offline verification
src/prompt-understanding.ts deterministic EN/IT prompt profiler
src/route-choice-screen.ts responsive confirmation and widgets
src/settings-screen.ts transactional settings UI
src/model-filters-screen.ts transactional provider/model filters
src/filter-presets/ built-in and saved presets
docs/algorithm.md algorithm invariants and limitations
docs/index.html self-contained public project page
tests/*.test.ts deterministic offline release suite
tests/fixtures/replays/ frozen route/ranking/score replay corpus
tests/live/*.live.test.ts explicit network observations
model-router.json.example complete global example
model-router.project.json.example project-safe example
Release notes and license
See CHANGELOG.md for the 1.2.1 Pi baseline update, the 1.2.0 observability release, and earlier migration notes. Licensed under MIT.