From 103033c77cd0cdcbb67d89787fe8e3cb962dc8d7 Mon Sep 17 00:00:00 2001 From: 1bcMax <195689928+1bcMax@users.noreply.github.com> Date: Mon, 20 Jul 2026 21:58:24 -0500 Subject: [PATCH] fix(tokens): raise the two Anthropic ceilings the gateway now honors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit claude-haiku-4.5 16384 -> 64000 claude-sonnet-4.6 64000 -> 128000 Both were held low on purpose. The gateway clamped haiku to 8192 and sonnet-4.6 to 64000, and asking past a clamp is not free: quotedOutputTokens scales with the ceiling, so a bigger ask inflated the upfront price quote while the reply stayed capped. blockrun#266 corrected the catalog and #268 added the context-headroom guard that raising the ceilings requires; both are deployed and verified live today. Deliberately NOT raised, though the catalog now reads higher: openai/gpt-5.5 32768 (catalog says 128000) openai/gpt-5.4 32768 (catalog says 128000) openai/gpt-5-mini 16384 (catalog says 65536) The catalog is the only evidence for those three, and it was 8x wrong about haiku-4.5. Raising on that basis alone repeats the mistake this table exists to guard against. A test pins them so the next person has to argue with a failing assertion rather than quietly sync them. One existing test had to change. It asserted haiku stays 16384 to prove the static table beats the catalog — valid when the catalog said 8192, but once both sides read 64000 it proved nothing while still passing. Repointed at gpt-5.5, where the disagreement is real and deliberate. Scope note: the raise only takes effect on the escalation path. A normal turn sends min(CAPPED_MAX_TOKENS 16384, ceiling), so 64000 is reachable only after a max_tokens stop escalates. Not exercised end-to-end — that needs a real long generation. The evidence is Anthropic's published limit plus the deployed gateway clamping at 64000, confirmed against the live catalog. Local suite 636 pass. --- src/agent/optimize.ts | 9 +++++++-- test/local.mjs | 42 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/src/agent/optimize.ts b/src/agent/optimize.ts index d006b1a..bee1150 100644 --- a/src/agent/optimize.ts +++ b/src/agent/optimize.ts @@ -42,9 +42,14 @@ const MODEL_MAX_OUTPUT: Record = { 'anthropic/claude-opus-4.6': 32_000, 'anthropic/claude-opus-4.5': 32_000, 'anthropic/claude-sonnet-5': 128_000, - 'anthropic/claude-sonnet-4.6': 64_000, + 'anthropic/claude-sonnet-4.6': 128_000, // Anthropic-documented; gateway corrected 2026-07-21 'anthropic/claude-sonnet-4.5': 64_000, - 'anthropic/claude-haiku-4.5': 16_384, + // Anthropic documents 64000 for Haiku 4.5. Held at 16384 until 2026-07-21 + // because the gateway clamped to 8192 — asking for more only inflated the + // price quote (quotedOutputTokens scales with the ceiling) while the reply + // stayed capped. blockrun#266 corrected the gateway and it is live, so the + // real ceiling is now reachable. + 'anthropic/claude-haiku-4.5': 64_000, 'openai/gpt-5.6-sol': 128_000, 'openai/gpt-5.6-terra': 128_000, 'openai/gpt-5.6-luna': 128_000, diff --git a/test/local.mjs b/test/local.mjs index 03e5b34..fab7da1 100644 --- a/test/local.mjs +++ b/test/local.mjs @@ -6698,6 +6698,30 @@ test('qwen3.7-max: paid aliases resolve, and bare `qwen` stays free', async () = 'bare `qwen` must keep resolving to a $0 model'); }); +test('anthropic max output matches the documented ceilings', async () => { + // Both were held below Anthropic's published limits while the gateway + // clamped lower (it reported 8192 for haiku, 64000 for sonnet-4.6). Asking + // past a clamp only inflates the price quote — quotedOutputTokens scales + // with the ceiling — while the reply stays capped, so raising these before + // the gateway was corrected would have cost money and bought nothing. + // blockrun#266 + #268 are deployed; verified live 2026-07-21. + const { getMaxOutputTokens } = await import('../dist/agent/optimize.js'); + assert.equal(getMaxOutputTokens('anthropic/claude-haiku-4.5'), 64_000); + assert.equal(getMaxOutputTokens('anthropic/claude-sonnet-4.6'), 128_000); +}); + +test('openai entries stay put until something other than the catalog backs them', async () => { + // gpt-5.5 / gpt-5.4 (32768) and gpt-5-mini (16384) all read lower than the + // gateway catalog claims. Deliberately NOT raised: the catalog is the only + // evidence, and it was 8x wrong about haiku-4.5. Raising on that basis alone + // repeats the mistake this whole table exists to guard against. Change these + // when a source outside the catalog confirms them — then update this test. + const { getMaxOutputTokens } = await import('../dist/agent/optimize.js'); + assert.equal(getMaxOutputTokens('openai/gpt-5.5'), 32_768); + assert.equal(getMaxOutputTokens('openai/gpt-5.4'), 32_768); + assert.equal(getMaxOutputTokens('openai/gpt-5-mini'), 16_384); +}); + // ─── gateway catalog as a GAP-FILLER, never an override ─────────────────── // // The static tables are authoritative. Several entries are deliberate @@ -6721,11 +6745,23 @@ test('gateway catalog never overrides a static context-window entry', async () = test('gateway catalog never overrides a static max-output entry', async () => { const { getMaxOutputTokens } = await import('../dist/agent/optimize.js'); - const { clearGatewayModelsCache } = await import('../dist/gateway-models.js'); + const { clearGatewayModelsCache, __primeGatewayModelsCache } = await import('../dist/gateway-models.js'); clearGatewayModelsCache(); - assert.equal(getMaxOutputTokens('anthropic/claude-haiku-4.5'), 16_384, - 'static entry wins over the gateway\'s (wrong) 8192'); + // Prime the cache with values that DISAGREE with the static table, so this + // actually exercises precedence. It used to assert on haiku-4.5 (static + // 16384 vs a catalog reporting 8192); once the gateway was corrected and the + // static entry raised to 64000 the two agreed, and the assertion silently + // stopped testing anything. gpt-5.5 is the live disagreement now: the + // catalog claims 128000, we hold 32768 because nothing outside the catalog + // confirms it, and the catalog was 8x wrong about haiku. + __primeGatewayModelsCache([ + { id: 'openai/gpt-5.5', name: 'x', billing_mode: 'paid', categories: ['chat'], + context_window: 400_000, max_output: 128_000, pricing: { input: 1, output: 2 } }, + ]); + assert.equal(getMaxOutputTokens('openai/gpt-5.5'), 32_768, + 'static entry must win even with a warm catalog claiming 128000'); assert.equal(getMaxOutputTokens('moonshot/kimi-k3'), 65_536); + clearGatewayModelsCache(); }); test('gateway catalog fills the gap for an uncatalogued model', async () => {