From a384d1cd6482af2cf552bf18bb5fd791d4a0c65a Mon Sep 17 00:00:00 2001 From: "yuan.wang" Date: Thu, 25 Dec 2025 20:51:56 +0800 Subject: [PATCH 1/3] add get_user_names_by_memory_ids api --- src/memos/api/product_models.py | 13 ++++++++++++ src/memos/api/routers/server_router.py | 28 ++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/src/memos/api/product_models.py b/src/memos/api/product_models.py index adcb68a96..3c7070ec9 100644 --- a/src/memos/api/product_models.py +++ b/src/memos/api/product_models.py @@ -1168,3 +1168,16 @@ class AllStatusResponse(BaseResponse[AllStatusResponseData]): """Response model for full scheduler status operations.""" message: str = "Scheduler status summary retrieved successfully" + + +# ─── Internal API Endpoints Models (for internal use) ─────────────────────────────────────────────────── + + +class GetUserNamesByMemoryIdsRequest(BaseRequest): + """Request model for getting user names by memory ids.""" + + memory_ids: list[str] = Field(..., description="Memory IDs") + + +class GetUserNamesByMemoryIdsResponse(BaseResponse[dict[str, list[str]]]): + """Response model for getting user names by memory ids.""" diff --git a/src/memos/api/routers/server_router.py b/src/memos/api/routers/server_router.py index e87e006dd..07c42bbb2 100644 --- a/src/memos/api/routers/server_router.py +++ b/src/memos/api/routers/server_router.py @@ -36,6 +36,8 @@ GetMemoryPlaygroundRequest, GetMemoryRequest, GetMemoryResponse, + GetUserNamesByMemoryIdsRequest, + GetUserNamesByMemoryIdsResponse, MemoryResponse, SearchResponse, StatusResponse, @@ -43,6 +45,7 @@ SuggestionResponse, TaskQueueResponse, ) +from memos.graph_dbs.polardb import PolarDBGraphDB from memos.log import get_logger from memos.mem_scheduler.base_scheduler import BaseScheduler from memos.mem_scheduler.utils.status_tracker import TaskStatusTracker @@ -84,6 +87,7 @@ redis_client = components["redis_client"] status_tracker = TaskStatusTracker(redis_client=redis_client) embedder = components["embedder"] +graph_db = components["graph_db"] # ============================================================================= @@ -329,3 +333,27 @@ def feedback_memories(feedback_req: APIFeedbackRequest): This endpoint uses the class-based FeedbackHandler for better code organization. """ return feedback_handler.handle_feedback_memories(feedback_req) + + +# ============================================================================= +# Other API Endpoints (for internal use) +# ============================================================================= + + +@router.get( + "/get_user_names_by_memory_ids", + summary="Get user names by memory ids", + response_model=GetUserNamesByMemoryIdsResponse, +) +def get_user_names_by_memory_ids(memory_ids: GetUserNamesByMemoryIdsRequest): + """Get user names by memory ids.""" + if not isinstance(graph_db, PolarDBGraphDB): + raise HTTPException( + status_code=400, + detail=( + "graph_db must be an instance of PolarDBGraphDB to use " + "get_user_names_by_memory_ids" + f"current graph_db is: {graph_db.__class__.__name__}" + ), + ) + return graph_db.get_user_names_by_memory_ids(memory_ids=memory_ids) From 6a13c1c43d73e62a79faf62d18381859bdf24b7b Mon Sep 17 00:00:00 2001 From: "yuan.wang" Date: Sun, 4 Jan 2026 12:00:24 +0800 Subject: [PATCH 2/3] modify delete api --- src/memos/api/handlers/memory_handler.py | 13 ++++--------- src/memos/api/product_models.py | 2 +- src/memos/api/routers/server_router.py | 2 +- src/memos/memories/textual/preference.py | 9 +++++++++ src/memos/memories/textual/tree.py | 9 ++++++++- src/memos/vec_dbs/milvus.py | 8 ++++++++ 6 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/memos/api/handlers/memory_handler.py b/src/memos/api/handlers/memory_handler.py index a744e16e2..ef829d757 100644 --- a/src/memos/api/handlers/memory_handler.py +++ b/src/memos/api/handlers/memory_handler.py @@ -246,8 +246,7 @@ def handle_delete_memories(delete_mem_req: DeleteMemoryRequest, naive_mem_cube: try: if delete_mem_req.memory_ids is not None: - for cube_id in delete_mem_req.writable_cube_ids: - naive_mem_cube.text_mem.delete(delete_mem_req.memory_ids, user_name=cube_id) + naive_mem_cube.text_mem.delete_by_memory_ids(delete_mem_req.memory_ids) if naive_mem_cube.pref_mem is not None: naive_mem_cube.pref_mem.delete(delete_mem_req.memory_ids) elif delete_mem_req.file_ids is not None: @@ -255,13 +254,9 @@ def handle_delete_memories(delete_mem_req: DeleteMemoryRequest, naive_mem_cube: writable_cube_ids=delete_mem_req.writable_cube_ids, file_ids=delete_mem_req.file_ids ) elif delete_mem_req.filter is not None: - # TODO: Implement deletion by filter - # Need to find memories matching filter and delete them - logger.warning("Deletion by filter not implemented yet") - return DeleteMemoryResponse( - message="Deletion by filter not implemented yet", - data={"status": "failure"}, - ) + naive_mem_cube.text_mem.delete_by_filter(filter=delete_mem_req.filter) + if naive_mem_cube.pref_mem is not None: + naive_mem_cube.pref_mem.delete_by_filter(filter=delete_mem_req.filter) except Exception as e: logger.error(f"Failed to delete memories: {e}", exc_info=True) return DeleteMemoryResponse( diff --git a/src/memos/api/product_models.py b/src/memos/api/product_models.py index 25e0d809d..c52d9e8d2 100644 --- a/src/memos/api/product_models.py +++ b/src/memos/api/product_models.py @@ -784,7 +784,7 @@ class GetMemoryRequest(BaseRequest): class DeleteMemoryRequest(BaseRequest): """Request model for deleting memories.""" - writable_cube_ids: list[str] = Field(..., description="Writable cube IDs") + writable_cube_ids: list[str] = Field(None, description="Writable cube IDs") memory_ids: list[str] | None = Field(None, description="Memory IDs") file_ids: list[str] | None = Field(None, description="File IDs") filter: dict[str, Any] | None = Field(None, description="Filter for the memory") diff --git a/src/memos/api/routers/server_router.py b/src/memos/api/routers/server_router.py index 07c42bbb2..c3b05e823 100644 --- a/src/memos/api/routers/server_router.py +++ b/src/memos/api/routers/server_router.py @@ -340,7 +340,7 @@ def feedback_memories(feedback_req: APIFeedbackRequest): # ============================================================================= -@router.get( +@router.post( "/get_user_names_by_memory_ids", summary="Get user names by memory ids", response_model=GetUserNamesByMemoryIdsResponse, diff --git a/src/memos/memories/textual/preference.py b/src/memos/memories/textual/preference.py index cb4f00735..a34315918 100644 --- a/src/memos/memories/textual/preference.py +++ b/src/memos/memories/textual/preference.py @@ -314,6 +314,15 @@ def delete(self, memory_ids: list[str]) -> None: for collection_name in collection_list: self.vector_db.delete(collection_name, memory_ids) + def delete_by_filter(self, filter: dict[str, Any]) -> None: + """Delete memories by filter. + Args: + filter (dict[str, Any]): Filter criteria. + """ + collection_list = self.vector_db.config.collection_name + for collection_name in collection_list: + self.vector_db.delete_by_filter(collection_name=collection_name, filter=filter) + def delete_with_collection_name(self, collection_name: str, memory_ids: list[str]) -> None: """Delete memories by their IDs and collection name. Args: diff --git a/src/memos/memories/textual/tree.py b/src/memos/memories/textual/tree.py index e576c0ea9..c486e6cf6 100644 --- a/src/memos/memories/textual/tree.py +++ b/src/memos/memories/textual/tree.py @@ -347,6 +347,13 @@ def delete(self, memory_ids: list[str], user_name: str | None = None) -> None: except Exception as e: logger.warning(f"TreeTextMemory.delete_hard: failed to delete {mid}: {e}") + def delete_by_memory_ids(self, memory_ids: list[str]) -> None: + """Delete memories by memory_ids.""" + try: + self.graph_store.delete_node_by_prams(memory_ids=memory_ids) + except Exception as e: + logger.error(f"An error occurred while deleting memories by memory_ids: {e}") + def delete_all(self) -> None: """Delete all memories and their relationships from the graph store.""" try: @@ -358,7 +365,7 @@ def delete_all(self) -> None: def delete_by_filter( self, - writable_cube_ids: list[str], + writable_cube_ids: list[str] | None = None, file_ids: list[str] | None = None, filter: dict | None = None, ) -> None: diff --git a/src/memos/vec_dbs/milvus.py b/src/memos/vec_dbs/milvus.py index ecbca5815..5dacf0499 100644 --- a/src/memos/vec_dbs/milvus.py +++ b/src/memos/vec_dbs/milvus.py @@ -646,3 +646,11 @@ def delete(self, collection_name: str, ids: list[str]) -> None: collection_name=collection_name, ids=ids, ) + + def delete_by_filter(self, collection_name: str, filter: dict[str, Any]) -> None: + """Delete items from the vector database by filter.""" + expr = self._dict_to_expr(filter) if filter else "" + self.client.delete( + collection_name=collection_name, + filter=expr, + ) From 6376e390deb95e542b09a962ce9429f6f6d56e1c Mon Sep 17 00:00:00 2001 From: "yuan.wang" Date: Sun, 4 Jan 2026 15:36:38 +0800 Subject: [PATCH 3/3] modify bug --- src/memos/api/product_models.py | 2 +- src/memos/api/routers/server_router.py | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/memos/api/product_models.py b/src/memos/api/product_models.py index c52d9e8d2..f0a4e333b 100644 --- a/src/memos/api/product_models.py +++ b/src/memos/api/product_models.py @@ -1195,5 +1195,5 @@ class GetUserNamesByMemoryIdsRequest(BaseRequest): memory_ids: list[str] = Field(..., description="Memory IDs") -class GetUserNamesByMemoryIdsResponse(BaseResponse[dict[str, list[str]]]): +class GetUserNamesByMemoryIdsResponse(BaseResponse[dict[str, str | None]]): """Response model for getting user names by memory ids.""" diff --git a/src/memos/api/routers/server_router.py b/src/memos/api/routers/server_router.py index c3b05e823..7c0f3ea8f 100644 --- a/src/memos/api/routers/server_router.py +++ b/src/memos/api/routers/server_router.py @@ -345,7 +345,7 @@ def feedback_memories(feedback_req: APIFeedbackRequest): summary="Get user names by memory ids", response_model=GetUserNamesByMemoryIdsResponse, ) -def get_user_names_by_memory_ids(memory_ids: GetUserNamesByMemoryIdsRequest): +def get_user_names_by_memory_ids(request: GetUserNamesByMemoryIdsRequest): """Get user names by memory ids.""" if not isinstance(graph_db, PolarDBGraphDB): raise HTTPException( @@ -356,4 +356,9 @@ def get_user_names_by_memory_ids(memory_ids: GetUserNamesByMemoryIdsRequest): f"current graph_db is: {graph_db.__class__.__name__}" ), ) - return graph_db.get_user_names_by_memory_ids(memory_ids=memory_ids) + result = graph_db.get_user_names_by_memory_ids(memory_ids=request.memory_ids) + return GetUserNamesByMemoryIdsResponse( + code=200, + message="Successfully", + data=result, + )