Skip to content

Python: fix: prevent superlinear history growth by deduplicating messages in save_messages - #7242

Merged
Evan Mattson (moonbox3) merged 17 commits into
microsoft:mainfrom
PratikWayase:fix/per-service-history-duplicate-persistence
Aug 19, 2026
Merged

Python: fix: prevent superlinear history growth by deduplicating messages in save_messages#7242
Evan Mattson (moonbox3) merged 17 commits into
microsoft:mainfrom
PratikWayase:fix/per-service-history-duplicate-persistence

Conversation

@PratikWayase

Copy link
Copy Markdown
Contributor

Motivation & Context

  1. Why is this change required?
    The current history providers (InMemoryHistoryProvider, FileHistoryProvider, and RedisHistoryProvider) blindly append messages on every service call without checking if they already exist in the store.
  2. What problem does it solve?
    In looped runs (e.g., AG-UI stateless clients or harness todo loops), the transport passes the full accumulated conversation as input on every request. This caused the entire conversation history to be re-persisted every round, leading to superlinear store growth, corrupted context, repeated tool calls, and massive token bloat (up to 3× the real conversation size).
  3. What scenario does it contribute to?
    Multi-iteration flows where each service call's message list carries the accumulated conversation rather than a delta, particularly when using stateless chat clients over the AG-UI endpoint.
  4. Issue link:FixesPython: [Bug]: per-service-call history persistence re-appends already-persisted messages #7211

Description & Review Guide

  • What are the major changes?

    • Added a new _get_message_identity helper function that generates a stable identity for a message (using message.id if available, otherwise falling back to a deterministic hash of role + serialized contents).
    • Updated save_messages in InMemoryHistoryProvider, FileHistoryProvider, and RedisHistoryProvider to build a set of existing message identities and filter out duplicates before appending/pushing new messages.
    • Added comprehensive regression tests for all three providers to ensure identical messages are ignored, mixed old/new messages only append the new ones, and messages with the same text but different roles are kept separate.
  • What is the impact of these changes?
    This hardens the system at the lowest storage level. It completely prevents duplicate message persistence regardless of how the middleware or transport layer constructs the message list. This stops token bloat, prevents context corruption, and ensures graceful degradation in long-running loops without requiring changes to the middleware routing logic.

  • What do you want reviewers to focus on?

    1. The stability and correctness of the _get_message_identity fallback logic (ensuring it handles missing IDs and serialization edge cases gracefully).
    2. The RedisHistoryProvider.save_messages implementation, specifically ensuring it correctly fetches existing messages via lrange to build the identity set before pushing only the new_messages via the pipeline.
    3. The new unit tests to ensure they adequately cover the deduplication edge cases.

Related Issue

Fixes#7211

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.

CopilotAI review requested due to automatic review settings July 21, 2026 18:52
@giles17Giles Odigwe (giles17) added the python Usage: [Issues, PRs], Target: Python label Jul 21, 2026
@github-actionsgithub-actionsBot changed the title fix: prevent superlinear history growth by deduplicating messages in save_messagesPython: fix: prevent superlinear history growth by deduplicating messages in save_messagesJul 21, 2026

CopilotAI 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.

Pull request overview

This PR fixes a Python history-persistence defect where per-service-call history providers re-persist the full accumulated conversation every round, causing superlinear growth and duplicated context. It introduces message-level deduplication in history providers (in-memory, file, Redis) and adds regression tests to ensure repeated inputs don’t bloat stored history.

Changes:

  • Added a message-identity helper and used it to filter out already-persisted messages before appending/pushing.
  • Updated save_messages behavior across in-memory, file (JSONL), and Redis history providers to skip duplicates.
  • Added unit/regression tests covering identical-message deduplication and mixed old/new message lists.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

FileDescription
python/packages/core/agent_framework/_sessions.pyAdds message identity + deduplication to in-memory and file history providers.
python/packages/core/tests/core/test_sessions.pyAdds regression tests for deduplication behavior in core providers and looped runs.
python/packages/redis/agent_framework_redis/_history_provider.pyAdds message identity + deduplication to Redis history persistence.
python/packages/redis/tests/test_providers.pyAdds Redis-provider tests validating deduplication behavior.

Comment threadpython/packages/core/agent_framework/_sessions.py Outdated
Comment threadpython/packages/core/agent_framework/_sessions.py Outdated
Comment threadpython/packages/core/agent_framework/_sessions.py Outdated
Comment threadpython/packages/core/agent_framework/_sessions.py Outdated
Comment threadpython/packages/redis/agent_framework_redis/_history_provider.py Outdated
@eavanvalkenburg

Copy link
Copy Markdown
Member

Please check the failing tests, and make sure the comments have a reply or are marked as resolved pratik wayase (@PratikWayase)

@github-actions

github-actionsBot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/core/agent_framework
_sessions.py10106493%164, 176–177, 207–208, 235–236, 269, 280, 294, 318, 363, 368, 370, 380, 412, 424, 434, 543, 612–613, 1347–1351, 1366, 1396, 1433–1434, 1448, 1450, 1470, 1472, 1571, 1611, 1688, 1692, 1702, 1919, 1952–1953, 1958, 1973, 2051–2052, 2054, 2138, 2237, 2310, 2325, 2330–2332, 2356, 2375, 2378, 2386–2387, 2399–2400, 2412, 2422, 2452
packages/redis/agent_framework_redis
_history_provider.py70198%213
TOTAL46873436790%

Python Unit Test Overview

TestsSkippedFailuresErrorsTime
948236 💤0 ❌0 🔥2m 31s ⏱️

@eavanvalkenburg

Copy link
Copy Markdown
Member

Still some checks failing pratik wayase (@PratikWayase)

@eavanvalkenburg

Copy link
Copy Markdown
Member

and a new merge conflict pratik wayase (@PratikWayase)

@PratikWayase

Copy link
Copy Markdown
ContributorAuthor

Eduard van Valkenburg (@eavanvalkenburg), Evan Mattson (@moonbox3)Giles Odigwe (@giles17) the merge conflict seems to be addressed now. Lmk if there's anything else

Comment threadpython/packages/core/agent_framework/_sessions.py
Comment threadpython/packages/redis/agent_framework_redis/_history_provider.py Outdated
@PratikWayase

Copy link
Copy Markdown
ContributorAuthor

Evan Mattson (@moonbox3) addressed your suggestions. Lmk if there's anything else.

Comment threadpython/packages/core/agent_framework/_sessions.py Outdated
Comment threadpython/packages/redis/agent_framework_redis/_history_provider.py Outdated
@PratikWayase

Copy link
Copy Markdown
ContributorAuthor

Evan Mattson (@moonbox3) addressed your recent suggestions. Lmk if there's anything else.

@moonbox3Evan Mattson (moonbox3) 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.

Please also have a look at the failing CI/CD checks.

Comment threadpython/packages/core/agent_framework/_sessions.py Outdated
Comment threadpython/packages/core/agent_framework/_sessions.py Outdated
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pythonUsage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: [Bug]: per-service-call history persistence re-appends already-persisted messages

5 participants

@PratikWayase@eavanvalkenburg@moonbox3@giles17