Skip to content

feat(python-sdks): SDK-level cross-source memory deduplication - #1532

Open
Dhravya wants to merge 1 commit into
sdk-dedup/tools-tsfrom
sdk-dedup/python
Open

feat(python-sdks): SDK-level cross-source memory deduplication#1532
Dhravya wants to merge 1 commit into
sdk-dedup/tools-tsfrom
sdk-dedup/python

Conversation

@Dhravya

@Dhravya Dhravya commented Aug 18, 2026

Copy link
Copy Markdown
Member

Stack Context

Part 2 of a 3-PR stack moving memory deduplication into the SDKs. See sdk-dedup/tools-ts (parent) for the full context and the TypeScript implementation this mirrors.

What?

Port the normalized, priority-ordered (static > dynamic > search) profile deduplication into the Python SDKs.

  • Each request injects one owned memory block that replaces the prior block rather than accumulating.
  • Dedup is request-local (no shared state), so it stays correct under concurrency.

Covers OpenAI, Agent Framework (middleware + context provider), Cartesia, and Pipecat.

Why?

Keeps the Python SDKs at behavioral parity with the TypeScript SDK so all integrations deduplicate memory the same way.

Testing

  • OpenAI: 31 passed, 11 skipped (live)
  • Agent Framework: 59 passed
  • Cartesia: 8 passed
  • Pipecat: 8 passed

🤖 Generated with Claude Code


Note

Medium Risk
Changes memory formatting and system-prompt injection across multiple SDK integrations; incorrect dedup or replacement could alter LLM context, but there is no auth or data-store risk.

Overview
Ports normalized cross-source memory deduplication and replace-not-append injection into the Python OpenAI, Agent Framework, Cartesia, and Pipecat packages so they match the TypeScript SDK behavior.

Deduplication uses request-local keys: strip optional [YYYY-MM-DD] prefixes, normalize whitespace, and compare with casefold, with priority static → dynamic → search. In query mode, profile static/dynamic are excluded from dedup input so facts that only appear in search (or overlap profile) are not dropped before formatting.

Injection no longer appends memory text every turn. OpenAI and Agent Framework middleware strip prior owned <supermemory context="user-memories" readonly> blocks and replace them once per request while keeping the caller’s system instructions; extra system messages lose stale blocks only. New helpers (strip/replace/wrap) live in each package’s utils.

Tests cover normalized fact variants, query-mode search retention, and stale block replacement.

Reviewed by Cursor Bugbot for commit 42f308b. Bugbot is set up for automated code reviews on this repo. Configure here.

Port the normalized, priority-ordered (static > dynamic > search) profile
deduplication into the Python SDKs, injecting one owned memory block per
request that replaces the prior block rather than accumulating. Dedup is
request-local (no shared state), so it stays correct under concurrency.

Covers OpenAI, Agent Framework (middleware + context provider), Cartesia,
and Pipecat.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@claude

claude Bot commented Aug 18, 2026

Copy link
Copy Markdown

Claude finished @Dhravya's task in 1m 45s —— View job


Code Review Complete

  • Fetching PR diff and existing comments
  • Analyzing changes for bugs, security issues, and logic errors
  • Submitting review

Result: No issues found. The implementation is solid and consistent across all four Python SDKs.

Key points reviewed:

  • Normalized fact deduplication (date stripping, whitespace normalization, case-folding)
  • Memory injection replacement with proper regex handling
  • Mode-aware filtering for query vs profile modes
  • Multi-system-message handling to prevent accumulation
  • Test coverage for new functionality

Score: 10/10 — Ready to ship.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Overview: Ports SDK-level memory deduplication to Python SDKs (OpenAI, Agent Framework, Cartesia, Pipecat), adding normalized fact comparison and request-local context replacement.

Issues found: None — this looks good to ship.

The implementation is consistent across all four Python SDKs:

  • Normalized deduplication (normalize_fact/fact_key): Correctly strips date prefixes [YYYY-MM-DD], normalizes whitespace, and case-folds for comparison. This handles the "User likes Python" vs " user likes python " vs "[2026-08-10] USER LIKES PYTHON" variations correctly.

  • Memory injection replacement: The regex pattern with re.DOTALL and non-greedy .*? properly matches <supermemory> blocks across newlines. The strip_memory_injectionreplace_memory_injection flow correctly replaces prior SDK blocks while preserving caller instructions.

  • Mode-aware filtering: In "query" mode, passing empty arrays for static/dynamic to deduplicate_memories is intentional — search results shouldn't be deduplicated against profile data when the mode only wants search results.

  • Multi-system-message handling: The injection logic correctly injects into the first system message and strips from subsequent ones, preventing accumulation.

  • Test coverage: Good coverage for normalized deduplication variants and mode-specific behavior.

Score: 10/10

Dhravya commented Aug 18, 2026

Copy link
Copy Markdown
Member Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more


How to use the Graphite Merge Queue

Add the label Main to this PR to add it to the merge queue.

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 42f308b. Configure here.

different Agent Framework providers.
"""
messages = context.messages
memory_text = f"\n\n{wrap_memory_injection(memories)}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale memory blocks not cleared

Medium Severity

_inject_memories now replaces owned &lt;supermemory&gt; blocks, but process only calls it when memories is truthy. On a later turn with an empty retrieval, prior SDK blocks stay in context.messages, so stale memory context keeps going to the model. The OpenAI path in this same PR still runs replace/strip when a system message exists even if new memories are empty.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 42f308b. Configure here.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
supermemory-app 42f308b Commit Preview URL

Branch Preview URL
Aug 18 2026, 03:20 PM

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
supermemory-mcp 42f308b Aug 18 2026, 03:19 PM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants