Repository navigation
Conversation
|
The following comment was made by an LLM, it may be inaccurate: Based on my search, I found several related PRs that address similar functionality: Potential Related PRs:
These PRs seem to be addressing the same problem space of discovering models from providers. You may want to check if #32731 is a closed/incomplete predecessor or if these are complementary features addressing different provider types. |
Scope: dynamic model discovery for custom OpenAI-compatible providers: new server-side
|
|
Perfect, that's just what I needed! |
|
+1 |
|
really needed this, merge it asap |
|
This is really what I'm lacking of |
|
Would be awesome if this is included in the new release! |
|
This is exactly where opencide still lacks behind. Absolutely needed! |
|
We really need this! |
…viders
A configured provider can opt in (discover: true, or the v1 config's
discoverModels: true) to have opencode call GET {baseURL}/models and
add whatever the endpoint reports to the catalog, instead of
requiring every model to be hand-listed -- useful for LM Studio,
vLLM, llama.cpp/llama-swap, or any generic OpenAI-compatible
self-hosted endpoint.
Ported from upstream anomalyco#6231 (247 reactions/57
comments -- the second-highest reaction count found across a full
survey of this repo's open issues). Three substantial PRs sat open
and unmerged for 3+ months despite that reaction count. PR anomalyco#32731 (8
files, tightly scoped) is the primary source: this fork reuses its
discoverModels field name, which limit fields to read from the
response, the discovered-model shape, and the "configured/models.dev
wins" merge rule. Deliberately changed from anomalyco#32731: discovery is
opt-in per provider here, not automatic for every provider with a
baseURL -- always-on would fire a startup network request at every
existing custom provider. Unknown limits are left at 0 rather than
anomalyco#32731's guessed 128k default. PR anomalyco#42660's URL-with-existing-path
handling was reused, rewritten with this repo's own HttpClient/Schema
idioms instead of raw fetch in a try/catch; its UI and endpoint
weren't ported. PR anomalyco#27554 (100+ files) had nothing usable beyond
confirming scope overlap. New, since none of the three PRs target it:
a v2 Catalog.Service plugin (this repo didn't have a plugin/catalog
system when those PRs were written).
Runs in both of this repo's provider surfaces: the legacy provider
service (inline before the filter pass, what the TUI model picker
reads) and a new v2 Catalog plugin (packages/core/src/config/plugin/
provider-discovery.ts, once at startup then hourly, same schedule as
the models.dev refresh).
Known gaps: the legacy provider service has no periodic refresh (a
newly-loaded local model appears only after restart/reload); stale
models aren't hidden; discovered capabilities are guesses (no
vision/reasoning detection); only the standard OpenAI models response
shape is understood, not Ollama's native /api/tags; no settings UI or
HTTP endpoint; legacy auth only checks a stored credential or
options.apiKey, not plugin OAuth loaders.
New packages/core/test/provider-discovery.test.ts + config/
provider-discovery.test.ts (unit-level) and packages/opencode/test/
provider/discovery.test.ts (against a real local Bun.serve server,
not a mock). Verified against this fork's current dev, not just the
agent's own stale worktree base: bun typecheck clean across all 30
packages; packages/core 1187 pass (only the pre-existing pty flake);
packages/opencode 3617 pass, only the 4 pre-existing cf-ai-gateway
failures. bun run generate from packages/client correctly produced
no changes -- this is a config-only field, not a Protocol/HttpApi
schema change.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
@Enough1122 Sorry it took me so long to get to these changes, and thanks for the review! Addressed in the latest push:
While in there I fixed two more things:
Added tests for concurrency, failure isolation, the whitelist and the config |
|
Hi @Hona! |
…/models opencode builds its picker from models.dev plus the static `models` map in opencode.json. It does not call a custom provider's /v1/models — discovery is hardcoded to Ollama, LM Studio and vLLM at their default ports. A provider written with only npm + baseURL shows up as "Provider not found" with an empty list. Verified on 1.18.3, where `opencode models archrouter` listed nothing at all; general discovery is still an open PR (anomalyco/opencode#42660), so there is nothing to wait for. So the static list is now the default and --variants becomes --no-models, which only exists to clean up an older install. The key is resolved from ARCHROUTER_KEY, then first-key.txt, then whatever /connect saved, and written into options.apiKey: auth is required by default, so /v1/models answers 401 without it — including the probe this command runs to fetch that list, which is why --variants died on every auth-on install with "router not answering". Models opencode cannot reach are still listed, with the measurements kept in modelCaps.js and opencodeConfig.js rather than hidden: muse-spark-*-contributor- free and jev-1.13-free answer only on /v1/responses and /v1/messages and 500 on the chat path, and ling-3.0-flash-fin-free (400) plus ling-3.1-flash-free (429) are reproducibly dead upstream, 3/3 attempts each on 2026-10-04. The other 7 answer 200 through /v1/chat/completions. 206 tests pass: test-p0 52, test-auth-store 6, test-setup-sh 40, test-uninstall 48, test-setup-flow 60. The suite carried a test whose name asserted the false premise above; it is inverted now, and two more cover the apiKey and the default shape. Not tested: WARP registration end to end (it burns real Cloudflare slots), and opencode's own picker — the catalog was confirmed through `opencode --pure models`, because a local plugin's dependency install deadlocks the plain CLI (anomalyco/opencode#47212, unrelated to this repo).
|
This branch has been upgraded to V2. We're no longer taking PRs for V1. If you think this is still relevant, please port it over and open it for V2. — from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊 |
Issue for this PR
Closes #13891
Closes #29308
Closes #28999
Closes #25624
Closes #23327
Closes #26863
Type of change
What does this PR do?
Right now, setting up custom OpenAI-compatible providers (like LiteLLM, LM Studio, etc.) is pretty tedious because users have to manually type in every single model ID. This PR adds dynamic model discovery so we can just pull the available models directly from the provider's
/v1/modelsendpoint.Backend (
packages/opencode):POST /provider/discoverendpoint. It takes the base URL (plus optional API key and headers) and fetches{baseURL}/v1/modelsserver-side, returning either the model IDs or a safely typed error.@ai-sdk/openai-compatibleconfig provider has a base URL and key, we now fetch its models at provider-build time (following the same pattern as the gitlab discovery loader). Hardcoded config models still win, so discovery just gracefully fills in the gaps.Effect.forkScopedschedule to re-run discovery in the background so new remote models show up without requiring an app restart. Errors are swallowed here so an unreachable endpoint won't tank the provider load.src/provider/discover.ts(discoverOpenAICompatibleModels&DiscoverError), which is shared by both the UI endpoint and the background loop.Implementation note on path resolution: I made sure to handle subpaths correctly. If a user's base URL already contains a path segment (e.g.,
https://host/api/v1), I append/modelsto it. Usingnew URL("/v1/models", base)would otherwise strip the/api/v1prefix away.Frontend (
packages/app):client.provider.discover(via the regenerated SDK), parses the response, and automatically populates the model rows. Any connection issues surface nicely as toast notifications.How did you verify your code works?
discover.test.tsagainst a realBun.servemock. Covered auth headers,{env:}resolution, deduplication, 401s, malformed responses, timeouts, invalid URLs, and the path preservation logic.dialog-custom-provider-models.serve --port 4096), mocked a/v1/modelsresponse, and tested the UI button. Verified that connection refusals return a safe{"ok":false,"kind":"failed"}instead of blowing up with a 500, and bad URLs return{"ok":false,"kind":"invalidUrl"}.bun run typecheckacross both packages. The i18n parity tests also pass clean (979 assertions across 61 locales).Screenshots / recordings
Checklist