From 4b34a75fe333945c58ad8307bbcf6783c9aa2ee4 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Date: Sun, 21 Jun 2026 19:13:56 -0300 Subject: [PATCH] refactor(chatCore): extrai extractSystemRoleMessages + buildUpstreamHeadersForExecute para leaves puros (#3501) (#4548) Integrated into release/v3.8.33 --- open-sse/handlers/chatCore.ts | 100 ++++-------------- .../handlers/chatCore/claudeSystemRole.ts | 48 +++++++++ .../chatCore/upstreamExecuteHeaders.ts | 71 +++++++++++++ .../chatcore-upstream-execute-headers.test.ts | 70 ++++++++++++ 4 files changed, 207 insertions(+), 82 deletions(-) create mode 100644 open-sse/handlers/chatCore/claudeSystemRole.ts create mode 100644 open-sse/handlers/chatCore/upstreamExecuteHeaders.ts create mode 100644 tests/unit/chatcore-upstream-execute-headers.test.ts diff --git a/open-sse/handlers/chatCore.ts b/open-sse/handlers/chatCore.ts index ae7365ad2b..fafa02fc8b 100644 --- a/open-sse/handlers/chatCore.ts +++ b/open-sse/handlers/chatCore.ts @@ -1,6 +1,8 @@ import { injectMemoryAndSkills } from "./chatCore/memorySkillsInjection.ts"; import { resolveChatCoreRequestSetup } from "./chatCore/requestSetup.ts"; import { buildFailureUsageRecord } from "./chatCore/failureUsage.ts"; +import { extractSystemRoleMessages } from "./chatCore/claudeSystemRole.ts"; +export { extractSystemRoleMessages } from "./chatCore/claudeSystemRole.ts"; import { checkIdempotencyCache } from "./chatCore/idempotency.ts"; import { checkSemanticCache } from "./chatCore/semanticCache.ts"; import { sanitizeChatRequestBody } from "./chatCore/sanitization.ts"; @@ -164,7 +166,6 @@ import { import { getModelNormalizeToolCallId, getModelPreserveOpenAIDeveloperRole, - getModelUpstreamExtraHeaders, } from "@/lib/localDb"; import { getProviderCredentials, extractSessionAffinityKey } from "@/sse/services/auth"; import { deleteSessionAccountAffinity } from "@/lib/db/sessionAccountAffinity"; @@ -181,10 +182,7 @@ import { } from "../utils/cacheControlPolicy.ts"; import { getCachedSettings } from "@/lib/db/readCache"; import { applyCodexGlobalFastServiceTier } from "@/lib/providers/codexFastTier"; -import { - CPA_FORCE_FAST_MODE_HEADER, - shouldRequestClaudeFastMode, -} from "@/lib/providers/claudeFastMode"; +import { buildUpstreamHeadersForExecute as buildUpstreamHeadersForExecuteFor } from "./chatCore/upstreamExecuteHeaders.ts"; import { resolveEffectiveServiceTier as resolveEffectiveServiceTierFor, resolveReportedServiceTier as resolveReportedServiceTierFor, @@ -542,43 +540,8 @@ function buildExecutorClientHeaders( return Object.keys(normalized).length > 0 ? normalized : null; } -export function extractSystemRoleMessages(payload: Record): void { - if (!Array.isArray(payload.messages)) return; - const messages = payload.messages as Array<{ role?: unknown; content?: unknown }>; - // Treat both `system` and `developer` as system-equivalent (OpenAI's Responses - // API renamed system → developer). Anthropic rejects either as a chat role, so - // both must be lifted into the top-level `system` field — parity with the - // normal-path extractSystemMessagesToBody closure. - const isSystemRole = (role: unknown): boolean => - typeof role === "string" && - (role.toLowerCase() === "system" || role.toLowerCase() === "developer"); - const systemMessages = messages.filter((m) => isSystemRole(m.role)); - if (systemMessages.length === 0) return; - - const extraBlocks: Array> = []; - for (const sm of systemMessages) { - if (typeof sm.content === "string" && sm.content.length > 0) { - extraBlocks.push({ type: "text", text: sm.content }); - } else if (Array.isArray(sm.content)) { - for (const block of sm.content as Array>) { - if (block?.type === "text" && typeof block.text === "string" && block.text.length > 0) { - extraBlocks.push({ ...block }); - } - } - } - } - if (extraBlocks.length > 0) { - const existingSystem = payload.system; - if (typeof existingSystem === "string" && existingSystem.length > 0) { - payload.system = [{ type: "text", text: existingSystem }, ...extraBlocks]; - } else if (Array.isArray(existingSystem)) { - payload.system = [...(existingSystem as Array>), ...extraBlocks]; - } else { - payload.system = extraBlocks; - } - } - payload.messages = messages.filter((m) => !isSystemRole(m.role)); -} +// extractSystemRoleMessages extracted to chatCore/claudeSystemRole.ts (#3501); re-exported above so +// existing importers (e.g. tests/unit/system-role-extraction.test.ts) keep resolving it from here. export async function handleChatCore({ body, @@ -1100,46 +1063,19 @@ export async function handleChatCore({ ? credentials.providerSpecificData.customUserAgent.trim() : ""; - const buildUpstreamHeadersForExecute = (modelToCall: string): Record => { - const upstreamHeaders = - modelToCall === effectiveModel - ? { - ...getModelUpstreamExtraHeaders(provider || "", model || "", sourceFormat), - ...getModelUpstreamExtraHeaders(provider || "", resolvedModel || "", sourceFormat), - } - : (() => { - const r = resolveModelAlias(modelToCall); - return { - ...getModelUpstreamExtraHeaders(provider || "", modelToCall || "", sourceFormat), - ...getModelUpstreamExtraHeaders(provider || "", r || "", sourceFormat), - }; - })(); - - if (connectionCustomUserAgent) { - upstreamHeaders["User-Agent"] = connectionCustomUserAgent; - if ("user-agent" in upstreamHeaders) { - upstreamHeaders["user-agent"] = connectionCustomUserAgent; - } - } - - // Claude Fast Mode opt-in. When the user has enabled this in - // Settings > AI AND the target provider is the canonical Anthropic - // `claude` provider (Claude Code-compatible CPA bridges are excluded - // since they already select their own entrypoint) AND the model id - // matches the configured list, signal to a paired CLIProxyAPI build to - // rewrite the cc_entrypoint so the request can reach Anthropic Fast - // Mode (speed:"fast"). CPA builds that do not understand the header - // forward it harmlessly. - if ( - provider === "claude" && - typeof settings !== "undefined" && - shouldRequestClaudeFastMode(settings, modelToCall) - ) { - upstreamHeaders[CPA_FORCE_FAST_MODE_HEADER] = "1"; - } - - return upstreamHeaders; - }; + // Upstream extra-header building extracted to chatCore/upstreamExecuteHeaders.ts (#3501); bind the + // per-request inputs once and delegate so the existing call sites stay byte-identical. + const buildUpstreamHeadersForExecute = (modelToCall: string): Record => + buildUpstreamHeadersForExecuteFor({ + modelToCall, + effectiveModel, + provider, + model, + resolvedModel, + sourceFormat, + connectionCustomUserAgent, + settings, + }); // Default to false unless client explicitly sets stream: true (OpenAI spec compliant) const acceptHeader = diff --git a/open-sse/handlers/chatCore/claudeSystemRole.ts b/open-sse/handlers/chatCore/claudeSystemRole.ts new file mode 100644 index 0000000000..0c22180166 --- /dev/null +++ b/open-sse/handlers/chatCore/claudeSystemRole.ts @@ -0,0 +1,48 @@ +/** + * chatCore Claude system-role lifter (Quality Gate v2 / Fase 9 — chatCore god-file + * decomposition, #3501). + * + * Pure helper extracted from chatCore.ts: lifts any `system`/`developer` role messages out of the + * messages[] array into the top-level `system` field. Anthropic's Messages API rejects either as a + * chat role, so they must be hoisted. `developer` is OpenAI's Responses-API rename of `system` and + * is treated identically. Mutates the payload in place; behaviour is byte-identical to the previous + * top-level definition (still re-exported from chatCore.ts for existing importers/tests). + */ + +export function extractSystemRoleMessages(payload: Record): void { + if (!Array.isArray(payload.messages)) return; + const messages = payload.messages as Array<{ role?: unknown; content?: unknown }>; + // Treat both `system` and `developer` as system-equivalent (OpenAI's Responses + // API renamed system → developer). Anthropic rejects either as a chat role, so + // both must be lifted into the top-level `system` field — parity with the + // normal-path extractSystemMessagesToBody closure. + const isSystemRole = (role: unknown): boolean => + typeof role === "string" && + (role.toLowerCase() === "system" || role.toLowerCase() === "developer"); + const systemMessages = messages.filter((m) => isSystemRole(m.role)); + if (systemMessages.length === 0) return; + + const extraBlocks: Array> = []; + for (const sm of systemMessages) { + if (typeof sm.content === "string" && sm.content.length > 0) { + extraBlocks.push({ type: "text", text: sm.content }); + } else if (Array.isArray(sm.content)) { + for (const block of sm.content as Array>) { + if (block?.type === "text" && typeof block.text === "string" && block.text.length > 0) { + extraBlocks.push({ ...block }); + } + } + } + } + if (extraBlocks.length > 0) { + const existingSystem = payload.system; + if (typeof existingSystem === "string" && existingSystem.length > 0) { + payload.system = [{ type: "text", text: existingSystem }, ...extraBlocks]; + } else if (Array.isArray(existingSystem)) { + payload.system = [...(existingSystem as Array>), ...extraBlocks]; + } else { + payload.system = extraBlocks; + } + } + payload.messages = messages.filter((m) => !isSystemRole(m.role)); +} diff --git a/open-sse/handlers/chatCore/upstreamExecuteHeaders.ts b/open-sse/handlers/chatCore/upstreamExecuteHeaders.ts new file mode 100644 index 0000000000..cb8adae950 --- /dev/null +++ b/open-sse/handlers/chatCore/upstreamExecuteHeaders.ts @@ -0,0 +1,71 @@ +/** + * chatCore per-model upstream extra-header builder (Quality Gate v2 / Fase 9 — chatCore god-file + * decomposition, #3501). + * + * Pure builder extracted from handleChatCore: assembles the per-model upstream extra headers for an + * execute() call. Merges the configured per-model headers (for the resolved model and its alias), + * applies a connection's custom User-Agent override, and opts into Claude Fast Mode (CPA bridge + * header) when enabled in settings for the canonical `claude` provider and a supported model. + * Side-effect-free; behaviour is byte-identical to the previous inline closure. + */ + +import { getModelUpstreamExtraHeaders } from "@/lib/db/models"; +import { resolveModelAlias } from "../../services/modelDeprecation.ts"; +import { CPA_FORCE_FAST_MODE_HEADER, shouldRequestClaudeFastMode } from "@/lib/providers/claudeFastMode"; + +export function buildUpstreamHeadersForExecute(opts: { + modelToCall: string; + effectiveModel: string; + provider: string | null | undefined; + model: string | null | undefined; + resolvedModel: string; + sourceFormat: string; + connectionCustomUserAgent: string; + settings: unknown; +}): Record { + const { + modelToCall, + effectiveModel, + provider, + model, + resolvedModel, + sourceFormat, + connectionCustomUserAgent, + settings, + } = opts; + + const upstreamHeaders: Record = + modelToCall === effectiveModel + ? { + ...getModelUpstreamExtraHeaders(provider || "", model || "", sourceFormat), + ...getModelUpstreamExtraHeaders(provider || "", resolvedModel || "", sourceFormat), + } + : (() => { + const r = resolveModelAlias(modelToCall); + return { + ...getModelUpstreamExtraHeaders(provider || "", modelToCall || "", sourceFormat), + ...getModelUpstreamExtraHeaders(provider || "", r || "", sourceFormat), + }; + })(); + + if (connectionCustomUserAgent) { + upstreamHeaders["User-Agent"] = connectionCustomUserAgent; + if ("user-agent" in upstreamHeaders) { + upstreamHeaders["user-agent"] = connectionCustomUserAgent; + } + } + + // Claude Fast Mode opt-in. When enabled in Settings > AI AND the target provider is the canonical + // Anthropic `claude` provider (Claude Code-compatible CPA bridges are excluded since they select + // their own entrypoint) AND the model id matches the configured list, signal to a paired + // CLIProxyAPI build to rewrite the cc_entrypoint so the request reaches Anthropic Fast Mode. + if ( + provider === "claude" && + typeof settings !== "undefined" && + shouldRequestClaudeFastMode(settings, modelToCall) + ) { + upstreamHeaders[CPA_FORCE_FAST_MODE_HEADER] = "1"; + } + + return upstreamHeaders; +} diff --git a/tests/unit/chatcore-upstream-execute-headers.test.ts b/tests/unit/chatcore-upstream-execute-headers.test.ts new file mode 100644 index 0000000000..f770a1276a --- /dev/null +++ b/tests/unit/chatcore-upstream-execute-headers.test.ts @@ -0,0 +1,70 @@ +// tests/unit/chatcore-upstream-execute-headers.test.ts +// Characterization of buildUpstreamHeadersForExecute — the per-model upstream extra-header builder +// extracted from handleChatCore (chatCore god-file decomposition, #3501). Locks: the +// connection custom User-Agent override, the Claude Fast Mode opt-in (claude provider + enabled +// settings + supported model only), and the modelToCall===effectiveModel vs alias branches. +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { buildUpstreamHeadersForExecute } from "../../open-sse/handlers/chatCore/upstreamExecuteHeaders.ts"; +import { CPA_FORCE_FAST_MODE_HEADER } from "../../src/lib/providers/claudeFastMode.ts"; + +const base = { + modelToCall: "some-model", + effectiveModel: "some-model", + provider: "openai", + model: "some-model", + resolvedModel: "some-model", + sourceFormat: "openai", + connectionCustomUserAgent: "", + settings: {}, +}; + +test("no custom UA / non-claude / no fast settings → no UA and no fast-mode header", () => { + const h = buildUpstreamHeadersForExecute({ ...base }); + assert.equal(h["User-Agent"], undefined); + assert.equal(h[CPA_FORCE_FAST_MODE_HEADER], undefined); +}); + +test("connection custom User-Agent overrides the upstream User-Agent", () => { + const h = buildUpstreamHeadersForExecute({ ...base, connectionCustomUserAgent: "MyAgent/1.0" }); + assert.equal(h["User-Agent"], "MyAgent/1.0"); +}); + +test("claude provider + enabled fast-mode settings + supported model → CPA header set", () => { + const h = buildUpstreamHeadersForExecute({ + ...base, + provider: "claude", + modelToCall: "claude-fast-x", + effectiveModel: "claude-fast-x", + settings: { claudeFastMode: { enabled: true, supportedModels: ["claude-fast-x"] } }, + }); + assert.equal(h[CPA_FORCE_FAST_MODE_HEADER], "1"); +}); + +test("fast-mode header is NOT set when the model is not in the supported list", () => { + const h = buildUpstreamHeadersForExecute({ + ...base, + provider: "claude", + modelToCall: "claude-other", + effectiveModel: "claude-other", + settings: { claudeFastMode: { enabled: true, supportedModels: ["claude-fast-x"] } }, + }); + assert.equal(h[CPA_FORCE_FAST_MODE_HEADER], undefined); +}); + +test("fast-mode header is NOT set for non-claude providers even with fast settings", () => { + const h = buildUpstreamHeadersForExecute({ + ...base, + provider: "openai", + modelToCall: "claude-fast-x", + effectiveModel: "claude-fast-x", + settings: { claudeFastMode: { enabled: true, supportedModels: ["claude-fast-x"] } }, + }); + assert.equal(h[CPA_FORCE_FAST_MODE_HEADER], undefined); +}); + +test("returns a plain object (no per-model extra headers configured for unknown models)", () => { + const h = buildUpstreamHeadersForExecute({ ...base, modelToCall: "totally-unknown", effectiveModel: "x" }); + assert.equal(typeof h, "object"); + assert.equal(h[CPA_FORCE_FAST_MODE_HEADER], undefined); +});