--- 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.