From 3a3cf525f3edd2210e293880dad92a8a0200022b Mon Sep 17 00:00:00 2001 From: octo-patch Date: Sun, 19 Apr 2026 11:17:47 +0800 Subject: [PATCH] fix: sync Qdrant payload on update_node in Neo4j community edition and add schedule dep MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Neo4jCommunityGraphDB.update_node only called the parent Neo4jGraphDB implementation, which updates Neo4j but not the external Qdrant payload. This caused Neo4j and Qdrant to diverge on key fields like status, tags, and memory_type — archived memories could still appear in vector searches. Fix: override update_node in Neo4jCommunityGraphDB to call the parent and then sync key fields (status, tags, memory_type, content, sources) to Qdrant via vec_db.update(). Sync errors are logged as warnings and do not abort the Neo4j update. Also add the missing schedule==1.2.2 dependency to docker/requirements.txt. The reorganizer.py uses the schedule library (already in requirements-full.txt and pyproject.toml) but it was absent from requirements.txt, causing an ImportError when Thread-3 (conflict detection / memory archiving) started. Fixes #1469 --- docker/requirements.txt | 1 + src/memos/graph_dbs/neo4j_community.py | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/docker/requirements.txt b/docker/requirements.txt index 988e64b83..afbcd4d26 100644 --- a/docker/requirements.txt +++ b/docker/requirements.txt @@ -98,6 +98,7 @@ rich-toolkit==0.15.1 rignore==0.7.6 rpds-py==0.28.0 safetensors==0.6.2 +schedule==1.2.2 scikit-learn==1.7.2 scipy==1.16.3 sentry-sdk==2.44.0 diff --git a/src/memos/graph_dbs/neo4j_community.py b/src/memos/graph_dbs/neo4j_community.py index b5c92f40a..cf0a89f81 100644 --- a/src/memos/graph_dbs/neo4j_community.py +++ b/src/memos/graph_dbs/neo4j_community.py @@ -225,6 +225,28 @@ def add_nodes_batch(self, nodes: list[dict[str, Any]], user_name: str | None = N logger.error(f"[add_nodes_batch] Failed to add nodes: {e}", exc_info=True) raise + def update_node(self, id: str, fields: dict[str, Any], user_name: str | None = None) -> None: + """ + Update node in Neo4j and sync key fields to Qdrant payload. + + The parent implementation only updates Neo4j. For the community edition, which + relies on an external vector DB (Qdrant), key status/metadata fields must also + be propagated to the Qdrant payload so that vector searches reflect the latest + node state (e.g. archived memories are excluded correctly). + """ + super().update_node(id, fields, user_name) + + sync_fields = {"status", "tags", "memory_type", "content", "sources"} + payload_updates = {k: v for k, v in fields.items() if k in sync_fields} + if payload_updates and self.vec_db: + try: + self.vec_db.update(id, VecDBItem(id=id, vector=None, payload=payload_updates)) + except Exception as e: + logger.warning( + f"[update_node] Failed to sync fields {list(payload_updates)} " + f"to Qdrant for node {id}: {e}" + ) + def get_children_with_embeddings( self, id: str, user_name: str | None = None ) -> list[dict[str, Any]]: