feat: dynamic model discovery from provider APIs#109
feat: dynamic model discovery from provider APIs#109tim4net wants to merge 8 commits intoPizzaface:mainfrom
Conversation
Adds an extension that fetches available models from provider APIs (OpenAI, Anthropic) on session start and registers any that are missing from the built-in registry. This ensures newly released models (e.g., GPT-5.4) are available without waiting for an upstream pi-ai update. Discovered models are registered under synthetic provider names (e.g., "openai-discovered") to avoid replacing curated built-in models. Results are cached to disk for 24 hours to avoid hitting APIs every session. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The web server's /api/models endpoint previously only returned the hardcoded pi-ai model list. Now it also reads an optional models.json from the data directory (/app/data/models.json in Docker, configurable via PIZZAPI_CUSTOM_MODELS_PATH env var) and merges custom models into the response. Built-in models are never overridden. Also adds modelRegistry.refresh() to the relay's get_available_models handler so the web UI picks up models.json changes without a CLI restart. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: af5fd46543
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| newModels = await discoverNewModels({ provider, baseUrl, apiKey, existingModelIds: existingIds }); | ||
| // Store ALL fetched models in cache (before filtering) | ||
| const allFetched = await PROVIDER_FETCHERS[provider].fetch(baseUrl, apiKey); |
There was a problem hiding this comment.
Reuse discovery results instead of refetching providers
The cache-miss path calls provider discovery twice (discoverNewModels(...) and then PROVIDER_FETCHERS[provider].fetch(...)), which doubles startup latency/API traffic and can produce inconsistent behavior when one call fails and the other succeeds (e.g., models are discovered but not registered this session, or registered but cached as empty). This is especially painful on timeout/error paths because each provider can incur two 10s waits.
Useful? React with 👍 / 👎.
| ); | ||
| const customModelsPath = process.env.PIZZAPI_CUSTOM_MODELS_PATH | ||
| ?? join(process.env.AUTH_DB_PATH ? join(process.env.AUTH_DB_PATH, "..") : "/app/data", "models.json"); | ||
| const models = getAllModels(customModelsPath); |
There was a problem hiding this comment.
Keep listed models compatible with chat model resolution
GET /api/models now returns custom entries from models.json, but POST /api/chat still resolves models through getModel(provider, modelId) from the built-in pi-ai registry. Any custom id not present upstream can be returned as “available” and then fail at chat time with a 500, so the two endpoints no longer agree on what is usable.
Useful? React with 👍 / 👎.
Patches ModelRegistry.registerProvider() to merge models additively instead of replacing all models for the provider. New models are only added if their ID isn't already registered; existing curated models are untouched. Also falls back to an existing model's baseUrl when none is provided, so callers don't need to repeat the endpoint. This allows the model discovery extension to register under the real provider name (e.g. "openai" instead of "openai-discovered"), giving correct sort order and provider labeling in the UI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The built-in OpenAI baseUrl is https://api.openai.com/v1, so appending /v1/models produces a double-path. Now detects if baseUrl already ends with /v1 and uses /models directly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds fetchOllamaModels() and wires it into the discovery extension. Set PIZZAPI_OLLAMA_URL to automatically scan an Ollama server for all available models on session start (cached 24h). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- zAI: JWT HS256 auth from ZAI_API_KEY env var, discovers glm-4.x/5 models - Ollama: plain fetch from PIZZAPI_OLLAMA_URL, all local models Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
I majorly broke apart |
Summary
openai-discovered) to avoid replacing curated built-in models~/.pizzapi/discovered-models-cache.json(24h TTL) to avoid hitting APIs every sessionskipModelDiscoveryflag toBuildExtensionFactoriesOptionsfor safe modeMotivation
When providers release new models (e.g., GPT-5.4), PizzaPi users can't see or use them until the upstream
pi-aipackage manually updates its hardcoded registry. This extension bridges that gap by querying provider APIs directly.Built-in models are never replaced — their curated settings (context window caps, compat flags, costs) are preserved. Only models with no existing registry entry are added with conservative defaults.
New files
packages/cli/src/extensions/model-discovery-providers.ts— OpenAI and Anthropic API fetcherspackages/cli/src/extensions/model-discovery.ts— Extension factory, caching, orchestrationpackages/cli/src/extensions/model-discovery.test.ts— 16 tests usingBun.serve()for HTTP mockingTest plan
factories.test.tswithskipModelDiscoveryflag testpizza modelsand confirm newly released models appear🤖 Generated with Claude Code