diff --git a/docker/requirements.txt b/docker/requirements.txt index 873cb4d2..d3268eda 100644 --- a/docker/requirements.txt +++ b/docker/requirements.txt @@ -159,3 +159,4 @@ websockets==15.0.1 xlrd==2.0.2 xlsxwriter==3.2.5 prometheus-client==0.23.1 +pymilvus==2.5.12 diff --git a/poetry.lock b/poetry.lock index e5e3bc1b..40d0f621 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. [[package]] name = "absl-py" @@ -6421,4 +6421,4 @@ tree-mem = ["neo4j", "schedule"] [metadata] lock-version = "2.1" python-versions = ">=3.10,<4.0" -content-hash = "a98b5ddffb4c031342ef1314a93666460ce0903e207bc79d23478b80a99b7f40" +content-hash = "95e737a53fed62215bcb523c162e19ed67ffc745e27fa081bc3da5e356eba086" diff --git a/pyproject.toml b/pyproject.toml index 7efd77d8..9a8db269 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -91,7 +91,7 @@ mem-reader = [ # PreferenceTextMemory pref-mem = [ - "pymilvus (>=2.6.1,<3.0.0)", # Milvus Vector DB + "pymilvus (>=2.5.12,<3.0.0)", # Milvus Vector DB "datasketch (>=1.6.5,<2.0.0)", # MinHash library ] diff --git a/src/memos/api/handlers/memory_handler.py b/src/memos/api/handlers/memory_handler.py index f0f3f39b..83f51428 100644 --- a/src/memos/api/handlers/memory_handler.py +++ b/src/memos/api/handlers/memory_handler.py @@ -180,22 +180,51 @@ def handle_get_memories( return GetMemoryResponse( message="Memories retrieved successfully", data={ - "text_mem": memories, - "pref_mem": preferences, + "text_mem": [{"cube_id": get_mem_req.mem_cube_id, "memories": memories}], + "pref_mem": [{"cube_id": get_mem_req.mem_cube_id, "memories": preferences}], }, ) def handle_delete_memories(delete_mem_req: DeleteMemoryRequest, naive_mem_cube: NaiveMemCube): + # Validate that only one of memory_ids, file_ids, or filter is provided + provided_params = [ + delete_mem_req.memory_ids is not None, + delete_mem_req.file_ids is not None, + delete_mem_req.filter is not None, + ] + if sum(provided_params) != 1: + return DeleteMemoryResponse( + message="Exactly one of memory_ids, file_ids, or filter must be provided", + data={"status": "failure"}, + ) + try: - naive_mem_cube.text_mem.delete(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) + if delete_mem_req.memory_ids is not None: + naive_mem_cube.text_mem.delete(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: + # TODO: Implement deletion by file_ids + # Need to find memory_ids associated with file_ids and delete them + logger.warning("Deletion by file_ids not implemented yet") + return DeleteMemoryResponse( + message="Deletion by file_ids not implemented yet", + data={"status": "failure"}, + ) + 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"}, + ) except Exception as e: logger.error(f"Failed to delete memories: {e}", exc_info=True) return DeleteMemoryResponse( message="Failed to delete memories", - data="failure", + data={"status": "failure"}, ) return DeleteMemoryResponse( message="Memories deleted successfully", diff --git a/src/memos/api/product_models.py b/src/memos/api/product_models.py index 5aa617d6..ceede3e0 100644 --- a/src/memos/api/product_models.py +++ b/src/memos/api/product_models.py @@ -690,7 +690,9 @@ class GetMemoryRequest(BaseRequest): class DeleteMemoryRequest(BaseRequest): """Request model for deleting memories.""" - memory_ids: list[str] = Field(..., description="Memory 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") class SuggestionRequest(BaseRequest): diff --git a/src/memos/memories/textual/prefer_text_memory/extractor.py b/src/memos/memories/textual/prefer_text_memory/extractor.py index cf40f109..e105500b 100644 --- a/src/memos/memories/textual/prefer_text_memory/extractor.py +++ b/src/memos/memories/textual/prefer_text_memory/extractor.py @@ -9,7 +9,11 @@ from memos.context.context import ContextThreadPoolExecutor from memos.log import get_logger from memos.mem_reader.simple_struct import detect_lang -from memos.memories.textual.item import PreferenceTextualMemoryMetadata, TextualMemoryItem +from memos.memories.textual.item import ( + PreferenceTextualMemoryMetadata, + TextualMemoryItem, + list_all_fields, +) from memos.memories.textual.prefer_text_memory.spliter import Splitter from memos.memories.textual.prefer_text_memory.utils import convert_messages_to_string from memos.templates.prefer_complete_prompt import ( @@ -114,8 +118,8 @@ def _process_single_chunk_explicit( vector_info = { "embedding": self.embedder.embed([pref["context_summary"]])[0], } - - extract_info = {**basic_info, **pref, **vector_info, **info} + user_info = {k: v for k, v in info.items() if k not in list_all_fields()} + extract_info = {**basic_info, **pref, **vector_info, **info, "info": user_info} metadata = PreferenceTextualMemoryMetadata( type=msg_type, preference_type="explicit_preference", **extract_info @@ -143,8 +147,8 @@ def _process_single_chunk_implicit( vector_info = { "embedding": self.embedder.embed([pref["context_summary"]])[0], } - - extract_info = {**basic_info, **pref, **vector_info, **info} + user_info = {k: v for k, v in info.items() if k not in list_all_fields()} + extract_info = {**basic_info, **pref, **vector_info, **info, "info": user_info} metadata = PreferenceTextualMemoryMetadata( type=msg_type, preference_type="implicit_preference", **extract_info