Skip to content

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

Open
Dhravya wants to merge 3 commits into
sdk-dedup/tools-tsfrom
sdk-dedup/python
Open

feat(python-sdks): SDK-level cross-source memory deduplication#1532
Dhravya wants to merge 3 commits 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.

@claude

claude Bot commented Aug 18, 2026

Copy link
Copy Markdown

Claude finished @ved015's task in 1m 7s —— View job


Reviewing PR #1532

  • Get PR diff and existing review comments
  • Review changes for bugs, security issues, and logic errors
  • Post review findings

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 Cursor Bugbot issue regarding stale memory blocks has been addressed in commit 90babae. The fix at middleware.py:279 calls _inject_memories(context, "") at the start of process(), ensuring stale SDK-owned <supermemory> blocks are always stripped before any conditional path executes.

Implementation highlights:

  • Normalized deduplication: Correctly strips [Recent] and [YYYY-MM-DD] prefixes, normalizes whitespace, and uses casefold() for case-insensitive comparison across all SDKs
  • Mode-aware filtering: In "query" mode, static/dynamic arrays are correctly passed as empty to deduplicate_memories, so search results aren't deduplicated against profile data
  • Context replacement: OpenAI SDK properly handles both string and structured content fields, and prioritizes developer role messages per the newer OpenAI API conventions
  • Tag escaping: Each SDK escapes its respective delimiter tags (<supermemory> for OpenAI/Agent Framework, <user_memories> for Cartesia/Pipecat) to prevent injection attacks
  • Edge case handling: When no user message is found, OpenAI SDK now calls _update_chat_memory_contexts(messages) to strip stale blocks before early return (lines 556, 643)
  • Test coverage: Solid coverage for normalized fact variants, query-mode behavior, and stale block replacement

Score: 10/10
|

@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

cloudflare-workers-and-pages Bot commented Aug 18, 2026

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 cb557f4 Commit Preview URL

Branch Preview URL
Aug 24 2026, 06:14 PM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 18, 2026

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 cb557f4 Aug 24 2026, 06:13 PM

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>
@ved015
ved015 force-pushed the sdk-dedup/tools-ts branch from 2fa2e0d to be97748 Compare August 24, 2026 16:21

@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 (casefold + whitespace normalization + date-prefix stripping) and request-local context replacement.

Issues found:

  1. Agent Framework stale block retention (inline comment): When memories is empty, _inject_memories is never called, so stale <supermemory> blocks from prior turns persist in the context. The OpenAI SDK correctly handles this by running strip/replace logic regardless of whether new memories exist. Cartesia and Pipecat use different patterns that avoid this issue.

What looks good:

  • Normalized deduplication is consistent across all SDKs: strips [YYYY-MM-DD] prefixes, normalizes whitespace, and uses casefold() for case-insensitive comparison
  • Priority order (static → dynamic → search) is correctly preserved
  • Mode-aware filtering in "query" mode correctly passes empty arrays for static/dynamic to avoid deduping search results against profile data
  • OpenAI SDK's multi-system-message handling correctly injects into the first and strips from subsequent ones
  • Test coverage is solid for the new normalized deduplication and mode-specific behavior

Score: 8/10

One real bug that should be addressed before merge, but the overall implementation is well-structured and consistent across the SDK ports.

@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 Cursor Bugbot issue regarding stale memory blocks has been addressed in commit 90babae. The fix at middleware.py:279 calls _inject_memories(context, "") at the start of process(), ensuring stale SDK-owned <supermemory> blocks are always stripped before any conditional path executes.

Implementation highlights:

  • Normalized deduplication: Correctly strips [Recent] and [YYYY-MM-DD] prefixes, normalizes whitespace, and uses casefold() for case-insensitive comparison across all SDKs
  • Mode-aware filtering: In "query" mode, static/dynamic arrays are correctly passed as empty to deduplicate_memories, so search results aren't deduplicated against profile data
  • Context replacement: OpenAI SDK properly handles both string and structured content fields, and prioritizes developer role messages per the newer OpenAI API conventions
  • Tag escaping: Each SDK escapes its respective delimiter tags (<supermemory> for OpenAI/Agent Framework, <user_memories> for Cartesia/Pipecat) to prevent injection attacks
  • Test coverage: Solid coverage for normalized fact variants, query-mode behavior, and stale block replacement

Score: 10/10

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