id: PX-RESEARCH-Q7N4M2K9
type: research
title: Pi Model SDK and Failover Prior Art
Pi Model SDK and Failover Prior Art
SDK surfaces around models/providers, and extensions that already attempted free-model catalogs, model switching, or failover.
Sources: installed Pi docs (extensions.md, custom-provider.md), pi-ext sources, GitHub community extensions (probed 2026).
Pi SDK surfaces relevant to gratis
pi.registerProvider(): full createProvider or legacy config form; async extension factories finish before startup, so discovered models exist for /model and pi --list-models; registrations after load apply immediately, so model sets can be replaced at runtime without /reload.
pi.setModel(model): programmatic model switch, await-able, returns success; used by masks (with restore) and every community failover extension.
ctx.modelRegistry: find(provider, id), getProvider(provider) (incl. .auth.oauth), getApiKeyForProvider(provider) — key resolution without touching env directly (pi-ext quota, model-info rely on it).
Pi's own retry: regex-classified retryable errors (429/5xx/network) get automatic retry with backoff; failover extensions classify with a copy of Pi's _isRetryableError regex to decide "swap now vs wait for Pi's retry".
Per-model api and baseUrl overrides exist in ProviderModelConfig; custom streamSimple gives full request control.
Prior art
Extension
Approach
Mechanism
dotfiles free (deleted)
keyless free catalog (Zen, Kilo)
3 synthetic providers; died with endpoint lock (see post-mortem)
preference stack of vendor routes (oauth + api_key per vendor), model policy follow_current, command UX + tool bridge
season179/pi-ecosystem pi-model-fallback
persisted fallback chain
setModel with internalSwitch guard against own model_select recursion; persisted active fallback + restore cooldown to avoid churn
opencode-rate-limit (npm, opencode ecosystem)
provider pool fallback
virtual model pool: on 429/concurrency error, transparently switch to next configured model, session context preserved, cooldown memory
Conclusions
The swap-on-error pattern (setModel + re-run + cooldown) is established and proven in at least four independent extensions; gratis's in-provider cascade is the less common but cleaner pattern — it keeps grts/auto a single selectable model instead of mutating the user's active model.
gratis can reuse, not reinvent: Pi's retryable-regex classification, after_provider_response for status/header observation, message_end for overflow normalization, modelRegistry.getApiKeyForProvider for native-key resolution (stronger than raw env: also finds OAuth/auth-store keys).
Catalog pattern proven in the wild: fetch → cache → baked-in fallback (pi-orcarouter); gratis static catalogs can adopt the same three-layer scheme.
The internalSwitch guard (season179) is a required detail for any gratis code that touches setModel or model_select; gratis avoids it entirely by not switching models.
Key-resolution delta worth a spec check: getApiKeyForProvider surfaces keys stored in Pi's auth store — gratis's spec says "process env only"; using the registry would honor user keys from auth.json too (e.g. an existing OpenRouter entry), broadening zero-config reach at the cost of the env-only simplicity rule.
Unresolved questions
Whether getApiKeyForProvider works for a custom provider id (gratis backends are not Pi-native providers); if not, env-only stands as the only uniform mechanism.
Whether after_provider_response fires for wrapped errors (Kilo's 429-with-body) before Pi's own retry consumes them — needs implementation-phase confirmation.
---
id: PX-RESEARCH-Q7N4M2K9
type: research
title: Pi Model SDK and Failover Prior Art
---
## Pi Model SDK and Failover Prior Art
SDK surfaces around models/providers, and extensions that already attempted free-model catalogs, model switching, or failover.
Sources: installed Pi docs (`extensions.md`, `custom-provider.md`), pi-ext sources, GitHub community extensions (probed 2026).
### Pi SDK surfaces relevant to gratis
- `pi.registerProvider()`: full `createProvider` or legacy config form; async extension factories finish before startup, so discovered models exist for `/model` and `pi --list-models`; registrations after load apply immediately, so model sets can be replaced at runtime without `/reload`.
- `pi.setModel(model)`: programmatic model switch, `await`-able, returns success; used by `masks` (with restore) and every community failover extension.
- `ctx.modelRegistry`: `find(provider, id)`, `getProvider(provider)` (incl. `.auth.oauth`), `getApiKeyForProvider(provider)` — key resolution without touching env directly (pi-ext `quota`, `model-info` rely on it).
- Events: `model_select`, `before_provider_headers`, `before_provider_request` (payload rewrite per request), `after_provider_response` (status + headers, pre-stream-consume), `message_end` (error-message rewrite → feeds Pi's overflow recovery), `agent_end`.
- Pi's own retry: regex-classified retryable errors (429/5xx/network) get automatic retry with backoff; failover extensions classify with a copy of Pi's `_isRetryableError` regex to decide "swap now vs wait for Pi's retry".
- Per-model `api` and `baseUrl` overrides exist in `ProviderModelConfig`; custom `streamSimple` gives full request control.
### Prior art
| Extension | Approach | Mechanism |
| --- | --- | --- |
| dotfiles `free` (deleted) | keyless free catalog (Zen, Kilo) | 3 synthetic providers; died with endpoint lock (see post-mortem) |
| [shyim/pi-orcarouter](https://github.com/shyim/pi-orcarouter) | one gateway as provider | dynamic catalog: fetch → disk cache → baked-in generated fallback; stale cache triggers visible refresh; only chat-capable models registered |
| [Columpio/pi-extensions](https://github.com/Columpio/pi-extensions) `pi-failover` | error-triggered model swap | `message_end` classification (copy of Pi's retryable regex) + consecutive-error threshold → `pi.setModel(fallback)` + prompt re-run; per-model cooldown with `degradedUntil`; own synthetic failover provider |
| [continua-ai/pi-lab](https://github.com/continua-ai/pi-lab) `subscription-fallback` | auth-route rotation | preference stack of vendor routes (oauth + api_key per vendor), model policy `follow_current`, command UX + tool bridge |
| season179/pi-ecosystem `pi-model-fallback` | persisted fallback chain | `setModel` with `internalSwitch` guard against own `model_select` recursion; persisted active fallback + restore cooldown to avoid churn |
| `opencode-rate-limit` (npm, opencode ecosystem) | provider pool fallback | virtual model pool: on 429/concurrency error, transparently switch to next configured model, session context preserved, cooldown memory |
### Conclusions
1. The swap-on-error pattern (setModel + re-run + cooldown) is established and proven in at least four independent extensions; gratis's in-provider cascade is the less common but cleaner pattern — it keeps `grts/auto` a single selectable model instead of mutating the user's active model.
2. gratis can reuse, not reinvent: Pi's retryable-regex classification, `after_provider_response` for status/header observation, `message_end` for overflow normalization, `modelRegistry.getApiKeyForProvider` for native-key resolution (stronger than raw env: also finds OAuth/auth-store keys).
3. Catalog pattern proven in the wild: fetch → cache → baked-in fallback (pi-orcarouter); gratis static catalogs can adopt the same three-layer scheme.
4. The `internalSwitch` guard (season179) is a required detail for any gratis code that touches `setModel` or `model_select`; gratis avoids it entirely by not switching models.
5. Key-resolution delta worth a spec check: `getApiKeyForProvider` surfaces keys stored in Pi's auth store — gratis's spec says "process env only"; using the registry would honor user keys from `auth.json` too (e.g. an existing OpenRouter entry), broadening zero-config reach at the cost of the env-only simplicity rule.
### Unresolved questions
- Whether `getApiKeyForProvider` works for a *custom* provider id (gratis backends are not Pi-native providers); if not, env-only stands as the only uniform mechanism.
- Whether `after_provider_response` fires for wrapped errors (Kilo's 429-with-body) before Pi's own retry consumes them — needs implementation-phase confirmation.