From e3c1861a3ee315e1c81bd7f0aee5e3c9faf7af83 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Sat, 24 Jan 2026 12:26:57 -0500 Subject: [PATCH] get rid of models.dev macro --- packages/opencode/.gitignore | 1 + packages/opencode/script/build.ts | 8 +++ packages/opencode/src/flag/flag.ts | 1 + packages/opencode/src/global/index.ts | 4 -- .../opencode/src/provider/models-macro.ts | 14 ----- packages/opencode/src/provider/models.ts | 52 +++++++++++++------ packages/opencode/test/preload.ts | 11 +--- 7 files changed, 48 insertions(+), 43 deletions(-) delete mode 100644 packages/opencode/src/provider/models-macro.ts diff --git a/packages/opencode/.gitignore b/packages/opencode/.gitignore index e057ca61f..69643b7af 100644 --- a/packages/opencode/.gitignore +++ b/packages/opencode/.gitignore @@ -2,3 +2,4 @@ research dist gen app.log +src/provider/models-snapshot.ts diff --git a/packages/opencode/script/build.ts b/packages/opencode/script/build.ts index cb88db2c4..e25a3c5f5 100755 --- a/packages/opencode/script/build.ts +++ b/packages/opencode/script/build.ts @@ -15,6 +15,14 @@ process.chdir(dir) import pkg from "../package.json" import { Script } from "@opencode-ai/script" +// Fetch and generate models.dev snapshot +const modelsData = await fetch(`https://models.dev/api.json`).then((x) => x.text()) +await Bun.write( + path.join(dir, "src/provider/models-snapshot.ts"), + `// Auto-generated by build.ts - do not edit\nexport const snapshot = ${modelsData} as const\n`, +) +console.log("Generated models-snapshot.ts") + const singleFlag = process.argv.includes("--single") const baselineFlag = process.argv.includes("--baseline") const skipInstall = process.argv.includes("--skip-install") diff --git a/packages/opencode/src/flag/flag.ts b/packages/opencode/src/flag/flag.ts index d106c2d86..0274dcc82 100644 --- a/packages/opencode/src/flag/flag.ts +++ b/packages/opencode/src/flag/flag.ts @@ -46,6 +46,7 @@ export namespace Flag { export const OPENCODE_EXPERIMENTAL_LSP_TOOL = OPENCODE_EXPERIMENTAL || truthy("OPENCODE_EXPERIMENTAL_LSP_TOOL") export const OPENCODE_DISABLE_FILETIME_CHECK = truthy("OPENCODE_DISABLE_FILETIME_CHECK") export const OPENCODE_EXPERIMENTAL_PLAN_MODE = OPENCODE_EXPERIMENTAL || truthy("OPENCODE_EXPERIMENTAL_PLAN_MODE") + export const OPENCODE_MODELS_URL = process.env["OPENCODE_MODELS_URL"] function number(key: string) { const value = process.env[key] diff --git a/packages/opencode/src/global/index.ts b/packages/opencode/src/global/index.ts index 25595abcd..d3011b415 100644 --- a/packages/opencode/src/global/index.ts +++ b/packages/opencode/src/global/index.ts @@ -22,10 +22,6 @@ export namespace Global { cache, config, state, - // Allow overriding models.dev URL for offline deployments - get modelsDevUrl() { - return process.env.OPENCODE_MODELS_URL || "https://models.dev" - }, } } diff --git a/packages/opencode/src/provider/models-macro.ts b/packages/opencode/src/provider/models-macro.ts deleted file mode 100644 index e8fd353c9..000000000 --- a/packages/opencode/src/provider/models-macro.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Global } from "../global" - -export async function data() { - const path = Bun.env.MODELS_DEV_API_JSON - if (path) { - const file = Bun.file(path) - if (await file.exists()) { - return await file.text() - } - } - const url = Global.Path.modelsDevUrl - const json = await fetch(`${url}/api.json`).then((x) => x.text()) - return json -} diff --git a/packages/opencode/src/provider/models.ts b/packages/opencode/src/provider/models.ts index 1f7b74081..68eac37f2 100644 --- a/packages/opencode/src/provider/models.ts +++ b/packages/opencode/src/provider/models.ts @@ -2,9 +2,16 @@ import { Global } from "../global" import { Log } from "../util/log" import path from "path" import z from "zod" -import { data } from "./models-macro" with { type: "macro" } import { Installation } from "../installation" import { Flag } from "../flag/flag" +import { lazy } from "@/util/lazy" + +// Try to import bundled snapshot (generated at build time) +// Falls back to undefined in dev mode when snapshot doesn't exist +/* @ts-ignore */ +const SNAPSHOT = await import("./models-snapshot") + .then((m) => m.snapshot as Record) + .catch(() => undefined) export namespace ModelsDev { const log = Log.create({ service: "models.dev" }) @@ -76,18 +83,24 @@ export namespace ModelsDev { export type Provider = z.infer - export async function get() { - refresh() + function url() { + return Flag.OPENCODE_MODELS_URL || "https://models.dev" + } + + export const Data = lazy(async () => { const file = Bun.file(filepath) const result = await file.json().catch(() => {}) - if (result) return result as Record - if (typeof data === "function") { - const json = await data() - return JSON.parse(json) as Record - } - const url = Global.Path.modelsDevUrl - const json = await fetch(`${url}/api.json`).then((x) => x.text()) - return JSON.parse(json) as Record + if (result) return result + if (SNAPSHOT) return SNAPSHOT + if (Flag.OPENCODE_DISABLE_MODELS_FETCH) return {} + const json = await fetch(`${url()}/api.json`).then((x) => x.text()) + return JSON.parse(json) + }) + + export async function get() { + refresh() + const result = await Data() + return result as Record } export async function refresh() { @@ -96,8 +109,7 @@ export namespace ModelsDev { log.info("refreshing", { file, }) - const url = Global.Path.modelsDevUrl - const result = await fetch(`${url}/api.json`, { + const result = await fetch(`${url()}/api.json`, { headers: { "User-Agent": Installation.USER_AGENT, }, @@ -107,8 +119,18 @@ export namespace ModelsDev { error: e, }) }) - if (result && result.ok) await Bun.write(file, await result.text()) + if (result && result.ok) { + await Bun.write(file, await result.text()) + ModelsDev.Data.reset() + } } } -setInterval(() => ModelsDev.refresh(), 60 * 1000 * 60).unref() +if (!Flag.OPENCODE_DISABLE_MODELS_FETCH) { + setInterval( + async () => { + await ModelsDev.refresh() + }, + 60 * 1000 * 60, + ).unref() +} diff --git a/packages/opencode/test/preload.ts b/packages/opencode/test/preload.ts index 819166c94..1cb777862 100644 --- a/packages/opencode/test/preload.ts +++ b/packages/opencode/test/preload.ts @@ -5,7 +5,6 @@ import path from "path" import fs from "fs/promises" import fsSync from "fs" import { afterAll } from "bun:test" -const { Global } = await import("../src/global") const dir = path.join(os.tmpdir(), "opencode-test-data-" + process.pid) await fs.mkdir(dir, { recursive: true }) @@ -23,18 +22,10 @@ process.env["XDG_CACHE_HOME"] = path.join(dir, "cache") process.env["XDG_CONFIG_HOME"] = path.join(dir, "config") process.env["XDG_STATE_HOME"] = path.join(dir, "state") -// Pre-fetch models.json so tests don't need the macro fallback -// Also write the cache version file to prevent global/index.ts from clearing the cache +// Write the cache version file to prevent global/index.ts from clearing the cache const cacheDir = path.join(dir, "cache", "opencode") await fs.mkdir(cacheDir, { recursive: true }) await fs.writeFile(path.join(cacheDir, "version"), "14") -const url = Global.Path.modelsDevUrl -const response = await fetch(`${url}/api.json`) -if (response.ok) { - await fs.writeFile(path.join(cacheDir, "models.json"), await response.text()) -} -// Disable models.dev refresh to avoid race conditions during tests -process.env["OPENCODE_DISABLE_MODELS_FETCH"] = "true" // Clear provider env vars to ensure clean test state delete process.env["ANTHROPIC_API_KEY"]