Skip to content

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

Merged
graphite-app[bot] merged 1 commit into
mainfrom
sdk-dedup/python
Sep 1, 2026
Merged

feat(python-sdks): SDK-level cross-source memory deduplication#1532
graphite-app[bot] merged 1 commit into
mainfrom
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 @Dhravya's task in 1m 35s —— View job


Review Complete

  • Fetch PR diff and existing review comments
  • Analyze changes across Python SDKs
  • Check for bugs, security issues, and logic errors
  • Post review summary

Overview: Ports normalized memory deduplication (casefold + whitespace normalization + date-prefix stripping) and request-local context replacement into Python OpenAI, Agent Framework, Cartesia, and Pipecat SDKs.

Issues found: None — this looks good to ship.

The Cursor Bugbot issue regarding stale memory blocks has been addressed:

  • Agent Framework (middleware.py:279): _inject_memories(context, "") is now called at the start of process(), ensuring stale SDK-owned <supermemory> blocks are always stripped before any conditional path executes
  • OpenAI SDK (middleware.py:559): When no user message is found, _update_chat_memory_contexts(messages) is still called without memories, which strips prior blocks

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


|

@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

@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 03773c4 Commit Preview URL

Branch Preview URL
Sep 01 2026, 06:22 AM

@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 03773c4 Sep 01 2026, 06:20 AM

@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

ved015
ved015 previously approved these changes Aug 24, 2026
@graphite-app

graphite-app Bot commented Sep 1, 2026

Copy link
Copy Markdown

Merge activity

@graphite-app
graphite-app Bot force-pushed the sdk-dedup/tools-ts branch from ec6c159 to d035932 Compare September 1, 2026 06:03
graphite-app Bot pushed a commit that referenced this pull request Sep 1, 2026
## 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](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!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.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 42f308b. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## 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](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!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.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 42f308b. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
@graphite-app
graphite-app Bot force-pushed the sdk-dedup/tools-ts branch from d035932 to d0f53b0 Compare September 1, 2026 06:12
@graphite-app
graphite-app Bot changed the base branch from sdk-dedup/tools-ts to main September 1, 2026 06:20
@graphite-app
graphite-app Bot dismissed ved015’s stale review September 1, 2026 06:20

The base branch was changed.

@graphite-app
graphite-app Bot merged commit 03773c4 into main Sep 1, 2026
14 of 15 checks passed
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