Skip to content

Python: Fix compaction message-id collisions and tool-loop summary persistence#6299

Open
eavanvalkenburg wants to merge 2 commits into
microsoft:mainfrom
eavanvalkenburg:fix/compaction-message-id-and-tool-loop-persistence
Open

Python: Fix compaction message-id collisions and tool-loop summary persistence#6299
eavanvalkenburg wants to merge 2 commits into
microsoft:mainfrom
eavanvalkenburg:fix/compaction-message-id-and-tool-loop-persistence

Conversation

@eavanvalkenburg
Copy link
Copy Markdown
Member

Motivation and Context

Two bugs in the compaction strategies produced incorrect behavior:

  • Fixes Python: [Bug]: Colliding Ids are assigned to message history creating issues when using compaction #5237 — Incremental group annotation assigned message ids by position within the re-annotated slice. When the re-annotation start moved back to a previous group start, ids restarted at 0 and collided (e.g. a user message reusing an assistant message's id). Colliding ids merged groups, which in turn caused tool-result compaction to wrongly exclude messages.
  • Fixes Python: [Bug]: Compaction strategies seem incorrect #4991 — The function-invocation (tool) loop copied the message list on each iteration, so summary messages inserted by compaction landed in a throwaway copy and were lost across tool-loop iterations (only the persistent _excluded flags survived), leaving groups dropped without their replacement summary.

Description

  • group_messages / _ensure_message_ids now accept an id_offset and guard against collisions with existing ids; annotate_message_groups threads the slice start index through as the offset, so incremental annotation produces stable, unique ids.
  • _prepare_messages_for_model_call now compacts the message list in place when messages is a list, so summaries inserted during compaction persist across tool-loop iterations alongside the already-persistent excluded flags.
  • Added regression tests: incremental id uniqueness, existing-id collision avoidance, tool-result compaction idempotency, and tool-loop summary persistence (including streaming and conversation-id modes).
  • Samples: added summarization.py demonstrating SummarizationStrategy directly with a real summarizing client, and reworked advanced.py with tool-call groups, a real summarizer, and a composed token-budget strategy.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • The PR follows the Contribution Guidelines
  • All unit tests pass, and I have added new tests where possible
  • Is this a breaking change? If yes, add "[BREAKING]" prefix to the title of the PR.

Fixes two bugs in the compaction strategies:

