Something went wrong. Try again.
opencode v2 mirror
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112import { define } from "@opencode/plugin/effect/plugin"import { Integration } from "@opencode/schema/integration"import { Provider } from "@opencode/schema/provider"import { Effect, Stream } from "effect"import { Bus } from "../bus.js"import { ModelsDev } from "../models-dev.js"
// These catalog entries require inference profiles on Bedrock Runtime.// Opus/Sonnet 4.6 support in-region calls in eu-west-2 and must remain available.const BEDROCK_PROFILE_ONLY_IDS = [ "amazon.nova-2-lite-v1:0", "anthropic.claude-fable-5", "anthropic.claude-fable-5-1", "anthropic.claude-haiku-4-5-20251001-v1:0", "anthropic.claude-opus-4-1-20250805-v1:0", "anthropic.claude-opus-4-5-20251101-v1:0", "anthropic.claude-opus-4-7", "anthropic.claude-opus-4-8", "anthropic.claude-opus-5", "anthropic.claude-sonnet-4-5-20250929-v1:0", "anthropic.claude-sonnet-5", "deepseek.r1-v1:0", "mistral.pixtral-large-2502-v1:0",]
export const ModelsDevPlugin = define({ id: "opencode.models.dev", effect: Effect.fn(function* (ctx) { const modelsDev = yield* ModelsDev.Service const bus = yield* Bus.Service // Filtering and definition indexes are shared across Locations. Only Model materialization // makes mutable copies, after provider configuration and access have been resolved. const loaded = { data: snapshots(yield* modelsDev.get()) } yield* ctx.integration.transform((integrations) => { for (const provider of loaded.data) { if (provider.environment.length === 0) continue const integrationID = provider.info.id integrations.update(integrationID, (integration) => (integration.name = provider.info.name)) integrations.method.update({ integrationID, method: { type: "key" }, }) integrations.method.update({ integrationID, method: { type: "env", names: environmentNames(provider), }, }) } }) yield* ctx.provider.transform((providers) => { for (const provider of loaded.data) { providers.add({ info: { ...provider.info, integrationID: Integration.ID.make(provider.info.id) }, models: provider.models, }) } }) const apply = (data: readonly ModelsDev.Snapshot[]) => { loaded.data = snapshots(data) return ctx.integration.reload().pipe(Effect.andThen(ctx.provider.reload())) } yield* bus.subscribe(ModelsDev.Event.Refreshed).pipe( Stream.runForEach(() => modelsDev.get().pipe(Effect.flatMap(apply))), Effect.forkScoped({ startImmediately: true }), ) // A refresh that landed between the initial read and the subscription above published // Refreshed to nobody here. On a cold cache that read served the bundled snapshot, so // re-read now instead of waiting for the next TTL refresh. const latest = yield* modelsDev.get() if (snapshots(latest) !== loaded.data) yield* apply(latest) }),})
function environmentNames(provider: ModelsDev.Snapshot) { if (provider.info.id === Provider.ID.azure) return [...provider.environment.filter((name) => name.endsWith("_API_KEY")), "AZURE_COGNITIVE_SERVICES_API_KEY"] // models.dev advertises project, location, and the ADC credentials file path for // Vertex. Those configure Google auth rather than carrying a key, so only the // Express Mode key may become a credential; GoogleVertexPlugin handles activation. if (provider.info.id === Provider.ID.googleVertex) return ["GOOGLE_VERTEX_API_KEY"] if (provider.info.id === "cloudflare-workers-ai") return ["CLOUDFLARE_API_KEY", "CLOUDFLARE_WORKERS_AI_TOKEN", "CLOUDFLARE_API_TOKEN"] return [...provider.environment]}
const prepared = new WeakMap<readonly ModelsDev.Snapshot[], readonly ModelsDev.Snapshot[]>()
function snapshots(data: readonly ModelsDev.Snapshot[]) { const cached = prepared.get(data) if (cached) return cached const result = data .filter( // These deprecated aliases are replaced by the canonical Azure and Google Vertex providers. (provider) => provider.info.id !== "azure-cognitive-services" && provider.info.id !== "google-vertex-anthropic", ) .map((provider) => ({ ...provider, models: provider.models.filter( (model) => model.status !== "deprecated" && !( provider.info.id === Provider.ID.amazonBedrock && BEDROCK_PROFILE_ONLY_IDS.includes(model.modelID ?? model.id) ), ), })) prepared.set(data, result) return result}