refactor(chatCore): extract Background Task Redirect decision (T41) (#4526, #3501)

Rebuilt onto release/v3.8.33 (base was the now-merged #4511 branch). Integrated into release/v3.8.33.
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-06-21 14:23:36 -03:00
committed by GitHub
parent b28f6e9346
commit 2601a557b8
3 changed files with 139 additions and 33 deletions

View File

@@ -249,11 +249,7 @@ import {
getTokenLimit,
resolveComboContextLimit,
} from "../services/contextManager.ts";
import {
getBackgroundTaskReason,
getDegradedModel,
getBackgroundDegradationConfig,
} from "../services/backgroundTaskDetector.ts";
import { resolveBackgroundTaskRedirect } from "./chatCore/backgroundRedirect.ts";
import type { CompressionConfig, CompressionPipelineStep } from "../services/compression/types.ts";
import { prepareWebSearchFallbackBody } from "../services/webSearchFallback.ts";
import {
@@ -904,35 +900,35 @@ export async function handleChatCore({
// Detect source format and get target format
// Model-specific targetFormat takes priority over provider default
// ── Background Task Redirection (T41) ──
const bgConfig = getBackgroundDegradationConfig();
const backgroundReason = bgConfig.enabled
? getBackgroundTaskReason(body, clientRawRequest?.headers)
: null;
if (backgroundReason) {
const degradedModel = getDegradedModel(model);
if (degradedModel !== model) {
const originalModel = model;
log?.info?.(
"BACKGROUND",
`Background task redirect (${backgroundReason}): ${originalModel}${degradedModel}`
);
model = degradedModel;
if (body && typeof body === "object") {
body.model = model;
}
logAuditEvent({
action: "routing.background_task_redirect",
actor: apiKeyInfo?.name || "system",
target: connectionId || provider || "chat",
details: {
original_model: originalModel,
redirected_to: degradedModel,
reason: backgroundReason,
},
});
// ── Background Task Redirection (T41) — decision extracted to chatCore/backgroundRedirect.ts (#3501)
// backgroundReason is the detection signal (threaded into memory/skills injection below); redirect
// is the actual model downgrade to apply, if any.
const { backgroundReason, redirect: bgRedirect } = resolveBackgroundTaskRedirect({
body,
headers: clientRawRequest?.headers,
model,
});
if (bgRedirect) {
const originalModel = model;
log?.info?.(
"BACKGROUND",
`Background task redirect (${bgRedirect.reason}): ${originalModel}${bgRedirect.degradedModel}`
);
model = bgRedirect.degradedModel;
if (body && typeof body === "object") {
body.model = model;
}
logAuditEvent({
action: "routing.background_task_redirect",
actor: apiKeyInfo?.name || "system",
target: connectionId || provider || "chat",
details: {
original_model: originalModel,
redirected_to: bgRedirect.degradedModel,
reason: bgRedirect.reason,
},
});
}
// Apply custom model aliases (Settings → Model Aliases → Pattern→Target) before routing (#315, #472)

View File

@@ -0,0 +1,41 @@
/**
* chatCore Background Task Redirection decision (T41) (Quality Gate v2 / Fase 9 — chatCore god-file
* decomposition, #3501).
*
* Decides whether a request should be downgraded to a cheaper "degraded" model: only when the
* feature is enabled, the request looks like a background/utility task, AND the model has a
* degradation mapping. Returns the target model + the detection reason, or null for no redirect.
* The handler keeps the side effects byte-identically (the BACKGROUND log, the model + body.model
* mutation, and the audit event). Reads the live backgroundTaskDetector config, mirroring the
* previous inline block.
*/
import {
getBackgroundDegradationConfig,
getBackgroundTaskReason,
getDegradedModel,
} from "../../services/backgroundTaskDetector.ts";
export function resolveBackgroundTaskRedirect(opts: {
body: unknown;
headers: Record<string, string> | null | undefined;
model: string;
}): {
/** The background-task detection reason (truthy iff the request looks like a background task),
* threaded downstream into memory/skills injection — independent of whether a redirect happens. */
backgroundReason: string | null;
/** The actual model redirect to apply, or null when there is none (disabled, not a background
* task, or the model has no — or a self — degradation mapping). */
redirect: { degradedModel: string; reason: string } | null;
} {
const bgConfig = getBackgroundDegradationConfig();
const backgroundReason = bgConfig.enabled
? getBackgroundTaskReason(opts.body, opts.headers ?? null)
: null;
if (!backgroundReason) return { backgroundReason: null, redirect: null };
const degradedModel = getDegradedModel(opts.model);
if (degradedModel === opts.model) return { backgroundReason, redirect: null };
return { backgroundReason, redirect: { degradedModel, reason: backgroundReason } };
}

View File

@@ -0,0 +1,69 @@
// tests/unit/chatcore-background-redirect.test.ts
// Characterization of resolveBackgroundTaskRedirect — the Background Task Redirection (T41)
// decision extracted from handleChatCore (chatCore god-file decomposition, #3501). Decides whether
// a request should be downgraded to a cheaper model: only when the feature is enabled, the request
// looks like a background task, AND the model has a degradation mapping. The handler keeps the
// effects (log, model/body mutation, audit). Uses the real backgroundTaskDetector config via its
// setter so the decision is exercised end-to-end.
import { test, afterEach } from "node:test";
import assert from "node:assert/strict";
import { resolveBackgroundTaskRedirect } from "../../open-sse/handlers/chatCore/backgroundRedirect.ts";
import { setBackgroundDegradationConfig } from "../../open-sse/services/backgroundTaskDetector.ts";
afterEach(() => {
setBackgroundDegradationConfig({ enabled: false, degradationMap: {} });
});
test("disabled config → no detection, no redirect (even for a clear background task)", () => {
setBackgroundDegradationConfig({ enabled: false, degradationMap: { "gpt-5": "gpt-5-mini" } });
assert.deepEqual(
resolveBackgroundTaskRedirect({ body: { max_tokens: 10 }, headers: null, model: "gpt-5" }),
{ backgroundReason: null, redirect: null }
);
});
test("enabled + low-max-tokens background + mapped model → detection + redirect", () => {
setBackgroundDegradationConfig({ enabled: true, degradationMap: { "gpt-5": "gpt-5-mini" } });
const r = resolveBackgroundTaskRedirect({
body: { max_tokens: 10 },
headers: null,
model: "gpt-5",
});
assert.deepEqual(r, {
backgroundReason: "low_max_tokens",
redirect: { degradedModel: "gpt-5-mini", reason: "low_max_tokens" },
});
});
test("enabled + x-task-type:background header → reason header_background", () => {
setBackgroundDegradationConfig({ enabled: true, degradationMap: { "gpt-5": "gpt-5-mini" } });
const r = resolveBackgroundTaskRedirect({
body: { messages: [{ role: "user", content: "hi" }] },
headers: { "x-task-type": "background" },
model: "gpt-5",
});
assert.deepEqual(r, {
backgroundReason: "header_background",
redirect: { degradedModel: "gpt-5-mini", reason: "header_background" },
});
});
test("enabled but the request is not a background task → no detection, no redirect", () => {
setBackgroundDegradationConfig({ enabled: true, degradationMap: { "gpt-5": "gpt-5-mini" } });
assert.deepEqual(
resolveBackgroundTaskRedirect({
body: { max_tokens: 4096, messages: [{ role: "user", content: "write an essay" }] },
headers: null,
model: "gpt-5",
}),
{ backgroundReason: null, redirect: null }
);
});
test("enabled + background but the model has no degradation mapping → detection but no redirect", () => {
setBackgroundDegradationConfig({ enabled: true, degradationMap: { "gpt-5": "gpt-5-mini" } });
assert.deepEqual(
resolveBackgroundTaskRedirect({ body: { max_tokens: 10 }, headers: null, model: "claude-opus-4" }),
{ backgroundReason: "low_max_tokens", redirect: null }
);
});