Skip to content

Byte-account hosted tool-call/result content in compaction sizing - #1008

Open
PratikDhanave (PratikDhanave) wants to merge 1 commit into
microsoft:mainfrom
PratikDhanaveFork:compaction-count-hosted-content-bytes
Open

Byte-account hosted tool-call/result content in compaction sizing#1008
PratikDhanave (PratikDhanave) wants to merge 1 commit into
microsoft:mainfrom
PratikDhanaveFork:compaction-count-hosted-content-bytes

Conversation

@PratikDhanave

Copy link
Copy Markdown
Contributor

Problem

computeContentByteCount (agent/compaction/index.go) computes each group's ByteCount and, via computeTokenCount's default branch (… / 4), the token estimate for non-text content. It accounts FunctionResultContent (fmt.Sprint(Result)) and DataContent (len(Data)), but MCPServerToolResultContent / CodeInterpreterToolResultContent (and the call variants) hit default: return 0.

So a MCPServerToolResultContent/CodeInterpreterToolResultContent carrying 10,000 bytes of Outputs contributes only ~2 bytes (just the CallID), whereas the equivalent FunctionResultContent contributes 10,004. Byte/token-based triggers (TokensExceed, and therefore ContextWindowStrategy) under-count history dominated by hosted code-interpreter / MCP outputs — precisely the large content that most needs compaction — so compaction can fail to fire when it should.

Fix

Account the hosted tool-call/result types by recursing into their Outputs/Inputs (summing computeContentByteCount) and counting their identity strings, mirroring the function-call/result cases. default stays 0 for genuinely unknown types.

Test

TestMessageIndex_CountsHostedToolResultBytes builds an MCP result and a code-interpreter result each carrying a 10k-byte Outputs text and asserts their TotalByteCount is at least that of the equivalent FunctionResultContent. Fails before the fix (2 bytes vs 10004), passes after.

computeContentByteCount (used by group ByteCount and, via /4, by the token
estimate for non-text content) counted FunctionResultContent and DataContent
payloads, but MCPServerToolResult/CodeInterpreterToolResult (and the call
variants) fell through to default: return 0. A 10k-byte hosted tool result
therefore contributed ~2 bytes, so byte/token-based triggers (TokensExceed,
ContextWindowStrategy) under-counted history dominated by hosted code-
interpreter / MCP outputs - exactly the large content that most needs
compaction - and could fail to fire.

Account these types by recursing into Outputs/Inputs and counting the
identity strings, mirroring the function-call/result cases.
Copilot AI lite review requested due to automatic review settings September 6, 2026 04:57
@github-actions github-actions Bot added area:agent Changes files in the agent area size:medium At most 100 changed lines across at most 5 files labels Sep 6, 2026
@github-actions github-actions Bot added the pending-auto-risk Automatic risk classification is in progress label Sep 6, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Tool-call grouping logic still doesn’t recognize the new hosted tool-call content types, and the new byte-count branches for hosted tool calls are untested.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR fixes under-counting in compaction sizing by ensuring hosted tool call/result content (MCP server + code interpreter) contributes its nested input/output bytes to group byte/token estimates, so byte/token-based compaction triggers fire appropriately for large hosted tool payloads.

Changes:

  • Extend computeContentByteCount to account for MCPServerTool{Call,Result}Content and CodeInterpreterTool{Call,Result}Content, recursing into Inputs/Outputs.
  • Add a regression test ensuring hosted tool result outputs are byte-accounted comparably to FunctionResultContent.
