From b02c586cb46c78249b397142f7164be2e3fbd262 Mon Sep 17 00:00:00 2001 From: Jan Leon Date: Thu, 30 Jul 2026 03:44:41 +0200 Subject: [PATCH] Bypass proxy compaction for native Codex context --- open-sse/handlers/chatCore.ts | 11 ++- open-sse/services/combo.ts | 8 ++- open-sse/services/combo/dispatchPrelude.ts | 2 + .../services/combo/knownContextOverflow.ts | 14 +++- open-sse/services/combo/targetResolution.ts | 5 +- open-sse/services/combo/types.ts | 2 + src/sse/handlers/chat.ts | 14 +++- .../unit/combo-context-window-filter.test.ts | 67 +++++++++++++++++-- 8 files changed, 107 insertions(+), 16 deletions(-) diff --git a/open-sse/handlers/chatCore.ts b/open-sse/handlers/chatCore.ts index 77f2ba0ddc..c397d0d887 100644 --- a/open-sse/handlers/chatCore.ts +++ b/open-sse/handlers/chatCore.ts @@ -1102,10 +1102,9 @@ export async function handleChatCore({ const compressionSettings: CompressionConfig | null = compressionSettingsResult.settings; // #8034 — operator-named model/endpoint exclusions bypass the whole pipeline, exactly // like compression being globally disabled, so the body is provably byte-identical. - const compressionExcluded = isCompressionExcluded( - { provider, model: effectiveModel }, - compressionSettings?.exclusions - ); + const compressionExcluded = + nativeCodexPassthrough || + isCompressionExcluded({ provider, model: effectiveModel }, compressionSettings?.exclusions); let promptCompressionEnabled = compressionSettingsResult.enabled && !compressionExcluded; contextEditingEnabled = compressionSettingsResult.contextEditingEnabled; if (compressionExcluded) { @@ -1756,7 +1755,7 @@ export async function handleChatCore({ // engines (Caveman/RTK). Codex Desktop / Responses clients need this path even // when those engines are off, otherwise multi-turn image sessions hard-reject // at the budget check below (#8560). - if (estimatedTokens > threshold) { + if (!nativeCodexPassthrough && estimatedTokens > threshold) { log?.info?.( "CONTEXT", `Proactive compression triggered: ${estimatedTokens} tokens > ${threshold} threshold (${contextLimit} limit)` @@ -1847,7 +1846,7 @@ export async function handleChatCore({ // Last-resort compaction against the concrete input budget (not the 70% threshold). // Covers cases where the proactive pass was skipped or still left the request oversized (#8560). - if (finalEstimatedInputTokens >= finalContextLimit && body) { + if (!nativeCodexPassthrough && finalEstimatedInputTokens >= finalContextLimit && body) { const lastResortTarget = Math.max(1, finalContextLimit - toolsReserve - 1); const lastResortAdapter = adaptBodyForCompression(body as Record); const lastResortResult = compressContext(lastResortAdapter.body, { diff --git a/open-sse/services/combo.ts b/open-sse/services/combo.ts index ce1e1dc7e2..0cad294d4a 100644 --- a/open-sse/services/combo.ts +++ b/open-sse/services/combo.ts @@ -569,6 +569,7 @@ export async function handleComboChat({ signal, apiKeyAllowedConnections = null, nesting = null, + clientManagedResponsesContext = false, }: HandleComboChatOptions): Promise { const comboCtx = createComboContext({ body, combo, settings, relayOptions, log }); const { @@ -683,6 +684,7 @@ export async function handleComboChat({ settings, allCombos, signal, + clientManagedResponsesContext, }); } @@ -707,6 +709,7 @@ export async function handleComboChat({ isModelAvailable, handleSingleModelWithTimeout, buildAutoCandidates, + clientManagedResponsesContext, }); if ("earlyResponse" in targetResolution) return targetResolution.earlyResponse; const { stickyWeightedLimit, getWeightedStepKeyForTarget, preScreenMap } = targetResolution; @@ -2177,6 +2180,7 @@ async function handleRoundRobinCombo({ settings, allCombos, signal, + clientManagedResponsesContext, }: HandleRoundRobinOptions): Promise { const config = settings ? resolveComboConfig(combo, settings) @@ -2219,7 +2223,9 @@ async function handleRoundRobinCombo({ ); const tagFilteredTargets = await applyRequestTagRouting(orderedTargets, body, log); const evalRankedTargets = orderTargetsByEvalScores(tagFilteredTargets, config.evalRouting, log); - const knownContextOverflow = getKnownContextOverflow(evalRankedTargets, body); + const knownContextOverflow = getKnownContextOverflow(evalRankedTargets, body, { + clientManagedResponsesContext, + }); if (knownContextOverflow) { return errorResponseWithComboDiagnostics( 400, diff --git a/open-sse/services/combo/dispatchPrelude.ts b/open-sse/services/combo/dispatchPrelude.ts index 69d2576c00..a39383d637 100644 --- a/open-sse/services/combo/dispatchPrelude.ts +++ b/open-sse/services/combo/dispatchPrelude.ts @@ -68,6 +68,7 @@ type PreludeBaseOptionArgs = { relayOptions?: HandleComboChatOptions["relayOptions"]; signal?: AbortSignal | null; apiKeyAllowedConnections?: string[] | null; + clientManagedResponsesContext?: boolean; }; /** Rebuild handleComboChat's option bag verbatim for a recursive dispatch. */ @@ -83,6 +84,7 @@ function buildBaseOptions(a: PreludeBaseOptionArgs): HandleComboChatOptions { relayOptions: a.relayOptions, signal: a.signal, apiKeyAllowedConnections: a.apiKeyAllowedConnections, + clientManagedResponsesContext: a.clientManagedResponsesContext, }; } diff --git a/open-sse/services/combo/knownContextOverflow.ts b/open-sse/services/combo/knownContextOverflow.ts index 4416d1d451..78a73fd551 100644 --- a/open-sse/services/combo/knownContextOverflow.ts +++ b/open-sse/services/combo/knownContextOverflow.ts @@ -61,7 +61,6 @@ export function getKnownContextLimit( return limits.length > 0 ? Math.min(...limits) : null; } - /** * Return a hard context-overflow decision only when every target has a known * context limit and every one of those limits is too small for the request. @@ -69,9 +68,20 @@ export function getKnownContextLimit( */ export function getKnownContextOverflow( targets: ResolvedComboTarget[], - body: Record + body: Record, + options: { clientManagedResponsesContext?: boolean } = {} ): KnownContextOverflow | null { if (targets.length === 0) return null; + // Native Codex Responses clients compact their own item history. Let the concrete + // Codex target enforce its effective context limit (including operator overrides) + // instead of rejecting early against a smaller catalog hint. Keep this scoped to + // all-Codex pools so other Responses clients/providers retain the hard preflight. + if ( + options.clientManagedResponsesContext === true && + targets.every((target) => target.provider === "codex") + ) { + return null; + } const requirements = deriveRequestCompatibilityRequirements(body); if (requirements.requiredContextTokens <= 0) return null; diff --git a/open-sse/services/combo/targetResolution.ts b/open-sse/services/combo/targetResolution.ts index 82f23b8c3d..cd20b6de02 100644 --- a/open-sse/services/combo/targetResolution.ts +++ b/open-sse/services/combo/targetResolution.ts @@ -111,6 +111,7 @@ export interface ResolveComboTargetPipelineDeps { * this leaf), so importing it directly would create an import cycle. */ buildAutoCandidates: ResolveAutoStrategyDeps["buildAutoCandidates"]; + clientManagedResponsesContext?: boolean; } export interface ResolvedComboTargetPipeline { @@ -692,7 +693,9 @@ export async function resolveComboTargetPipeline( orderedTargets = await applyRequestTagRouting(orderedTargets, body, log); - const overflow = getKnownContextOverflow(orderedTargets, body); + const overflow = getKnownContextOverflow(orderedTargets, body, { + clientManagedResponsesContext: deps.clientManagedResponsesContext, + }); if (overflow) { return { earlyResponse: buildContextOverflowResponse(overflow, orderedTargets, log) }; } diff --git a/open-sse/services/combo/types.ts b/open-sse/services/combo/types.ts index 9371d11529..3247ac1daa 100644 --- a/open-sse/services/combo/types.ts +++ b/open-sse/services/combo/types.ts @@ -107,6 +107,8 @@ export type HandleComboChatOptions = { signal?: AbortSignal | null; apiKeyAllowedConnections?: string[] | null; nesting?: ComboNestingContext | null; + /** Native Responses clients (for example Codex CLI/Desktop) manage compaction themselves. */ + clientManagedResponsesContext?: boolean; }; export type HandleRoundRobinOptions = Omit< diff --git a/src/sse/handlers/chat.ts b/src/sse/handlers/chat.ts index da10e9a550..aaf883a672 100644 --- a/src/sse/handlers/chat.ts +++ b/src/sse/handlers/chat.ts @@ -33,7 +33,11 @@ import { HTTP_STATUS, ANTIGRAVITY_PRE_RESPONSE_TIMEOUT_CODE, } from "@omniroute/open-sse/config/constants.ts"; -import { getTargetFormat, detectFormatFromUrl } from "@omniroute/open-sse/services/provider.ts"; +import { + getTargetFormat, + detectFormatFromEndpoint, + detectFormatFromUrl, +} from "@omniroute/open-sse/services/provider.ts"; import { getModelsByProviderId, getModelTargetFormat, @@ -778,6 +782,9 @@ export async function handleChat( const response = await (handleComboChat as any)({ body, combo, + clientManagedResponsesContext: + sourceFormat === "openai-responses" && + new URL(request.url).pathname.split("/").includes("responses"), handleSingleModel: ( b: any, m: string, @@ -1045,6 +1052,11 @@ async function handleSingleModelChat( return handleComboChat({ body, combo: redirectCombo, + clientManagedResponsesContext: + detectFormatFromEndpoint(body, clientRawRequest?.endpoint || "") === "openai-responses" && + String(clientRawRequest?.endpoint || "") + .split("/") + .includes("responses"), handleSingleModel: ( b: any, m: string, diff --git a/tests/unit/combo-context-window-filter.test.ts b/tests/unit/combo-context-window-filter.test.ts index 12214ce1ba..2f0bfa64cf 100644 --- a/tests/unit/combo-context-window-filter.test.ts +++ b/tests/unit/combo-context-window-filter.test.ts @@ -57,7 +57,11 @@ function capabilityEntry(limitContext: number | null) { }; } -function capabilityEntryWithLimits(limitInput: number | null, limitContext: number | null, limitOutput = 4096) { +function capabilityEntryWithLimits( + limitInput: number | null, + limitContext: number | null, + limitOutput = 4096 +) { return { ...capabilityEntry(limitContext), limit_input: limitInput, @@ -270,6 +274,59 @@ test("combo rejects a known oversized request before upstream dispatch", async ( assert.equal(body.diagnostics.attempted, 0); }); +test("native Responses context bypasses catalog overflow only for all-Codex pools (#8932)", () => { + saveModelsDevCapabilities({ + codex: { + large: capabilityEntry(272_000), + }, + "unit-known-context": { + large: capabilityEntry(272_000), + }, + }); + const body = bigContextBody(275_000); + + assert.equal( + getKnownContextOverflow([target("codex/large")], body, { + clientManagedResponsesContext: true, + }), + null + ); + assert.ok( + getKnownContextOverflow([target("unit-known-context/large")], body, { + clientManagedResponsesContext: true, + }), + "non-Codex pools must retain the catalog overflow guard" + ); +}); + +test("native Responses context reaches an all-Codex target beyond its catalog hint (#8932)", async () => { + saveModelsDevCapabilities({ + codex: { + large: capabilityEntry(272_000), + }, + }); + let dispatches = 0; + + const response = await handleComboChat({ + body: bigContextBody(275_000), + combo: { + name: "native-codex-overflow", + strategy: "priority", + models: ["codex/large"], + }, + clientManagedResponsesContext: true, + isModelAvailable: async () => true, + handleSingleModel: async () => { + dispatches += 1; + return new Response("ok", { status: 200 }); + }, + log: noopLog, + }); + + assert.notEqual(response.status, 400); + assert.equal(dispatches, 1); +}); + test("input-only maxInputTokens is not double-counted against the output reserve (#7039)", () => { // Faithful reproduction of #7039 (Codex gpt-5.5-xhigh): // maxInputTokens = 272_000, contextWindow = 400_000, maxOutputTokens = 128_000 @@ -387,10 +444,10 @@ test("model_context_override lets a small-catalog target survive a large-context largeContextBody(), noopLog ); - assert.deepEqual( - out.map((entry) => entry.modelStr).sort(), - ["unit-override/big", "unit-override/capped"] - ); + assert.deepEqual(out.map((entry) => entry.modelStr).sort(), [ + "unit-override/big", + "unit-override/capped", + ]); } finally { removeModelContextOverride("unit-override", "capped"); }