@superjeason/pi-model-manager
v0.4.0
Published
Pi commands for managing custom model providers with models.dev-first metadata enrichment.
Readme
@superjeason/pi-model-manager
Four pi commands for managing custom model providers in ~/.pi/agent/models.json, with models.dev-first metadata enrichment and request-header disguising.
Commands
/add-provider — interactive wizard
Adds a new OpenAI-compatible provider end-to-end:
- Enter provider name, base URL, API key
- Choose API type (all built-in pi formats)
- Fetches
{baseUrl}/models(standard OpenAI format) with a cancellable loader - Multi-select models (Space toggle, Enter confirm, Esc cancel)
- If
/modelsfetch fails, type model ids manually (comma-separated)
- If
- Optionally enrich config from models.dev first, then the built-in pi-ai library (same logic as
/sync-model) - Writes the provider to
models.json
Then /reload and the new provider appears in /model.
/edit-provider — modify an existing provider
Select a provider (shown with model count and baseUrl), then choose an action:
- Change API format — pick from built-in API types
- Manage models
- Add from
/modelsendpoint, or type ids manually - Remove existing models (multi-select + confirm)
- Adding can enrich from models.dev first, then the built-in library
- Add from
- Edit connection — change
baseUrl/apiKey
(current value in the prompt; empty keeps,-removes, Esc cancels) - Enrich model config
- Safe: fill missing fields only
- Overwrite: clear matched fields then re-fill (
reasoningis preserved)
- Delete provider — remove provider and all models (confirm)
All destructive ops ask for confirmation first.
/sync-model — fill missing model config
Reads models.json, matches each custom model by id against models.dev first, then built-in models loaded at runtime, and fills fields you didn't set:
thinkingLevelMap(from models.devreasoning_optionswhen available; enablesmax/xhighthinking levels)cost(from models.devapi.jsonpricing → pi{input,output,cacheRead,cacheWrite[,tiers]}USD/1M; fills missing or all-zero placeholders)compat(from built-in pi metadata; merged, your values win)maxTokens,contextWindow,reasoning,input(onlytext/image; pdf/audio/video from models.dev are stripped),name
Only fills missing fields — anything set explicitly is preserved. Idempotent.
Matching prefers canonical families over reseller/gateway clones:
| bare id example | preferred family |
|---|---|
| gpt-5.5 / o3 | openai |
| claude-* | anthropic |
| gemini-* | google |
| grok-* | xai |
| glm-* | zhipuai / zai |
| qwen* / qwq* / qvq* | alibaba / qwen / dashscope |
| doubao* / seed-1.* | volcengine / bytedance |
| mimo-* / xiaomi-mimo* | xiaomi |
| minimax* / abab* | minimax |
| kimi* / moonshot* | moonshotai |
You can also set an explicit family on the provider or model:
{
"providers": {
"cpa": {
"modelFamily": "openai",
"models": [
{ "id": "gpt-5.5", "modelFamily": "openai" }
]
}
}
}/sync-model # fill missing fields, write back
/sync-model preview # show what would change without writing
/sync-model force # clear enrichable fields, then re-match (rewrites maps)
/sync-model force previewTab completion is available for preview / dry-run / force.
Use force when an older sparse/wrong thinkingLevelMap is stuck (safe mode never overwrites existing fields).
/disguise — disguise request headers
Makes pi's outgoing requests look like they come from the official Codex CLI or Claude Code CLI, by writing a headers map onto the provider in models.json. pi's core natively merges configured headers into every request and they override pi's default User-Agent: pi-coding-agent. Header values support $ENV_VAR interpolation and !cmd shell commands (same resolution as API keys).
Flow:
- Select a provider (the label shows whether it is already disguised and how many headers are set)
- Choose a preset:
- Codex CLI —
originator: codex_cli_rs,User-Agent: codex_cli_rs/<ver> (Arch Unknown; x86_64) kitty,OpenAI-Beta: responses=experimental - Claude Code —
anthropic-version: 2023-06-01,anthropic-beta: <full token list>,x-app: cli,User-Agent: claude-cli/<ver> (external, cli),anthropic-client-platform: claude-code - Custom headers — enter
Key: Value(orKey=Value) lines, blank line to finish - Clear disguise — remove
provider.headersentirely - Cancel
- Codex CLI —
- For Codex/Claude presets: enter a version for the
User-Agent(pre-filled with a default), then optionally merge extra/override headers on top - Preview the current → new header diff
- Confirm and write; run
/reload
The Codex preset targets openai-responses providers (originator + OpenAI-Beta). The Claude preset targets anthropic-messages providers (anthropic-version/anthropic-beta). Applying a Claude preset to an OpenAI-format provider is allowed but won't make sense — pick the preset that matches the upstream's expected protocol.
Default versions (DEFAULT_CODEX_VERSION, DEFAULT_CLAUDE_VERSION) and the anthropic-beta token list are constants at the top of index.ts; edit them to keep current.
Why
Custom providers don't inherit authoritative model metadata. models.dev exposes model limits (context, output), modalities, reasoning, and provider-level reasoning_options; pi's built-in registry can also provide compat details. Without thinkingLevelMap, the max / xhigh thinking levels may be unavailable or clamped. These commands fill that in by matching model ids.
Install
pi install git:github.com/superjeason/pi-model-manager
# or
pi install npm:@superjeason/pi-model-manager
# or local path
pi install ./pi-model-managerThen /reload.
Multi-select controls
| Key | Action |
|-----|--------|
| Space | Toggle current item |
| ↑ / ↓ | Move cursor |
| PageUp / PageDown | Page |
| Home / End | First / last |
| Enter | Confirm selection |
| Esc / Ctrl+C | Cancel (uses pi's tui.select.cancel plus low-level matchesKey("escape"); returns without changes) |
| type | Filter list (Unicode supported) |
| Backspace | Clear filter char |
| Ctrl+A | Select all visible (uses low-level matchesKey("ctrl+a")) |
| Ctrl+D | Deselect all (uses low-level matchesKey("ctrl+d")) |
Selection is tracked by model id, so filtering does not scramble checks. In non-TUI modes (RPC/print), falls back to one-by-one Add/Skip prompts. Cancel is never treated as "fall back to one-by-one".
Requirements & Notes
/add-providerprefers a standard OpenAI-compatible/modelsendpoint
(GET {baseUrl}/models→{"data":[{"id":"..."}]}).baseUrlwith or without trailing/v1both work.
If the endpoint is missing (e.g. anthropic-messages / google-generative-ai), type ids manually.- The API format picker is built dynamically at runtime from pi's api-provider
registry (
getApiProviders()from@earendil-works/pi-ai/compat), so it always matches the running pi: the 10 built-in formats (openai-completions,openai-responses,anthropic-messages,google-generative-ai,google-vertex,mistral-conversations,openai-codex-responses,azure-openai-responses,bedrock-converse-stream,pi-messages) plus any custom API types registered by other extensions. No hardcoded list to go stale. models.jsonmust be pure JSON (no//comments).- Overwriting an existing provider prompts for confirmation; other providers untouched.
- Enrich only adds missing fields; manual edits are never clobbered (unless you choose overwrite /
/sync-model force). - Metadata source order: models.dev
models.json+api.jsonfirst (canonical family preferred), then pi built-in registry. - Bare ids like
gpt-5.5preferopenai/gpt-5.5over reseller copies (vivgrid,302ai, …). - Optional
modelFamilyon provider or model overrides family inference. costis filled when missing or all zeros (common custom-provider placeholders); non-zero user costs are kept.- models.dev
models.jsonhas limits/modalities; pricing usually comes fromapi.jsonand is merged in. - models.dev responses are cached under
~/.cache/pi-model-manager/for 7 days; stale cache is used if refresh fails. - Uses pi theme tokens for multi-select colors and focus state (
selectedBg,accent,success,dim,muted,warning). - Focused rows use a full-width
selectedBgband plus an accent bar (▌); checked rows use[x]without stealing focus. - Runtime deps: Node built-ins only; peer:
@earendil-works/pi-coding-agent(providespi-tui).
Structure
pi-model-manager/
├── index.ts # /add-provider + /edit-provider + /sync-model + /disguise
├── enrich.ts # shared model-matching & config-filling
├── multi-select.ts # themed multi-select TUI component
├── package.json
└── README.md