Skip to content

feat(server): let /provider return the connected providers only - #47678

Open
CannonRS wants to merge 2 commits into
anomalyco:devfrom
mQorva:provider-connected-list
Open

CannonRS wants to merge 2 commits into
anomalyco:devfrom
mQorva:provider-connected-list

Conversation

@CannonRS

@CannonRS CannonRS commented Sep 6, 2026

Copy link
Copy Markdown

Issue for this PR

Closes #47677

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

GET /provider always builds the full models.dev catalog. In the HTTP API test harness on
current dev that response is 4,168,456 bytes and takes ~1.5 s; the connected providers in the
same instance are 4,468 bytes. The catalog is 99.9% of the payload, and only the model picker
needs it.

The app bootstrap asks for it anyway, once globally and once per project directory, and
loadProvidersQuery has no staleTime, so any consumer mounting before the bootstrap result is
written fetches it again.

Two commits:

Server. An optional connected query parameter on GET /provider. With connected=true the
handler answers from Provider.list() and never reads the models.dev snapshot or the config
filter, so none of the expensive work happens. The parameter is optional and the default path is
untouched, so existing clients see no change. One deliberate difference in the connected view: the
full response also reports catalog providers that only have stored credentials, which cannot be
resolved without the catalog; they appear once the caller fetches it.

Client. The bootstrap requests the connected providers, then pulls the catalog once, three
seconds later, into the same cache entry. loadProvidersQuery defaults to connected-only on
purpose — every consumer shares the query key, so leaving the full catalog as the default means a
single early consumer pulls all of it and the saving is gone. The catalog is written with
setQueryData rather than fetchQuery, because fetchQuery rewrites the stored query options
and a staleTime override there leaves the entry permanently stale, making every consumer refetch.
Only the global entry warms the catalog; it is identical for every directory, so warming per
directory would pull the same payload once per project.

How did you verify your code works?

Measured in the HTTP API test harness, same instance, both endpoints:

GET /provider                 4,168,456 bytes   1507 ms
GET /provider?connected=true      4,468 bytes     19 ms

New regression test in packages/opencode/test/server/httpapi-provider.test.ts: the connected view
is non-empty, strictly smaller than the catalog, a subset of it, and lists exactly the providers it
returns. Verified that it fails without the change — with the early return disabled the catalog and
the connected view both return 159 providers and the size assertion fails.

New regression test in packages/app/src/context/global-sync/bootstrap.test.ts: the query asks for
{ connected: true } by default and for the full catalog only when explicitly requested.

  • bun test test/server/httpapi-provider.test.ts in packages/opencode: 6 pass, 1 skip, 0 fail
  • bun test --conditions=solid --preload ./happydom.ts src/context/global-sync/bootstrap.test.ts in
    packages/app: 11 pass, 0 fail
  • tsgo --noEmit in packages/opencode: clean
  • tsgo -b in packages/app: clean
  • prettier --check on all changed files: clean
  • oxlint on the changed files: 5 warnings, all present on dev before this change

The SDK and OpenAPI changes are the output of bun ./script/generate.ts.

Not measured: startup wall-clock in a packaged desktop build. The numbers above are payload size
and handler time from the test harness.

Screenshots / recordings

Not a UI change.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Ronny added 2 commits September 7, 2026 00:06
GET /provider always builds the full models.dev catalog, which is what the
model picker needs and nothing else does. A client that only renders the
currently selected model pays for the whole snapshot on every call.

Add an optional `connected` query parameter. With `connected=true` the
handler answers from `Provider.list()` alone and never touches the catalog
or the config filter. The parameter is optional and defaults to the existing
behavior, so current clients are unaffected.
The bootstrap fetched the full provider list on every start, for the global
scope and once more per project directory. Only the model picker needs the
models.dev catalog, and it is identical for every directory.

Request the connected providers during bootstrap and pull the catalog once,
a few seconds later, into the same cache entry. `loadProvidersQuery` defaults
to connected-only because every consumer shares its key: one that mounts
before the bootstrap has written its result would otherwise fetch the full
catalog on its own and undo the saving.

The catalog is written through `setQueryData` rather than `fetchQuery`, which
would rewrite the stored query options and leave the entry permanently stale.
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

I found one potentially related PR:

Related PR:

The current PR (#47678) takes a more targeted approach by adding a connected query parameter to return only connected providers (~4,468 bytes, 19ms) instead of the full catalog (~4,168,456 bytes, 1507ms), whereas #44132 focused on memoization and compression of the existing response.

No other duplicate PRs found addressing the same feature.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

App bootstrap fetches the whole models.dev catalog from /provider on every start, once per project

1 participant