mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-08 09:37:47 +03:00
83 lines
2.8 KiB
TypeScript
83 lines
2.8 KiB
TypeScript
import type { BunPlugin } from "bun"
|
|
import { Effect, Schema } from "effect"
|
|
import { ModelsDev } from "../src/models-dev"
|
|
|
|
// A floor, not equality: guards against an error page or a truncated body
|
|
// that still parses as a small object.
|
|
const MINIMUM_PROVIDERS = 100
|
|
export const committedSnapshot = new URL("../src/models-dev/snapshot.txt", import.meta.url)
|
|
|
|
export async function fetchModelsSnapshot() {
|
|
const source = process.env.OPENCODE_MODELS_URL || "https://models.opencode.ai"
|
|
const url = `${source}${ModelsDev.catalogPath}`
|
|
const response = await fetch(url, { signal: AbortSignal.timeout(30_000) })
|
|
if (!response.ok) throw new Error(`${url} returned ${response.status} ${response.statusText}`)
|
|
return validateModelsSnapshot(trim(await response.text()))
|
|
}
|
|
|
|
// Model fields opencode never reads. Dropping them keeps the bundled copy small; every
|
|
// other field, including ones models.dev adds later, is kept as published.
|
|
const UNUSED_MODEL_FIELDS = new Set([
|
|
"description",
|
|
"canonical_id",
|
|
"knowledge",
|
|
"last_updated",
|
|
"deprecation_date",
|
|
"open_weights",
|
|
"metadata",
|
|
])
|
|
|
|
const Catalog = Schema.fromJsonString(
|
|
Schema.Record(
|
|
Schema.String,
|
|
Schema.StructWithRest(
|
|
Schema.Struct({ models: Schema.Record(Schema.String, Schema.Record(Schema.String, Schema.Unknown)) }),
|
|
[Schema.Record(Schema.String, Schema.Unknown)],
|
|
),
|
|
),
|
|
)
|
|
|
|
function trim(text: string) {
|
|
const catalog = Schema.decodeUnknownSync(Catalog)(text)
|
|
return JSON.stringify(
|
|
Object.fromEntries(
|
|
Object.entries(catalog).map(([id, provider]) => [
|
|
id,
|
|
{
|
|
...provider,
|
|
models: Object.fromEntries(
|
|
Object.entries(provider.models).map(([modelID, model]) => [
|
|
modelID,
|
|
Object.fromEntries(Object.entries(model).filter(([key]) => !UNUSED_MODEL_FIELDS.has(key))),
|
|
]),
|
|
),
|
|
},
|
|
]),
|
|
),
|
|
)
|
|
}
|
|
|
|
async function validateModelsSnapshot(text: string) {
|
|
const providers = (await Effect.runPromise(ModelsDev.decodeSnapshot(text))).length
|
|
if (providers < MINIMUM_PROVIDERS)
|
|
throw new Error(`models.dev catalog has ${providers} providers, expected at least ${MINIMUM_PROVIDERS}`)
|
|
return { text, providers }
|
|
}
|
|
|
|
export async function bundledModelsSnapshot() {
|
|
const file = process.env.OPENCODE_MODELS_SNAPSHOT
|
|
if (!file) return Bun.file(committedSnapshot).text()
|
|
const snapshot = await validateModelsSnapshot(await Bun.file(file).text())
|
|
console.log(`bundling models.dev catalog from ${file} (${snapshot.providers} providers)`)
|
|
return snapshot.text
|
|
}
|
|
|
|
export function modelsSnapshotPlugin(text: string): BunPlugin {
|
|
return {
|
|
name: "opencode-models-snapshot",
|
|
setup(build) {
|
|
build.onLoad({ filter: /models-dev[/\\]snapshot\.txt$/ }, () => ({ contents: text, loader: "text" }))
|
|
},
|
|
}
|
|
}
|