From 42bb79357401cb92d1725032a657c2c74a483712 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Wed, 24 Jun 2026 12:18:43 -0400 Subject: [PATCH] feat(core): generate model variants --- packages/core/src/plugin/internal.ts | 4 +- packages/core/src/plugin/variant.ts | 39 ++++++++++ packages/core/test/plugin/variant.test.ts | 87 +++++++++++++++++++++++ 3 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 packages/core/src/plugin/variant.ts create mode 100644 packages/core/test/plugin/variant.test.ts diff --git a/packages/core/src/plugin/internal.ts b/packages/core/src/plugin/internal.ts index 4e80e60337..51c6e279b7 100644 --- a/packages/core/src/plugin/internal.ts +++ b/packages/core/src/plugin/internal.ts @@ -29,6 +29,7 @@ import { CommandPlugin } from "./command" import { ModelsDevPlugin } from "./models-dev" import { ProviderPlugins } from "./provider" import { SkillPlugin } from "./skill" +import { VariantPlugin } from "./variant" export type Requirements = | AgentV2.Service @@ -107,12 +108,13 @@ export const locationLayer = Layer.effectDiscard( yield* add(CommandPlugin.Plugin) yield* add(SkillPlugin.Plugin) yield* add(ModelsDevPlugin) - yield* add(ConfigProviderPlugin.Plugin) yield* add(ConfigAgentPlugin.Plugin) yield* add(ConfigCommandPlugin.Plugin) yield* add(ConfigSkillPlugin.Plugin) for (const item of ProviderPlugins) yield* add(item) yield* add(ConfigExternalPlugin.Plugin) + yield* add(ConfigProviderPlugin.Plugin) + yield* add(VariantPlugin.Plugin) }).pipe(Effect.withSpan("PluginInternal.boot"), Effect.forkScoped({ startImmediately: true })) }), ).pipe( diff --git a/packages/core/src/plugin/variant.ts b/packages/core/src/plugin/variant.ts new file mode 100644 index 0000000000..05e1653b6e --- /dev/null +++ b/packages/core/src/plugin/variant.ts @@ -0,0 +1,39 @@ +export * as VariantPlugin from "./variant" + +import type { ModelV2Info } from "@opencode-ai/sdk/v2/types" +import { Effect } from "effect" +import { define } from "./internal" + +export const Plugin = define({ + id: "variant", + effect: Effect.fn(function* (ctx) { + yield* ctx.catalog.transform((catalog) => { + for (const record of catalog.provider.list()) { + for (const model of record.models.values()) { + catalog.model.update(model.providerID, model.id, (draft) => { + const generated = generate(draft) + if (generated.length === 0) return + + const explicit = new Map(draft.variants.map((variant) => [variant.id, variant])) + const generatedIDs = new Set(generated.map((variant) => variant.id)) + draft.variants = [ + ...generated.map((variant) => explicit.get(variant.id) ?? variant), + ...draft.variants.filter((variant) => !generatedIDs.has(variant.id)), + ] + }) + } + } + }) + }), +}) + +export function generate(model: ModelV2Info): ModelV2Info["variants"] { + if (model.api.type !== "aisdk" || model.api.package !== "@ai-sdk/openai-compatible") return [] + const ids = `${model.id} ${model.api.id}`.toLowerCase() + if (!["glm-5.2", "glm-5-2", "glm-5p2"].some((name) => ids.includes(name))) return [] + return ["high", "max"].map((id) => ({ + id, + headers: {}, + body: { reasoning_effort: id }, + })) +} diff --git a/packages/core/test/plugin/variant.test.ts b/packages/core/test/plugin/variant.test.ts new file mode 100644 index 0000000000..c3075975fa --- /dev/null +++ b/packages/core/test/plugin/variant.test.ts @@ -0,0 +1,87 @@ +import { describe, expect } from "bun:test" +import { Catalog } from "@opencode-ai/core/catalog" +import { Credential } from "@opencode-ai/core/credential" +import { EventV2 } from "@opencode-ai/core/event" +import { Integration } from "@opencode-ai/core/integration" +import { Location } from "@opencode-ai/core/location" +import { ModelV2 } from "@opencode-ai/core/model" +import { Policy } from "@opencode-ai/core/policy" +import { VariantPlugin } from "@opencode-ai/core/plugin/variant" +import { ProviderV2 } from "@opencode-ai/core/provider" +import { AbsolutePath } from "@opencode-ai/core/schema" +import { Effect, Layer } from "effect" +import { location } from "../fixture/location" +import { testEffect } from "../lib/effect" +import { catalogHost, host } from "./host" + +const events = EventV2.defaultLayer +const locationLayer = Layer.succeed( + Location.Service, + Location.Service.of(location({ directory: AbsolutePath.make(import.meta.dir) })), +) +const connections = Credential.defaultLayer.pipe(Layer.fresh) +const integrations = Integration.locationLayer.pipe(Layer.provide(events), Layer.provide(connections)) +const catalog = Catalog.layer.pipe( + Layer.provide( + Layer.mergeAll( + events, + locationLayer, + Policy.layer.pipe(Layer.provide(locationLayer)), + connections, + integrations, + ), + ), +) +const it = testEffect( + Layer.mergeAll(catalog.pipe(Layer.provide(connections)), integrations, connections, events, locationLayer), +) + +describe("VariantPlugin", () => { + it.effect("adds GLM 5.2 variants after catalog sources", () => + Effect.gen(function* () { + const service = yield* Catalog.Service + yield* service.transform((catalog) => { + catalog.provider.update(ProviderV2.ID.opencode, (provider) => { + provider.api = { type: "aisdk", package: "@ai-sdk/openai-compatible" } + }) + catalog.model.update(ProviderV2.ID.opencode, ModelV2.ID.make("glm-5.2"), (model) => { + model.api = { + id: ModelV2.ID.make("glm-5.2"), + type: "aisdk", + package: "@ai-sdk/openai-compatible", + } + }) + }) + yield* VariantPlugin.Plugin.effect(host({ catalog: catalogHost(service) })) + + expect((yield* service.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("glm-5.2")))?.variants).toEqual([ + expect.objectContaining({ id: "high", body: { reasoning_effort: "high" } }), + expect.objectContaining({ id: "max", body: { reasoning_effort: "max" } }), + ]) + }), + ) + + it.effect("keeps explicit variants over generated defaults", () => + Effect.gen(function* () { + const service = yield* Catalog.Service + yield* service.transform((catalog) => { + catalog.model.update(ProviderV2.ID.opencode, ModelV2.ID.make("glm-5.2"), (model) => { + model.api = { + id: ModelV2.ID.make("glm-5.2"), + type: "aisdk", + package: "@ai-sdk/openai-compatible", + } + model.variants = [ + { id: ModelV2.VariantID.make("high"), headers: { custom: "true" }, body: {} }, + ] + }) + }) + yield* VariantPlugin.Plugin.effect(host({ catalog: catalogHost(service) })) + + expect((yield* service.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("glm-5.2")))?.variants).toEqual([ + expect.objectContaining({ id: "high", headers: { custom: "true" } }), + expect.objectContaining({ id: "max", body: { reasoning_effort: "max" } }), + ]) + }), + ) +})