fix(anthropic-effort): skip effort injection for Haiku models (#3308)

Haiku models do not support the effort parameter and return API errors
when it is passed. The hook now explicitly checks for Haiku model
patterns and skips effort injection, preventing silent title generation
failures.

Added EFFORT_UNSUPPORTED_PATTERN and isEffortUnsupportedModel() to
detect and skip Haiku models.

Fixes #3308
This commit is contained in:
YeonGyu-Kim
2026-04-11 13:06:24 +09:00
parent 29c27ad408
commit 7c154c6659
2 changed files with 31 additions and 0 deletions
+7
View File
@@ -1,6 +1,7 @@
import { log, normalizeModelID } from "../../shared"
const OPUS_PATTERN = /claude-.*opus/i
const EFFORT_UNSUPPORTED_PATTERN = /claude-.*haiku/i
const INTERNAL_SKIP_AGENTS = new Set(["title", "summary", "compaction"])
function isClaudeProvider(providerID: string, modelID: string): boolean {
@@ -14,6 +15,11 @@ function isOpusModel(modelID: string): boolean {
return OPUS_PATTERN.test(normalized)
}
function isEffortUnsupportedModel(modelID: string): boolean {
const normalized = normalizeModelID(modelID)
return EFFORT_UNSUPPORTED_PATTERN.test(normalized)
}
function shouldSkipForInternalAgent(agentName: string | undefined): boolean {
if (!agentName) return false
return INTERNAL_SKIP_AGENTS.has(agentName.trim().toLowerCase())
@@ -60,6 +66,7 @@ export function createAnthropicEffortHook() {
if (!isClaudeProvider(model.providerID, model.modelID)) return
if (shouldSkipForInternalAgent(agent?.name)) return
if (output.options.effort !== undefined) return
if (isEffortUnsupportedModel(model.modelID)) return
const opus = isOpusModel(model.modelID)
const clamped = clampVariant(message.variant, opus)
+24
View File
@@ -147,6 +147,30 @@ describe("createAnthropicEffortHook", () => {
expect(output.options.effort).toBeUndefined()
})
describe("#given haiku models (effort unsupported)", () => {
const haikuModels = [
"claude-haiku-4-5",
"claude-haiku-4.6",
"claude-haiku",
"claude-haiku-20240307",
]
for (const modelID of haikuModels) {
it(`skips effort injection for ${modelID}`, async () => {
// given
const hook = createAnthropicEffortHook()
const { input, output } = createMockParams({ modelID })
// when
await hook["chat.params"](input, output)
// then
expect(output.options.effort).toBeUndefined()
expect(input.message.variant).toBe("max")
})
}
})
})
describe("existing options", () => {