Skip to content

Commit 1f94c48

Browse files
authored
fix(opencode): keep user message variants scoped to model (anomalyco#21332)
1 parent 01c5eb6 commit 1f94c48

13 files changed

Lines changed: 42 additions & 36 deletions

File tree

packages/app/src/components/prompt-input/submit.test.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ beforeAll(async () => {
146146
add: (value: {
147147
directory?: string
148148
sessionID?: string
149-
message: { agent: string; model: { providerID: string; modelID: string }; variant?: string }
149+
message: { agent: string; model: { providerID: string; modelID: string; variant?: string } }
150150
}) => {
151151
optimistic.push(value)
152152
optimisticSeeded.push(
@@ -310,8 +310,7 @@ describe("prompt submit worktree selection", () => {
310310
expect(optimistic[0]).toMatchObject({
311311
message: {
312312
agent: "agent",
313-
model: { providerID: "provider", modelID: "model" },
314-
variant: "high",
313+
model: { providerID: "provider", modelID: "model", variant: "high" },
315314
},
316315
})
317316
})

packages/app/src/components/prompt-input/submit.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,7 @@ export async function sendFollowupDraft(input: FollowupSendInput) {
121121
role: "user",
122122
time: { created: Date.now() },
123123
agent: input.draft.agent,
124-
model: input.draft.model,
125-
variant: input.draft.variant,
124+
model: { ...input.draft.model, variant: input.draft.variant },
126125
}
127126

128127
const add = () =>

packages/app/src/context/local.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { cycleModelVariant, getConfiguredAgentVariant, resolveModelVariant } fro
1111
import { useSDK } from "./sdk"
1212
import { useSync } from "./sync"
1313

14-
export type ModelKey = { providerID: string; modelID: string }
14+
export type ModelKey = { providerID: string; modelID: string; variant?: string }
1515

1616
type State = {
1717
agent?: string
@@ -373,7 +373,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
373373
handoff.set(handoffKey(dir, session), next)
374374
setStore("draft", undefined)
375375
},
376-
restore(msg: { sessionID: string; agent: string; model: ModelKey; variant?: string }) {
376+
restore(msg: { sessionID: string; agent: string; model: ModelKey }) {
377377
const session = id()
378378
if (!session) return
379379
if (msg.sessionID !== session) return
@@ -383,7 +383,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
383383
setSaved("session", session, {
384384
agent: msg.agent,
385385
model: msg.model,
386-
variant: msg.variant ?? null,
386+
variant: msg.model.variant ?? null,
387387
})
388388
},
389389
},

packages/app/src/context/sync.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -416,8 +416,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
416416
role: "user",
417417
time: { created: Date.now() },
418418
agent: input.agent,
419-
model: input.model,
420-
variant: input.variant,
419+
model: { ...input.model, variant: input.variant },
421420
}
422421
const [, setStore] = target()
423422
setOptimistic(sdk.directory, input.sessionID, { message, parts: input.parts })

packages/app/src/pages/session/session-model-helpers.test.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@ import { describe, expect, test } from "bun:test"
22
import type { UserMessage } from "@opencode-ai/sdk/v2"
33
import { resetSessionModel, syncSessionModel } from "./session-model-helpers"
44

5-
const message = (input?: Partial<Pick<UserMessage, "agent" | "model" | "variant">>) =>
5+
const message = (input?: { agent?: string; model?: UserMessage["model"] }) =>
66
({
77
id: "msg",
88
sessionID: "session",
99
role: "user",
1010
time: { created: 1 },
1111
agent: input?.agent ?? "build",
1212
model: input?.model ?? { providerID: "anthropic", modelID: "claude-sonnet-4" },
13-
variant: input?.variant,
1413
}) as UserMessage
1514

1615
describe("syncSessionModel", () => {
@@ -26,10 +25,12 @@ describe("syncSessionModel", () => {
2625
reset() {},
2726
},
2827
},
29-
message({ variant: "high" }),
28+
message({ model: { providerID: "anthropic", modelID: "claude-sonnet-4", variant: "high" } }),
3029
)
3130

32-
expect(calls).toEqual([message({ variant: "high" })])
31+
expect(calls).toEqual([
32+
message({ model: { providerID: "anthropic", modelID: "claude-sonnet-4", variant: "high" } }),
33+
])
3334
})
3435
})
3536

packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { useRenderer, type JSX } from "@opentui/solid"
2323
import { Editor } from "@tui/util/editor"
2424
import { useExit } from "../../context/exit"
2525
import { Clipboard } from "../../util/clipboard"
26-
import type { AssistantMessage, FilePart } from "@opencode-ai/sdk/v2"
26+
import type { AssistantMessage, FilePart, UserMessage } from "@opencode-ai/sdk/v2"
2727
import { TuiEvent } from "../../event"
2828
import { iife } from "@/util/iife"
2929
import { Locale } from "@/util/locale"
@@ -145,7 +145,7 @@ export function Prompt(props: PromptProps) {
145145
if (!props.sessionID) return undefined
146146
const messages = sync.data.message[props.sessionID]
147147
if (!messages) return undefined
148-
return messages.findLast((m) => m.role === "user")
148+
return messages.findLast((m): m is UserMessage => m.role === "user")
149149
})
150150

