pi-dynamic-models0.2.0
Discover models from OpenAI-compatible endpoints and register them dynamically in Pi
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
pi-dynamic-models
Pi extension that discovers models from OpenAI-compatible endpoints and registers them as Pi providers dynamically.
Instead of maintaining a static ~/.pi/agent/models.json, you keep a small endpoint list in ~/.pi/agent/dynamic-models.json and let the extension refresh provider/model registration automatically.
Getting started
pi install npm:pi-dynamic-models
Run /reload if Pi is already running, then /dynamic-models-config and choose Add endpoint. Enter a name and the endpoint's OpenAI-compatible base URL, then choose authentication if needed. Models and advertised parameters are discovered immediately; there is no model list to maintain.
The command creates ~/.pi/agent/dynamic-models.json automatically. You can also create or edit this file manually, then run /dynamic-models-reload.
Minimal config example:
{
"endpoints": [
{
"name": "my-provider",
"baseUrl": "http://localhost:8000/v1"
}
]
}
If your endpoint requires bearer auth, the smallest useful version is:
{
"endpoints": [
{
"name": "my-provider",
"baseUrl": "http://localhost:8000/v1",
"apiKey": "env:MY_PROVIDER_API_KEY",
"authHeader": true
}
]
}
Features
- discovers models from OpenAI-compatible
GET /v1/modelsendpoints - registers one Pi provider per configured endpoint with
pi.registerProvider() - removes providers that disappear from config on the next refresh
- filters out embedding models by default
- supports provider-level defaults and per-model overrides
- supports auth via bearer token, custom headers, env vars, and shell commands
- exposes commands for reload and status
- logs refresh activity to
~/.pi/agent/dynamic-models.log - supports verbose debug logging with
PI_DYNAMIC_MODELS_DEBUG=1
Commands
/dynamic-models-config- add, edit, or remove endpoints interactively and refresh discovery immediately
/dynamic-models-reload- reloads endpoints from config and re-registers providers
/dynamic-models-status- shows all currently discovered providers and models, including known metadata
How it works
At startup and on session_start, the extension:
- reads
~/.pi/agent/dynamic-models.json - fetches each endpoint's model list
- normalizes the returned model metadata
- applies defaults and per-model overrides
- registers a Pi provider for that endpoint
If an endpoint is removed from config, its provider is unregistered on the next refresh.
Installation
Install from npm:
pi install npm:pi-dynamic-models
Alternatively, install directly from GitHub:
pi install git:github.com/apetersson/pi-dynamic-models
For local development:
pi install /absolute/path/to/pi-dynamic-models
Run /reload in Pi after installing or changing the extension.
Requires Node.js 22.19.0 or newer and pi 1.x (@earendil-works/pi-coding-agent). Validated against pi 1.0.0.
To try the extension without installing it, run from this checkout:
pi -e ./src/index.ts
Manage endpoints in Pi
Run /dynamic-models-config in an interactive Pi session:
- Add endpoint asks for a unique provider name, base URL (for example
http://localhost:8000/v1), and optional bearer authentication using an environment variable. - Edit endpoint lets you change its name, base URL, custom models URL, authentication, API, or enabled state. Existing custom headers, filters, defaults, and model overrides are preserved.
- Remove endpoint asks for confirmation, saves the change, and unregisters the provider.
Changes are saved to the configured JSON path and discovery runs immediately. Cancel a dialog to leave the file unchanged. If discovery fails, the endpoint remains saved so you can correct it or retry with /dynamic-models-reload.
Model IDs, context windows, output limits, input modalities, reasoning support, pricing, and compatibility flags are read from the model-list response when available. Missing values use defaults (128,000 context tokens, 16,384 output tokens, text input, no reasoning, and zero reported cost); these are fallback settings, not measured capabilities. The extension does not probe context limits or send test completions. Capability flags are read in either shape an endpoint may use: a map of booleans ({ "vision": true }) or a list of enabled names (["chat", "vision"]). Use the JSON configuration for custom headers, filters, and model overrides.
New endpoints use the OpenAI chat completions API. For a Responses endpoint, change API to openai-responses. Authentication environment variables must be available to the running Pi process.
Configuration
Default config path:
~/.pi/agent/dynamic-models.json
Override it with:
export PI_DYNAMIC_MODELS_CONFIG=/path/to/dynamic-models.json
Top-level shape
{
"endpoints": []
}
Endpoint fields
Each endpoint supports:
name: provider name as shown in PibaseUrl: base API URL, usually ending in/v1modelsUrl: optional full URL for model discoverymodelsPath: optional path appended tobaseUrl, default:modelsapi: Pi provider API type, default:openai-completions- allowed values:
openai-completionsopenai-responses
- allowed values:
apiKey: optional auth value- literal string:
"abc123" - env var:
"env:MY_API_KEY" - shell command:
"!security find-generic-password -ws my-key"
- literal string:
authHeader: whentrue, sendsAuthorization: Bearer <apiKey>headers: additional request headers using the same value formats asapiKeytimeoutMs: request timeout for model discoveryenabled: enable/disable endpointincludeEmbeddings: include embedding models, defaultfalseincludePatterns: only include matching model IDs, glob syntaxexcludePatterns: exclude matching model IDs, glob syntaxdefaults: fallback metadata for discovered modelsmodelOverrides: exact model-id overrides
defaults and modelOverrides
Supported fields:
namereasoninginputcontextWindowmaxTokenscostcompatheaders
modelOverrides also supports:
enabled
Example config
{
"endpoints": [
{
"name": "EXAMPLE_SERVER",
"baseUrl": "http://EXAMPLE_SERVER:8000/v1",
"apiKey": "env:EXAMPLE_SERVER_API_KEY",
"authHeader": true,
"defaults": {
"reasoning": true,
"input": ["text"],
"contextWindow": 262144,
"maxTokens": 32768,
"compat": {
"supportsDeveloperRole": false,
"supportsReasoningEffort": false,
"thinkingFormat": "qwen-chat-template"
}
},
"excludePatterns": ["text-embedding-*", "*embedding*", "*embed*"],
"modelOverrides": {
"Qwen3.5-122B-A10B-5bit": {
"name": "Qwen3.5 122B A10B 5bit",
"maxTokens": 65536
}
}
}
]
}
A more minimal example is also available in:
dynamic-models.example.json
Authentication
Standard bearer auth
If your endpoint expects:
Authorization: Bearer <token>
use:
{
"apiKey": "env:EXAMPLE_SERVER_API_KEY",
"authHeader": true
}
Custom header auth
If your endpoint expects something like x-api-key:
{
"authHeader": false,
"headers": {
"x-api-key": "env:EXAMPLE_SERVER_API_KEY"
}
}
Shell-command-backed secrets
{
"apiKey": "!security find-generic-password -ws EXAMPLE_SERVER_API_KEY",
"authHeader": true
}
Model normalization
Many OpenAI-compatible /v1/models endpoints only return a model id and very little metadata.
This extension tries to infer or normalize from several common field names, including:
- model id/name
- context window (including top-level
context_lengthand nestedtop_provider.context_length) - max output tokens (including nested
top_provider.max_completion_tokens) - reasoning support
- input modalities
- cost/pricing
- compat flags
When an endpoint does not return enough metadata, defaults and modelOverrides fill the gap.
Status output
/dynamic-models-status shows:
- config path
- loaded providers
- per-provider metadata such as base URL, models URL, API type, authHeader
- every discovered model
- known metadata for each model
- skipped providers
- errors from the last refresh
Logging
Log file:
~/.pi/agent/dynamic-models.log
The extension appends:
- refresh summaries
- discovery errors
Enable verbose console debugging with:
export PI_DYNAMIC_MODELS_DEBUG=1
This adds debug output for:
- refresh start/finish
- endpoint discovery attempts
- model counts
- error context
Notes
- embedding models are excluded by default because Pi expects chat/tool-capable models
- the extension refreshes on startup and on
session_start - disappearing endpoints are unregistered automatically
Development
Install dependencies:
npm install
Run TypeScript checks and tests:
npm run verify
Coverage:
npm run test:coverage
Publishing
From a clean checkout with dependencies installed:
npm ci
npm run verify
npm publish --dry-run
npm publish
Publishing runs TypeScript checks and tests automatically. The npm package contains the TypeScript extension, README, example configuration, and license. Pi loads the source directly; no build step is required. For subsequent releases, bump the version before publishing.
Repository structure
src/index.ts: extension implementationdynamic-models.example.json: minimal example configtest/: testspackage.json: extension package metadata and scripts