diff --git a/src/memos/api/handlers/chat_handler.py b/src/memos/api/handlers/chat_handler.py index 283e95ee7..732197658 100644 --- a/src/memos/api/handlers/chat_handler.py +++ b/src/memos/api/handlers/chat_handler.py @@ -421,17 +421,21 @@ def generate_chat_response() -> Generator[str, None, None]: query=chat_req.query, user_id=chat_req.user_id, readable_cube_ids=readable_cube_ids, - mode=chat_req.mode, + mode="fast", internet_search=False, - top_k=chat_req.top_k, + top_k=5, chat_history=chat_req.history, session_id=chat_req.session_id, - include_preference=chat_req.include_preference, + include_preference=False, pref_top_k=chat_req.pref_top_k, filter=chat_req.filter, + search_tool_memory=False, playground_search_goal_parser=False, ) + start_time = time.time() search_response = self.search_handler.handle_search_memories(search_req) + end_time = time.time() + self.logger.info(f"first search time: {end_time - start_time}") yield f"data: {json.dumps({'type': 'status', 'data': '1'})}\n\n" @@ -447,18 +451,9 @@ def generate_chat_response() -> Generator[str, None, None]: # Prepare reference data (first search) reference = prepare_reference_data(filtered_memories) - # get preference string - pref_string = search_response.data.get("pref_string", "") yield f"data: {json.dumps({'type': 'reference', 'data': reference})}\n\n" - # Prepare preference markdown string - if chat_req.include_preference: - pref_list = search_response.data.get("pref_mem") or [] - pref_memories = pref_list[0].get("memories", []) if pref_list else [] - pref_md_string = self._build_pref_md_string_for_playground(pref_memories) - yield f"data: {json.dumps({'type': 'pref_md_string', 'data': pref_md_string})}\n\n" - # parse goal for internet search searcher = self.dependencies.searcher parsed_goal = searcher.task_goal_parser.parse( @@ -487,17 +482,22 @@ def generate_chat_response() -> Generator[str, None, None]: or chat_req.query + (f"{parsed_goal.tags}" if parsed_goal.tags else ""), user_id=chat_req.user_id, readable_cube_ids=readable_cube_ids, - mode=chat_req.mode, + mode="fast", internet_search=chat_req.internet_search, top_k=chat_req.top_k, chat_history=chat_req.history, session_id=chat_req.session_id, - include_preference=False, + include_preference=chat_req.include_preference, + pref_top_k=chat_req.pref_top_k, filter=chat_req.filter, search_memory_type="All", + search_tool_memory=False, playground_search_goal_parser=False, ) + start_time = time.time() search_response = self.search_handler.handle_search_memories(search_req) + end_time = time.time() + self.logger.info(f"second search time: {end_time - start_time}") # Extract memories from search results (second search) memories_list = [] @@ -516,12 +516,19 @@ def generate_chat_response() -> Generator[str, None, None]: # Prepare remain reference data (second search) reference = prepare_reference_data(filtered_memories) + # get preference string + pref_string = search_response.data.get("pref_string", "") # get internet reference internet_reference = self._get_internet_reference( search_response.data.get("text_mem")[0]["memories"] ) - yield f"data: {json.dumps({'type': 'reference', 'data': reference})}\n\n" + # Prepare preference markdown string + if chat_req.include_preference: + pref_list = search_response.data.get("pref_mem") or [] + pref_memories = pref_list[0].get("memories", []) if pref_list else [] + pref_md_string = self._build_pref_md_string_for_playground(pref_memories) + yield f"data: {json.dumps({'type': 'pref_md_string', 'data': pref_md_string})}\n\n" # Step 2: Build system prompt with memories system_prompt = self._build_enhance_system_prompt( diff --git a/src/memos/mem_reader/read_multi_modal/system_parser.py b/src/memos/mem_reader/read_multi_modal/system_parser.py index 3f467d649..2e856365a 100644 --- a/src/memos/mem_reader/read_multi_modal/system_parser.py +++ b/src/memos/mem_reader/read_multi_modal/system_parser.py @@ -1,5 +1,6 @@ """Parser for system messages.""" +import ast import json import re import uuid @@ -137,8 +138,14 @@ def parse_fine( tool_schema = json.loads(content) assert isinstance(tool_schema, list), "Tool schema must be a list[dict]" except json.JSONDecodeError: - logger.warning(f"[SystemParser] Failed to parse tool schema: {content}") - return [] + try: + tool_schema = ast.literal_eval(content) + assert isinstance(tool_schema, list), "Tool schema must be a list[dict]" + except (ValueError, SyntaxError, AssertionError): + logger.warning( + f"[SystemParser] Failed to parse tool schema with both JSON and ast.literal_eval: {content}" + ) + return [] except AssertionError: logger.warning(f"[SystemParser] Tool schema must be a list[dict]: {content}") return [] diff --git a/src/memos/memories/textual/tree.py b/src/memos/memories/textual/tree.py index b4b1c0f23..7f022b439 100644 --- a/src/memos/memories/textual/tree.py +++ b/src/memos/memories/textual/tree.py @@ -343,6 +343,17 @@ def delete_all(self) -> None: logger.error(f"An error occurred while deleting all memories: {e}") raise + def delete_by_filter( + self, + writable_cube_ids: list[str], + file_ids: list[str] | None = None, + filter: dict | None = None, + ) -> None: + """Delete memories by filter.""" + self.graph_store.delete_node_by_prams( + writable_cube_ids=writable_cube_ids, file_ids=file_ids, filter=filter + ) + def load(self, dir: str) -> None: try: memory_file = os.path.join(dir, self.config.memory_filename) diff --git a/src/memos/memories/textual/tree_text_memory/retrieve/searcher.py b/src/memos/memories/textual/tree_text_memory/retrieve/searcher.py index 4225ed99b..fa91bd4f8 100644 --- a/src/memos/memories/textual/tree_text_memory/retrieve/searcher.py +++ b/src/memos/memories/textual/tree_text_memory/retrieve/searcher.py @@ -701,15 +701,35 @@ def _sort_and_trim( """Sort results by score and trim to top_k""" final_items = [] if search_tool_memory: - tool_results = [ + tool_schema_results = [ (item, score) for item, score in results - if item.metadata.memory_type in ["ToolSchemaMemory", "ToolTrajectoryMemory"] + if item.metadata.memory_type == "ToolSchemaMemory" ] - sorted_tool_results = sorted(tool_results, key=lambda pair: pair[1], reverse=True)[ - :tool_mem_top_k + sorted_tool_schema_results = sorted( + tool_schema_results, key=lambda pair: pair[1], reverse=True + )[:tool_mem_top_k] + for item, score in sorted_tool_schema_results: + if plugin and round(score, 2) == 0.00: + continue + meta_data = item.metadata.model_dump() + meta_data["relativity"] = score + final_items.append( + TextualMemoryItem( + id=item.id, + memory=item.memory, + metadata=SearchedTreeNodeTextualMemoryMetadata(**meta_data), + ) + ) + tool_trajectory_results = [ + (item, score) + for item, score in results + if item.metadata.memory_type == "ToolTrajectoryMemory" ] - for item, score in sorted_tool_results: + sorted_tool_trajectory_results = sorted( + tool_trajectory_results, key=lambda pair: pair[1], reverse=True + )[:tool_mem_top_k] + for item, score in sorted_tool_trajectory_results: if plugin and round(score, 2) == 0.00: continue meta_data = item.metadata.model_dump() diff --git a/src/memos/multi_mem_cube/single_cube.py b/src/memos/multi_mem_cube/single_cube.py index 4ae0c207e..f0157952b 100644 --- a/src/memos/multi_mem_cube/single_cube.py +++ b/src/memos/multi_mem_cube/single_cube.py @@ -30,6 +30,7 @@ SearchMode, UserContext, ) +from memos.utils import timed logger = get_logger(__name__) @@ -198,6 +199,7 @@ def _get_search_mode(self, mode: str) -> str: """ return mode + @timed def _search_text( self, search_req: APISearchRequest, @@ -363,6 +365,7 @@ def _fine_search( return formatted_memories + @timed def _search_pref( self, search_req: APISearchRequest, @@ -429,7 +432,7 @@ def _fast_search( top_k=search_req.top_k, mode=SearchMode.FAST, manual_close_internet=not search_req.internet_search, - momory_type=search_req.search_memory_type, + memory_type=search_req.search_memory_type, search_filter=search_filter, search_priority=search_priority, info={