Repository navigation
Python: fix(redis): deduplicate RedisContextProvider writes - #10179
pratik wayase (PratikWayase) wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Reused pipeline mocks accumulate EXISTS calls and break multi-run replay tests, while regression coverage does not verify that existing documents are skipped during embedding and loading.
2 open issues
What changed in this PR
This PR prevents replayed conversations from creating duplicate Redis memory documents, extending replay protection to Python’s RedisContextProvider.
Changes:
- Derives deterministic keys from scope, role, and trimmed content, while respecting the index’s configured prefix.
- Checks existing keys before embedding or storing documents and deduplicates messages within each batch.
- Adds replay, key-stability, and whitespace-handling tests, plus updated pipeline mocks.
| File | Description |
|---|---|
python/packages/redis/tests/test_providers.py |
Adds pipeline and deterministic-key mocks. |
python/packages/redis/tests/test_context_provider_sdk.py |
Tests replay filtering, embedding skips, and key identity. |
python/packages/redis/tests/test_context_provider_integration.py |
Adds live Redis replay and stable-key checks. |
python/packages/redis/tests/test_context_provider_edges.py |
Updates mocks for existence checks and explicit keys. |
python/packages/redis/agent_framework_redis/_context_provider.py |
Implements deterministic keys, deduplicated writes, and shared scope assembly. |
Review effort: Balanced
|
The failing test |
|
pratik wayase (@PratikWayase), thank you for the contribution. The linked issue (#10127) is not marked |


Motivation & Context
RedisContextProvider.after_runsaves every message incontext.input_messageson each run. Behind the AG-UI endpoint, the TypeScript client (@ag-ui/clientHttpAgent) resends the whole conversation on every request, so each turn re-saves everything that came before. In a 15-message chat I ended up with 240 documents in Redis; "We deploy to westus3." was stored 14 times. Search results got flooded with copies of the earliest messages, so the deployment fact never reached the model the agent answered "I don't have the deployment location" in a new chat.The same chat, sent one message at a time, stored 30 documents and answered correctly.
_addmade it worse: it calledredis_index.load(prepared)without keys, so RedisVL handed every document a brand-new random key. Nothing checked whether a message was already stored.History providers were fixed for this same replay pattern in #7242 and #8269. Memory providers were not.
Description & Review Guide
What are the major changes?
sha256(scope_fields + role + content). I deliberately did not usemessage_idthe assistant reply comes back with a different id than the one stored (the stored id is the chatcmpl id; the AG-UI stream uses its own id), so message_id-based keys would never match._addpipelines a singleEXISTScheck to find which candidate keys are already in Redis, and skips them. They are neither re-embedded nor re-stored.redis_index.key(suffix). This reads the real index prefix and separator, so borrowed indexes with non-default prefixes still produce searchable keys..strip()-ed so whitespace differences between the stored model text and the replayed AG-UI text don't produce a second copy._scope_values(session_id)helper, used by_add,_document_redis_key, and_redis_search. They can no longer drift apart.What is the impact of these changes?
What do you want reviewers to focus on?
_document_key_suffixand the dedup flow in_add..strip()normalization these are intentional and called out in the issue.mock_indexintest_context_provider_edges.pyandtest_providers.pyboth files had separate fixtures that needed the same update.Related Issue
Fixes #10127
AI Assistance
AI assistance details: AI assisted with issue investigation, the reproduction script, and PR text error handling. I verified everything against a live Redis and went through multiple review rounds myself.
Contribution Checklist
breaking changelabel (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.