fix(llmloop): account for tool_calls and arguments in messageTokens context budgeting - #1413
AyushDubey23 wants to merge 1 commit into
Conversation
Assistant turns issuing tool calls under OpenAI-compatible chat completions store their calls and arguments on ToolCalls rather than Native.Payload. Because messageTokens only inspected ExtractText() and Native.EstimatedTokens(), these turns evaluated to 0 tokens during active zone budget calculations, causing computeActiveZoneSize to pack too many rounds and risk exceeding model context limits. Adds EstimatedTokens to ToolCall and Message, and updates messageTokens to account for tool calls when not already covered by Native payload.
|
✅ OpenCodeReview: Review complete: 0 finding(s) across 4 selected item(s). |
There was a problem hiding this comment.
This addresses the missing tool-call contribution to context budgeting. The native-payload guard avoids counting tool calls again when Anthropic or Responses payloads already include them, and the agent/session fallback estimates use the same accounting path.
The regression tests cover large arguments consuming the active-zone budget instead of being treated as zero. No blocking findings from me; approved.
This review was conducted by Qiyuanqiii's review bot, using the model GPT 6 Astra Max. If you need a human review, please manually @.
|
Hi @Qiyuanqiii, the bot review has passed and all 13 CI checks are green. Could you please take a quick look and provide the human write-access approval/merge when you have a moment? Thanks! |
|
Waiting for the maintainer themselves to review it; he may not have time to look at it. |
Description
In
internal/llmloop/compression.go,messageTokens(m llm.Message)calculates the token weight of messages when partitioning conversation rounds between the active budget and history compression.When an assistant turn issues tool calls:
m.ExtractText()returns""because tool calls and arguments are not stored inm.Content.m.Native.Payloadis eithernilorReasoningPayload(reasoning_content) — it does not store tool calls.m.ToolCalls []llm.ToolCall.Because
m.ToolCallswas not accounted for, every assistant tool-call turn evaluated to 0 tokens. In multi-turn reviews with heavy tool usage,computeActiveZoneSizeseverely under-calculated active zone token usage and packed too many rounds into the context budget, risking context window exhaustion (400 context_length_exceeded).What Changed
ToolCall.EstimatedTokens(): Added tointernal/llm/client.goto estimate tokens for a tool call's name, arguments, and ID framing using the project's standardbytes / 4heuristic.Message.EstimatedTokens(): Combinesm.Native.EstimatedTokens()withm.ToolCallstoken estimation. AddedNativeTurn.countsToolCalls()to prevent double-counting whenNative.Payloadalready accounts for tool calls (such as AnthropicMessageParamtool-use blocks or Responses API function-call items).llmloop.messageTokens(): Updated to usem.EstimatedTokens(), properly accounting for tool calls and arguments in active zone size calculation (computeActiveZoneSize) andCountMessagesTokens.internal/session/history.goandinternal/agent/util.goto includem.EstimatedTokens().Tests Added
internal/llm/client_test.go:TestToolCall_EstimatedTokens: Covers empty, short, and large tool-call argument payloads.TestMessage_EstimatedTokens: Verifies text-only, tool-call, mixed reasoning+tool-call messages, and guards against double-counting with Anthropic native tool use blocks.internal/llmloop/compression_test.go:TestCountMessagesTokens_IncludesToolCalls: Asserts that assistant messages with tool calls produce accurate, non-zero token counts.TestComputeActiveZoneSize_AccountsForToolCalls: Verifies that rounds with large tool-call arguments consume active-zone budget properly rather than being treated as 0 tokens.Checklist
gofmt,go vet)go test ./internal/...)verify-english-only.gopasses cleanlyCloses #1411