- microsoft#5237: incremental group annotation assigned message ids by position
  within the re-annotated slice, so moving the re-annotation start back to
  a previous group start restarted ids at 0 and produced collisions
  (e.g. a user message reusing an assistant message's id), merging groups
  and causing tool-result compaction to wrongly exclude messages.
  group_messages/_ensure_message_ids now take an id_offset and guard
  against existing-id collisions; annotate_message_groups threads the
  slice start index through as the offset.

- microsoft#4991: the function-invocation loop copied the message list each
  iteration, so summaries inserted by compaction landed in a throwaway
  copy and were lost across tool-loop iterations (only the persistent
  excluded flags survived). _prepare_messages_for_model_call now compacts
  the list in place when messages is a list, so inserted summaries persist.

Adds regression tests (incremental id uniqueness, existing-id collision
avoidance, idempotency, and tool-loop summary persistence including
streaming and conversation-id modes).

Also adds a summarization.py sample demonstrating SummarizationStrategy
directly with a real client, and reworks advanced.py with tool-call
groups and a real summarizer.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 3, 2026 09:08
@moonbox3 moonbox3 added documentation Improvements or additions to documentation python labels Jun 3, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 3, 2026

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/core/agent_framework
   _clients.py138794%325, 376, 539–542, 657
   _compaction.py6765192%108–109, 117, 184–185, 203–204, 221, 255, 269, 276, 291–292, 346, 353, 369, 419, 443, 474, 521, 527, 529, 548, 592–597, 609, 681, 683, 698, 742, 804, 926, 987, 989, 1005, 1012, 1017, 1032, 1040, 1134, 1137, 1155, 1171, 1267, 1294, 1299, 1378
TOTAL37836440388% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
7533 34 💤 0 ❌ 0 🔥 1m 56s ⏱️

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes two Python compaction bugs that could corrupt group tracking: (1) message-id collisions during incremental group annotation, and (2) loss of compaction-inserted summary messages across tool-loop iterations due to operating on throwaway message-list copies.

Changes:

  • Make auto-assigned Message.message_id generation stable for incremental annotation by threading an id_offset through grouping/id assignment.
  • Compact message lists in place (when the input is a list) so inserted summaries persist across function-invocation loop iterations.
  • Add regression tests for id uniqueness/collision avoidance and tool-loop/compaction idempotency; update compaction samples (new summarization sample, enhanced advanced sample).

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
python/packages/core/agent_framework/_compaction.py Adds id_offset support to prevent incremental re-annotation id collisions; wires offset through annotation.
python/packages/core/agent_framework/_clients.py Applies compaction against the caller’s list when possible so inserted summaries persist through tool loops.
python/packages/core/tests/core/test_compaction.py Adds regression tests for incremental id uniqueness, collision avoidance, and tool-result compaction idempotency.
python/packages/core/tests/core/test_clients.py Adds regression tests ensuring summaries persist across tool-loop iterations (streaming/non-streaming) and respects conversation-id mode behavior.
python/samples/02-agents/compaction/summarization.py New sample demonstrating SummarizationStrategy directly with a real summarizing client.
python/samples/02-agents/compaction/advanced.py Reworks sample to include tool-call groups and a real summarizer under a composed token-budget strategy.
python/samples/02-agents/compaction/README.md Updates documentation to include the new/updated samples and their run commands.

Comment thread python/packages/core/agent_framework/_compaction.py Outdated
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Automated Code Review

Reviewers: 4 | Confidence: 92%

✓ Correctness

The PR correctly fixes both reported bugs: (1) message-id collisions in incremental annotation by threading an absolute offset through _ensure_message_ids, and (2) tool-loop summary persistence by compacting the caller's list in-place when it's already a list. The logic is sound, regression tests are well-designed, and the idempotency/conversation-id edge cases are properly handled. No blocking correctness issues found.

✓ Security Reliability

This PR fixes two compaction bugs (message-id collisions and tool-loop summary persistence) with well-tested, sound implementations. The collision avoidance in _ensure_message_ids is correctly bounded (the while-loop terminates because existing_ids is finite and the counter is monotonically increasing). The in-place mutation design for _prepare_messages_for_model_call is explicitly documented and validated by regression tests. No security vulnerabilities, resource leaks, injection risks, or unhandled failure modes were found.

✓ Test Coverage

The PR adds comprehensive regression tests for both bugs fixed. The id-collision fix (#5237) has two unit tests covering incremental uniqueness and existing-id avoidance. The tool-loop summary persistence fix (#4991) has three integration tests covering non-streaming, streaming, and conversation-id modes, plus a unit test verifying idempotency after summary insertion. Test assertions are meaningful (checking actual summary content and uniqueness, not just no-exception). No significant test coverage gaps were found for the changed production behavior.

✗ Design Approach

I found one design issue in the message-id collision fix: it only guards against ids already present in the re-annotated slice, so incremental append/suffix re-annotation can still collide with msg_N ids that already exist in the preserved prefix and recreate the group-merge problem the PR is trying to eliminate.

Flagged Issues

  • _ensure_message_ids only checks for collisions inside the suffix being re-annotated, but existing incremental callers such as extend_compaction_messages (python/packages/core/agent_framework/_compaction.py:523-529) annotate only the appended tail. If the preserved prefix already contains a user-supplied id like msg_2, appending at index 2 will still assign msg_2 to the new message, and _group_id_for derives group ids from message_id (_compaction.py:101-104), so the old and new groups collapse together again.

Automated review by eavanvalkenburg's agents

Comment thread python/packages/core/agent_framework/_compaction.py Outdated
Addresses PR review on microsoft#5237: _ensure_message_ids only guarded against
collisions within the re-annotated slice. A preexisting (e.g. user-supplied)
id in the preserved prefix could still be reassigned in the suffix when the
id was numerically out of position, merging groups across the re-annotation
boundary again.

group_messages/_ensure_message_ids now accept reserved_ids, and
annotate_message_groups passes the preserved prefix's ids so auto-assigned
suffix ids never collide across the full list. Adds a regression test
reproducing the out-of-position prefix-id collision.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation python

Projects

None yet

3 participants