A Pi extension that exposes stable
alias/* role models backed by configurable provider/model chains. It adds
pre-output failover, shared cooldowns, latency controls, footer status, and
durable transcript warnings without owning target-provider credentials.
Requires Node 22.19 or newer and Pi. Development and validation use
@earendil-works/pi-ai 0.84.4, @earendil-works/pi-coding-agent 0.84.4,
and @earendil-works/pi-tui 0.85.1.
pi install npm:pi-model-fallback-aliasRun /reload in a live session after installation or changes to $settings or
alias definitions, including target chains.
Aliases live in <agent-dir>/model-alias.json, normally
~/.pi/agent/model-alias.json. Set PI_MODEL_ALIAS_MAP to use another file.
{
"$settings": {
"statusRefreshMs": 2000
},
"$defaults": {
"timeouts": {
"firstEventMs": 30000,
"stallMs": 60000,
"commitMs": 90000
},
"cooldown": { "baseMs": 300000, "capMs": 3600000, "resetSuccesses": 3 }
},
"coder": [
"provider-a/model-primary",
"alias/fallback"
],
"fallback": [
"provider-b/model-secondary",
"provider-c/model-tertiary"
]
}Each key registers alias/<key> as a Pi model. A value can be a model string,
an ordered array, or an object with targets, timeouts, and cooldown.
timeouts applies while another target remains:
firstEventMsis the maximum wait for the first stream event.stallMsis the maximum gap between pre-commit stream events.commitMsis the maximum total time before output commits.
cooldown.baseMs defaults to 5 minutes, cooldown.capMs defaults to 1 hour,
and cooldown.resetSuccesses defaults to 1 successful terminal stream before
clearing a target's cooldown state. $defaults.cooldownResetSuccesses is a
legacy, defaults-only shorthand for $defaults.cooldown.resetSuccesses; set one
or the other, not both.
Set $settings.statusRefreshMs to control how often the footer rechecks shared
cooldown state. It defaults to 2000 milliseconds and must be an integer from 1
to 60000. Invalid settings warn and use the default without disabling aliases.
$settings and alias definitions, including target chains, load on /reload.
Model selection and failover refresh the footer immediately. Timeout and cooldown
policy fields are re-read at the start of each stream; if the current map is
invalid, the alias uses its last valid policy and emits a warning.
Nested aliases are flattened at load time. Loops are allowed and collapse during
expansion. Duplicate concrete targets keep their first position in each flattened
chain. A loop with no reachable concrete target produces an unusable empty chain.
Missing aliases skip only the invalid reference and produce a warning. Other top-level
keys prefixed with $, such as $comment, are treated as metadata and ignored.
Use an alias anywhere Pi accepts a model reference:
{
"defaultProvider": "alias",
"defaultModel": "coder"
}- Failover advances on resolution, authentication, connection, and HTTP errors until output commits. It never swaps providers after text or tool output is visible.
- Start and thinking events are buffered before commit, so a failed attempt cannot leave partial protocol state behind.
- Failed concrete targets enter an exponential cooldown. State is shared by aliases and Pi processes. Cooled targets are skipped while alternatives remain and retried before final exhaustion.
- Per-role latency limits can cover time to first event, event stalls, and the total pre-commit interval. A timeout is active only while another target remains.
- The alias model mirrors the selected target's context window, output limit,
and cost. Assistant messages preserve the logical alias identity while
responseModelrecords the concrete model. /model-alias-reset-cooldownclears shared cooldown state./model-alias-targets [role]renders all flattened fallback chains, or one selected role, in the transcript./model-alias-latency-report [role]renders attempt statistics in the transcript.
Runtime state is outside the installed package. The default directory is:
<agent-dir>/state/pi-model-fallback-alias/
Set PI_MODEL_ALIAS_STATE_DIR to an absolute or relative path to override it.
The directory contains:
cooldown-state.json, atomically replaced and reloaded by modification time.pi-model-alias-debug.jsonl, a rotating troubleshooting and latency log.
The active log rotates after 1 MiB and retained archives expire after seven
days. Configure positive integer overrides with
PI_MODEL_ALIAS_LOG_MAX_BYTES and PI_MODEL_ALIAS_LOG_RETENTION_DAYS.
From a checkout or local package directory, run the standalone report with:
node --import ./scripts/pi-resolve.mjs scripts/latency-report.mjs [log-path ...]The loader in scripts/pi-resolve.mjs resolves Pi packages from PI_ROOT, local
node_modules, or a global Pi install.
With no arguments, the report reads the default debug log and its retained rotated files. Optional log paths replace the default and include each path's retained files.
Logs contain alias roles, target references, durations, status codes, and allowlisted request or trace identifiers. They do not record authorization headers, cookies, request payloads, response bodies, alias configuration, or provider credentials. Review logs before sharing them because model names and request identifiers can still be operationally sensitive.
The extension's alias provider returns a fixed non-secret API-key value only
to mark aliases available to Pi's registry. Streaming discards that value and
resolves the selected target through Pi's provider registry.
When forwarding request options, target authentication replaces caller
authentication. The extension removes caller authorization, x-api-key,
api-key, x-goog-api-key, and anthropic-* headers. Other caller headers
only fill gaps left by target authentication. This prevents credentials from
one provider from crossing a failover boundary.
Pi extensions run with the Pi process's permissions. Review the source and configuration before installation. See SECURITY.md for private reporting.
- Alias is missing: verify the map path and JSON, then run
/reload. - Restored session cannot find an alias: restore the role, select a current model, or start a new session.
- Footer looks stale: verify
$settings.statusRefreshMs, run/reload, inspect transcript warnings, or run/model-alias-reset-cooldown. - Policy edit is temporarily invalid: existing aliases use their last valid policy and warn once per distinct failure. Replace configuration atomically to keep concurrent processes aligned.
- Investigating provider failures: correlate an allowlisted request or trace identifier with the record timestamp and provider/model. No credential or response body is written to the log.
npm ci
npm run checknpm run check runs the test suite under c8 with all production TypeScript files
included, enforces 95% statements, lines, functions, and branches, writes text
and coverage/lcov.info reports, then runs type checking and package validation.
The only narrow coverage exclusion is src/fallback/types.ts, which contains
TypeScript type declarations only and emits no executable JavaScript.
Generated coverage output is ignored by Git. Development
dependencies pin @earendil-works/pi-ai and @earendil-works/pi-coding-agent
to 0.84.4, and @earendil-works/pi-tui to 0.85.1. Runtime Pi packages remain
optional wildcard peer dependencies because Pi supplies them when loading the
extension. Continuous integration tests Node 22.19 and Node 24.
Releases use semantic versioning. Release Please maintains the release pull request from semantic commits; merging it creates the GitHub release and publishes to npm with OpenID Connect (OIDC) provenance. Follow RELEASING.md.
Repository: https://github.com/unrelentingfox/pi-model-fallback-alias
MIT. See LICENSE.