Skip to content

Python: exclude encrypted reasoning payloads from compaction token counts - #8354

Merged
Eduard van Valkenburg (eavanvalkenburg) merged 6 commits into
microsoft:mainfrom
Lubaoshuai:fix/compaction-protected-data-tokens
Sep 17, 2026
Merged

Eduard van Valkenburg (eavanvalkenburg) merged 6 commits into
microsoft:mainfrom
Lubaoshuai:fix/compaction-protected-data-tokens

Conversation

@Lubaoshuai

@Lubaoshuai Lubaoshuai commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Motivation & Context

The compaction token counter serialises each message to JSON and tokenises the string. That serialisation included provider reasoning payloads: opaque encrypted blobs (OpenAI Responses encrypted_content, Anthropic thinking signature) and the JSON-serialised reasoning_details stored by the Chat Completions client. Encrypted blobs are replayed opaquely and never tokenised by the model, so they inflated local token estimates and could trigger premature compaction (#8348).

Description & Review Guide

  • What are the major changes?
    • _serialize_content excludes provider-encrypted reasoning payloads from the token-estimation serialisation:
      • Opaque protected_data / additional_properties["encrypted_content"] blobs are dropped.
      • JSON reasoning_details (Chat Completions) keep their clear-text members (summary, reasoning_text, nested reasoning.text) — the client replays those to the provider as input, so only their opaque encrypted_content members are excluded (review follow-up; previously the whole structure was dropped).
    • Group token-count annotations are stamped with a serialization basis version (token_count_basis). Counts cached under an older basis — e.g. sessions annotated before this change, since annotations survive Message.to_dict()/from_dict() while annotate_token_counts skips messages that already have a count — are recomputed instead of reused (review follow-up).
  • What is the impact of these changes?
    • Token estimates match the text the provider actually processes; transcripts annotated by older versions are lazily re-tokenised on their next compaction pass instead of carrying inflated counts that could truncate valid history or fire summarisation early.
  • What do you want reviewers to focus on?
    • The recursive opaque-member filter (_strip_opaque_reasoning_payload) and the basis-version handling in _write_group_annotation / _token_count / _write_token_count.

Related Issue

Fixes #8348

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.

…unts

The compaction token counter serialises each message and tokenises the JSON.
Content.protected_data (OpenAI encrypted_content, Anthropic thinking
signature, Gemini thought_signature, OpenAI reasoning_details) is opaque
ciphertext the model replays but never tokenises, so counting it inflated
every token-aware compaction decision. The same blob can also ride in
additional_properties as encrypted_content.

Both are now excluded from serialised content, matching the existing
exclusions for raw_representation and mirrored items.

Fixes microsoft#8348

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.

🔵 Needs a closer look

Previously cached token counts can remain inflated and must be invalidated or recomputed.

Pull request overview

This PR prevents opaque encrypted reasoning payloads from inflating Python compaction token estimates.

Changes:

  • Excludes protected_data and additional_properties["encrypted_content"].
  • Adds regression tests for both payload locations.
  • Cached token-count annotations still require invalidation or recomputation.
File summaries
File Summary
python/packages/core/tests/core/test_compaction.py Adds regression tests for both encrypted payload locations.
python/packages/core/agent_framework/_compaction.py Filters encrypted reasoning payloads during token serialization.
Review details

Suppressed comments (1)

python/packages/core/agent_framework/_compaction.py:707

  • This only changes newly computed counts. Token counts are cached in the _group annotation, and annotate_token_counts skips messages whose count is already present; those annotations are also preserved by Message.to_dict()/from_dict(). A transcript annotated before this fix (or one whose reasoning payload is added after annotation) will therefore continue to use the inflated count and can still trigger premature compaction. Please invalidate/version the cached count when the serialization basis changes, or otherwise force a retokenization for such stale annotations.
    payload.pop("protected_data", None)
  • Files reviewed: 2/2 changed files
  • Comments generated: 0
  • Review effort level: Lite (auto)

Note

Copilot is running an experiment and ran this review at Lite.


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

@Lubaoshuai

Copy link
Copy Markdown
Contributor Author

@microsoft-github-policy-service agree

Comment thread python/packages/core/agent_framework/_compaction.py Outdated
Comment thread python/packages/core/agent_framework/_compaction.py Outdated
@eavanvalkenburg

Copy link
Copy Markdown
Member

Thanks for the update. Before this is ready, could you please:

Once those are addressed, please re-request review. Thanks!

…ale token caches

Review follow-up for the protected-data token exclusion:

- reasoning_details stored in protected_data are JSON-serialised and
  replayed to the provider as clear text (summary, reasoning_text and
  nested reasoning.text), so only opaque members (encrypted_content) are
  excluded from the estimate; non-JSON payloads (Anthropic signature,
  Responses API encrypted blobs) are still dropped entirely.
- group token counts are stamped with a serialization basis version;
  counts cached under an older basis (e.g. sessions annotated before a
  serialization change) are recomputed instead of reused.
@Lubaoshuai
Lubaoshuai deployed to github-app-auth September 15, 2026 12:51 — with GitHub Actions Active
@Lubaoshuai
Lubaoshuai deployed to github-app-auth September 15, 2026 12:59 — with GitHub Actions Active
@Lubaoshuai

Copy link
Copy Markdown
Contributor Author

Thanks Eduard van Valkenburg (@eavanvalkenburg) Evan Mattson (@moonbox3) — all three points are addressed in efb44d8:

  1. Remaining review discussions: both resolved above (clear-text reasoning preserved via recursive encrypted_content stripping in _serialize_content; stale group token counts invalidated via the new token_count_basis version stamp, so pre-change sessions are lazily re-tokenized).
  2. Checks: re-triggered by the new push; watching for green.
  3. PR body: rewritten per the repository template — all four sections present, checklist completed.

Tests: the two original regression tests remain, plus two new ones (clear-text reasoning counted while opaque members are excluded; stale-basis counts recomputed and re-stamped). pytest packages/core/tests/core/test_compaction.py: 101 passed; ruff check / ruff format clean.

Re-requesting review.

@Lubaoshuai
Lubaoshuai deployed to github-app-auth September 15, 2026 13:17 — with GitHub Actions Active
@moonbox3

Copy link
Copy Markdown
Contributor

Please fix the failing CI/CD checks.

@eavanvalkenburg

Copy link
Copy Markdown
Member

Thanks for the update. The current head is not green: Package Checks and Test Typing Checks fail on pyright/mypy errors in _compaction.py and test_compaction.py (unknown dictionary key/value types and an unsupported comparison involving an optional integer), and Merge Gatekeeper fails consequently. Could you please fix the typing failures and re-request review? Thanks!

- annotate the recursive reasoning-payload filter with explicit
  dict[Any, Any] / list[Any] casts so iterating the narrowed Any dict
  does not leak Unknown types under pyright strict
- cast additional_properties before the encrypted_content filter
- assert non-None token counts in the clear-text regression test so the
  ordering comparison does not involve optional integers
@Lubaoshuai
Lubaoshuai deployed to github-app-auth September 17, 2026 01:15 — with GitHub Actions Active
@Lubaoshuai
Lubaoshuai deployed to github-app-auth September 17, 2026 01:23 — with GitHub Actions Active
@Lubaoshuai

Copy link
Copy Markdown
Contributor Author

Thanks Eduard van Valkenburg (@eavanvalkenburg) Evan Mattson (@moonbox3) — the pyright/mypy failures are fixed in 48bde32:

  • _strip_opaque_reasoning_payload iterates explicitly cast dict[Any, Any] / list[Any] sources — the isinstance narrowing on Any was leaking Unknown key/item types under pyright strict ("unknown dictionary key/value types").
  • additional_properties is cast to dict[str, Any] before the encrypted_content filter (same root cause).
  • The clear-text regression test now asserts non-None counts before the ordering comparison ("unsupported comparison involving an optional integer").

Local verification: pyright agent_framework/_compaction.py → 0 errors; pytest packages/core/tests/core/test_compaction.py → 111 passed; ruff check / ruff format clean. The branch was also rebased on the current main merge.

One note: tests/core/test_agent_hooks.py has a pre-existing Import "agent_hooks" could not be resolved in the static sweep — the module is created by the test fixture at runtime, the file is untouched by this PR and the error appears on main identically.

CI is re-running on the new head; re-requesting review.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Lubaoshuai Reviewed the complete diff and found no actionable issues. All reported checks for the current head are green.

Merged via the queue into microsoft:main with commit d90355f Sep 17, 2026
42 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: [Bug]: Compaction token counter charges encrypted reasoning payloads (protected_data) as prompt text

4 participants