Browse Source

fix(app): use agent configured variant (#12993)

Adam 2 weeks ago
parent
commit
6f5dfe125a

+ 25 - 12
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(),
+              }),
+            )
           },
         },
       }

+ 66 - 0
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")
+  })
+})

+ 50 - 0
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]
+}