From f4851d1304d468a47dfe342fd21248e04b6e7c1c Mon Sep 17 00:00:00 2001 From: Marco Beretta <81851188+berry-13@users.noreply.github.com> Date: Thu, 6 Aug 2026 19:36:41 +0200 Subject: [PATCH] fix(api): disable summarization below a viable context budget A tiny user-set maxContextTokens re-triggers summarization on every graph step (the summary allocation rounds to nothing and history still overflows), burning dozens of LLM calls until the recursion limit aborts the run with an opaque LangGraph error. Fall back to plain pruning under a 1024-token floor so the run either fits or fails fast with the actionable empty_messages breakdown. --- .../__tests__/run-summarization.test.ts | 29 +++++++++++++++++++ packages/api/src/agents/run.ts | 28 +++++++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/packages/api/src/agents/__tests__/run-summarization.test.ts b/packages/api/src/agents/__tests__/run-summarization.test.ts index bfb9ab13cd..6d3e923f23 100644 --- a/packages/api/src/agents/__tests__/run-summarization.test.ts +++ b/packages/api/src/agents/__tests__/run-summarization.test.ts @@ -648,6 +648,35 @@ describe('summarizationEnabled resolution', () => { expect(config.provider).toBe('openAI'); expect(config.model).toBe('gpt-4o'); }); + + it('false when the effective context budget is below the viable minimum', async () => { + /** + * A tiny user-set maxContextTokens re-triggers summarization on every + * graph step until the recursion limit aborts the run; the guard falls + * back to plain pruning instead. + */ + const agents = await callAndCapture({ + agents: [makeAgent({ maxContextTokens: 10 })], + summarizationConfig: { + enabled: true, + provider: 'anthropic', + model: 'claude-3-haiku', + }, + }); + expect(agents[0].summarizationEnabled).toBe(false); + }); + + it('true at exactly the 1024-token viable minimum', async () => { + const agents = await callAndCapture({ + agents: [makeAgent({ maxContextTokens: 1024 })], + summarizationConfig: { + enabled: true, + provider: 'anthropic', + model: 'claude-3-haiku', + }, + }); + expect(agents[0].summarizationEnabled).toBe(true); + }); }); // --------------------------------------------------------------------------- diff --git a/packages/api/src/agents/run.ts b/packages/api/src/agents/run.ts index 77cff7a4e7..648d71d572 100644 --- a/packages/api/src/agents/run.ts +++ b/packages/api/src/agents/run.ts @@ -941,6 +941,18 @@ function shapeSummarizationConfig( }; } +/** + * Below this context budget a summarization cycle cannot make progress: the + * summary allocation rounds down to a handful of tokens, the rewritten history + * still overflows, and the graph re-triggers summarization on every step until + * the recursion limit aborts the run — dozens of wasted LLM calls surfaced to + * the user as an opaque LangGraph error. Falling back to plain pruning instead + * either fits the request or fails fast with the actionable `empty_messages` + * token-budget breakdown. Matches the floor `initializeAgent` applies when the + * user supplies no override. + */ +const MIN_SUMMARIZATION_CONTEXT_TOKENS = 1024; + /** * Applies `reserveRatio` against the pre-ratio base context budget, falling * back to the pre-computed `maxContextTokens` from initializeAgent. @@ -1879,6 +1891,20 @@ export async function createRun({ agent.maxContextTokens, ); + const summarizationViable = + effectiveMaxContextTokens == null || + effectiveMaxContextTokens >= MIN_SUMMARIZATION_CONTEXT_TOKENS; + if (summarization.enabled && !summarizationViable) { + logger.warn( + '[createRun] Summarization disabled for this run: context budget below viable minimum', + { + agentId: agent.id, + effectiveMaxContextTokens, + minimum: MIN_SUMMARIZATION_CONTEXT_TOKENS, + }, + ); + } + const reasoningKey = getReasoningKey(provider, llmConfig, agent.endpoint, agent.reasoningKey); const agentInput: AgentInputs = { provider, @@ -1896,7 +1922,7 @@ export async function createRun({ useLegacyContent: agent.useLegacyContent ?? false, discoveredTools: !isSubagent && discoveredTools.size > 0 ? Array.from(discoveredTools) : undefined, - summarizationEnabled: summarization.enabled, + summarizationEnabled: summarization.enabled && summarizationViable, summarizationConfig: summarization.config, ...(!isSubagent && compactionSemanticIndex != null ? { compactionSemanticIndex } : {}), initialSummary: isSubagent ? undefined : initialSummary,