From 3f6a6612b7fc9f8938866940a731f45cca16a14e Mon Sep 17 00:00:00 2001 From: chengluyu <2239547+chengluyu@users.noreply.github.com> Date: Tue, 7 Jul 2026 14:50:50 +0800 Subject: [PATCH 1/2] fix(kosong): fall back to nearest lower catalogued minor for Claude max tokens An uncatalogued Claude minor version (e.g. claude-opus-4-8) previously dropped straight to the family/major baseline ceiling, so Opus 4.8 resolved to 32k max output tokens instead of the 128k it supports. Walk the minor version down to the nearest catalogued entry before using the family baseline, so a newer minor inherits its predecessor's documented ceiling. --- .changeset/opus-4-8-max-tokens.md | 5 +++++ packages/kosong/src/providers/anthropic.ts | 18 +++++++++++++----- packages/kosong/test/anthropic.test.ts | 12 ++++++++---- 3 files changed, 26 insertions(+), 9 deletions(-) create mode 100644 .changeset/opus-4-8-max-tokens.md diff --git a/.changeset/opus-4-8-max-tokens.md b/.changeset/opus-4-8-max-tokens.md new file mode 100644 index 0000000000..3ccd002784 --- /dev/null +++ b/.changeset/opus-4-8-max-tokens.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Fix newer Claude minor versions (e.g. Opus 4.8) defaulting to the family-baseline max output tokens; an uncatalogued minor now reuses the nearest earlier known version's limit. diff --git a/packages/kosong/src/providers/anthropic.ts b/packages/kosong/src/providers/anthropic.ts index 2f57354f74..dccc5eb148 100644 --- a/packages/kosong/src/providers/anthropic.ts +++ b/packages/kosong/src/providers/anthropic.ts @@ -151,9 +151,9 @@ const ANTHROPIC_TOOL_CALL_ID_POLICY: ToolCallIdPolicy = { * can silently truncate mid-`tool_use`. * * Keys are `-[-]`. Lookups try the most specific - * key first, then fall back to the family/major-only entry, so an - * unrecognized minor version (e.g. a future `opus-4-10`) gets the - * family's baseline rather than the generic fallback. + * key first, then the nearest lower catalogued minor of the same + * family/major (a not-yet-catalogued `opus-4-8` reuses `opus-4-7`'s + * ceiling), and finally the family/major-only baseline entry. */ const CEILING_BY_FAMILY_VERSION: Readonly> = { // Claude Fable 5 documents a 128k output ceiling. @@ -269,8 +269,16 @@ function parseClaudeFamilyVersion(model: string, requireClaudeMarker: boolean): function lookupClaudeCeiling(version: ClaudeVersion): number | undefined { const { family, major, minor } = version; if (minor !== null) { - const exact = CEILING_BY_FAMILY_VERSION[`${family}-${major}-${minor}`]; - if (exact !== undefined) return exact; + // Exact minor first, then walk down to the nearest catalogued minor: + // a newer minor release inherits at least its predecessor's ceiling + // (Anthropic has never lowered the cap within a major), so a + // not-yet-catalogued 4.8 reuses 4.7's value instead of dropping to + // the family baseline. The regex caps minors at two digits, so this + // walk is bounded. + for (let candidate = minor; candidate >= 0; candidate--) { + const ceiling = CEILING_BY_FAMILY_VERSION[`${family}-${major}-${candidate}`]; + if (ceiling !== undefined) return ceiling; + } } return CEILING_BY_FAMILY_VERSION[`${family}-${major}`]; } diff --git a/packages/kosong/test/anthropic.test.ts b/packages/kosong/test/anthropic.test.ts index 50f2451917..4e77f0ddb3 100644 --- a/packages/kosong/test/anthropic.test.ts +++ b/packages/kosong/test/anthropic.test.ts @@ -2672,12 +2672,16 @@ describe('resolveDefaultMaxTokens', () => { expect(resolveDefaultMaxTokens('anthropic.claude-3-5-sonnet-20240620-v1:0')).toBe(8192); }); - it('falls back to family-only ceiling for unknown minor versions', () => { - // Future opus-4-X release: minor not in table, falls back to opus-4 = 32000. - // Better to under-quote and fail loudly than over-quote a model we can't verify. - expect(resolveDefaultMaxTokens('claude-opus-4-10')).toBe(32000); + it('falls back to the nearest lower catalogued minor for unknown minors', () => { + // opus-4-8 is not in the table; it reuses opus-4-7's 128k ceiling + // (a newer minor inherits at least its predecessor's cap). + expect(resolveDefaultMaxTokens('claude-opus-4-8')).toBe(128000); + expect(resolveDefaultMaxTokens('claude-opus-4.8')).toBe(128000); + expect(resolveDefaultMaxTokens('claude-opus-4-10')).toBe(128000); expect(resolveDefaultMaxTokens('claude-sonnet-4-9')).toBe(64000); expect(resolveDefaultMaxTokens('claude-haiku-4-9')).toBe(64000); + // A gap between catalogued minors also resolves to the nearest lower one. + expect(resolveDefaultMaxTokens('claude-opus-4-3')).toBe(32000); }); it('matches case-insensitively', () => { From 6be70a089f3e7f6638bf169eb5da1c67d1a7431e Mon Sep 17 00:00:00 2001 From: chengluyu <2239547+chengluyu@users.noreply.github.com> Date: Tue, 7 Jul 2026 14:57:18 +0800 Subject: [PATCH 2/2] fix(kosong): catalogue Opus 4.8's documented 128k output ceiling Known models should resolve from explicit table entries; the nearest-lower-minor fallback now only covers minors that are not yet catalogued. Fable 5 already has an explicit entry. --- packages/kosong/src/providers/anthropic.ts | 3 ++- packages/kosong/test/anthropic.test.ts | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/kosong/src/providers/anthropic.ts b/packages/kosong/src/providers/anthropic.ts index dccc5eb148..3282a22d8d 100644 --- a/packages/kosong/src/providers/anthropic.ts +++ b/packages/kosong/src/providers/anthropic.ts @@ -158,8 +158,9 @@ const ANTHROPIC_TOOL_CALL_ID_POLICY: ToolCallIdPolicy = { const CEILING_BY_FAMILY_VERSION: Readonly> = { // Claude Fable 5 documents a 128k output ceiling. 'fable-5': 128000, - // Claude Opus per minor version. 4.6 and 4.7 raised the cap to 128k; + // Claude Opus per minor version. 4.6 through 4.8 document a 128k cap; // 4.5 ships at 64k; 4.1 and the dated 4.0 release stay at 32k. + 'opus-4-8': 128000, 'opus-4-7': 128000, 'opus-4-6': 128000, 'opus-4-5': 64000, diff --git a/packages/kosong/test/anthropic.test.ts b/packages/kosong/test/anthropic.test.ts index 4e77f0ddb3..4a8d81eded 100644 --- a/packages/kosong/test/anthropic.test.ts +++ b/packages/kosong/test/anthropic.test.ts @@ -2619,6 +2619,7 @@ describe('AnthropicChatProvider', () => { describe('resolveDefaultMaxTokens', () => { it('returns per-version Messages-API caps for known Claude 4 models', () => { expect(resolveDefaultMaxTokens('claude-fable-5')).toBe(128000); + expect(resolveDefaultMaxTokens('claude-opus-4-8')).toBe(128000); expect(resolveDefaultMaxTokens('claude-opus-4-7')).toBe(128000); expect(resolveDefaultMaxTokens('claude-opus-4-6')).toBe(128000); expect(resolveDefaultMaxTokens('claude-opus-4-5-20251101')).toBe(64000); @@ -2650,6 +2651,7 @@ describe('resolveDefaultMaxTokens', () => { }); it('matches dotted version separators', () => { + expect(resolveDefaultMaxTokens('claude-opus-4.8')).toBe(128000); expect(resolveDefaultMaxTokens('claude-opus-4.7')).toBe(128000); expect(resolveDefaultMaxTokens('claude-opus-4.6')).toBe(128000); expect(resolveDefaultMaxTokens('claude-sonnet-4.6')).toBe(64000); @@ -2673,10 +2675,9 @@ describe('resolveDefaultMaxTokens', () => { }); it('falls back to the nearest lower catalogued minor for unknown minors', () => { - // opus-4-8 is not in the table; it reuses opus-4-7's 128k ceiling - // (a newer minor inherits at least its predecessor's cap). - expect(resolveDefaultMaxTokens('claude-opus-4-8')).toBe(128000); - expect(resolveDefaultMaxTokens('claude-opus-4.8')).toBe(128000); + // opus-4-9/4-10 are not in the table; they reuse opus-4-8's 128k + // ceiling (a newer minor inherits at least its predecessor's cap). + expect(resolveDefaultMaxTokens('claude-opus-4-9')).toBe(128000); expect(resolveDefaultMaxTokens('claude-opus-4-10')).toBe(128000); expect(resolveDefaultMaxTokens('claude-sonnet-4-9')).toBe(64000); expect(resolveDefaultMaxTokens('claude-haiku-4-9')).toBe(64000);