From eceaf0b6e0f3597bbe10643a50a67734c5fe5e22 Mon Sep 17 00:00:00 2001 From: Nguyen Thanh Dat Date: Wed, 19 Aug 2026 09:11:53 +0700 Subject: [PATCH] fix(admission): resolve the per-model window the way the catalog does resolveInputCeiling read modelContextWindows and modelMaxInputTokens with a bare lookup, while the catalog resolves the same two maps through modelRecordValue, which also accepts a family entry for a tagged id. With contextWindow 8_000 and modelContextWindows {"gpt-oss": 131_072}: catalog advertises 131_072 provider-fetch.ts:612 admission ceiling 8_000 before this change So the gate refused turns the model can plainly hold, using a window that belongs to a different model. That is the opposite of what this module documents about itself -- "every uncertainty resolves toward admitting". modelMaxInputTokens had the mirror of it: a family cap never applied to the tagged sibling it was written for. Three tests, all red without the src change and green with it. The first asserts the catalog's value first so the two can never drift apart again. No behavior changes for ids that already resolved exactly. --- src/server/responses/input-admission.ts | 9 ++++-- tests/input-admission.test.ts | 37 +++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/server/responses/input-admission.ts b/src/server/responses/input-admission.ts index 219c3b6f89..a2a06e5b73 100644 --- a/src/server/responses/input-admission.ts +++ b/src/server/responses/input-admission.ts @@ -13,6 +13,7 @@ import { nativeOpenAiContextWindow, nativeOpenAiMaxInputTokens, type NativeContextLimitsInput } from "../../codex/catalog/metadata"; import { estimateTokens } from "../../lib/token-estimate"; import { isCanonicalOpenAiForwardProvider, OPENAI_CODEX_PROVIDER_ID } from "../../providers/openai-tiers"; +import { modelRecordValue } from "../../reasoning-effort"; import type { OcxContentPart, OcxParsedRequest, OcxProviderConfig } from "../../types"; /** @@ -133,7 +134,11 @@ export function resolveInputCeiling( // config here so this stays pure: no filesystem, no catalog, no registry scan. nativeContextCap?: NativeContextLimitsInput, ): number | null { - const configured = positive(provider.modelContextWindows?.[modelId]) ?? positive(provider.contextWindow); + // `modelRecordValue`, not a bare lookup: the catalog resolves these same two maps that + // way, so a `gpt-oss` entry covers `gpt-oss:120b`. Reading raw here made the gate fall + // back to the provider-wide window and refuse turns the model can plainly hold. + const configured = positive(modelRecordValue(provider.modelContextWindows, modelId)) + ?? positive(provider.contextWindow); // The canonical `openai` registry entry declares no context fields, so without this the // gate would be inert on the default Codex route. All three clauses are load-bearing: a @@ -156,7 +161,7 @@ export function resolveInputCeiling( const window = canonicalNativeBare ? native : configured; // modelMaxInputTokens is an input-only cap, so it can only tighten the window. - const configuredMaxInput = positive(provider.modelMaxInputTokens?.[modelId]); + const configuredMaxInput = positive(modelRecordValue(provider.modelMaxInputTokens, modelId)); const limits = [window, configuredMaxInput, nativeMaxInput].filter((v): v is number => v !== null); return limits.length === 0 ? null : Math.min(...limits); } diff --git a/tests/input-admission.test.ts b/tests/input-admission.test.ts index 44422d502c..d97e0fe82e 100644 --- a/tests/input-admission.test.ts +++ b/tests/input-admission.test.ts @@ -5,6 +5,7 @@ import { estimateInputTokens, resolveInputCeiling, } from "../src/server/responses/input-admission"; +import { modelRecordValue } from "../src/reasoning-effort"; import type { OcxMessage, OcxParsedRequest, OcxProviderConfig, OcxTool } from "../src/types"; const CANONICAL_NATIVE: OcxProviderConfig = { @@ -91,6 +92,42 @@ describe("resolveInputCeiling", () => { const pinned: OcxProviderConfig = { ...CANONICAL_NATIVE, modelContextWindows: { "gpt-5.6-sol": 50_000 } }; expect(resolveInputCeiling(pinned, "openai", "gpt-5.6-sol")).toBe(50_000); }); + + test("a family entry covers its tagged siblings, like the catalog", () => { + // The catalog resolves modelContextWindows through modelRecordValue, so it advertises + // 131_072 for gpt-oss:120b off this config. A bare lookup here resolved nothing and + // fell back to contextWindow, leaving the gate refusing turns the model can hold. + const provider: OcxProviderConfig = { + adapter: "openai-chat", + baseUrl: "https://example.test/v1", + contextWindow: 8_000, + modelContextWindows: { "gpt-oss": 131_072 }, + }; + expect(modelRecordValue(provider.modelContextWindows, "gpt-oss:120b")).toBe(131_072); + expect(resolveInputCeiling(provider, "custom", "gpt-oss:120b")).toBe(131_072); + // An id with no tag and no entry still falls back to the provider-wide window. + expect(resolveInputCeiling(provider, "custom", "other")).toBe(8_000); + }); + + test("an exact entry still beats the family entry", () => { + const provider: OcxProviderConfig = { + adapter: "openai-chat", + baseUrl: "https://example.test/v1", + modelContextWindows: { "gpt-oss": 131_072, "gpt-oss:20b": 32_000 }, + }; + expect(resolveInputCeiling(provider, "custom", "gpt-oss:20b")).toBe(32_000); + expect(resolveInputCeiling(provider, "custom", "gpt-oss:120b")).toBe(131_072); + }); + + test("a family modelMaxInputTokens tightens its tagged siblings", () => { + const provider: OcxProviderConfig = { + adapter: "openai-chat", + baseUrl: "https://example.test/v1", + modelContextWindows: { "gpt-oss": 131_072 }, + modelMaxInputTokens: { "gpt-oss": 40_000 }, + }; + expect(resolveInputCeiling(provider, "custom", "gpt-oss:120b")).toBe(40_000); + }); }); describe("estimateInputTokens", () => {