fix(sse): strip Claude effort-suffix ids for any provider serving a real Claude model

This commit is contained in:
Will Gordon
2026-07-30 14:51:53 -04:00
parent a8fb526e9c
commit 0d2678360f
2 changed files with 75 additions and 8 deletions

View File

@@ -15,6 +15,7 @@
import { splitClaudeEffortSuffix } from "../../config/providerModels.ts";
import { isClaudeCodeCompatibleProvider } from "../../services/claudeCodeCompatible.ts";
import { FORMATS } from "../../translator/formats.ts";
import { isKnownClaudeEffortBaseModel } from "../../utils/claudeEffortVariants.ts";
/**
* True when the client already supplied an explicit reasoning effort (top-level reasoning_effort,
@@ -40,12 +41,10 @@ export function applyClaudeEffortVariant(opts: {
let effectiveModel = opts.effectiveModel;
let log: string | null = null;
if (
(provider === "claude" || isClaudeCodeCompatibleProvider(provider)) &&
typeof effectiveModel === "string"
) {
if (typeof effectiveModel === "string") {
const { baseModel, effort } = splitClaudeEffortSuffix(effectiveModel);
if (effort) {
const isDirectClaudeLane = provider === "claude" || isClaudeCodeCompatibleProvider(provider);
if (effort && (isDirectClaudeLane || isKnownClaudeEffortBaseModel(baseModel))) {
effectiveModel = baseModel;
if (body && typeof body === "object" && !Array.isArray(body)) {
const claudeBody = body as Record<string, unknown>;

View File

@@ -2,8 +2,9 @@
// Characterization of applyClaudeEffortVariant — the Claude effort-suffix normalization extracted
// from handleChatCore (chatCore god-file decomposition, #3501). The VS Code "Effort" slider
// advertises claude-...-{low,medium,high,xhigh,max}; Anthropic has no such model, so the suffix is
// stripped to the base id and surfaced as reasoning_effort. Locks: the provider gate (claude /
// claude-code-compatible only), the in-place body mutation (model + reasoning_effort), the
// stripped to the base id and surfaced as reasoning_effort. Locks: the direct-Claude-lane
// unconditional strip (claude / claude-code-compatible), the predicate-gated strip for any other
// provider serving a real Claude model, the in-place body mutation (model + reasoning_effort), the
// sourceFormat==="claude" skip, the explicit-effort-wins rule, and the returned effectiveModel/log.
import { test } from "node:test";
import assert from "node:assert/strict";
@@ -51,7 +52,11 @@ test("sourceFormat 'claude' strips the model but does NOT inject reasoning_effor
});
test("an explicit client reasoning_effort wins (not overwritten)", () => {
const body: Record<string, unknown> = { model: "claude-sonnet-4-low", reasoning_effort: "high", messages: [] };
const body: Record<string, unknown> = {
model: "claude-sonnet-4-low",
reasoning_effort: "high",
messages: [],
};
const r = applyClaudeEffortVariant({
provider: "claude",
effectiveModel: "claude-sonnet-4-low",
@@ -105,3 +110,66 @@ test("non-claude provider is a no-op even with an effort suffix", () => {
assert.equal(body.reasoning_effort, undefined);
assert.equal(r.log, null);
});
test("non-claude provider serving a real Claude model strips the effort suffix", () => {
const body: Record<string, unknown> = { model: "claude-sonnet-5-high", messages: [] };
const r = applyClaudeEffortVariant({
provider: "vertex",
effectiveModel: "claude-sonnet-5-high",
body,
sourceFormat: FORMATS.OPENAI,
});
assert.equal(r.effectiveModel, "claude-sonnet-5");
assert.equal(body.model, "claude-sonnet-5");
assert.equal(body.reasoning_effort, "high");
});
test("safety guard: non-claude provider with a non-Claude model ending in a suffix word is left unchanged", () => {
const body: Record<string, unknown> = { model: "custom-model-high", messages: [] };
const r = applyClaudeEffortVariant({
provider: "some-other-provider",
effectiveModel: "custom-model-high",
body,
sourceFormat: FORMATS.OPENAI,
});
assert.equal(r.effectiveModel, "custom-model-high");
assert.equal(body.model, "custom-model-high");
assert.equal(body.reasoning_effort, undefined);
assert.equal(r.log, null);
});
test("claude-code-compatible provider strips even an unregistered model id (direct lane short-circuits the predicate)", () => {
// Proves the "unconditional strip, zero regression" claim: isDirectClaudeLane short-circuits
// the `||`, so isKnownClaudeEffortBaseModel() is never consulted for claude/CC-compatible
// providers — unlike the safety-guard case above, which requires the predicate to pass.
const body: Record<string, unknown> = {
model: "totally-unregistered-model-xyz-high",
messages: [],
};
const r = applyClaudeEffortVariant({
provider: "anthropic-compatible-cc-default",
effectiveModel: "totally-unregistered-model-xyz-high",
body,
sourceFormat: FORMATS.OPENAI,
});
assert.equal(r.effectiveModel, "totally-unregistered-model-xyz");
assert.equal(body.model, "totally-unregistered-model-xyz");
assert.equal(body.reasoning_effort, "high");
});
test("no-think alias's explicit reasoning_effort:none is not overwritten by a stripped effort suffix", () => {
const body: Record<string, unknown> = {
model: "claude-sonnet-5-high",
reasoning_effort: "none",
messages: [],
};
const r = applyClaudeEffortVariant({
provider: "vertex",
effectiveModel: "claude-sonnet-5-high",
body,
sourceFormat: FORMATS.OPENAI,
});
assert.equal(r.effectiveModel, "claude-sonnet-5");
assert.equal(body.model, "claude-sonnet-5");
assert.equal(body.reasoning_effort, "none");
});