Skip to content

Commit eb6f1da

Browse files
authored
fix: call models.dev once instead of twice on start (#20765)
1 parent 8e9e79d commit eb6f1da

3 files changed

Lines changed: 45 additions & 15 deletions

File tree

packages/opencode/src/cli/cmd/models.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export const ModelsCommand = cmd({
2828
},
2929
handler: async (args) => {
3030
if (args.refresh) {
31-
await ModelsDev.refresh()
31+
await ModelsDev.refresh(true)
3232
UI.println(UI.Style.TEXT_SUCCESS_BOLD + "Models cache refreshed" + UI.Style.TEXT_NORMAL)
3333
}
3434

packages/opencode/src/cli/cmd/providers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ export const ProvidersLoginCommand = cmd({
303303
prompts.outro("Done")
304304
return
305305
}
306-
await ModelsDev.refresh().catch(() => {})
306+
await ModelsDev.refresh(true).catch(() => {})
307307

308308
const config = await Config.get()
309309

packages/opencode/src/provider/models.ts

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,21 @@ import { Installation } from "../installation"
66
import { Flag } from "../flag/flag"
77
import { lazy } from "@/util/lazy"
88
import { Filesystem } from "../util/filesystem"
9+
import { Flock } from "@/util/flock"
10+
import { Hash } from "@/util/hash"
911

1012
// Try to import bundled snapshot (generated at build time)
1113
// Falls back to undefined in dev mode when snapshot doesn't exist
1214
/* @ts-ignore */
1315

1416
export namespace ModelsDev {
1517
const log = Log.create({ service: "models.dev" })
16-
const filepath = path.join(Global.Path.cache, "models.json")
18+
const source = url()
19+
const filepath = path.join(
20+
Global.Path.cache,
21+
source === "https://models.dev" ? "models.json" : `models-${Hash.fast(source)}.json`,
22+
)
23+
const ttl = 5 * 60 * 1000
1724

1825
export const Model = z.object({
1926
id: z.string(),
@@ -85,6 +92,22 @@ export namespace ModelsDev {
8592
return Flag.OPENCODE_MODELS_URL || "https://models.dev"
8693
}
8794

95+
function fresh() {
96+
return Date.now() - Number(Filesystem.stat(filepath)?.mtimeMs ?? 0) < ttl
97+
}
98+
99+
function skip(force: boolean) {
100+
return !force && fresh()
101+
}
102+
103+
const fetchApi = async () => {
104+
const result = await fetch(`${url()}/api.json`, {
105+
headers: { "User-Agent": Installation.USER_AGENT },
106+
signal: AbortSignal.timeout(10000),
107+
})
108+
return { ok: result.ok, text: await result.text() }
109+
}
110+
88111
export const Data = lazy(async () => {
89112
const result = await Filesystem.readJson(Flag.OPENCODE_MODELS_PATH ?? filepath).catch(() => {})
90113
if (result) return result
@@ -94,30 +117,37 @@ export namespace ModelsDev {
94117
.catch(() => undefined)
95118
if (snapshot) return snapshot
96119
if (Flag.OPENCODE_DISABLE_MODELS_FETCH) return {}
97-
const json = await fetch(`${url()}/api.json`).then((x) => x.text())
98-
return JSON.parse(json)
120+
return Flock.withLock(`models-dev:${filepath}`, async () => {
121+
const result = await Filesystem.readJson(Flag.OPENCODE_MODELS_PATH ?? filepath).catch(() => {})
122+
if (result) return result
123+
const result2 = await fetchApi()
124+
if (result2.ok) {
125+
await Filesystem.write(filepath, result2.text).catch((e) => {
126+
log.error("Failed to write models cache", { error: e })
127+
})
128+
}
129+
return JSON.parse(result2.text)
130+
})
99131
})
100132

101133
export async function get() {
102134
const result = await Data()
103135
return result as Record<string, Provider>
104136
}
105137

106-
export async function refresh() {
107-
const result = await fetch(`${url()}/api.json`, {
108-
headers: {
109-
"User-Agent": Installation.USER_AGENT,
110-
},
111-
signal: AbortSignal.timeout(10 * 1000),
138+
export async function refresh(force = false) {
139+
if (skip(force)) return ModelsDev.Data.reset()
140+
await Flock.withLock(`models-dev:${filepath}`, async () => {
141+
if (skip(force)) return ModelsDev.Data.reset()
142+
const result = await fetchApi()
143+
if (!result.ok) return
144+
await Filesystem.write(filepath, result.text)
145+
ModelsDev.Data.reset()
112146
}).catch((e) => {
113147
log.error("Failed to fetch models.dev", {
114148
error: e,
115149
})
116150
})
117-
if (result && result.ok) {
118-
await Filesystem.write(filepath, await result.text())
119-
ModelsDev.Data.reset()
120-
}
121151
}
122152
}
123153

0 commit comments

Comments
 (0)