fix(runtime-fallback): preserve agent variant and reasoningEffort on model fallback (fixes #2621)
When runtime fallback switches to a different model, the agent's configured variant and reasoningEffort were lost because buildRetryModelPayload only extracted variant from the fallback model string itself. Now buildRetryModelPayload accepts optional agentSettings and uses the agent's variant as fallback when the model string doesn't include one. reasoningEffort is also passed through. 🤖 Generated with assistance of [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -101,7 +101,13 @@ export function createAutoRetryHelpers(deps: HookDeps) {
|
||||
return
|
||||
}
|
||||
|
||||
const retryModelPayload = buildRetryModelPayload(newModel)
|
||||
const agentSettings = resolvedAgent
|
||||
? pluginConfig?.agents?.[resolvedAgent as keyof typeof pluginConfig.agents]
|
||||
: undefined
|
||||
const retryModelPayload = buildRetryModelPayload(newModel, agentSettings ? {
|
||||
variant: agentSettings.variant,
|
||||
reasoningEffort: agentSettings.reasoningEffort,
|
||||
} : undefined)
|
||||
if (!retryModelPayload) {
|
||||
log(`[${HOOK_NAME}] Invalid model format (missing provider prefix): ${newModel}`)
|
||||
const state = sessionStates.get(sessionID)
|
||||
|
||||
114
src/hooks/runtime-fallback/retry-model-payload.test.ts
Normal file
114
src/hooks/runtime-fallback/retry-model-payload.test.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { describe, test, expect } from "bun:test"
|
||||
import { buildRetryModelPayload } from "./retry-model-payload"
|
||||
|
||||
describe("buildRetryModelPayload", () => {
|
||||
test("should return undefined for empty model string", () => {
|
||||
// given
|
||||
const model = ""
|
||||
|
||||
// when
|
||||
const result = buildRetryModelPayload(model)
|
||||
|
||||
// then
|
||||
expect(result).toBeUndefined()
|
||||
})
|
||||
|
||||
test("should return undefined for model without provider prefix", () => {
|
||||
// given
|
||||
const model = "kimi-k2.5"
|
||||
|
||||
// when
|
||||
const result = buildRetryModelPayload(model)
|
||||
|
||||
// then
|
||||
expect(result).toBeUndefined()
|
||||
})
|
||||
|
||||
test("should parse provider and model ID", () => {
|
||||
// given
|
||||
const model = "chutes/kimi-k2.5"
|
||||
|
||||
// when
|
||||
const result = buildRetryModelPayload(model)
|
||||
|
||||
// then
|
||||
expect(result).toEqual({
|
||||
model: { providerID: "chutes", modelID: "kimi-k2.5" },
|
||||
})
|
||||
})
|
||||
|
||||
test("should include variant from model string", () => {
|
||||
// given
|
||||
const model = "anthropic/claude-sonnet-4-5 high"
|
||||
|
||||
// when
|
||||
const result = buildRetryModelPayload(model)
|
||||
|
||||
// then
|
||||
expect(result).toEqual({
|
||||
model: { providerID: "anthropic", modelID: "claude-sonnet-4-5" },
|
||||
variant: "high",
|
||||
})
|
||||
})
|
||||
|
||||
test("should use agent variant when model string has no variant", () => {
|
||||
// given
|
||||
const model = "chutes/kimi-k2.5"
|
||||
const agentSettings = { variant: "max" }
|
||||
|
||||
// when
|
||||
const result = buildRetryModelPayload(model, agentSettings)
|
||||
|
||||
// then
|
||||
expect(result).toEqual({
|
||||
model: { providerID: "chutes", modelID: "kimi-k2.5" },
|
||||
variant: "max",
|
||||
})
|
||||
})
|
||||
|
||||
test("should prefer model string variant over agent variant", () => {
|
||||
// given
|
||||
const model = "anthropic/claude-sonnet-4-5 high"
|
||||
const agentSettings = { variant: "max" }
|
||||
|
||||
// when
|
||||
const result = buildRetryModelPayload(model, agentSettings)
|
||||
|
||||
// then
|
||||
expect(result).toEqual({
|
||||
model: { providerID: "anthropic", modelID: "claude-sonnet-4-5" },
|
||||
variant: "high",
|
||||
})
|
||||
})
|
||||
|
||||
test("should include reasoningEffort from agent settings", () => {
|
||||
// given
|
||||
const model = "openai/gpt-5.4"
|
||||
const agentSettings = { variant: "high", reasoningEffort: "xhigh" }
|
||||
|
||||
// when
|
||||
const result = buildRetryModelPayload(model, agentSettings)
|
||||
|
||||
// then
|
||||
expect(result).toEqual({
|
||||
model: { providerID: "openai", modelID: "gpt-5.4" },
|
||||
variant: "high",
|
||||
reasoningEffort: "xhigh",
|
||||
})
|
||||
})
|
||||
|
||||
test("should not include reasoningEffort when agent settings has none", () => {
|
||||
// given
|
||||
const model = "chutes/kimi-k2.5"
|
||||
const agentSettings = { variant: "medium" }
|
||||
|
||||
// when
|
||||
const result = buildRetryModelPayload(model, agentSettings)
|
||||
|
||||
// then
|
||||
expect(result).toEqual({
|
||||
model: { providerID: "chutes", modelID: "kimi-k2.5" },
|
||||
variant: "medium",
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -2,24 +2,29 @@ import { parseModelString } from "../../tools/delegate-task/model-string-parser"
|
||||
|
||||
export function buildRetryModelPayload(
|
||||
model: string,
|
||||
): { model: { providerID: string; modelID: string }; variant?: string } | undefined {
|
||||
agentSettings?: { variant?: string; reasoningEffort?: string },
|
||||
): { model: { providerID: string; modelID: string }; variant?: string; reasoningEffort?: string } | undefined {
|
||||
const parsedModel = parseModelString(model)
|
||||
if (!parsedModel) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return parsedModel.variant
|
||||
? {
|
||||
model: {
|
||||
providerID: parsedModel.providerID,
|
||||
modelID: parsedModel.modelID,
|
||||
},
|
||||
variant: parsedModel.variant,
|
||||
}
|
||||
: {
|
||||
model: {
|
||||
providerID: parsedModel.providerID,
|
||||
modelID: parsedModel.modelID,
|
||||
},
|
||||
}
|
||||
const variant = parsedModel.variant ?? agentSettings?.variant
|
||||
const reasoningEffort = agentSettings?.reasoningEffort
|
||||
|
||||
const payload: { model: { providerID: string; modelID: string }; variant?: string; reasoningEffort?: string } = {
|
||||
model: {
|
||||
providerID: parsedModel.providerID,
|
||||
modelID: parsedModel.modelID,
|
||||
},
|
||||
}
|
||||
|
||||
if (variant) {
|
||||
payload.variant = variant
|
||||
}
|
||||
if (reasoningEffort) {
|
||||
payload.reasoningEffort = reasoningEffort
|
||||
}
|
||||
|
||||
return payload
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user