Skip to content

fix(api): support delete_memory by user_id/conversation_id (Fixes #1103)#1104

Open
fancyboi999 wants to merge 3 commits intoMemTensor:mainfrom
fancyboi999:fancy/issue-1103-delete-by-user-conversation
Open

fix(api): support delete_memory by user_id/conversation_id (Fixes #1103)#1104
fancyboi999 wants to merge 3 commits intoMemTensor:mainfrom
fancyboi999:fancy/issue-1103-delete-by-user-conversation

Conversation

@fancyboi999
Copy link
Contributor

Description

This PR implements fast memory cleanup for delete_memory by request-level user_id and conversation_id (alias of session_id) to address issue #1103.

Problem solved:

  • Previously, users had to get_memory first and then delete by memory_ids, which is inefficient for large datasets.
  • Under neo4j-community, filter-based delete had an implementation gap for simple dict filters (e.g. {"user_id": "..."}), which could lead to incorrect matching behavior.

Implementation approach:

  • Extend DeleteMemoryRequest with quick-delete fields: user_id, session_id, conversation_id.
  • Add alias normalization: conversation_id -> session_id.
  • In memory_handler, merge quick-delete fields into filter-mode deletion with explicit AND semantics.
  • Keep delete modes mutually exclusive (memory_ids / file_ids / filter-or-quick-delete).
  • In neo4j-community, fix simple dict filter handling in get_by_metadata and relax writable_cube_ids requirement for non-file_ids deletion.

Dependencies:

  • No new runtime dependency introduced by this code change.

Related Issue (Required): Fixes #1103

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactor (does not change functionality, e.g. code style improvements, linting)
  • Documentation update

How Has This Been Tested?

  • Unit Test
  • Test Script Or Test Steps (please provide)
  • Pipeline Automated API Test (please provide)

Test commands:

  • pytest -q tests/api/test_memory_handler_delete.py
  • pytest -q tests/api/test_server_router.py

Runtime smoke test (docker-backed env: neo4j + qdrant + redis):

  1. Start dependencies.
  2. Start API service.
  3. Verify POST /product/delete_memory with payloads:
    • {"user_id":"..."}
    • {"conversation_id":"..."}
  4. Verify target records are deleted while control records remain.

Checklist

  • I have performed a self-review of my own code | 我已自行检查了自己的代码
  • I have commented my code in hard-to-understand areas | 我已在难以理解的地方对代码进行了注释
  • I have added tests that prove my fix is effective or that my feature works | 我已添加测试以证明我的修复有效或功能正常
  • I have created related documentation issue/PR in MemOS-Docs (if applicable) | 我已在 MemOS-Docs 中创建了相关的文档 issue/PR(如果适用)
  • I have linked the issue to this PR (if applicable) | 我已将 issue 链接到此 PR(如果适用)
  • I have mentioned the person who will review this PR | 我已提及将审查此 PR 的人

Reviewer Checklist

Copilot AI review requested due to automatic review settings February 20, 2026 09:18
Copy link
Contributor

Copilot AI left a comment

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 implements fast memory cleanup functionality by adding support for deleting memories directly by user_id and conversation_id/session_id, addressing issue #1103. Previously, users had to fetch all memories first via get_memory() and then delete them one by one using memory_ids, which was inefficient for large datasets (e.g., 142 seconds to delete 793 memories).

Changes:

  • Added quick-delete fields (user_id, session_id, conversation_id) to DeleteMemoryRequest with automatic aliasing of conversation_id to session_id
  • Implemented filter merging logic in the memory handler to combine quick-delete constraints with existing filters using AND semantics for and filters and distribution for or filters
  • Fixed simple dict filter handling (e.g., {"user_id": "..."}) in neo4j-community's get_by_metadata method and made writable_cube_ids optional for non-file_ids deletion paths

Reviewed changes

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

File Description
src/memos/api/product_models.py Extended DeleteMemoryRequest with quick-delete fields and conversation_id aliasing validator
src/memos/api/handlers/memory_handler.py Added filter merging logic and validation for quick-delete parameters
src/memos/graph_dbs/neo4j_community.py Fixed simple dict filter support and relaxed writable_cube_ids requirement for filter-based deletion
tests/api/test_memory_handler_delete.py Added comprehensive unit tests for quick-delete functionality and filter merging

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

{"memory_type": "UserMemory", "user_id": "u_1"},
]
},
)
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

The test for OR filter distribution doesn't verify that pref_mem.delete_by_filter is called with the same merged filter. This is inconsistent with the other tests (e.g., test_delete_memories_quick_by_user_id lines 25-27) which verify both text_mem and pref_mem calls. The handler code on lines 512-513 does call delete_by_filter on pref_mem when it's not None, so this test should verify that behavior.

Suggested change
)
)
naive_mem_cube.pref_mem.delete_by_filter.assert_called_once_with(
filter={
"or": [
{"memory_type": "WorkingMemory", "user_id": "u_1"},
{"memory_type": "UserMemory", "user_id": "u_1"},
]
}
)

Copilot uses AI. Check for mistakes.
)
# Validate that only one of memory_ids, file_ids, or filter is provided
quick_constraints = _build_quick_delete_constraints(delete_mem_req)
has_filter_mode = delete_mem_req.filter is not None or bool(quick_constraints)
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

The validation logic doesn't properly handle the edge case where filter is set to an empty dict. If a user sends {"filter": {}}, the check delete_mem_req.filter is not None on line 480 will be True (an empty dict is not None), but when passed to the database layer, an empty filter dict would match ALL memories in the system, which is dangerous.

Consider adding validation to reject empty filter dicts, or explicitly check for non-empty filters in the has_filter_mode calculation.

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +27
def test_delete_memories_quick_by_user_id():
naive_mem_cube = _build_naive_mem_cube()
req = DeleteMemoryRequest(user_id="u_1")

resp = handle_delete_memories(req, naive_mem_cube)

assert resp.data["status"] == "success"
naive_mem_cube.text_mem.delete_by_filter.assert_called_once_with(
writable_cube_ids=None,
filter={"and": [{"user_id": "u_1"}]},
)
naive_mem_cube.pref_mem.delete_by_filter.assert_called_once_with(
filter={"and": [{"user_id": "u_1"}]}
)
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

The test suite doesn't cover the case where naive_mem_cube.pref_mem is None. In the actual handler code (line 512-513), there's a None check before calling delete_by_filter on pref_mem, but this code path isn't tested. Consider adding a test case to verify the handler works correctly when preference memory is disabled.

Copilot uses AI. Check for mistakes.
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.

feat: Reset an user's memory (quicker approach to delete all memories)

1 participant

Comments