core: embed models.dev snapshot instead of compile-time define - #41838
Open
kitlangton wants to merge 2 commits into
Open
core: embed models.dev snapshot instead of compile-time define#41838kitlangton wants to merge 2 commits into
kitlangton wants to merge 2 commits into
Conversation
The models.dev catalog snapshot previously reached core only through the OPENCODE_MODELS_DEV compile-time define, injected solely by the CLI builds. Any embedded/SDK-built server got no snapshot: with a cold cache and fetch disabled or failed, the catalog was empty, ModelsDevPlugin registered zero env methods, and the provider env-key credential chain never engaged even with e.g. ANTHROPIC_API_KEY set. Commit the snapshot at packages/core/src/models-dev/snapshot.txt (raw api.json, refreshed via script/update-models-snapshot.ts) and load it in loadSnapshot via a static text import, so every build profile gets the same boot-time floor. Runtime fetch/refresh behavior is unchanged: file and KV cache still take precedence, and the periodic refresh still fetches on top. Delete the define plumbing from the CLI build scripts and vite config, and the build-time fetch in script/generate.ts.
kitlangton
marked this pull request as ready for review
August 11, 2026 20:55
Vite's built-in asset plugin claims known asset types (.txt) before normal-priority plugins load, rewriting the import to an asset URL string — the models.dev snapshot shipped as "/assets/snapshot-*.txt" and cold-cache catalog access died decoding it. Pre-existing instances of the same class: the node bundle's prompt and tool-description .txt imports. enforce: "pre" intercepts first; a build assertion now fails the build if text imports surface as asset URLs, since the bundle still builds and runs --help convincingly without their content. Also: drop the redundant text.d.ts (bun-types already declares *.txt), give the snapshot refresh script a provider-count floor and Windows-safe path printing.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Moves the models.dev catalog snapshot into core as a real static import. The snapshot lives at
packages/core/src/models-dev/snapshot.txt(rawapi.json), is loaded inModelsDev.layerviaimport snapshotText from "./models-dev/snapshot.txt" with { type: "text" }, and is refreshed withbun run script/update-models-snapshot.tsinpackages/core. TheOPENCODE_MODELS_DEVcompile-time define and its build-time fetch plumbing are deleted.Why
The catalog snapshot reached core only through
OPENCODE_MODELS_DEV, a bare identifier injected as a define exclusively by the CLI builds. Any embedded/SDK-built server got no snapshot: with a cold cache and fetch disabled or failed, the catalog was empty,ModelsDevPluginregistered zero env methods, and the provider env-key credential chain inintegration.tsresolveConnectionssilently never engaged even when e.g.ANTHROPIC_API_KEYwas set. CLI users were masked by the define plus saved credentials, so the dead chain only showed in embedded profiles.How
packages/core/script/update-models-snapshot.tsfetcheshttps://models.opencode.ai/api.json, validates it parses non-empty, and writessrc/models-dev/snapshot.txt; wired as theupdate-models-snapshotpackage script.loadSnapshotdecodes the imported text through the sameCatalogJsonschema used byfetchAndWrite(shareddecodeCataloghelper), so a corrupt snapshot fails loudly instead of silently..txttext import rather thansnapshot.json with { type: "text" }— Vite's built-in JSON plugin parses.jsonregardless of import attributes, which would hand the CLI build an object where the code expects a string..txtis text natively under bun (test runner and compiled builds), typechecks via asrc/text.d.tswildcard declaration mirroring the existingmarkdown.d.ts, and the CLI'srawTextPluginwas extended from.mdto.md/.txtfor the vite-node build.declare const OPENCODE_MODELS_DEVplumbing inmodels-dev.ts, the define entries inpackages/cli/script/build.tsandpackages/cli/vite.node.config.ts(includingNodeBuildInput.models), andpackages/cli/script/generate.ts(the build-time fetch, now unused).ModelsDev.Optionsgains an optionalsnapshot: boolean(defaulttrue) so tests exercising cold-cache fetch paths can disable the bundled floor; production callers are unaffected.linguist-generatedin.gitattributes.Scope
The snapshot is the boot-time floor, not a replacement for live fetch: population order (file → KV cache → bundled snapshot → fetch) and the periodic refresh loop are unchanged. Trade-off: the bundled snapshot ages with the repo where CLI releases previously fetched a fresh copy at build time — but the 5-minute background refresh still updates the catalog on top at runtime, so there is no behavior regression, and hermetic builds (e.g. nix) no longer need a network fetch or
MODELS_DEV_API_JSONinjection. Regenerate withbun run update-models-snapshotinpackages/corewhenever freshness matters.Testing
get()returns the bundled snapshot with a non-empty provider list andenvironmentnames populated (anthropic→ANTHROPIC_API_KEY), confirming the env-key chain has data in embedded profiles.snapshot: falsewhere the bundled floor would mask the behavior under test.packages/core:bun run typecheckclean,bun test1665 pass / 0 fail.packages/cli:bun run typecheckclean;script/build-node.ts --bundle-only --skip-installbuilds and the bundle runs--version/--help(exercises the.txtimport through the vite build).bun run typecheckclean (33/33 tasks).bunx oxlinton touched files: 0 errors, no new warnings.Review round (adversarial, pre-handoff)
A full adversarial review verified: population order byte-identical to v2 (file → KV → snapshot → fetch), refresh loop and cache writes ungated by snapshot presence (no bundle-time freeze), no dangling
OPENCODE_MODELS_DEV/generate.ts references, snapshot artifact valid (183 providers), parse lazy (cold-cache only). It also found one Critical, fixed in the follow-up commit:.txtis a Vite built-in asset type, so the raw-text plugin'sloadnever ran — the node bundle containedsnapshotText = "/assets/snapshot-*.txt"and cold-cache catalog access died decoding it. Fixed withenforce: "pre", plus a build assertion that fails if any text import surfaces as an asset URL (the bundle builds and runs--helpconvincingly without its content, so only content assertions catch this class)."/assets/anthropic-*.txt"as prompt/tool-description strings (base.txt, anthropic prompt, patch tool). The build assertion now guards these too.text.d.tsdeleted (bun-types already declares*.txt— the earlier claim about needing it was wrong), refresh script gained a 100-provider floor and Windows-safe path printing.