Register custom LLM variants in Pi agent configuration
Registers an unrecognized OpenRouter model slug (like a :nitro variant) as a custom Pi model so it stops silently falling back.
Why it matters
Ensure Pi agent correctly loads and persists custom or variant model slugs (like OpenRouter routing shortcuts :nitro, :floor, :exacto) by registering them in the local model registry, preventing silent fallback to bundled defaults.
Outcomes
What it gets done
Add custom model metadata to ~/.pi/agent/models.json under the correct provider
Set defaultProvider and defaultModel in settings.json to match the registered slug exactly
Verify provider authentication exists in auth.json or environment variables
Confirm the model appears in --list-models and test with a smoke-test query
Install
Add it to your toolbox
Run in your project directory:
curl -fsSL https://spark.entire.vc/get/ag-pi-custom-model | bash Overview
Pi custom / variant model
Registers an OpenRouter routing-shortcut variant or new model slug as a custom Pi model in models.json, fixing the silent fallback to Pi's built-in default when the slug isn't in the bundled registry. Use it whenever Pi appears to have reset your chosen model with no error shown. Confirm the slug and provider auth are real before registering it.
What it does
Fixes a Pi agent bug where a saved default model silently falls back to the built-in per-provider default (for OpenRouter, moonshotai/kimi-k2.6) instead of loading the intended one - because Pi's saved default only loads if the exact provider/id exists in its model registry, and Pi ships a static bundled list per provider that does not include OpenRouter routing-shortcut variants (:nitro for throughput, :floor for cheapest, :exacto for quality tool-use) or any brand-new model slug. The fix is registering the slug as a custom model so find(provider, id) actually matches it.
When to use - and when NOT to
The symptom to watch for is a model that appears to have reset itself with zero warning text anywhere - that silence is the diagnostic tell, since Pi never prints an error for this class of failure, it just quietly shows something else in the footer. Before registering anything, confirm two things first: that the model or variant id is spelled and typed correctly (a typo'd id fails exactly the same silent way as a genuinely missing one), and that the provider actually has a working auth key configured, since a model can be fully registered and still fall back if its provider has no credentials at all.
Inputs and outputs
Three global files are involved: ~/.pi/agent/settings.json holds defaultProvider, defaultModel, and defaultThinkingLevel; ~/.pi/agent/models.json holds custom models keyed by provider; and ~/.pi/agent/auth.json holds provider credentials. Add the model under providers.<provider>.models in models.json - for a built-in provider like OpenRouter or Anthropic, only metadata is needed, since api, baseUrl, and auth are inherited from the bundled defaults:
{
"providers": {
"openrouter": {
"models": [
{
"id": "z-ai/glm-5.2:nitro",
"name": "Z.ai: GLM 5.2 (nitro)",
"reasoning": true,
"thinkingLevelMap": { "xhigh": "xhigh" },
"input": ["text"],
"cost": { "input": 0.95, "output": 3, "cacheRead": 0.18, "cacheWrite": 0 },
"contextWindow": 1048576,
"maxTokens": 32768,
"compat": { "supportsDeveloperRole": false, "thinkingFormat": "openrouter" }
}
]
}
}
}
Copy cost, contextWindow, and compat from the base model rather than hardcoding a generic context or token size, since the variant shares them - the bundled entry can be found in <pi-pkg>/node_modules/@earendil-works/pi-ai/dist/providers/<provider>.models.js. Then set defaultProvider and defaultModel in settings.json to the exact id, leaving defaultThinkingLevel as the user already had it. Verify with pi --list-models | grep <id> and confirm the JSON parses, optionally smoke-testing with pi --provider <p> --model "<id>" "which model are you?".
Integrations
Several quirks matter: the saved-default lookup in find() requires an exact provider+id match, with no fuzzy matching or colon-stripping, so the slug must be byte-identical between settings.json and models.json; editing settings.json alone does nothing if the slug isn't also registered in models.json; the optional enabledModels field pins the model picker so Ctrl+P cycling can't drift back, formatted as "enabledModels": ["<provider>/<id>:<thinking>"]; a repo's local .pi/settings.json overrides the global one, so a default that only reverts inside one project should be checked there first; and Pi must be fully restarted, since the model registry loads at startup.
Who it's for
Anyone debugging why Pi's model picker won't hold the choice they just made - the fix is a config edit rather than a Pi bug report, and once the right file carries the registration, the chosen model behaves like any other bundled one from then on.
Source README
Pi custom / variant model
When to Use
Pi's saved default only loads if the exact provider/id exists in its model registry. Pi ships a static bundled list per provider - so OpenRouter routing-shortcut variants (:nitro = sort by throughput, :floor = cheapest, :exacto = quality tool-use) and any brand-new slug are NOT in it. When the default doesn't resolve, Pi silently falls through to its built-in per-provider default (for openrouter that's moonshotai/kimi-k2.6) - looking like Pi "reset" your model. Fix = register the slug as a custom model so find(provider, id) matches.
Files (global)
~/.pi/agent/settings.json-defaultProvider,defaultModel,defaultThinkingLevel~/.pi/agent/models.json- custom models, keyed by provider~/.pi/agent/auth.json- provider credentials (check the provider key exists)
Steps
- Confirm the slug is real before adding it (e.g. check the OpenRouter model/variant exists). A typo'd id also silently falls back.
- Confirm auth. The provider must have a key in
auth.json(or an env var likeOPENROUTER_API_KEY). No auth → the model is registered but unavailable → still falls back. - Add the model to
models.jsonunderproviders.<provider>.models. For a built-in provider (openrouter, anthropic, etc.) you only supply metadata -api,baseUrl, and auth are inherited from the bundled defaults. Example:
Copy{ "providers": { "openrouter": { "models": [ { "id": "z-ai/glm-5.2:nitro", "name": "Z.ai: GLM 5.2 (nitro)", "reasoning": true, "thinkingLevelMap": { "xhigh": "xhigh" }, "input": ["text"], "cost": { "input": 0.95, "output": 3, "cacheRead": 0.18, "cacheWrite": 0 }, "contextWindow": 1048576, "maxTokens": 32768, "compat": { "supportsDeveloperRole": false, "thinkingFormat": "openrouter" } } ] } } }cost/contextWindow/compatfrom the base model (the variant shares them) - find the bundled entry in<pi-pkg>/node_modules/@earendil-works/pi-ai/dist/providers/<provider>.models.js. Don't hardcode generic 128k/16k if the real model is bigger. - Set the default in
settings.json:defaultProvider+defaultModel= the exact id. LeavedefaultThinkingLevelas the user has it. - Verify:
pi --list-models | grep <id>shows it, and JSON parses. Optionally smoke-test:pi --provider <p> --model "<id>" "which model are you?".
Quirks
- Exact match only.
find()is exactprovider+id- no fuzzy/colon-stripping for the saved default path. The slug insettings.jsonandmodels.jsonmust be byte-identical. - Silent fallback. Pi prints no error when the default doesn't resolve; it just shows a different model in the footer. That's the tell.
- Don't edit
settings.jsonalone. SettingdefaultModelto an unregistered slug does nothing -models.jsonis the actual fix. enabledModels(optional) pins the model picker so Ctrl+P cycling can't drift back:"enabledModels": ["<provider>/<id>:<thinking>"].- Project override. A repo's
.pi/settings.jsonoverrides global. If a default reverts only inside one project, check that file first. - Restart Pi fully - the registry loads at startup.
Limitations
- Adapted from
davidondrej/skills; verify local paths, tools, credentials, and agent features before acting. - For commands, remote access, scheduling, browser automation, or file-changing workflows, get explicit user approval and confirm the target environment first.
Discussion
Questions & comments · 0
Sign In Sign in to leave a comment.