From 938a53b5a25a657538890228e09464102a764b70 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Tue, 30 Dec 2025 16:12:01 -0600 Subject: [PATCH] fix: ensure variants can be disabled --- .opencode/opencode.jsonc | 21 ++++++++++++++ AGENTS.md | 4 +++ packages/opencode/src/config/config.ts | 19 +++++++++++- packages/opencode/src/provider/models.ts | 1 + packages/opencode/src/provider/provider.ts | 32 ++++++++++++--------- packages/opencode/src/provider/transform.ts | 2 +- packages/opencode/src/session/llm.ts | 7 +++-- packages/sdk/js/src/v2/gen/types.gen.ts | 26 +++++++++++++---- 8 files changed, 87 insertions(+), 25 deletions(-) diff --git a/.opencode/opencode.jsonc b/.opencode/opencode.jsonc index cbcbb0c65..206560308 100644 --- a/.opencode/opencode.jsonc +++ b/.opencode/opencode.jsonc @@ -8,6 +8,27 @@ "provider": { "opencode": { "options": {}, + "models": { + "claude-haiku-4-5": { + "variants": { + "high": { + "disabled": true, + }, + "max": { + "disabled": true, + }, + }, + }, + "custom-model": { + "variants": { + "special": { + "This is special": { + "so": true, + }, + }, + }, + }, + }, }, }, "mcp": {}, diff --git a/AGENTS.md b/AGENTS.md index bbb2a96f2..7fc710f6d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,6 +2,10 @@ - To test opencode in the `packages/opencode` directory you can run `bun dev` +## SDK + +To regenerate the javascript SDK, run ./packages/sdk/js/script/build.ts + ## Tool Calling - ALWAYS USE PARALLEL TOOLS WHEN APPLICABLE. diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 4f4ff1188..9d34c6e47 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -620,7 +620,24 @@ export namespace Config { .extend({ whitelist: z.array(z.string()).optional(), blacklist: z.array(z.string()).optional(), - models: z.record(z.string(), ModelsDev.Model.partial()).optional(), + models: z + .record( + z.string(), + ModelsDev.Model.partial().extend({ + variants: z + .record( + z.string(), + z + .object({ + disabled: z.boolean().optional().describe("Disable this variant for the model"), + }) + .catchall(z.any()), + ) + .optional() + .describe("Variant-specific configuration"), + }), + ) + .optional(), options: z .object({ apiKey: z.string().optional(), diff --git a/packages/opencode/src/provider/models.ts b/packages/opencode/src/provider/models.ts index c58638d28..796dcb7c2 100644 --- a/packages/opencode/src/provider/models.ts +++ b/packages/opencode/src/provider/models.ts @@ -60,6 +60,7 @@ export namespace ModelsDev { options: z.record(z.string(), z.any()), headers: z.record(z.string(), z.string()).optional(), provider: z.object({ npm: z.string() }).optional(), + variants: z.record(z.string(), z.record(z.string(), z.any())).optional(), }) export type Model = z.infer diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 778bf6d87..4dec3c345 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -1,7 +1,7 @@ import z from "zod" import fuzzysort from "fuzzysort" import { Config } from "../config/config" -import { mapValues, mergeDeep, sortBy } from "remeda" +import { mapValues, mergeDeep, omit, pickBy, sortBy } from "remeda" import { NoSuchModelError, type Provider as SDK } from "ai" import { Log } from "../util/log" import { BunProc } from "../bun" @@ -405,16 +405,6 @@ export namespace Provider { }, } - export const Variant = z - .object({ - disabled: z.boolean(), - }) - .catchall(z.any()) - .meta({ - ref: "Variant", - }) - export type Variant = z.infer - export const Model = z .object({ id: z.string(), @@ -478,7 +468,7 @@ export namespace Provider { options: z.record(z.string(), z.any()), headers: z.record(z.string(), z.string()), release_date: z.string(), - variants: z.record(z.string(), Variant).optional(), + variants: z.record(z.string(), z.record(z.string(), z.any())).optional(), }) .meta({ ref: "Model", @@ -561,7 +551,7 @@ export namespace Provider { variants: {}, } - m.variants = mapValues(ProviderTransform.variants(m), (v) => ({ disabled: false, ...v })) + m.variants = mapValues(ProviderTransform.variants(m), (v) => v) return m } @@ -699,7 +689,11 @@ export namespace Provider { release_date: model.release_date ?? existingModel?.release_date ?? "", variants: {}, } - parsedModel.variants = mapValues(ProviderTransform.variants(parsedModel), (v) => ({ disabled: false, ...v })) + const merged = mergeDeep(ProviderTransform.variants(parsedModel), model.variants ?? {}) + parsedModel.variants = mapValues( + pickBy(merged, (v) => !v.disabled), + (v) => omit(v, ["disabled"]), + ) parsed.models[modelID] = parsedModel } database[providerID] = parsed @@ -824,6 +818,16 @@ export namespace Provider { (configProvider?.whitelist && !configProvider.whitelist.includes(modelID)) ) delete provider.models[modelID] + + // Filter out disabled variants from config + const configVariants = configProvider?.models?.[modelID]?.variants + if (configVariants && model.variants) { + const merged = mergeDeep(model.variants, configVariants) + model.variants = mapValues( + pickBy(merged, (v) => !v.disabled), + (v) => omit(v, ["disabled"]), + ) + } } if (Object.keys(provider.models).length === 0) { diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 9de2ad52c..e99c76d73 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -246,7 +246,7 @@ export namespace ProviderTransform { const WIDELY_SUPPORTED_EFFORTS = ["low", "medium", "high"] const OPENAI_EFFORTS = ["none", "minimal", ...WIDELY_SUPPORTED_EFFORTS, "xhigh"] - export function variants(model: Provider.Model) { + export function variants(model: Provider.Model): Record> { if (!model.capabilities.reasoning) return {} const id = model.id.toLowerCase() diff --git a/packages/opencode/src/session/llm.ts b/packages/opencode/src/session/llm.ts index 0736a1f9e..ccd7af1f0 100644 --- a/packages/opencode/src/session/llm.ts +++ b/packages/opencode/src/session/llm.ts @@ -82,13 +82,14 @@ export namespace LLM { } const provider = await Provider.getProvider(input.model.providerID) - const variant = input.model.variants && input.user.variant ? input.model.variants[input.user.variant] : undefined + const small = input.small ? ProviderTransform.smallOptions(input.model) : {} + const variant = input.model.variants && input.user.variant ? input.model.variants[input.user.variant] : {} const options = pipe( ProviderTransform.options(input.model, input.sessionID, provider.options), - mergeDeep(input.small ? ProviderTransform.smallOptions(input.model) : {}), + mergeDeep(small), mergeDeep(input.model.options), mergeDeep(input.agent.options), - mergeDeep(variant && !variant.disabled ? variant : {}), + mergeDeep(variant), ) const params = await Plugin.trigger( diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 58b44fe17..947636f7c 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -1309,6 +1309,18 @@ export type ProviderConfig = { provider?: { npm: string } + /** + * Variant-specific configuration + */ + variants?: { + [key: string]: { + /** + * Disable this variant for the model + */ + disabled?: boolean + [key: string]: unknown | boolean | undefined + } + } } } whitelist?: Array @@ -1717,11 +1729,6 @@ export type Command = { subtask?: boolean } -export type Variant = { - disabled: boolean - [key: string]: unknown | boolean -} - export type Model = { id: string providerID: string @@ -1786,7 +1793,9 @@ export type Model = { } release_date: string variants?: { - [key: string]: Variant + [key: string]: { + [key: string]: unknown + } } } @@ -3497,6 +3506,11 @@ export type ProviderListResponses = { provider?: { npm: string } + variants?: { + [key: string]: { + [key: string]: unknown + } + } } } }>