summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src/context
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-02-10 10:15:09 -0600
committerGitHub <[email protected]>2026-02-10 10:15:09 -0600
commit6f5dfe125aaa82514318ea39d0ae443da37612f6 (patch)
treee2cc44f2603658e77ad98ce87bb1b77cb7105019 /packages/app/src/context
parent27fa9dc843d002b67113a1a01727aae61563427e (diff)
downloadopencode-6f5dfe125aaa82514318ea39d0ae443da37612f6.tar.gz
opencode-6f5dfe125aaa82514318ea39d0ae443da37612f6.zip
fix(app): use agent configured variant (#12993)
Diffstat (limited to 'packages/app/src/context')
-rw-r--r--packages/app/src/context/local.tsx37
-rw-r--r--packages/app/src/context/model-variant.test.ts66
-rw-r--r--packages/app/src/context/model-variant.ts50
3 files changed, 141 insertions, 12 deletions
diff --git a/packages/app/src/context/local.tsx b/packages/app/src/context/local.tsx
index f51bb6930..85f93f368 100644
--- a/packages/app/src/context/local.tsx
+++ b/packages/app/src/context/local.tsx
@@ -6,6 +6,7 @@ import { useSync } from "./sync"
import { base64Encode } from "@opencode-ai/util/encode"
import { useProviders } from "@/hooks/use-providers"
import { useModels } from "@/context/models"
+import { cycleModelVariant, getConfiguredAgentVariant, resolveModelVariant } from "./model-variant"
export type ModelKey = { providerID: string; modelID: string }
@@ -184,11 +185,27 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
models.setVisibility(model, visible)
},
variant: {
- current() {
+ configured() {
+ const a = agent.current()
+ const m = current()
+ if (!a || !m) return undefined
+ return getConfiguredAgentVariant({
+ agent: { model: a.model, variant: a.variant },
+ model: { providerID: m.provider.id, modelID: m.id, variants: m.variants },
+ })
+ },
+ selected() {
const m = current()
if (!m) return undefined
return models.variant.get({ providerID: m.provider.id, modelID: m.id })
},
+ current() {
+ return resolveModelVariant({
+ variants: this.list(),
+ selected: this.selected(),
+ configured: this.configured(),
+ })
+ },
list() {
const m = current()
if (!m) return []
@@ -203,17 +220,13 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
cycle() {
const variants = this.list()
if (variants.length === 0) return
- const currentVariant = this.current()
- if (!currentVariant) {
- this.set(variants[0])
- return
- }
- const index = variants.indexOf(currentVariant)
- if (index === -1 || index === variants.length - 1) {
- this.set(undefined)
- return
- }
- this.set(variants[index + 1])
+ this.set(
+ cycleModelVariant({
+ variants,
+ selected: this.selected(),
+ configured: this.configured(),
+ }),
+ )
},
},
}
diff --git a/packages/app/src/context/model-variant.test.ts b/packages/app/src/context/model-variant.test.ts
new file mode 100644
index 000000000..01b149fd2
--- /dev/null
+++ b/packages/app/src/context/model-variant.test.ts
@@ -0,0 +1,66 @@
+import { describe, expect, test } from "bun:test"
+import { cycleModelVariant, getConfiguredAgentVariant, resolveModelVariant } from "./model-variant"
+
+describe("model variant", () => {
+ test("resolves configured agent variant when model matches", () => {
+ const value = getConfiguredAgentVariant({
+ agent: {
+ model: { providerID: "openai", modelID: "gpt-5.2" },
+ variant: "xhigh",
+ },
+ model: {
+ providerID: "openai",
+ modelID: "gpt-5.2",
+ variants: { low: {}, high: {}, xhigh: {} },
+ },
+ })
+
+ expect(value).toBe("xhigh")
+ })
+
+ test("ignores configured variant when model does not match", () => {
+ const value = getConfiguredAgentVariant({
+ agent: {
+ model: { providerID: "openai", modelID: "gpt-5.2" },
+ variant: "xhigh",
+ },
+ model: {
+ providerID: "anthropic",
+ modelID: "claude-sonnet-4",
+ variants: { low: {}, high: {}, xhigh: {} },
+ },
+ })
+
+ expect(value).toBeUndefined()
+ })
+
+ test("prefers selected variant over configured variant", () => {
+ const value = resolveModelVariant({
+ variants: ["low", "high", "xhigh"],
+ selected: "high",
+ configured: "xhigh",
+ })
+
+ expect(value).toBe("high")
+ })
+
+ test("cycles from configured variant to next", () => {
+ const value = cycleModelVariant({
+ variants: ["low", "high", "xhigh"],
+ selected: undefined,
+ configured: "high",
+ })
+
+ expect(value).toBe("xhigh")
+ })
+
+ test("wraps from configured last variant to first", () => {
+ const value = cycleModelVariant({
+ variants: ["low", "high", "xhigh"],
+ selected: undefined,
+ configured: "xhigh",
+ })
+
+ expect(value).toBe("low")
+ })
+})
diff --git a/packages/app/src/context/model-variant.ts b/packages/app/src/context/model-variant.ts
new file mode 100644
index 000000000..6b7ae7256
--- /dev/null
+++ b/packages/app/src/context/model-variant.ts
@@ -0,0 +1,50 @@
+type AgentModel = {
+ providerID: string
+ modelID: string
+}
+
+type Agent = {
+ model?: AgentModel
+ variant?: string
+}
+
+type Model = AgentModel & {
+ variants?: Record<string, unknown>
+}
+
+type VariantInput = {
+ variants: string[]
+ selected: string | undefined
+ configured: string | undefined
+}
+
+export function getConfiguredAgentVariant(input: { agent: Agent | undefined; model: Model | undefined }) {
+ if (!input.agent?.variant) return undefined
+ if (!input.agent.model) return undefined
+ if (!input.model?.variants) return undefined
+ if (input.agent.model.providerID !== input.model.providerID) return undefined
+ if (input.agent.model.modelID !== input.model.modelID) return undefined
+ if (!(input.agent.variant in input.model.variants)) return undefined
+ return input.agent.variant
+}
+
+export function resolveModelVariant(input: VariantInput) {
+ if (input.selected && input.variants.includes(input.selected)) return input.selected
+ if (input.configured && input.variants.includes(input.configured)) return input.configured
+ return undefined
+}
+
+export function cycleModelVariant(input: VariantInput) {
+ if (input.variants.length === 0) return undefined
+ if (input.selected && input.variants.includes(input.selected)) {
+ const index = input.variants.indexOf(input.selected)
+ if (index === input.variants.length - 1) return undefined
+ return input.variants[index + 1]
+ }
+ if (input.configured && input.variants.includes(input.configured)) {
+ const index = input.variants.indexOf(input.configured)
+ if (index === input.variants.length - 1) return input.variants[0]
+ return input.variants[index + 1]
+ }
+ return input.variants[0]
+}