فهرست منبع

fix(opencode): scope agent variant to model (#11410)

neavo 2 ماه پیش
والد
کامیت
f15755684f

+ 2 - 0
packages/opencode/src/agent/agent.ts

@@ -37,6 +37,7 @@ export namespace Agent {
           providerID: z.string(),
         })
         .optional(),
+      variant: z.string().optional(),
       prompt: z.string().optional(),
       options: z.record(z.string(), z.any()),
       steps: z.number().int().positive().optional(),
@@ -214,6 +215,7 @@ export namespace Agent {
           native: false,
         }
       if (value.model) item.model = Provider.parseModel(value.model)
+      item.variant = value.variant ?? item.variant
       item.prompt = value.prompt ?? item.prompt
       item.description = value.description ?? item.description
       item.temperature = value.temperature ?? item.temperature

+ 5 - 0
packages/opencode/src/config/config.ts

@@ -593,6 +593,10 @@ export namespace Config {
   export const Agent = z
     .object({
       model: z.string().optional(),
+      variant: z
+        .string()
+        .optional()
+        .describe("Default model variant for this agent (applies only when using the agent's configured model)."),
       temperature: z.number().optional(),
       top_p: z.number().optional(),
       prompt: z.string().optional(),
@@ -624,6 +628,7 @@ export namespace Config {
       const knownKeys = new Set([
         "name",
         "model",
+        "variant",
         "prompt",
         "description",
         "temperature",

+ 13 - 2
packages/opencode/src/session/prompt.ts

@@ -827,6 +827,17 @@ export namespace SessionPrompt {
 
   async function createUserMessage(input: PromptInput) {
     const agent = await Agent.get(input.agent ?? (await Agent.defaultAgent()))
+
+    const model = input.model ?? agent.model ?? (await lastModel(input.sessionID))
+    const variant =
+      input.variant ??
+      (agent.variant &&
+      agent.model &&
+      model.providerID === agent.model.providerID &&
+      model.modelID === agent.model.modelID
+        ? agent.variant
+        : undefined)
+
     const info: MessageV2.Info = {
       id: input.messageID ?? Identifier.ascending("message"),
       role: "user",
@@ -836,9 +847,9 @@ export namespace SessionPrompt {
       },
       tools: input.tools,
       agent: agent.name,
-      model: input.model ?? agent.model ?? (await lastModel(input.sessionID)),
+      model,
       system: input.system,
-      variant: input.variant,
+      variant,
     }
     using _ = defer(() => InstructionPrompt.clear(info.id))
 

+ 31 - 0
packages/opencode/test/config/config.test.ts

@@ -255,6 +255,37 @@ test("handles agent configuration", async () => {
   })
 })
 
+test("treats agent variant as model-scoped setting (not provider option)", async () => {
+  await using tmp = await tmpdir({
+    init: async (dir) => {
+      await writeConfig(dir, {
+        $schema: "https://opencode.ai/config.json",
+        agent: {
+          test_agent: {
+            model: "openai/gpt-5.2",
+            variant: "xhigh",
+            max_tokens: 123,
+          },
+        },
+      })
+    },
+  })
+
+  await Instance.provide({
+    directory: tmp.path,
+    fn: async () => {
+      const config = await Config.get()
+      const agent = config.agent?.["test_agent"]
+
+      expect(agent?.variant).toBe("xhigh")
+      expect(agent?.options).toMatchObject({
+        max_tokens: 123,
+      })
+      expect(agent?.options).not.toHaveProperty("variant")
+    },
+  })
+})
+
 test("handles command configuration", async () => {
   await using tmp = await tmpdir({
     init: async (dir) => {

+ 60 - 0
packages/opencode/test/session/prompt-variant.test.ts

@@ -0,0 +1,60 @@
+import { describe, expect, test } from "bun:test"
+import { Instance } from "../../src/project/instance"
+import { Session } from "../../src/session"
+import { SessionPrompt } from "../../src/session/prompt"
+import { tmpdir } from "../fixture/fixture"
+
+describe("session.prompt agent variant", () => {
+  test("applies agent variant only when using agent model", async () => {
+    await using tmp = await tmpdir({
+      git: true,
+      config: {
+        agent: {
+          build: {
+            model: "openai/gpt-5.2",
+            variant: "xhigh",
+          },
+        },
+      },
+    })
+
+    await Instance.provide({
+      directory: tmp.path,
+      fn: async () => {
+        const session = await Session.create({})
+
+        const other = await SessionPrompt.prompt({
+          sessionID: session.id,
+          agent: "build",
+          model: { providerID: "opencode", modelID: "kimi-k2.5-free" },
+          noReply: true,
+          parts: [{ type: "text", text: "hello" }],
+        })
+        if (other.info.role !== "user") throw new Error("expected user message")
+        expect(other.info.variant).toBeUndefined()
+
+        const match = await SessionPrompt.prompt({
+          sessionID: session.id,
+          agent: "build",
+          noReply: true,
+          parts: [{ type: "text", text: "hello again" }],
+        })
+        if (match.info.role !== "user") throw new Error("expected user message")
+        expect(match.info.model).toEqual({ providerID: "openai", modelID: "gpt-5.2" })
+        expect(match.info.variant).toBe("xhigh")
+
+        const override = await SessionPrompt.prompt({
+          sessionID: session.id,
+          agent: "build",
+          noReply: true,
+          variant: "high",
+          parts: [{ type: "text", text: "hello third" }],
+        })
+        if (override.info.role !== "user") throw new Error("expected user message")
+        expect(override.info.variant).toBe("high")
+
+        await Session.remove(session.id)
+      },
+    })
+  })
+})