Bypass proxy compaction for native Codex context

This commit is contained in:
Jan Leon
2026-07-30 03:44:41 +02:00
parent ed2db6cb19
commit b02c586cb4
8 changed files with 107 additions and 16 deletions

View File

@@ -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<string, unknown>);
const lastResortResult = compressContext(lastResortAdapter.body, {

View File

@@ -569,6 +569,7 @@ export async function handleComboChat({
signal,
apiKeyAllowedConnections = null,
nesting = null,
clientManagedResponsesContext = false,
}: HandleComboChatOptions): Promise<Response> {
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<Response> {
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,

View File

@@ -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,
};
}

View File

@@ -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<string, unknown>
body: Record<string, unknown>,
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;

View File

@@ -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) };
}

View File

@@ -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<

View File

@@ -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,

View File

@@ -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");
}