Skip to content

Bulk delete memories by filter with dry-run default#73

Merged
imonroe merged 5 commits into
mainfrom
claude/wizardly-planck-pwvl93-i67
Jun 9, 2026
Merged

Bulk delete memories by filter with dry-run default#73
imonroe merged 5 commits into
mainfrom
claude/wizardly-planck-pwvl93-i67

Conversation

@imonroe

@imonroe imonroe commented Jun 9, 2026

Copy link
Copy Markdown
Owner

Closes #67

What

POST /api/v1/memories/delete_bulk deletes every memory matching exact-match filters (agent_id, run_id, source, confidence, review_status, optional user_id), so a bad import run or a misbehaving capture agent's writes can be undone in two curl calls instead of scripting one-by-one deletes against the 100-item list page cap:

# dry run (default)
... -d '{"source": "import:chatgpt"}'
# -> {"matched": 412, "deleted": 0, "dry_run": true, "has_more": false, "sample": [...]}

# confirm
... -d '{"source": "import:chatgpt", "confirm": true}'

Safety rails

  • Dry-run by defaultconfirm: false reports the match count and a 10-item sample; nothing is deleted until the caller re-posts with confirm: true.
  • No unscoped wipes — at least one filter besides user_id is required (422 otherwise); deleting the entire store through this endpoint is deliberately impossible.
  • Bounded — at most 1000 deletions per call (matched/deleted are per-call); has_more tells the caller to loop. This keeps request duration under proxy timeouts.
  • Partial-failure honest — if a delete fails mid-loop, the response carries "error": "delete_failed_partway" with the partial count instead of losing it to a 500; deletes are idempotent, so the caller just re-posts.
  • History-consistent — deletions go through mem0's Memory.delete per ID, never a raw vector-store filter delete, so mem0's bookkeeping doesn't drift.
  • Not exposed over MCP — a destructive filter-delete is an operator/script action, not something a connected agent should reach for (documented in both guides).

Confirmed deletions emit a structlog warning with the filter and counts, and increment a new memories_bulk_deleted_total metric.

Tests

8 new tests across tests/test_rest.py / tests/test_memory.py: unscoped-request 422 (including user_id-only), dry-run deletes nothing, confirm deletes each ID through mem0 (raw vector-store delete asserted unused), filter composition, per-call cap + has_more, 10-item sample cap, partial-failure reporting. Full suite: 175 passed, ruff clean.

Review

Adversarial review surfaced 5 candidates; 4 were fixed (defensive getattr on point IDs, partial-failure handling, matched-is-per-call doc clarification, clearer 422 wording).

https://claude.ai/code/session_01H2Dbh6kD8bseWZZEf7kGhx


Generated by Claude Code

claude added 5 commits June 9, 2026 20:33
POST /api/v1/memories/delete_bulk deletes every memory matching exact
filters (agent_id, run_id, source, confidence, review_status) so a bad
import run or a misbehaving capture agent's writes can be undone in two
calls instead of scripted one-by-one deletes against a 100-item page cap.

Safety rails: dry-run by default (count + 10-item sample, nothing
deleted) until the caller re-posts with confirm=true; at least one filter
besides user_id is required so an unscoped POST can never wipe the store;
deletions are capped at 1000 per call with has_more signalling the caller
to loop, bounding request duration under proxy timeouts. Deletions go
through Memory.delete per ID, keeping mem0's history consistent.
Deliberately not exposed as an MCP tool. Deletions are logged and counted
in a new memories_bulk_deleted_total metric.

Closes #67

https://claude.ai/code/session_01H2Dbh6kD8bseWZZEf7kGhx
@imonroe imonroe merged commit d630a62 into main Jun 9, 2026
1 check 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.

REST: bulk delete memories by filter (agent_id / run_id / source) with dry-run

2 participants