summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--packages/opencode/src/cli/cmd/models.ts2
-rw-r--r--packages/opencode/src/cli/cmd/providers.ts2
-rw-r--r--packages/opencode/src/provider/models.ts56
3 files changed, 45 insertions, 15 deletions
diff --git a/packages/opencode/src/cli/cmd/models.ts b/packages/opencode/src/cli/cmd/models.ts
index 3e3672926..4670aa5f2 100644
--- a/packages/opencode/src/cli/cmd/models.ts
+++ b/packages/opencode/src/cli/cmd/models.ts
@@ -28,7 +28,7 @@ export const ModelsCommand = cmd({
},
handler: async (args) => {
if (args.refresh) {
- await ModelsDev.refresh()
+ await ModelsDev.refresh(true)
UI.println(UI.Style.TEXT_SUCCESS_BOLD + "Models cache refreshed" + UI.Style.TEXT_NORMAL)
}
diff --git a/packages/opencode/src/cli/cmd/providers.ts b/packages/opencode/src/cli/cmd/providers.ts
index 581809e90..1ab0ecc7b 100644
--- a/packages/opencode/src/cli/cmd/providers.ts
+++ b/packages/opencode/src/cli/cmd/providers.ts
@@ -303,7 +303,7 @@ export const ProvidersLoginCommand = cmd({
prompts.outro("Done")
return
}
- await ModelsDev.refresh().catch(() => {})
+ await ModelsDev.refresh(true).catch(() => {})
const config = await Config.get()
diff --git a/packages/opencode/src/provider/models.ts b/packages/opencode/src/provider/models.ts
index 30901ea74..c6ab5d836 100644
--- a/packages/opencode/src/provider/models.ts
+++ b/packages/opencode/src/provider/models.ts
@@ -6,6 +6,8 @@ import { Installation } from "../installation"
import { Flag } from "../flag/flag"
import { lazy } from "@/util/lazy"
import { Filesystem } from "../util/filesystem"
+import { Flock } from "@/util/flock"
+import { Hash } from "@/util/hash"
// Try to import bundled snapshot (generated at build time)
// Falls back to undefined in dev mode when snapshot doesn't exist
@@ -13,7 +15,12 @@ import { Filesystem } from "../util/filesystem"
export namespace ModelsDev {
const log = Log.create({ service: "models.dev" })
- const filepath = path.join(Global.Path.cache, "models.json")
+ const source = url()
+ const filepath = path.join(
+ Global.Path.cache,
+ source === "https://models.dev" ? "models.json" : `models-${Hash.fast(source)}.json`,
+ )
+ const ttl = 5 * 60 * 1000
export const Model = z.object({
id: z.string(),
@@ -85,6 +92,22 @@ export namespace ModelsDev {
return Flag.OPENCODE_MODELS_URL || "https://models.dev"
}
+ function fresh() {
+ return Date.now() - Number(Filesystem.stat(filepath)?.mtimeMs ?? 0) < ttl
+ }
+
+ function skip(force: boolean) {
+ return !force && fresh()
+ }
+
+ const fetchApi = async () => {
+ const result = await fetch(`${url()}/api.json`, {
+ headers: { "User-Agent": Installation.USER_AGENT },
+ signal: AbortSignal.timeout(10000),
+ })
+ return { ok: result.ok, text: await result.text() }
+ }
+
export const Data = lazy(async () => {
const result = await Filesystem.readJson(Flag.OPENCODE_MODELS_PATH ?? filepath).catch(() => {})
if (result) return result
@@ -94,8 +117,17 @@ export namespace ModelsDev {
.catch(() => undefined)
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)
+ return Flock.withLock(`models-dev:${filepath}`, async () => {
+ const result = await Filesystem.readJson(Flag.OPENCODE_MODELS_PATH ?? filepath).catch(() => {})
+ if (result) return result
+ const result2 = await fetchApi()
+ if (result2.ok) {
+ await Filesystem.write(filepath, result2.text).catch((e) => {
+ log.error("Failed to write models cache", { error: e })
+ })
+ }
+ return JSON.parse(result2.text)
+ })
})
export async function get() {
@@ -103,21 +135,19 @@ export namespace ModelsDev {
return result as Record<string, Provider>
}
- export async function refresh() {
- const result = await fetch(`${url()}/api.json`, {
- headers: {
- "User-Agent": Installation.USER_AGENT,
- },
- signal: AbortSignal.timeout(10 * 1000),
+ export async function refresh(force = false) {
+ if (skip(force)) return ModelsDev.Data.reset()
+ await Flock.withLock(`models-dev:${filepath}`, async () => {
+ if (skip(force)) return ModelsDev.Data.reset()
+ const result = await fetchApi()
+ if (!result.ok) return
+ await Filesystem.write(filepath, result.text)
+ ModelsDev.Data.reset()
}).catch((e) => {
log.error("Failed to fetch models.dev", {
error: e,
})
})
- if (result && result.ok) {
- await Filesystem.write(filepath, await result.text())
- ModelsDev.Data.reset()
- }
}
}