Files
OmniRoute/tests/unit/claude-effort-suffix-strip.test.ts
Felipe Almeman cccb087011 fix(claude): strip reasoning-effort suffix from Claude model ids (#3807)
The Claude / Claude-Code model picker (VS Code Copilot's "Effort" slider)
advertises effort variants by appending a suffix to the base model id
(claude-...-{low,medium,high,xhigh,max}). Anthropic has no such model, so the
suffixed id was forwarded verbatim and 404'd upstream — repeated 404s then
tripped the account circuit breaker, surfacing to clients as a bogus
"rate limited" cooldown.

splitClaudeEffortSuffix() strips the suffix off Claude / Claude-Code targets
(the upstream receives the real base id) and surfaces the level as
reasoning_effort so the OpenAI->Claude translator / CC bridge convert it into
thinking/effort config. Explicit client effort is never overridden; native
Claude passthrough is untouched.

Integrated into release/v3.8.25.

Co-authored-by: Felipe Almeman <felipe@aireset.com.br>
2026-06-14 01:09:52 -03:00

83 lines
2.9 KiB
TypeScript

/**
* Tests for splitClaudeEffortSuffix — stripping the reasoning-effort suffix that
* the Claude / Claude-Code model picker (VS Code "Effort" slider) appends to a base
* model id (claude-...-{low,medium,high,xhigh,max}). The suffix must be stripped so
* the upstream Anthropic relay receives a real model id (else HTTP 404) and surfaced
* as reasoning_effort downstream. See chatCore.ts effort-variant normalization.
*/
import { describe, it } from "node:test";
import assert from "node:assert/strict";
import {
splitClaudeEffortSuffix,
CLAUDE_EFFORT_SUFFIXES,
} from "../../open-sse/config/providerModels.ts";
describe("splitClaudeEffortSuffix", () => {
it("splits every effort level off claude-opus-4-8", () => {
assert.deepEqual(splitClaudeEffortSuffix("claude-opus-4-8-low"), {
baseModel: "claude-opus-4-8",
effort: "low",
});
assert.deepEqual(splitClaudeEffortSuffix("claude-opus-4-8-medium"), {
baseModel: "claude-opus-4-8",
effort: "medium",
});
assert.deepEqual(splitClaudeEffortSuffix("claude-opus-4-8-high"), {
baseModel: "claude-opus-4-8",
effort: "high",
});
assert.deepEqual(splitClaudeEffortSuffix("claude-opus-4-8-xhigh"), {
baseModel: "claude-opus-4-8",
effort: "xhigh",
});
assert.deepEqual(splitClaudeEffortSuffix("claude-opus-4-8-max"), {
baseModel: "claude-opus-4-8",
effort: "max",
});
});
it("does not confuse 'xhigh' with 'high' (no strip to '...-x')", () => {
const split = splitClaudeEffortSuffix("claude-opus-4-8-xhigh");
assert.equal(split.baseModel, "claude-opus-4-8");
assert.equal(split.effort, "xhigh");
});
it("returns null effort for a bare base id", () => {
assert.deepEqual(splitClaudeEffortSuffix("claude-opus-4-8"), {
baseModel: "claude-opus-4-8",
effort: null,
});
assert.deepEqual(splitClaudeEffortSuffix("claude-sonnet-4-6"), {
baseModel: "claude-sonnet-4-6",
effort: null,
});
});
it("is case-insensitive and works across model families", () => {
assert.deepEqual(splitClaudeEffortSuffix("claude-sonnet-4-6-HIGH"), {
baseModel: "claude-sonnet-4-6",
effort: "high",
});
assert.deepEqual(splitClaudeEffortSuffix("claude-haiku-4-5-low"), {
baseModel: "claude-haiku-4-5",
effort: "low",
});
});
it("tolerates non-string / empty input", () => {
assert.deepEqual(splitClaudeEffortSuffix(undefined), { baseModel: "", effort: null });
assert.deepEqual(splitClaudeEffortSuffix(null), { baseModel: "", effort: null });
assert.deepEqual(splitClaudeEffortSuffix(123), { baseModel: "", effort: null });
assert.deepEqual(splitClaudeEffortSuffix(""), { baseModel: "", effort: null });
});
it("orders 'xhigh' before 'high' in the suffix list", () => {
assert.ok(
CLAUDE_EFFORT_SUFFIXES.indexOf("xhigh") < CLAUDE_EFFORT_SUFFIXES.indexOf("high"),
"xhigh must be tested before high so it wins the longest-match"
);
});
});