File summaries
File Description
agent/compaction/index.go Adds byte counting for hosted tool call/result content (including recursive counting of nested contents).
agent/compaction/index_test.go Adds a regression test to catch under-counting of hosted tool result output bytes.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread agent/compaction/index.go
Comment on lines +410 to +414
case *message.CodeInterpreterToolCallContent:
total := stringByteCount(typed.CallID)
for _, input := range typed.Inputs {
total += computeContentByteCount(input)
}
Comment on lines +377 to +401
// A hosted tool result carrying 10k bytes of Outputs must be byte-accounted
// like the equivalent FunctionResultContent, so token/byte-based compaction
// triggers see the payload that most needs compacting.
fnMsg := &message.Message{Role: message.RoleTool, Contents: message.Contents{
&message.FunctionResultContent{CallID: "c1", Result: big},
}}
mcpMsg := &message.Message{Role: message.RoleTool, Contents: message.Contents{
&message.MCPServerToolResultContent{CallID: "c1", Outputs: message.Contents{&message.TextContent{Text: big}}},
}}
ciMsg := &message.Message{Role: message.RoleTool, Contents: message.Contents{
&message.CodeInterpreterToolResultContent{CallID: "c1", Outputs: message.Contents{&message.TextContent{Text: big}}},
}}
prefix := textMessage(message.RoleUser, "hi")

fnBytes := compaction.CreateMessageIndex([]*message.Message{prefix, fnMsg}, nil).TotalByteCount()
mcpBytes := compaction.CreateMessageIndex([]*message.Message{prefix, mcpMsg}, nil).TotalByteCount()
ciBytes := compaction.CreateMessageIndex([]*message.Message{prefix, ciMsg}, nil).TotalByteCount()

if mcpBytes < fnBytes {
t.Errorf("MCP tool-result bytes = %d, want >= FunctionResult bytes %d (Outputs undercounted)", mcpBytes, fnBytes)
}
if ciBytes < fnBytes {
t.Errorf("code-interpreter result bytes = %d, want >= FunctionResult bytes %d (Outputs undercounted)", ciBytes, fnBytes)
}
}
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Scope: internal-only

Changed Go contract: None. The change is confined to the unexported computeContentByteCount helper in agent/compaction/index.go, adding cases for *message.MCPServerToolCallContent, *message.MCPServerToolResultContent, *message.CodeInterpreterToolCallContent, and *message.CodeInterpreterToolResultContent (recursing into Inputs/Outputs and summing identity-string bytes, mirroring the existing FunctionCallContent/FunctionResultContent cases). No exported functions, types, fields, or options are added, removed, or changed. agent/compaction/index_test.go only adds a new test.

Upstream evidence reviewed: This is a Go-only internal byte/token-estimation heuristic (computeTokenCount's bytes / 4 fallback) used by ContextWindowStrategy/TokensExceed triggers. No equivalent internal byte-counting estimator was found in dotnet/src/Microsoft.Agents.AI* or python/packages/core/agent_framework/ — .NET and Python context-window/compaction strategies rely on model-provided or externally-supplied token counters rather than a private per-content-type byte heuristic, so there is no analogous public or internal contract to compare against (No equivalent found).

Result: out of scope for cross-repo parity review — the change fixes an accounting bug in an unexported Go-only helper with no user-visible API surface change. It does correct an internal under-counting bug that could affect compaction trigger timing, but it brings hosted tool-call/result accounting in line with the framework's own existing pattern (FunctionResultContent/DataContent) rather than introducing new behavior divergent from any upstream design. No inconsistency with .NET/Python was found because neither implements this internal estimator. No labels applied (no exported API change); no stale public-api-change/parity-approved labels were present to remove.

Warning

Firewall blocked 3 domains

The following domains were blocked by the firewall during workflow execution:

  • api.github.com
  • github.com
  • patchdiff.githubusercontent.com

[!TIP]
api.github.com is blocked because GitHub API access uses the built-in GitHub tools by default. Instead of adding api.github.com to network.allowed, use tools.github.mode: gh-proxy for direct pre-authenticated GitHub CLI access without requiring network access to api.github.com:

tools:
  github:
    mode: gh-proxy

See GitHub Tools for more information on gh-proxy mode.

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "api.github.com"
    - "github.com"
    - "patchdiff.githubusercontent.com"

See Network Configuration for more information.

Generated by Go API Consistency Review Agent · copilot · auto · 39.8 AIC · ⌖ 7.51 AIC · ⊞ 9.5K ·

@github-actions github-actions Bot added risk:medium Contained production impact requiring normal review depth and removed pending-auto-risk Automatic risk classification is in progress labels Sep 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:agent Changes files in the agent area risk:medium Contained production impact requiring normal review depth size:medium At most 100 changed lines across at most 5 files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants