Skip to content

Python: fix(python): return call-level compaction summaries in agent responses - #8117

Merged
Eduard van Valkenburg (eavanvalkenburg) merged 13 commits into
microsoft:mainfrom
vedantsonkar:fix/8099-compaction-summary-in-response
Sep 17, 2026
Merged

Eduard van Valkenburg (eavanvalkenburg) merged 13 commits into
microsoft:mainfrom
vedantsonkar:fix/8099-compaction-summary-in-response

Conversation

@vedantsonkar

@vedantsonkar Vedant Sonkar (vedantsonkar) commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

Motivation & Context

Call-level compaction (compaction_strategy on an Agent or client.as_agent()) works mid-run: it marks older self-generated tool-call/tool-result messages as excluded and inserts a summary the model sees. However, the summary messages never reached the final AgentResponse - only the exclusion flags did, because the flags are in-place mutations on Message objects shared between the model-input list (prepared_messages) and the returned transcript (function_call_messages), while the inserted summaries live only in the model-input list.

As a result, HistoryProvider.after_run() persisted that asymmetric response, and with InMemoryHistoryProvider(skip_excluded=True) - the documented way to combine compaction with history - the next get_messages() silently dropped the excluded tool groups with no summary replacing them. Any agent using tools + call-level compaction + a history provider hits this.

Fixes #8099

Description & Review Guide

  • What are the major changes?

    • Generalized the existing private _reconcile_compaction_summaries helper in _compaction.py so both ChatMiddlewareLayer and the function-invocation loop use the same ownership-aware, fixed-point reconciliation algorithm.
    • Two reconciliation call sites in FunctionInvocationLayer._get_response_with_function_invocation (_tools.py): after each Phase-2 model call and after the Phase-3 final no-tools call, before the transcript is returned or persisted.
    • The function-loop transcript's message identities define the ownership boundary. Nested summary dependencies resolve to a fixed point, summaries are inserted before their transcript-owned source messages, and summaries involving caller-owned input are rejected so persisting the response does not duplicate caller history.
    • Centralized non-streaming terminal transcript assembly so ordinary completion, approval requests, user-input requests, and middleware termination all prepend exactly the accumulated transcript prefix without duplicating the current call/result messages.
    • Regression tests cover the Phase-2 terminal path, max-iterations path, nested summary-of-summary dependencies, mixed caller/transcript ownership, approval/user-input/middleware early exits, and agent-level persistence with InMemoryHistoryProvider(skip_excluded=True).
    • Spec traceability (normative bullet, matrix row, related-issue entry) in docs/specs/004-python-function-calling-loop.md, per python/AGENTS.md.
    • A separate chore commit applies the one-line ruff formatting to test_agent_executor.py that the locked formatter / pre-commit hook now requires (unrelated to the fix, isolated for clarity).
  • What is the impact of these changes?

    • Non-streaming runs that compact tool groups in-run now return the inserted summaries in ChatResponse.messages / AgentResponse.messages, positioned before the group each summary replaces - so history loaded with skip_excluded keeps the summarized content instead of losing it silently.
    • Streaming is untouched (its final response is assembled from accumulated updates, a mechanism that never carried compaction summaries or exclusion flags; existing streaming compaction tests remain green).
    • Scope is limited to the non-streaming function-invocation loop; no public API changes, no breaking change.
  • What do you want reviewers to focus on?

    • Reusing _reconcile_compaction_summaries with transcript identities as the ownership boundary, especially fixed-point handling of nested summaries and rejection of summaries involving caller-owned input.
    • Centralized terminal transcript assembly across ordinary completion, approval requests, user-input requests, and middleware termination.
    • Why reconciliation is unconditional rather than gated on the compaction_strategy parameter: compaction may come from the inner client's own default strategy, which the invocation layer does not see as a parameter.

Validated with uv run poe test -P core (5136 passed, 38 skipped, 2 xfailed), uv run poe syntax -P core, uv run poe pyright -P core, and uv run poe test-typing -P core - all green.

Related Issue

Fixes #8099

Contribution Checklist

  • The code builds clean without any errors or warnings
  • All unit tests pass, and I have added new tests where possible
  • The PR follows the Contribution Guidelines
  • This PR is linked to an issue and there is no other open PR for this issue (see Related Issue above).
  • This is not a breaking change. If it is a breaking change, add the breaking change label (or add "[BREAKING]" to the title prefix, before or after any language prefix) - a workflow keeps the label and title prefix in sync automatically.