151151
const usage = createMemo(() => {
@@ -209,8 +209,10 @@ export function Prompt(props: PromptProps) {
209209
const isPrimaryAgent = local.agent.list().some((x) => x.name === msg.agent)
210210
if (msg.agent && isPrimaryAgent) {
211211
local.agent.set(msg.agent)
212-
if (msg.model) local.model.set(msg.model)
213-
if (msg.variant) local.model.variant.set(msg.variant)
212+
if (msg.model) {
213+
local.model.set(msg.model)
214+
local.model.variant.set(msg.model.variant)
215+
}
214216
}
215217
}
216218
})

packages/opencode/src/session/compaction.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ When constructing the summary, try to stick to this template:
228228
sessionID: input.sessionID,
229229
mode: "compaction",
230230
agent: "compaction",
231-
variant: userMessage.variant,
231+
variant: userMessage.model.variant,
232232
summary: true,
233233
path: {
234234
cwd: ctx.directory,
@@ -295,7 +295,6 @@ When constructing the summary, try to stick to this template:
295295
format: original.format,
296296
tools: original.tools,
297297
system: original.system,
298-
variant: original.variant,
299298
})
300299
for (const part of replay.parts) {
301300
if (part.type === "compaction") continue

packages/opencode/src/session/llm.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,9 @@ export namespace LLM {
127127
}
128128

129129
const variant =
130-
!input.small && input.model.variants && input.user.variant ? input.model.variants[input.user.variant] : {}
130+
!input.small && input.model.variants && input.user.model.variant
131+
? input.model.variants[input.user.model.variant]
132+
: {}
131133
const base = input.small
132134
? ProviderTransform.smallOptions(input.model)
133135
: ProviderTransform.options({

packages/opencode/src/session/message-v2.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,10 +371,10 @@ export namespace MessageV2 {
371371
model: z.object({
372372
providerID: ProviderID.zod,
373373
modelID: ModelID.zod,
374+
variant: z.string().optional(),
374375
}),
375376
system: z.string().optional(),
376377
tools: z.record(z.string(), z.boolean()).optional(),
377-
variant: z.string().optional(),
378378
}).meta({
379379
ref: "UserMessage",
380380
})

packages/opencode/src/session/prompt.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the
569569
sessionID,
570570
mode: task.agent,
571571
agent: task.agent,
572-
variant: lastUser.variant,
572+
variant: lastUser.model.variant,
573573
path: { cwd: ctx.directory, root: ctx.worktree },
574574
cost: 0,
575575
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
@@ -967,17 +967,20 @@ NOTE: At any point in time through this workflow you should feel free to ask the
967967
: undefined
968968
const variant = input.variant ?? (ag.variant && full?.variants?.[ag.variant] ? ag.variant : undefined)
969969

970-
const info: MessageV2.Info = {
970+
const info: MessageV2.User = {
971971
id: input.messageID ?? MessageID.ascending(),
972972
role: "user",
973973
sessionID: input.sessionID,
974974
time: { created: Date.now() },
975975
tools: input.tools,
976976
agent: ag.name,
977-
model,
977+
model: {
978+
providerID: model.providerID,
979+
modelID: model.modelID,
980+
variant,
981+
},
978982
system: input.system,
979983
format: input.format,
980-
variant,
981984
}
982985

983986
yield* Effect.addFinalizer(() =>
@@ -1436,7 +1439,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the
14361439
role: "assistant",
14371440
mode: agent.name,
14381441
agent: agent.name,
1439-
variant: lastUser.variant,
1442+
variant: lastUser.model.variant,
14401443
path: { cwd: ctx.directory, root: ctx.worktree },
14411444
cost: 0,
14421445
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },

0 commit comments

Comments
 (0)