refactor(chatCore): extrai normalização de effort-variant do Claude (#3501)

Move o bloco de effort-variant do Claude/Claude-Code do topo de handleChatCore
(strip de claude-...-{low,medium,high,xhigh,max} de volta ao id base + expõe o
nível como reasoning_effort; effort explícito do cliente vence; passthrough
nativo com sourceFormat==='claude' fica intocado) para o novo leaf
open-sse/handlers/chatCore/claudeEffortVariant.ts (applyClaudeEffortVariant). A
função muta o body in-place e retorna { effectiveModel, log }, que o handler
aplica (reatribuição de effectiveModel + log?.info), byte-idêntico.

Import órfão splitClaudeEffortSuffix migra para o leaf (getModelTargetFormat /
PROVIDER_ID_TO_ALIAS permanecem no import de providerModels);
isClaudeCodeCompatibleProvider e FORMATS seguem no handler (usados em outros
pontos). A detecção de effort explícito foi isolada no helper privado
hasExplicitClaudeEffort para manter applyClaudeEffortVariant abaixo do teto de
complexidade (16->≤15; mesmo padrão de resolveContextCachePin/
markConnectionLevelExhaustion).

chatCore.ts 4993->4978 (shrink -15); baseline file-size ratchetado.
complexity 1905=1905 (neutro). Coberto por
tests/unit/chatcore-claude-effort-variant.test.ts (7 casos).
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-06-21 10:45:17 -03:00
parent b28f6e9346
commit 36b2f7ffab
3 changed files with 183 additions and 29 deletions

View File

@@ -70,11 +70,8 @@ import {
import { createRequestLogger } from "../utils/requestLogger.ts";
import { createPreparedRequestLogger, runWithCapture } from "../utils/providerRequestLogging.ts";
import { applyResponsesPreviousResponseIdPolicy } from "../utils/responsesStatePolicy.ts";
import {
getModelTargetFormat,
PROVIDER_ID_TO_ALIAS,
splitClaudeEffortSuffix,
} from "../config/providerModels.ts";
import { getModelTargetFormat, PROVIDER_ID_TO_ALIAS } from "../config/providerModels.ts";
import { applyClaudeEffortVariant } from "./chatCore/claudeEffortVariant.ts";
import { DEFAULT_THINKING_CLAUDE_SIGNATURE } from "../config/defaultThinkingSignature.ts";
import {
getStripTypesForProviderModel,
@@ -954,30 +951,18 @@ export async function handleChatCore({
// it into Claude thinking/effort config. An explicit client-supplied effort always
// wins; native Claude passthrough is left untouched (it carries its own `thinking`),
// and non-thinking base models are cleaned up later by normalizeThinkingForModel().
if (
(provider === "claude" || isClaudeCodeCompatibleProvider(provider)) &&
typeof effectiveModel === "string"
) {
const { baseModel, effort } = splitClaudeEffortSuffix(effectiveModel);
if (effort) {
effectiveModel = baseModel;
if (body && typeof body === "object" && !Array.isArray(body)) {
const claudeBody = body as Record<string, unknown>;
claudeBody.model = baseModel;
if (sourceFormat !== FORMATS.CLAUDE) {
const explicitEffort =
claudeBody.reasoning_effort ??
(claudeBody.reasoning as Record<string, unknown> | undefined)?.effort ??
(claudeBody.output_config as Record<string, unknown> | undefined)?.effort;
if (explicitEffort === undefined || explicitEffort === null || explicitEffort === "") {
claudeBody.reasoning_effort = effort;
}
}
}
log?.info?.(
"PARAMS",
`Claude effort variant: stripped "-${effort}" → ${baseModel} (reasoning_effort=${effort})`
);
// Extracted to chatCore/claudeEffortVariant.ts (#3501); mutates body in place and returns the
// stripped model + an optional log line, keeping behaviour byte-identical.
{
const effortVariant = applyClaudeEffortVariant({
provider,
effectiveModel,
body,
sourceFormat,
});
effectiveModel = effortVariant.effectiveModel;
if (effortVariant.log) {
log?.info?.("PARAMS", effortVariant.log);
}
}

View File

@@ -0,0 +1,62 @@
/**
* chatCore Claude effort-variant normalizer (Quality Gate v2 / Fase 9 — chatCore god-file
* decomposition, #3501).
*
* The Claude / Claude-Code model picker (e.g. VS Code's "Effort" slider) advertises
* claude-...-{low,medium,high,xhigh,max}. Anthropic has no such model, so the suffixed id 404s
* upstream. This strips it back to the real base id and surfaces the level as reasoning_effort so
* the OpenAI→Claude translator / Claude-Code bridge can turn it into Claude thinking/effort config.
* An explicit client-supplied effort always wins; native Claude passthrough (sourceFormat === claude)
* is left untouched (it carries its own `thinking`). The body is mutated in place (model +
* reasoning_effort), byte-identical to the previous inline block; the new effectiveModel and an
* optional log line are returned for the handler to apply.
*/
import { splitClaudeEffortSuffix } from "../../config/providerModels.ts";
import { isClaudeCodeCompatibleProvider } from "../../services/claudeCodeCompatible.ts";
import { FORMATS } from "../../translator/formats.ts";
/**
* True when the client already supplied an explicit reasoning effort (top-level reasoning_effort,
* reasoning.effort, or output_config.effort) — in which case the stripped suffix must not overwrite
* it. A blank string counts as "not supplied".
*/
function hasExplicitClaudeEffort(claudeBody: Record<string, unknown>): boolean {
const explicitEffort =
claudeBody.reasoning_effort ??
(claudeBody.reasoning as Record<string, unknown> | undefined)?.effort ??
(claudeBody.output_config as Record<string, unknown> | undefined)?.effort;
return !(explicitEffort === undefined || explicitEffort === null || explicitEffort === "");
}
export function applyClaudeEffortVariant(opts: {
provider: string | null | undefined;
effectiveModel: string;
/** Mutated in place (model + reasoning_effort) when an effort suffix is stripped. */
body: unknown;
sourceFormat: string;
}): { effectiveModel: string; log: string | null } {
const { provider, body, sourceFormat } = opts;
let effectiveModel = opts.effectiveModel;
let log: string | null = null;
if (
(provider === "claude" || isClaudeCodeCompatibleProvider(provider)) &&
typeof effectiveModel === "string"
) {
const { baseModel, effort } = splitClaudeEffortSuffix(effectiveModel);
if (effort) {
effectiveModel = baseModel;
if (body && typeof body === "object" && !Array.isArray(body)) {
const claudeBody = body as Record<string, unknown>;
claudeBody.model = baseModel;
if (sourceFormat !== FORMATS.CLAUDE && !hasExplicitClaudeEffort(claudeBody)) {
claudeBody.reasoning_effort = effort;
}
}
log = `Claude effort variant: stripped "-${effort}" → ${baseModel} (reasoning_effort=${effort})`;
}
}
return { effectiveModel, log };
}

View File

@@ -0,0 +1,107 @@
// tests/unit/chatcore-claude-effort-variant.test.ts
// 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
// sourceFormat==="claude" skip, the explicit-effort-wins rule, and the returned effectiveModel/log.
import { test } from "node:test";
import assert from "node:assert/strict";
import { applyClaudeEffortVariant } from "../../open-sse/handlers/chatCore/claudeEffortVariant.ts";
import { FORMATS } from "../../open-sse/translator/formats.ts";
test("claude provider + effort suffix → strips to base, mutates body model + reasoning_effort, returns log", () => {
const body: Record<string, unknown> = { model: "claude-sonnet-4-high", messages: [] };
const r = applyClaudeEffortVariant({
provider: "claude",
effectiveModel: "claude-sonnet-4-high",
body,
sourceFormat: FORMATS.OPENAI,
});
assert.equal(r.effectiveModel, "claude-sonnet-4");
assert.equal(body.model, "claude-sonnet-4");
assert.equal(body.reasoning_effort, "high");
assert.match(String(r.log), /stripped "-high" → claude-sonnet-4 \(reasoning_effort=high\)/);
});
test("claude-code-compatible provider triggers the same stripping", () => {
const body: Record<string, unknown> = { model: "claude-opus-4-xhigh", messages: [] };
const r = applyClaudeEffortVariant({
provider: "anthropic-compatible-cc-default",
effectiveModel: "claude-opus-4-xhigh",
body,
sourceFormat: FORMATS.OPENAI,
});
assert.equal(r.effectiveModel, "claude-opus-4");
assert.equal(body.model, "claude-opus-4");
assert.equal(body.reasoning_effort, "xhigh");
});
test("sourceFormat 'claude' strips the model but does NOT inject reasoning_effort", () => {
const body: Record<string, unknown> = { model: "claude-sonnet-4-medium", messages: [] };
const r = applyClaudeEffortVariant({
provider: "claude",
effectiveModel: "claude-sonnet-4-medium",
body,
sourceFormat: FORMATS.CLAUDE,
});
assert.equal(r.effectiveModel, "claude-sonnet-4");
assert.equal(body.model, "claude-sonnet-4");
assert.equal(body.reasoning_effort, undefined);
});
test("an explicit client reasoning_effort wins (not overwritten)", () => {
const body: Record<string, unknown> = { model: "claude-sonnet-4-low", reasoning_effort: "high", messages: [] };
const r = applyClaudeEffortVariant({
provider: "claude",
effectiveModel: "claude-sonnet-4-low",
body,
sourceFormat: FORMATS.OPENAI,
});
assert.equal(r.effectiveModel, "claude-sonnet-4");
assert.equal(body.reasoning_effort, "high"); // unchanged
});
test("explicit effort nested under reasoning.effort also wins", () => {
const body: Record<string, unknown> = {
model: "claude-sonnet-4-low",
reasoning: { effort: "medium" },
messages: [],
};
const r = applyClaudeEffortVariant({
provider: "claude",
effectiveModel: "claude-sonnet-4-low",
body,
sourceFormat: FORMATS.OPENAI,
});
assert.equal(body.reasoning_effort, undefined); // explicit reasoning.effort present → no injection
assert.equal(r.effectiveModel, "claude-sonnet-4");
});
test("no effort suffix → no change, no log", () => {
const body: Record<string, unknown> = { model: "claude-sonnet-4", messages: [] };
const r = applyClaudeEffortVariant({
provider: "claude",
effectiveModel: "claude-sonnet-4",
body,
sourceFormat: FORMATS.OPENAI,
});
assert.equal(r.effectiveModel, "claude-sonnet-4");
assert.equal(body.model, "claude-sonnet-4");
assert.equal(body.reasoning_effort, undefined);
assert.equal(r.log, null);
});
test("non-claude provider is a no-op even with an effort suffix", () => {
const body: Record<string, unknown> = { model: "gpt-5-high", messages: [] };
const r = applyClaudeEffortVariant({
provider: "openai",
effectiveModel: "gpt-5-high",
body,
sourceFormat: FORMATS.OPENAI,
});
assert.equal(r.effectiveModel, "gpt-5-high");
assert.equal(body.model, "gpt-5-high");
assert.equal(body.reasoning_effort, undefined);
assert.equal(r.log, null);
});