diff --git a/packages/ai/CHANGELOG.md b/packages/ai/CHANGELOG.md index 2984c0d0..e49aebd7 100644 --- a/packages/ai/CHANGELOG.md +++ b/packages/ai/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixed context overflow detection to recognize z.ai `model_context_window_exceeded` errors surfaced through OpenAI-compatible stop reason handling ([#1937](https://github.com/badlogic/pi-mono/issues/1937)) + ## [0.57.0] - 2026-03-07 ### Added diff --git a/packages/ai/src/utils/overflow.ts b/packages/ai/src/utils/overflow.ts index 6ba1a162..c6f4696e 100644 --- a/packages/ai/src/utils/overflow.ts +++ b/packages/ai/src/utils/overflow.ts @@ -38,6 +38,7 @@ const OVERFLOW_PATTERNS = [ /context window exceeds limit/i, // MiniMax /exceeded model token limit/i, // Kimi For Coding /too large for model with \d+ maximum context length/i, // Mistral + /model_context_window_exceeded/i, // z.ai non-standard finish_reason surfaced as error text /context[_ ]length[_ ]exceeded/i, // Generic fallback /too many tokens/i, // Generic fallback /token limit exceeded/i, // Generic fallback diff --git a/packages/ai/test/context-overflow.test.ts b/packages/ai/test/context-overflow.test.ts index cca56f11..413e9860 100644 --- a/packages/ai/test/context-overflow.test.ts +++ b/packages/ai/test/context-overflow.test.ts @@ -384,29 +384,32 @@ describe("Context overflow error handling", () => { // ============================================================================= // z.ai - // Special case: Sometimes accepts overflow silently, sometimes rate limits - // Detection via usage.input > contextWindow when successful + // Special case: may return explicit overflow error text, may accept overflow silently, + // or may rate limit instead // ============================================================================= describe.skipIf(!process.env.ZAI_API_KEY)("z.ai", () => { - it("glm-4.5-flash - should detect overflow via isContextOverflow (silent overflow or rate limit)", async () => { + it("glm-4.5-flash - should detect overflow via isContextOverflow when z.ai reports it", async () => { const model = getModel("zai", "glm-4.5-flash"); const result = await testContextOverflow(model, process.env.ZAI_API_KEY!); logResult(result); // z.ai behavior is inconsistent: + // - Sometimes returns explicit overflow error text via non-standard finish_reason handling // - Sometimes accepts overflow and returns successfully with usage.input > contextWindow // - Sometimes returns rate limit error - // Either way, isContextOverflow should detect it (via usage check or we skip if rate limited) - if (result.stopReason === "stop") { + if (result.stopReason === "error") { + if (result.errorMessage?.match(/model_context_window_exceeded/i)) { + expect(isContextOverflow(result.response, model.contextWindow)).toBe(true); + } else { + console.log(" z.ai returned non-overflow error (possibly rate limited), skipping overflow detection"); + } + } else if (result.stopReason === "stop") { if (result.hasUsageData && result.usage.input > model.contextWindow) { expect(isContextOverflow(result.response, model.contextWindow)).toBe(true); } else { console.log(" z.ai returned stop without overflow usage data, skipping overflow detection"); } - } else { - // Rate limited or other error - just log and pass - console.log(" z.ai returned error (possibly rate limited), skipping overflow detection"); } }, 120000); });