Skip to content

refactor: Defer loading provider catalog at startup - #52816

Closed
devicenull wants to merge 1 commit into
anomalyco:devfrom
devicenull:defer-provider-catalog
Closed

devicenull wants to merge 1 commit into
anomalyco:devfrom
devicenull:defer-provider-catalog

Conversation

@devicenull

@devicenull devicenull commented Oct 2, 2026 •

Copy link
Copy Markdown

Issue for this PR

Closes #52821, #47677, #41078

Type of change

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

What does this PR do?

The provider list is loaded on every startup, even though it's only needed on the very first startup (or when you run /connect). This is a ~6MB json blob, and my machine takes ~850ms to load it. Given that this isn't necessary, this seems like a pretty big performance win.

How did you verify your code works?

I am actively using a version with this change - also verified that /connect still works

Screenshots / recordings

n/a

Checklist

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

If you do not follow this template your PR will be automatically rejected.

@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Hey! Your PR title Defer loading provider catalog at startup doesn't follow conventional commit format.

Please update it to start with one of:

  • feat: or feat(scope): new feature
  • fix: or fix(scope): bug fix
  • docs: or docs(scope): documentation changes
  • chore: or chore(scope): maintenance tasks
  • refactor: or refactor(scope): code refactoring
  • test: or test(scope): adding or updating tests

Where scope is the package name (e.g., app, desktop, opencode).

See CONTRIBUTING.md for details.

@github-actions github-actions Bot added the needs:compliance This means the issue will auto-close after 2 hours. label Oct 2, 2026
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

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

@devicenull devicenull changed the title Defer loading provider catalog at startup refactor: Defer loading provider catalog at startup Oct 2, 2026
@github-actions github-actions Bot removed needs:title needs:compliance This means the issue will auto-close after 2 hours. labels Oct 2, 2026
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Thanks for updating your PR! It now meets our contributing guidelines. 👍

@paoValle

paoValle commented Oct 6, 2026

Copy link
Copy Markdown

I measured this while looking at startup, and the win is real but a lot smaller than the 850 ms in the description — worth having the number before it is prioritised.

Method: build the CLI twice, once with the real catalog and once with an empty one, and time the compiled binary.

$ MODELS_DEV_API_JSON=/tmp/empty.json bun packages/opencode/script/build.ts --single --skip-embed-web-ui
$ ./packages/opencode/dist/opencode-darwin-arm64/bin/opencode --version   # N runs, wall clock
$ ./packages/opencode/dist/opencode-darwin-arm64/bin/opencode serve --port 4200   # until "listening"

Apple M4 Pro, macOS 27.0.1, bun 1.4.2, catalog api.json 5.3 MB / 226 providers:

build --version (median, n=10) serve → listening (median, n=6)
real catalog 301 ms 497 ms
empty catalog 274 ms 429 ms

So the whole catalog costs ~27 ms at CLI load and ~68 ms at server boot on this machine. For reference, JSON.parse of the 5.3 MB api.json alone is 13 ms (n=3), so the 850 ms is likely the decode/instantiation rather than the parse — which makes deferring it the right shape of fix, just a smaller one.

For scale, the CLI command graph currently costs more: a build of src/index.ts with the .command(...) registrations removed comes in at 113 ms for --version versus 301 ms, so ~188 ms of that 301 ms is modules the path never uses.

@moritzWa

moritzWa commented Oct 6, 2026

Copy link
Copy Markdown

I opened #53593 for the same thing before I found this one, sorry for the overlap.

One case this misses: the model picker also uses createDialogProviderOptions(), and when no provider is connected it shows "Popular providers" from provider_next. With this PR that section stays empty until /connect has been opened once. In mine I call the loader from createDialogProviderOptions on mount, so both dialogs get it.

On the size question above: the TUI fetches /provider on every bootstrap, and that includes every time it reloads for another directory, not only at startup. Here it's 6.7 MB and 185–420 ms on a warm server. Over 24 session switches across directories, the blocking part of that reload went from a median 495 ms to 87 ms without it.

Happy to close mine if you add the model picker case, or the maintainers can take whichever they prefer.

@devicenull

Copy link
Copy Markdown
Author

I measured this while looking at startup, and the win is real but a lot smaller than the 850 ms in the description — worth having the number before it is prioritised.

Method: build the CLI twice, once with the real catalog and once with an empty one, and time the compiled binary.

$ MODELS_DEV_API_JSON=/tmp/empty.json bun packages/opencode/script/build.ts --single --skip-embed-web-ui
$ ./packages/opencode/dist/opencode-darwin-arm64/bin/opencode --version   # N runs, wall clock
$ ./packages/opencode/dist/opencode-darwin-arm64/bin/opencode serve --port 4200   # until "listening"

Apple M4 Pro, macOS 27.0.1, bun 1.4.2, catalog api.json 5.3 MB / 226 providers:
build --version (median, n=10) serve → listening (median, n=6)
real catalog 301 ms 497 ms
empty catalog 274 ms 429 ms

So the whole catalog costs ~27 ms at CLI load and ~68 ms at server boot on this machine. For reference, JSON.parse of the 5.3 MB api.json alone is 13 ms (n=3), so the 850 ms is likely the decode/instantiation rather than the parse — which makes deferring it the right shape of fix, just a smaller one.

For scale, the CLI command graph currently costs more: a build of src/index.ts with the .command(...) registrations removed comes in at 113 ms for --version versus 301 ms, so ~188 ms of that 301 ms is modules the path never uses.

I was running this on a xen VM with limited memory - so my parse times are probably significantly longer.

I opened #53593 for the same thing before I found this one, sorry for the overlap.

One case this misses: the model picker also uses createDialogProviderOptions(), and when no provider is connected it shows "Popular providers" from provider_next. With this PR that section stays empty until /connect has been opened once. In mine I call the loader from createDialogProviderOptions on mount, so both dialogs get it.

On the size question above: the TUI fetches /provider on every bootstrap, and that includes every time it reloads for another directory, not only at startup. Here it's 6.7 MB and 185–420 ms on a warm server. Over 24 session switches across directories, the blocking part of that reload went from a median 495 ms to 87 ms without it.

Happy to close mine if you add the model picker case, or the maintainers can take whichever they prefer.

If yours solves the issue, I'm happy to close this in favor of yours. I was just tired of the slow startup times I was seeing.

@thdxr

thdxr commented Oct 10, 2026

Copy link
Copy Markdown
Member

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 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊

@thdxr thdxr closed this Oct 10, 2026
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.

Provider catalog is slowly loaded at startup

4 participants