Removes a blank line between stdlib import groups that the locked ruff
version (and therefore the pre-commit format hook) now requires.
In-run compaction excluded tool groups via flags on Message objects shared
with the function-invocation transcript, but inserted summary messages only
into the model-input list. The final ChatResponse (and the history persisted
from it) therefore kept only the exclusion flags, so loading history with
skip_excluded silently dropped the summarized tool results (microsoft#8099).

Merge summaries back into the transcript after each model call, matching
each summary to its excluded group via the SUMMARIZED_BY_SUMMARY_ID_KEY
back-link. Summaries of caller-owned input messages stay out of the
returned transcript.

Adds regression tests for the Phase-2 terminal path, the max-iterations
terminal path, and agent-level persistence with InMemoryHistoryProvider.
…oop spec

Adds the normative invariant that non-streaming runs return inserted
compaction summaries in the final response transcript, a traceability
matrix row pointing at the new regression tests, and a related-issue
entry for microsoft#8099.
Copilot AI balanced review requested due to automatic review settings September 7, 2026 10:02

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@agent-framework-automation agent-framework-automation Bot added documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs python Usage: [Issues, PRs], Target: Python labels Sep 7, 2026
@github-actions github-actions Bot changed the title fix(python): return call-level compaction summaries in agent responses Python: fix(python): return call-level compaction summaries in agent responses Sep 7, 2026
@vedantsonkar

Copy link
Copy Markdown
Contributor Author

@microsoft-github-policy-service agree

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@vedantsonkar

Copy link
Copy Markdown
Contributor Author

Eduard van Valkenburg (@eavanvalkenburg) Please do review and let me know!

@vedantsonkar

Copy link
Copy Markdown
Contributor Author

Eduard van Valkenburg (@eavanvalkenburg) I investigated the failing Python 3.14 Ubuntu job locally. The new #8099 regression cases pass under Python 3.14 (9/9), including the terminal-exit and nested-summary cases. The job’s public annotation only reports a generic exit code 1, while the remaining matrix jobs were cancelled due to fail-fast. Could someone with Actions-log access share the failing node ID and traceback? At present this looks likely to be an unrelated or CI-only failure rather than a reproducible regression from this PR.

@eavanvalkenburg

Copy link
Copy Markdown
Member

Thanks for the contribution. The current head is not green: Python - Tests and Merge Gatekeeper are failing, with failures and cancellations across the Python test matrix. Could you please get the checks passing, then re-request review?

auto-merge was automatically disabled September 15, 2026 11:33

Head branch was pushed to by a user without write access

@vedantsonkar

Copy link
Copy Markdown
Contributor Author

Eduard van Valkenburg (@eavanvalkenburg) Investigated the Python test failure. The Responses tests were using the real local file-backed AgentServer session store while pytest-xdist ran workers in parallel. Workers could write the same agent_sessions.json, causing intermittent storage errors and a failed response status.
The fix preserves the default file-backed store and instead gives each xdist worker its own temporary AGENTSERVER_STATE_ROOT. The change is scoped to test_responses.py.
Local validation passed:

  • Responses suite under xdist: passed
  • Python 3.10: 12750 passed, 74 skipped, 2 xfailed (with the workflow’s expected two-package exclusions)
  • Python 3.11: 13000 passed, 66 skipped, 2 xfailed
  • Python 3.12: 13000 passed, 66 skipped, 2 xfailed
  • Python 3.13: 13000 passed, 66 skipped, 2 xfailed
  • Python 3.14: 11910 passed, 66 skipped, 2 xfailed
  • Ruff and pre-commit checks: passed

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@vedantsonkar

Copy link
Copy Markdown
Contributor Author

Eduard van Valkenburg (@eavanvalkenburg) All tests seems to be passing now, can we merge this one ?

Merged via the queue into microsoft:main with commit e24a1f2 Sep 17, 2026
43 checks passed

This branch was previously deployed

1 inactive deployment
github-app-auth — 8b4e5fc3 Deployed Sep 16, 2026 by moonbox3 via add_label #23051
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: [Bug]: Call-level compaction summary dropped from AgentResponse (only exclusion flags persist)

4 participants