Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions backend/database/vector_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -611,14 +611,26 @@ def delete_conversation_vectors_batch(uid: str, conversation_ids: List[str]):
logger.info(f'delete_conversation_vectors_batch count={len(vector_ids)}')


def delete_memory_vectors_batch(uid: str, memory_ids: List[str]):
"""Delete a user's memory vectors (ns2) in one batched, chunked call."""
def delete_memory_vectors_batch(uid: str, memory_ids: List[str]) -> int:
"""Delete a user's memory vectors (ns2) in batched, chunked calls.
Each chunk is individually wrapped in try/except so a transient failure
on one chunk does not abandon the rest. Returns the number of vectors
successfully deleted (0 if Pinecone is not configured).
"""
if index is None:
logger.warning('Pinecone index not initialized, skipping memory vector batch delete')
return
return 0
if not memory_ids:
return
return 0
vector_ids = [f'{uid}-{mid}' for mid in memory_ids]
total_deleted = 0
for i in range(0, len(vector_ids), 1000):
index.delete(ids=vector_ids[i : i + 1000], namespace=MEMORIES_NAMESPACE)
logger.info(f'delete_memory_vectors_batch count={len(vector_ids)}')
chunk = vector_ids[i : i + 1000]
try:
index.delete(ids=chunk, namespace=MEMORIES_NAMESPACE)
total_deleted += len(chunk)
except Exception:
logger.warning(f'delete_memory_vectors_batch chunk failed uid={uid} chunk={i // 1000}')
logger.info(f'delete_memory_vectors_batch uid={uid} total_deleted={total_deleted}')
return total_deleted
19 changes: 19 additions & 0 deletions backend/routers/memories.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import database.memories as memories_db
from database.vector_db import (
delete_memory_vector,
delete_memory_vectors_batch,
upsert_memory_vector,
upsert_memory_vectors_batch,
)
Expand Down Expand Up @@ -187,7 +188,25 @@ def delete_memory(
def delete_memories(
uid: str = Depends(auth.with_rate_limit(auth.get_current_user_uid, "memories:delete_all")),
):
# Collect all memory IDs before Firestore delete so we can also purge
# their Pinecone vectors — otherwise orphaned vectors become search
# noise that never gets cleaned up.
memory_ids = []
offset = 0
batch_size = 1000
while True:
memories = memories_db.get_memories(uid, limit=batch_size, offset=offset, include_invalidated=True)
if not memories:
break
batch_ids = [m.get('id') for m in memories if m.get('id')]
memory_ids.extend(batch_ids)
offset += batch_size
Comment thread
AasheeshLikePanner marked this conversation as resolved.

memories_db.delete_all_memories(uid)

if memory_ids:
delete_memory_vectors_batch(uid, memory_ids)

return {'status': 'ok'}


Expand Down
Loading