pi-custom-openai-providers

Maintain multiple OpenAI-compatible providers in pi with /custom-providers; switch between them with /model.

Packages

Package details

extension

Install pi-custom-openai-providers from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:pi-custom-openai-providers
Package
pi-custom-openai-providers
Version
0.2.1
Published
Aug 7, 2026
Downloads
441/mo · 441/wk
Author
zongchenghao
License
MIT
Types
extension
Size
41.6 KB
Dependencies
0 dependencies · 1 peer
Pi manifest JSON
{
  "extensions": [
    "./src/index.ts"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

pi-custom-openai-providers

Maintain multiple OpenAI-compatible providers in pi and switch between them with /model.

License: MIT pi

Features

  • One command for everything/custom-providers with add / list / edit / remove subcommands
  • Switch with /model — each provider registers as custom-<name> and appears in pi's model picker
  • Persistent storage in pi's official path~/.pi/agent/models.json (pi's own models.json schema, written mode 0600)
  • Full models.md schemaheaders / compat / thinkingLevelMap / cost.tiers / modelOverrides / input modes (text / image) / custom api etc. all supported
  • Editor for the full specadd <name> --raw / edit <name> --raw opens $VISUAL / $EDITOR (falls back to vi) so you can edit the complete provider JSON including fields the 3-prompt flow doesn't expose
  • Auto-migration — the legacy <cwd>/.pi/custom-providers.json (multi-provider) and <cwd>/.pi/custom-openai.json (single-provider) are detected on first load, copied into ~/.pi/agent/models.json, then deleted
  • Env-var seedCUSTOM_OPENAI_* env vars create a custom-default provider when no providers are configured anywhere (CI / scripting friendly)
  • Zero runtime dependencies — pure TypeScript loaded by pi's jiti; only node:fs / node:path / node:os / node:child_process

Installation

⚠ Install in exactly one location. Installing both globally (~/.pi/agent/extensions/) and project-locally (.pi/extensions/) — or installing via pi install while a symlink / dev copy already exists — causes pi to load the extension twice. pi then adds a :1 suffix to the command's invocation name, so Tab autocomplete inserts /custom-providers:1 instead of /custom-providers . The extension detects this at load time, logs a warning to the terminal, and skips the duplicate command registration, but the cleanest fix is to remove one installation.

One-shot install (recommended)

# From GitHub
pi install github:playmaker/pi-custom-openai-providers

# Or from npm
pi install npm:pi-custom-openai-providers

Manual install (project-local)

git clone https://github.com/playmaker/pi-custom-openai-providers
cd <your-project>
ln -s "$(pwd)/../pi-custom-openai-providers" .pi/extensions/pi-custom-openai-providers

Manual install (global)

git clone https://github.com/playmaker/pi-custom-openai-providers ~/.pi/agent/extensions/pi-custom-openai-providers

Quick try (no install)

pi -e /path/to/pi-custom-openai-providers/src/index.ts

Usage

Add a provider

/custom-providers add deepseek
  Base URL: https://api.deepseek.com/v1
  API key: sk-...
  Model id: deepseek-chat

<name> is a short identifier without whitespace. It becomes part of the provider id custom-<name> shown in /model.

List configured providers

/custom-providers

Shows a persistent widget above the editor listing all configured providers with their endpoints and models. Stays visible until the next command or model change.

Edit a provider

/custom-providers edit deepseek
  Base URL [https://api.deepseek.com/v1]:
  API key (leave blank to keep current ending in xxxx):
  Model id [deepseek-chat]:

Empty input keeps the current value. Pressing Esc on any prompt cancels the edit and emits Edit cancelled. (nothing is saved or re-registered). Pressing Enter through all three prompts with no changes emits No changes for "<name>". and skips the save + re-register. Any actual change emits Updated custom provider "<name>".

Add or edit via the editor (full schema)

The 3-prompt flow only edits baseUrl / apiKey / the first model's id. For everything else in pi's models.md schema (headers / compat / thinkingLevelMap / cost.tiers / modelOverrides / image input / custom api per model / multiple models / ...), pass --raw and the extension opens $VISUAL / $EDITOR (falls back to vi) with the full provider JSON:

/custom-providers add my-openrouter --raw
/custom-providers edit my-openrouter --raw

The editor temp file is created with mode 0600 and cleaned up on exit. The extension validates the JSON before writing: it must be parseable, have a non-empty baseUrl, and at least one models entry with a non-empty id. Each model gets api: "openai-completions" injected if missing.

Honored environment variables for the editor (in order):

  1. $VISUAL
  2. $EDITOR
  3. vi (fallback if neither is set)

If the editor is not found, you get Editor "..." not found. Set $VISUAL or $EDITOR to a valid editor path. instead of a silent failure. If the file's contents are unchanged after you exit, the edit is a no-op and the disk state is preserved.

Remove a provider

/custom-providers remove deepseek

Switch providers

After configuring, use /model and pick custom-<name>/<modelId>:

/model
> custom-openrouter / anthropic/claude-sonnet-4.5

Tab completion

The /custom-providers command surfaces its subcommand shape through pi's autocomplete:

  • Typing /custom-providers shows the dropdown with [list | add <name> | edit <name> | remove <name>] — <description>.
  • Typing /custom-providers (with a trailing space) lists the four subcommands; keep typing to filter (e.g. aadd).
  • After /custom-providers edit or /custom-providers remove , existing provider names are offered for tab completion, filtered as you type.

Configuration

Storage

~/.pi/agent/models.json (mode 0600) — pi's official model.json schema. The extension adds a key per provider under the top-level providers object, prefixed with custom- to avoid clashing with built-ins (e.g. custom-my-qianwen).

Full ProviderEntry shape (every field from models.md is supported):

{
  "providers": {
    "custom-my-qianwen": {
      "baseUrl": "https://dashscope.aliyuncs.com/compatible-mode/v1",
      "apiKey": "$DASHSCOPE_KEY",
      "headers": {
        "x-portkey-api-key": "$PORTKEY_KEY"
      },
      "compat": {
        "supportsDeveloperRole": false,
        "supportsReasoningEffort": false
      },
      "models": [
        {
          "id": "qwen3-max",
          "name": "Qwen3 Max",
          "input": ["text"],
          "reasoning": true,
          "contextWindow": 262144,
          "maxTokens": 8192,
          "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 }
        }
      ]
    }
  }
}

You can also edit ~/.pi/agent/models.json directly with any text editor — the extension is just a convenience layer on top of pi's own config. Add the path to your global gitignore (or HOME-level) if you don't want it in version control:

.pi/agent/models.json

Environment variable seed

If no providers are configured anywhere and these are set on startup, a custom-default provider is created and persisted to ~/.pi/agent/models.json:

Variable Example
CUSTOM_OPENAI_BASE_URL https://api.deepseek.com/v1
CUSTOM_OPENAI_API_KEY sk-...
CUSTOM_OPENAI_MODEL deepseek-chat

Legacy migration

The earlier single-provider .pi/custom-openai.json is auto-migrated as a default provider on first load; the old file is deleted.

Examples

DeepSeek

/custom-providers add deepseek
  Base URL: https://api.deepseek.com/v1
  API key: sk-...
  Model id: deepseek-chat

OpenRouter (route to any upstream model)

/custom-providers add openrouter
  Base URL: https://openrouter.ai/api/v1
  API key: sk-or-...
  Model id: anthropic/claude-sonnet-4.5

Local llama.cpp / Ollama OpenAI-compat mode

/custom-providers add local-llama
  Base URL: http://localhost:8080/v1
  API key: local
  Model id: llama-3.1-70b

Multiple endpoints side-by-side

/custom-providers
  Custom providers (3):
    custom-deepseek    https://api.deepseek.com/v1     model=deepseek-chat
    custom-openrouter  https://openrouter.ai/api/v1    model=anthropic/claude-sonnet-4.5
    custom-local-llama http://localhost:8080/v1        model=llama-3.1-70b

/model   # pick custom-openrouter / anthropic/claude-sonnet-4.5

Compatibility

  • pi ≥ 0.83 — uses registerProvider, registerCommand, setWidget, unregisterProvider
  • Node.js ≥ 20 — uses node:fs, node:path
  • Any endpoint speaking the OpenAI Chat Completions protocol: DeepSeek, OpenRouter, OpenAI, Azure OpenAI gateways, llama.cpp server, vLLM, Ollama's OpenAI-compat mode, custom corporate gateways, etc.

If your endpoint speaks a different protocol, adjust api: "openai-completions" in src/index.ts to the appropriate value (see pi docs for the full list).

Project layout

pi-custom-openai-providers/
├── src/
│   └── index.ts        # extension entry — exported default function(pi)
├── package.json        # pi.extensions → "./src/index.ts"
├── README.md
├── README_zh.md
├── LICENSE
└── .gitignore

This follows the official pi extension convention (pi.extensions in package.json pointing at the entry file). No build step — pi loads src/index.ts directly via jiti.

Development

git clone https://github.com/playmaker/pi-custom-openai-providers
cd pi-custom-openai-providers

There is no build step and no runtime npm dependency. To test against your local pi:

# Symlink into pi's global extensions directory
ln -s "$(pwd)" ~/.pi/agent/extensions/pi-custom-openai-providers

# Or load directly via -e flag
pi -e "$(pwd)/src/index.ts"

Run pi and try /custom-providers add test to exercise the flow.

Contributing

Issues and pull requests welcome at https://github.com/playmaker/pi-custom-openai-providers.

License

MIT © 2026 Zongchenghao

Acknowledgements

中文文档