From 4c3a7169776846a0836d20b75766d39fe8acb528 Mon Sep 17 00:00:00 2001 From: Exploreunive Date: Thu, 19 Mar 2026 20:47:32 +0800 Subject: [PATCH 1/5] fix: search API now supports multiple memory_types instead of only using the first one Previously, the search API silently ignored all memory_types except the first one. Now all specified memory_types are searched and results are merged. This fixes the issue where passing memory_types=episodic_memory,foresight would only search episodic_memory and silently ignore foresight. Fixes #78 --- src/agentic_layer/memory_manager.py | 308 +++++++++++++++------------- 1 file changed, 166 insertions(+), 142 deletions(-) diff --git a/src/agentic_layer/memory_manager.py b/src/agentic_layer/memory_manager.py index df42b05a..da7f397d 100644 --- a/src/agentic_layer/memory_manager.py +++ b/src/agentic_layer/memory_manager.py @@ -298,8 +298,8 @@ async def retrieve_mem_keyword( ) -> RetrieveMemResponse: """Keyword-based memory retrieval""" start_time = time.perf_counter() - memory_type = ( - retrieve_mem_request.memory_types[0].value + memory_type_label = ( + ','.join([mt.value for mt in retrieve_mem_request.memory_types]) if retrieve_mem_request.memory_types else 'unknown' ) @@ -312,7 +312,7 @@ async def retrieve_mem_keyword( status = 'success' if hits else 'empty_result' record_retrieve_request( - memory_type=memory_type, + memory_type=memory_type_label, retrieve_method=RetrieveMethod.KEYWORD.value, status=status, duration_seconds=duration, @@ -323,7 +323,7 @@ async def retrieve_mem_keyword( except Exception as e: duration = time.perf_counter() - start_time record_retrieve_request( - memory_type=memory_type, + memory_type=memory_type_label, retrieve_method=RetrieveMethod.KEYWORD.value, status='error', duration_seconds=duration, @@ -337,11 +337,12 @@ async def get_keyword_search_results( retrieve_mem_request: 'RetrieveMemRequest', retrieve_method: str = RetrieveMethod.KEYWORD.value, ) -> List[Dict[str, Any]]: - """Keyword search with stage-level metrics""" + """Keyword search with stage-level metrics, supports multiple memory_types""" stage_start = time.perf_counter() - memory_type = ( - retrieve_mem_request.memory_types[0].value - if retrieve_mem_request.memory_types + memory_types = retrieve_mem_request.memory_types or [] + memory_type_label = ( + ','.join([mt.value for mt in memory_types]) + if memory_types else 'unknown' ) @@ -356,7 +357,6 @@ async def get_keyword_search_results( group_id = retrieve_mem_request.group_id start_time = retrieve_mem_request.start_time end_time = retrieve_mem_request.end_time - memory_types = retrieve_mem_request.memory_types # Convert query string to search word list # Use jieba for search mode word segmentation, then filter stopwords @@ -375,47 +375,49 @@ async def get_keyword_search_results( if end_time is not None: date_range["lte"] = end_time - mem_type = memory_types[0] - - repo_class = ES_REPO_MAP.get(mem_type) - if not repo_class: - logger.warning(f"Unsupported memory_type: {mem_type}") - return [] + # Search across ALL memory_types, not just the first one + all_results = [] + for mem_type in memory_types: + repo_class = ES_REPO_MAP.get(mem_type) + if not repo_class: + logger.warning(f"Unsupported memory_type: {mem_type}, skipping") + continue - es_repo = get_bean_by_type(repo_class) - logger.debug(f"Using {repo_class.__name__} for {mem_type}") + es_repo = get_bean_by_type(repo_class) + logger.debug(f"Using {repo_class.__name__} for {mem_type}") - results = await es_repo.multi_search( - query=query_words, - user_id=user_id, - group_id=group_id, - size=top_k, - from_=0, - date_range=date_range, - ) + results = await es_repo.multi_search( + query=query_words, + user_id=user_id, + group_id=group_id, + size=top_k, + from_=0, + date_range=date_range, + ) - # Mark memory_type, search_source, and unified score - if results: - for r in results: - r['memory_type'] = mem_type.value - r['_search_source'] = RetrieveMethod.KEYWORD.value - r['id'] = r.get('_id', '') # Unify ES '_id' to 'id' - r['score'] = r.get('_score', 0.0) # Unified score field + # Mark memory_type, search_source, and unified score + if results: + for r in results: + r['memory_type'] = mem_type.value + r['_search_source'] = RetrieveMethod.KEYWORD.value + r['id'] = r.get('_id', '') # Unify ES '_id' to 'id' + r['score'] = r.get('_score', 0.0) # Unified score field + all_results.extend(results) # Record stage metrics record_retrieve_stage( retrieve_method=retrieve_method, stage=RetrieveMethod.KEYWORD.value, - memory_type=memory_type, + memory_type=memory_type_label, duration_seconds=time.perf_counter() - stage_start, ) - return results or [] + return all_results except Exception as e: record_retrieve_stage( retrieve_method=retrieve_method, stage=RetrieveMethod.KEYWORD.value, - memory_type=memory_type, + memory_type=memory_type_label, duration_seconds=time.perf_counter() - stage_start, ) record_retrieve_error( @@ -433,8 +435,8 @@ async def retrieve_mem_vector( ) -> RetrieveMemResponse: """Vector-based memory retrieval""" start_time = time.perf_counter() - memory_type = ( - retrieve_mem_request.memory_types[0].value + memory_type_label = ( + ','.join([mt.value for mt in retrieve_mem_request.memory_types]) if retrieve_mem_request.memory_types else 'unknown' ) @@ -447,7 +449,7 @@ async def retrieve_mem_vector( status = 'success' if hits else 'empty_result' record_retrieve_request( - memory_type=memory_type, + memory_type=memory_type_label, retrieve_method=RetrieveMethod.VECTOR.value, status=status, duration_seconds=duration, @@ -458,7 +460,7 @@ async def retrieve_mem_vector( except Exception as e: duration = time.perf_counter() - start_time record_retrieve_request( - memory_type=memory_type, + memory_type=memory_type_label, retrieve_method=RetrieveMethod.VECTOR.value, status='error', duration_seconds=duration, @@ -472,12 +474,14 @@ async def get_vector_search_results( retrieve_mem_request: 'RetrieveMemRequest', retrieve_method: str = RetrieveMethod.VECTOR.value, ) -> List[Dict[str, Any]]: - """Vector search with stage-level metrics (embedding + milvus_search)""" - memory_type = ( - retrieve_mem_request.memory_types[0].value - if retrieve_mem_request.memory_types + """Vector search with stage-level metrics (embedding + milvus_search), supports multiple memory_types""" + memory_types = retrieve_mem_request.memory_types or [] + memory_type_label = ( + ','.join([mt.value for mt in memory_types]) + if memory_types else 'unknown' ) + stage_start = time.perf_counter() try: # Get parameters from Request @@ -497,7 +501,6 @@ async def get_vector_search_results( top_k = retrieve_mem_request.top_k start_time = retrieve_mem_request.start_time end_time = retrieve_mem_request.end_time - mem_type = retrieve_mem_request.memory_types[0] logger.debug( f"retrieve_mem_vector called with query: {query}, user_id: {user_id}, group_id: {group_id}, top_k: {top_k}" @@ -506,7 +509,7 @@ async def get_vector_search_results( # Get vectorization service vectorize_service = get_vectorize_service() - # Convert query text to vector (embedding stage) + # Convert query text to vector (embedding stage) - only once for all types logger.debug(f"Starting to vectorize query text: {query}") embedding_start = time.perf_counter() query_vector = await vectorize_service.get_embedding(query) @@ -514,100 +517,108 @@ async def get_vector_search_results( record_retrieve_stage( retrieve_method=retrieve_method, stage='embedding', - memory_type=memory_type, + memory_type=memory_type_label, duration_seconds=time.perf_counter() - embedding_start, ) logger.debug( f"Query text vectorization completed, vector dimension: {len(query_vector_list)}" ) - # Select Milvus repository based on memory type - match mem_type: - case MemoryType.FORESIGHT: - milvus_repo = get_bean_by_type(ForesightMilvusRepository) - case MemoryType.EVENT_LOG: - milvus_repo = get_bean_by_type(EventLogMilvusRepository) - case MemoryType.EPISODIC_MEMORY: - milvus_repo = get_bean_by_type(EpisodicMemoryMilvusRepository) - case _: - raise ValueError(f"Unsupported memory type: {mem_type}") - - # Handle time range filter conditions - start_time_dt = None - end_time_dt = None - current_time_dt = None - - if start_time is not None: - start_time_dt = ( - from_iso_format(start_time) - if isinstance(start_time, str) - else start_time - ) + # Map memory type to Milvus repository + MILVUS_REPO_MAP = { + MemoryType.FORESIGHT: ForesightMilvusRepository, + MemoryType.EVENT_LOG: EventLogMilvusRepository, + MemoryType.EPISODIC_MEMORY: EpisodicMemoryMilvusRepository, + } + + # Search across ALL memory_types, not just the first one + all_search_results = [] + for mem_type in memory_types: + milvus_repo_class = MILVUS_REPO_MAP.get(mem_type) + if not milvus_repo_class: + logger.warning(f"Unsupported memory type for vector search: {mem_type}, skipping") + continue + + milvus_repo = get_bean_by_type(milvus_repo_class) + + # Handle time range filter conditions + start_time_dt = None + end_time_dt = None + current_time_dt = None + + if start_time is not None: + start_time_dt = ( + from_iso_format(start_time) + if isinstance(start_time, str) + else start_time + ) - if end_time is not None: - if isinstance(end_time, str): - end_time_dt = from_iso_format(end_time) - # If date only format, set to end of day - if len(end_time) == 10: - end_time_dt = end_time_dt.replace(hour=23, minute=59, second=59) + if end_time is not None: + if isinstance(end_time, str): + end_time_dt = from_iso_format(end_time) + # If date only format, set to end of day + if len(end_time) == 10: + end_time_dt = end_time_dt.replace(hour=23, minute=59, second=59) + else: + end_time_dt = end_time + + # Handle foresight time range (only valid for foresight) + if mem_type == MemoryType.FORESIGHT: + if retrieve_mem_request.start_time: + start_time_dt = from_iso_format(retrieve_mem_request.start_time) + if retrieve_mem_request.end_time: + end_time_dt = from_iso_format(retrieve_mem_request.end_time) + if retrieve_mem_request.current_time: + current_time_dt = from_iso_format(retrieve_mem_request.current_time) + + # Call Milvus vector search (pass different parameters based on memory type) + milvus_start = time.perf_counter() + if mem_type == MemoryType.FORESIGHT: + # Foresight: supports time range and validity filtering, supports radius parameter + search_results = await milvus_repo.vector_search( + query_vector=query_vector_list, + user_id=user_id, + group_id=group_id, + start_time=start_time_dt, + end_time=end_time_dt, + current_time=current_time_dt, + limit=top_k, + score_threshold=0.0, + radius=retrieve_mem_request.radius, + ) else: - end_time_dt = end_time - - # Handle foresight time range (only valid for foresight) - if mem_type == MemoryType.FORESIGHT: - if retrieve_mem_request.start_time: - start_time_dt = from_iso_format(retrieve_mem_request.start_time) - if retrieve_mem_request.end_time: - end_time_dt = from_iso_format(retrieve_mem_request.end_time) - if retrieve_mem_request.current_time: - current_time_dt = from_iso_format(retrieve_mem_request.current_time) - - # Call Milvus vector search (pass different parameters based on memory type) - milvus_start = time.perf_counter() - if mem_type == MemoryType.FORESIGHT: - # Foresight: supports time range and validity filtering, supports radius parameter - search_results = await milvus_repo.vector_search( - query_vector=query_vector_list, - user_id=user_id, - group_id=group_id, - start_time=start_time_dt, - end_time=end_time_dt, - current_time=current_time_dt, - limit=top_k, - score_threshold=0.0, - radius=retrieve_mem_request.radius, - ) - else: - # Episodic memory and event log: use timestamp filtering, supports radius parameter - search_results = await milvus_repo.vector_search( - query_vector=query_vector_list, - user_id=user_id, - group_id=group_id, - start_time=start_time_dt, - end_time=end_time_dt, - limit=top_k, - score_threshold=0.0, - radius=retrieve_mem_request.radius, + # Episodic memory and event log: use timestamp filtering, supports radius parameter + search_results = await milvus_repo.vector_search( + query_vector=query_vector_list, + user_id=user_id, + group_id=group_id, + start_time=start_time_dt, + end_time=end_time_dt, + limit=top_k, + score_threshold=0.0, + radius=retrieve_mem_request.radius, + ) + record_retrieve_stage( + retrieve_method=retrieve_method, + stage='milvus_search', + memory_type=mem_type.value, + duration_seconds=time.perf_counter() - milvus_start, ) - record_retrieve_stage( - retrieve_method=retrieve_method, - stage='milvus_search', - memory_type=memory_type, - duration_seconds=time.perf_counter() - milvus_start, - ) - for r in search_results: - r['memory_type'] = mem_type.value - r['_search_source'] = RetrieveMethod.VECTOR.value - # Milvus already uses 'score', no need to rename + for r in search_results: + r['memory_type'] = mem_type.value + r['_search_source'] = RetrieveMethod.VECTOR.value + # Milvus already uses 'score', no need to rename + + all_search_results.extend(search_results) - return search_results + return all_search_results except Exception as e: record_retrieve_stage( retrieve_method=retrieve_method, stage=RetrieveMethod.VECTOR.value, - memory_type=memory_type, - duration_seconds=time.perf_counter() - milvus_start, + memory_type=memory_type_label, + duration_seconds=time.perf_counter() - stage_start, ) record_retrieve_error( retrieve_method=retrieve_method, @@ -615,6 +626,7 @@ async def get_vector_search_results( error_type=self._classify_retrieve_error(e), ) logger.error(f"Error in get_vector_search_results: {e}") + return [] raise # Hybrid memory retrieval @@ -624,8 +636,8 @@ async def retrieve_mem_hybrid( ) -> RetrieveMemResponse: """Hybrid memory retrieval: keyword + vector + rerank""" start_time = time.perf_counter() - memory_type = ( - retrieve_mem_request.memory_types[0].value + memory_type_label = ( + ','.join([mt.value for mt in retrieve_mem_request.memory_types]) if retrieve_mem_request.memory_types else 'unknown' ) @@ -638,7 +650,7 @@ async def retrieve_mem_hybrid( status = 'success' if hits else 'empty_result' record_retrieve_request( - memory_type=memory_type, + memory_type=memory_type_label, retrieve_method=RetrieveMethod.HYBRID.value, status=status, duration_seconds=duration, @@ -649,7 +661,7 @@ async def retrieve_mem_hybrid( except Exception as e: duration = time.perf_counter() - start_time record_retrieve_request( - memory_type=memory_type, + memory_type=memory_type_label, retrieve_method=RetrieveMethod.HYBRID.value, status='error', duration_seconds=duration, @@ -699,8 +711,10 @@ async def _search_hybrid( retrieve_method: str = RetrieveMethod.HYBRID.value, ) -> List[Dict]: """Core hybrid search: keyword + vector + rerank, returns flat list""" - memory_type = ( - request.memory_types[0].value if request.memory_types else 'unknown' + memory_type_label = ( + ','.join([mt.value for mt in request.memory_types]) + if request.memory_types + else 'unknown' ) # Run keyword and vector search concurrently kw_results, vec_results = await asyncio.gather( @@ -713,7 +727,7 @@ async def _search_hybrid( h for h in vec_results if h.get('id') not in seen_ids ] return await self._rerank( - request.query, merged_results, request.top_k, memory_type, retrieve_method + request.query, merged_results, request.top_k, memory_type_label, retrieve_method ) async def _search_rrf( @@ -722,8 +736,10 @@ async def _search_rrf( retrieve_method: str = RetrieveMethod.RRF.value, ) -> List[Dict]: """Core RRF search: keyword + vector + RRF fusion, returns flat list""" - memory_type = ( - request.memory_types[0].value if request.memory_types else 'unknown' + memory_type_label = ( + ','.join([mt.value for mt in request.memory_types]) + if request.memory_types + else 'unknown' ) # Run keyword and vector search concurrently @@ -740,7 +756,7 @@ async def _search_rrf( record_retrieve_stage( retrieve_method=retrieve_method, stage='rrf_fusion', - memory_type=memory_type, + memory_type=memory_type_label, duration_seconds=time.perf_counter() - rrf_start, ) @@ -766,7 +782,11 @@ async def _to_response( """Convert flat hits list to grouped RetrieveMemResponse""" user_id = req.user_id if req else "" source_type = req.retrieve_method.value - memory_type = req.memory_types[0].value + memory_type_label = ( + ','.join([mt.value for mt in req.memory_types]) + if req.memory_types + else 'unknown' + ) if not hits: return RetrieveMemResponse( @@ -777,10 +797,10 @@ async def _to_response( total_count=0, has_more=False, query_metadata=Metadata( - source=source_type, user_id=user_id or "", memory_type=memory_type + source=source_type, user_id=user_id or "", memory_type=memory_type_label ), metadata=Metadata( - source=source_type, user_id=user_id or "", memory_type=memory_type + source=source_type, user_id=user_id or "", memory_type=memory_type_label ), ) memories, scores, importance_scores, original_data, total_count = ( @@ -808,8 +828,8 @@ async def retrieve_mem_rrf( ) -> RetrieveMemResponse: """RRF-based memory retrieval: keyword + vector + RRF fusion""" start_time = time.perf_counter() - memory_type = ( - retrieve_mem_request.memory_types[0].value + memory_type_label = ( + ','.join([mt.value for mt in retrieve_mem_request.memory_types]) if retrieve_mem_request.memory_types else 'unknown' ) @@ -822,7 +842,7 @@ async def retrieve_mem_rrf( status = 'success' if hits else 'empty_result' record_retrieve_request( - memory_type=memory_type, + memory_type=memory_type_label, retrieve_method=RetrieveMethod.RRF.value, status=status, duration_seconds=duration, @@ -833,7 +853,7 @@ async def retrieve_mem_rrf( except Exception as e: duration = time.perf_counter() - start_time record_retrieve_request( - memory_type=memory_type, + memory_type=memory_type_label, retrieve_method=RetrieveMethod.RRF.value, status='error', duration_seconds=duration, @@ -855,7 +875,11 @@ async def retrieve_mem_agentic( req = retrieve_mem_request # alias top_k = req.top_k config = AgenticConfig() - memory_type = req.memory_types[0].value if req.memory_types else 'unknown' + memory_type_label = ( + ','.join([mt.value for mt in req.memory_types]) + if req.memory_types + else 'unknown' + ) try: llm_provider = LLMProvider( From 40d743fd76082e2d429b0344f4247c7405c68753 Mon Sep 17 00:00:00 2001 From: Exploreunive Date: Fri, 20 Mar 2026 10:04:26 +0800 Subject: [PATCH 2/5] fix: use memory_type_label instead of undefined memory_type in _to_response and retrieve_mem_agentic The local variable 'memory_type_label' is defined but was incorrectly referenced as 'memory_type' in 8 places across two methods: - _to_response: lines 817, 820 (Metadata construction) - retrieve_mem_agentic: lines 910, 920, 944, 991, 997, 1009 (record_retrieve_request calls and _rerank positional args) Using undefined 'memory_type' would cause NameError at runtime when these code paths are executed. --- src/agentic_layer/memory_manager.py | 16 +- tree_less.txt | 809 ++++++++++++++++++++++++++++ 2 files changed, 817 insertions(+), 8 deletions(-) create mode 100644 tree_less.txt diff --git a/src/agentic_layer/memory_manager.py b/src/agentic_layer/memory_manager.py index da7f397d..80d2334c 100644 --- a/src/agentic_layer/memory_manager.py +++ b/src/agentic_layer/memory_manager.py @@ -814,10 +814,10 @@ async def _to_response( total_count=total_count, has_more=False, query_metadata=Metadata( - source=source_type, user_id=user_id or "", memory_type=memory_type + source=source_type, user_id=user_id or "", memory_type=memory_type_label ), metadata=Metadata( - source=source_type, user_id=user_id or "", memory_type=memory_type + source=source_type, user_id=user_id or "", memory_type=memory_type_label ), ) @@ -907,7 +907,7 @@ async def retrieve_mem_agentic( if not round1: duration = time.perf_counter() - start_time record_retrieve_request( - memory_type=memory_type, + memory_type=memory_type_label, retrieve_method=RetrieveMethod.AGENTIC.value, status='empty_result', duration_seconds=duration, @@ -918,7 +918,7 @@ async def retrieve_mem_agentic( # ========== Rerank → max(5, top_k) for LLM & return ========== rerank_n = max(config.round1_rerank_top_n, top_k) reranked = await self._rerank( - req.query, round1, rerank_n, memory_type, 'agentic', + req.query, round1, rerank_n, memory_type_label, 'agentic', instruction=config.reranker_instruction, ) # Use top 5 for sufficiency check @@ -941,7 +941,7 @@ async def retrieve_mem_agentic( final_results = reranked[:top_k] duration = time.perf_counter() - start_time record_retrieve_request( - memory_type=memory_type, + memory_type=memory_type_label, retrieve_method=RetrieveMethod.AGENTIC.value, status='success', duration_seconds=duration, @@ -988,13 +988,13 @@ async def do_search(q: str) -> List[Dict]: # ========== Final Rerank ========== final = await self._rerank( - req.query, combined, top_k, memory_type, 'agentic', + req.query, combined, top_k, memory_type_label, 'agentic', instruction=config.reranker_instruction, ) duration = time.perf_counter() - start_time record_retrieve_request( - memory_type=memory_type, + memory_type=memory_type_label, retrieve_method=RetrieveMethod.AGENTIC.value, status='success', duration_seconds=duration, @@ -1006,7 +1006,7 @@ async def do_search(q: str) -> List[Dict]: except Exception as e: duration = time.perf_counter() - start_time record_retrieve_request( - memory_type=memory_type, + memory_type=memory_type_label, retrieve_method=RetrieveMethod.AGENTIC.value, status='error', duration_seconds=duration, diff --git a/tree_less.txt b/tree_less.txt new file mode 100644 index 00000000..f155718e --- /dev/null +++ b/tree_less.txt @@ -0,0 +1,809 @@ +EverMemOS +├── AGENTS.md +├── CLAUDE.md +├── CONTRIBUTING.md +├── Dockerfile +├── LICENSE +├── Makefile +├── NOTICE +├── README.md +├── README.zh.md +├── SECURITY.md +├── config.json +├── data +│ ├── README.md +│ ├── assistant_chat_en.json +│ ├── assistant_chat_zh.json +│ ├── group_chat_en.json +│ ├── group_chat_zh.json +│ └── locomo10.json +├── data_format +│ ├── __init__.py +│ └── group_chat +│ ├── __init__.py +│ ├── group_chat_format.md +│ └── group_chat_format.py +├── demo +│ ├── README.md +│ ├── __init__.py +│ ├── chat +│ │ ├── __init__.py +│ │ ├── orchestrator.py +│ │ ├── selectors.py +│ │ ├── session.py +│ │ └── ui.py +│ ├── chat_with_memory.py +│ ├── config +│ │ ├── __init__.py +│ │ └── memory_config.py +│ ├── extract_memory.py +│ ├── simple_demo.py +│ ├── tools +│ │ ├── clear_all_data.py +│ │ ├── debug_view_databases.py +│ │ ├── reset_databases.py +│ │ ├── resync_memcells.py +│ │ ├── resync_personal_memories.py +│ │ ├── test_retrieval_comprehensive.py +│ │ └── test_v1api_search.py +│ ├── ui +│ │ ├── __init__.py +│ │ └── i18n_texts.py +│ └── utils +│ ├── __init__.py +│ ├── memory_utils.py +│ └── simple_memory_manager.py +├── docker-compose.yaml +├── docs +│ ├── ACKNOWLEDGMENTS.md +│ ├── ARCHITECTURE.md +│ ├── CHANGELOG.md +│ ├── CITATION.md +│ ├── OVERVIEW.md +│ ├── README.md +│ ├── STARTER_KIT.md +│ ├── advanced +│ │ ├── GROUP_CHAT_GUIDE.md +│ │ ├── METADATA_CONTROL.md +│ │ └── RETRIEVAL_STRATEGIES.md +│ ├── api_docs +│ │ └── memory_api.md +│ ├── dev_docs +│ │ ├── agentic_retrieval_guide.md +│ │ ├── agentic_retrieve_testing.md +│ │ ├── api_usage_guide.md +│ │ ├── bootstrap_usage.md +│ │ ├── development_guide.md +│ │ ├── development_standards.md +│ │ ├── getting_started.md +│ │ ├── memory_types_guide.md +│ │ ├── metrics_library_design.md +│ │ └── run_memorize_usage.md +│ ├── installation +│ │ ├── DOCKER_SETUP.md +│ │ └── SETUP.md +│ └── usage +│ ├── BATCH_OPERATIONS.md +│ ├── CONFIGURATION_GUIDE.md +│ ├── DEMOS.md +│ ├── MONGODB_GUIDE.md +│ └── USAGE_EXAMPLES.md +├── env.template +├── evaluation +│ ├── README.md +│ ├── __init__.py +│ ├── cli.py +│ ├── config +│ │ ├── datasets +│ │ │ ├── evermembench.yaml +│ │ │ ├── locomo.yaml +│ │ │ ├── longmemeval.yaml +│ │ │ └── personamem.yaml +│ │ ├── prompts.yaml +│ │ └── systems +│ │ ├── evermemos.yaml +│ │ ├── evermemos_cloud_api.yaml +│ │ ├── evermemos_local_api.yaml +│ │ ├── mem0.yaml +│ │ ├── memos.yaml +│ │ ├── memu.yaml +│ │ └── zep.yaml +│ ├── data +│ │ ├── evermembench +│ │ ├── locomo +│ │ │ └── locomo10.json +│ │ ├── longmemeval +│ │ └── personamem +│ └── src +│ ├── __init__.py +│ ├── adapters +│ │ ├── __init__.py +│ │ ├── base.py +│ │ ├── evermemos +│ │ │ ├── README.md +│ │ │ ├── __init__.py +│ │ │ ├── config.py +│ │ │ ├── prompts +│ │ │ │ ├── __init__.py +│ │ │ │ ├── answer_prompts.py +│ │ │ │ ├── multi_query_prompts.py +│ │ │ │ ├── refined_query_prompts.py +│ │ │ │ └── sufficiency_check_prompts.py +│ │ │ ├── stage1_memcells_extraction.py +│ │ │ ├── stage2_index_building.py +│ │ │ ├── stage3_memory_retrivel.py +│ │ │ ├── stage4_response.py +│ │ │ ├── stage5_eval.py +│ │ │ └── tools +│ │ │ ├── __init__.py +│ │ │ ├── agentic_utils.py +│ │ │ ├── compute_acc.py +│ │ │ ├── in_memory_cluster_storage.py +│ │ │ └── in_memory_profile_storage.py +│ │ ├── evermemos_adapter.py +│ │ ├── evermemos_api_adapter.py +│ │ ├── mem0_adapter.py +│ │ ├── memos_adapter.py +│ │ ├── memu_adapter.py +│ │ ├── online_base.py +│ │ ├── registry.py +│ │ └── zep_adapter.py +│ ├── converters +│ │ ├── __init__.py +│ │ ├── base.py +│ │ ├── longmemeval_converter.py +│ │ ├── personamem_converter.py +│ │ └── registry.py +│ ├── core +│ │ ├── __init__.py +│ │ ├── data_models.py +│ │ ├── loaders.py +│ │ ├── pipeline.py +│ │ └── stages +│ │ ├── __init__.py +│ │ ├── add_stage.py +│ │ ├── answer_stage.py +│ │ ├── evaluate_stage.py +│ │ └── search_stage.py +│ ├── evaluators +│ │ ├── __init__.py +│ │ ├── base.py +│ │ ├── exact_match.py +│ │ ├── hybrid.py +│ │ ├── llm_judge.py +│ │ └── registry.py +│ └── utils +│ ├── __init__.py +│ ├── checkpoint.py +│ ├── cleaner.py +│ ├── config.py +│ ├── logger.py +│ ├── prompts.py +│ └── saver.py +├── examples +│ └── openclaw-plugin +│ ├── README.md +│ ├── README.zh.md +│ ├── SKILL.md +│ ├── bin +│ │ └── install.js +│ ├── index.js +│ ├── openclaw.plugin.json +│ ├── package.json +│ └── src +│ ├── api.js +│ ├── config.js +│ ├── convert.js +│ ├── engine.js +│ ├── http.js +│ ├── messages.js +│ ├── prompt.js +│ ├── subagent-assembler.js +│ ├── subagent-tracker.js +│ └── types.js +├── figs +├── pyproject.toml +├── pyrightconfig.json +├── pytest.ini +├── src +│ ├── __init__.py +│ ├── addon.py +│ ├── agentic_layer +│ │ ├── __init__.py +│ │ ├── agentic_utils.py +│ │ ├── fetch_mem_service.py +│ │ ├── memory_manager.py +│ │ ├── metrics +│ │ │ ├── __init__.py +│ │ │ ├── memorize_metrics.py +│ │ │ ├── rerank_metrics.py +│ │ │ ├── retrieve_metrics.py +│ │ │ └── vectorize_metrics.py +│ │ ├── rerank_deepinfra.py +│ │ ├── rerank_interface.py +│ │ ├── rerank_service.py +│ │ ├── rerank_vllm.py +│ │ ├── retrieval_utils.py +│ │ ├── vectorize_base.py +│ │ ├── vectorize_deepinfra.py +│ │ ├── vectorize_interface.py +│ │ ├── vectorize_service.py +│ │ └── vectorize_vllm.py +│ ├── api_specs +│ │ ├── __init__.py +│ │ ├── dtos +│ │ │ ├── __init__.py +│ │ │ ├── base.py +│ │ │ ├── conversation_meta.py +│ │ │ └── memory.py +│ │ ├── memory_models.py +│ │ ├── memory_types.py +│ │ └── request_converter.py +│ ├── app.py +│ ├── application_startup.py +│ ├── base_app.py +│ ├── biz_layer +│ │ ├── mem_db_operations.py +│ │ ├── mem_memorize.py +│ │ ├── mem_sync.py +│ │ └── memorize_config.py +│ ├── bootstrap.py +│ ├── common_utils +│ │ ├── __init__.py +│ │ ├── app_meta.py +│ │ ├── base62_utils.py +│ │ ├── cli_ui.py +│ │ ├── datetime_utils.py +│ │ ├── language_utils.py +│ │ ├── load_env.py +│ │ ├── project_path.py +│ │ ├── text_utils.py +│ │ └── url_extractor.py +│ ├── config +│ │ ├── __init__.py +│ │ ├── llm_backends.yaml +│ │ └── stopwords +│ │ └── hit_stopwords.txt +│ ├── core +│ │ ├── addons +│ │ │ ├── __init__.py +│ │ │ ├── addon_registry.py +│ │ │ ├── addonize +│ │ │ │ ├── __init__.py +│ │ │ │ ├── addon_bean_order_strategy.py +│ │ │ │ ├── asynctasks_setup.py +│ │ │ │ ├── di_setup.py +│ │ │ │ └── tests +│ │ │ │ ├── __init__.py +│ │ │ │ └── test_addon_bean_order_strategy.py +│ │ │ ├── addons_registry.py +│ │ │ └── introduction.md +│ │ ├── asynctasks +│ │ │ ├── __init__.py +│ │ │ ├── examples +│ │ │ │ └── hello_word_job.py +│ │ │ ├── task_manager.py +│ │ │ └── task_scan_registry.py +│ │ ├── authorize +│ │ │ ├── __init__.py +│ │ │ ├── decorators.py +│ │ │ ├── enums.py +│ │ │ ├── interfaces.py +│ │ │ └── strategies.py +│ │ ├── cache +│ │ │ ├── __init__.py +│ │ │ └── redis_cache_queue +│ │ │ ├── __init__.py +│ │ │ ├── redis_data_processor.py +│ │ │ ├── redis_length_cache_manager.py +│ │ │ └── redis_windows_cache_manager.py +│ │ ├── capability +│ │ │ ├── __init__.py +│ │ │ ├── app_capability.py +│ │ │ ├── configuration +│ │ │ │ └── __init__.py +│ │ │ └── logging +│ │ │ └── __init__.py +│ │ ├── class_annotations +│ │ │ ├── __init__.py +│ │ │ ├── decorator.py +│ │ │ ├── types.py +│ │ │ └── utils.py +│ │ ├── component +│ │ │ ├── __init__.py +│ │ │ ├── auth_provider.py +│ │ │ ├── config_provider.py +│ │ │ ├── database_connection_provider.py +│ │ │ ├── database_session_provider.py +│ │ │ ├── elasticsearch_client_factory.py +│ │ │ ├── kafka_consumer_factory.py +│ │ │ ├── kafka_producer_factory.py +│ │ │ ├── llm +│ │ │ │ ├── llm_adapter +│ │ │ │ │ ├── __init__.py +│ │ │ │ │ ├── anthropic_adapter.py +│ │ │ │ │ ├── completion.py +│ │ │ │ │ ├── gemini_adapter.py +│ │ │ │ │ ├── gemini_client.py +│ │ │ │ │ ├── llm_backend_adapter.py +│ │ │ │ │ ├── message.py +│ │ │ │ │ └── openai_adapter.py +│ │ │ │ └── tokenizer +│ │ │ │ ├── __init__.py +│ │ │ │ └── tokenizer_factory.py +│ │ │ ├── milvus_client_factory.py +│ │ │ ├── mongodb_client_factory.py +│ │ │ ├── openai_compatible_client.py +│ │ │ └── redis_provider.py +│ │ ├── config +│ │ │ └── __init__.py +│ │ ├── constants +│ │ │ ├── __init__.py +│ │ │ ├── errors.py +│ │ │ └── exceptions.py +│ │ ├── context +│ │ │ ├── __init__.py +│ │ │ ├── context.py +│ │ │ └── context_manager.py +│ │ ├── di +│ │ │ ├── __init__.py +│ │ │ ├── bean_definition.py +│ │ │ ├── bean_order_strategy.py +│ │ │ ├── container.py +│ │ │ ├── decorators.py +│ │ │ ├── exceptions.py +│ │ │ ├── introduction.md +│ │ │ ├── scan_context.py +│ │ │ ├── scan_path_registry.py +│ │ │ ├── scanner.py +│ │ │ ├── tests +│ │ │ │ ├── __init__.py +│ │ │ │ ├── test_bean_order_strategy.py +│ │ │ │ ├── test_di_container.py +│ │ │ │ ├── test_di_scanner.py +│ │ │ │ └── test_fixtures.py +│ │ │ └── utils.py +│ │ ├── events +│ │ │ ├── __init__.py +│ │ │ ├── base_event.py +│ │ │ ├── event_listener.py +│ │ │ └── event_publisher.py +│ │ ├── interface +│ │ │ ├── __init__.py +│ │ │ ├── controller +│ │ │ │ ├── __init__.py +│ │ │ │ ├── base_controller.py +│ │ │ │ └── debug +│ │ │ │ ├── __init__.py +│ │ │ │ └── debug_controller.py +│ │ │ └── decorator +│ │ │ ├── __init__.py +│ │ │ └── require_tenant.py +│ │ ├── lifespan +│ │ │ ├── __init__.py +│ │ │ ├── business_lifespan.py +│ │ │ ├── database_lifespan.py +│ │ │ ├── elasticsearch_lifespan.py +│ │ │ ├── lifespan_factory.py +│ │ │ ├── lifespan_interface.py +│ │ │ ├── longjob_lifespan.py +│ │ │ ├── metrics_lifespan.py +│ │ │ ├── milvus_lifespan.py +│ │ │ └── mongodb_lifespan.py +│ │ ├── lock +│ │ │ ├── __init__.py +│ │ │ └── redis_distributed_lock.py +│ │ ├── longjob +│ │ │ ├── __init__.py +│ │ │ ├── interfaces.py +│ │ │ ├── longjob_error.py +│ │ │ ├── longjob_runner.py +│ │ │ └── recycle_consumer_base.py +│ │ ├── middleware +│ │ │ ├── __init__.py +│ │ │ ├── app_logic_middleware.py +│ │ │ ├── database_session_middleware.py +│ │ │ ├── global_exception_handler.py +│ │ │ ├── hmac_signature_middleware.py +│ │ │ ├── profile_middleware.py +│ │ │ ├── prometheus_middleware.py +│ │ │ ├── sse_exception_middleware.py +│ │ │ └── user_context_middleware.py +│ │ ├── nlp +│ │ │ ├── __init__.py +│ │ │ └── stopwords_utils.py +│ │ ├── observation +│ │ │ ├── logger.py +│ │ │ ├── metrics +│ │ │ │ ├── __init__.py +│ │ │ │ ├── counter.py +│ │ │ │ ├── gauge.py +│ │ │ │ ├── histogram.py +│ │ │ │ ├── registry.py +│ │ │ │ └── server.py +│ │ │ └── tracing +│ │ │ ├── __init__.py +│ │ │ └── decorators.py +│ │ ├── oxm +│ │ │ ├── __init__.py +│ │ │ ├── constants.py +│ │ │ ├── es +│ │ │ │ ├── __init__.py +│ │ │ │ ├── analyzer.py +│ │ │ │ ├── base_converter.py +│ │ │ │ ├── base_repository.py +│ │ │ │ ├── doc_base.py +│ │ │ │ ├── es_utils.py +│ │ │ │ ├── mapping_templates.py +│ │ │ │ └── migration +│ │ │ │ ├── __init__.py +│ │ │ │ └── utils.py +│ │ │ ├── milvus +│ │ │ │ ├── __init__.py +│ │ │ │ ├── async_collection.py +│ │ │ │ ├── base_converter.py +│ │ │ │ ├── base_repository.py +│ │ │ │ ├── migration +│ │ │ │ │ └── utils.py +│ │ │ │ └── milvus_collection_base.py +│ │ │ ├── mongo +│ │ │ │ ├── __init__.py +│ │ │ │ ├── audit_base.py +│ │ │ │ ├── base_repository.py +│ │ │ │ ├── constant +│ │ │ │ │ └── annotations.py +│ │ │ │ ├── document_base.py +│ │ │ │ ├── document_base_with_soft_delete.py +│ │ │ │ ├── migration +│ │ │ │ │ ├── __init__.py +│ │ │ │ │ ├── cli.py +│ │ │ │ │ └── manager.py +│ │ │ │ └── mongo_utils.py +│ │ │ └── pg +│ │ │ ├── __init__.py +│ │ │ ├── audit_base.py +│ │ │ └── base_repository.py +│ │ ├── queue +│ │ │ ├── __init__.py +│ │ │ ├── msg_group_queue +│ │ │ │ ├── __init__.py +│ │ │ │ ├── msg_group_queue_manager.py +│ │ │ │ └── msg_group_queue_manager_factory.py +│ │ │ └── redis_group_queue +│ │ │ ├── __init__.py +│ │ │ ├── kafka_consumer_record_item.py +│ │ │ ├── redis_group_queue_item.py +│ │ │ ├── redis_group_queue_lua_scripts.py +│ │ │ ├── redis_msg_group_queue_manager.py +│ │ │ └── redis_msg_group_queue_manager_factory.py +│ │ ├── rate_limit +│ │ │ ├── __init__.py +│ │ │ └── rate_limiter.py +│ │ ├── request +│ │ │ ├── __init__.py +│ │ │ ├── app_logic_provider.py +│ │ │ ├── request_history_config.py +│ │ │ ├── request_history_decorator.py +│ │ │ ├── request_history_event.py +│ │ │ └── timeout_background.py +│ │ └── tenants +│ │ ├── __init__.py +│ │ ├── init_tenant_all.py +│ │ ├── request_tenant_provider.py +│ │ ├── tenant_config.py +│ │ ├── tenant_contextvar.py +│ │ ├── tenant_info_provider.py +│ │ ├── tenant_models.py +│ │ ├── tenant_switch.py +│ │ └── tenantize +│ │ ├── __init__.py +│ │ ├── kv +│ │ │ ├── __init__.py +│ │ │ └── redis +│ │ │ ├── __init__.py +│ │ │ └── tenant_key_utils.py +│ │ ├── oxm +│ │ │ ├── __init__.py +│ │ │ ├── es +│ │ │ │ ├── __init__.py +│ │ │ │ ├── config_utils.py +│ │ │ │ └── tenant_aware_async_document.py +│ │ │ ├── milvus +│ │ │ │ ├── __init__.py +│ │ │ │ ├── config_utils.py +│ │ │ │ ├── tenant_aware_collection.py +│ │ │ │ └── tenant_aware_collection_with_suffix.py +│ │ │ └── mongo +│ │ │ ├── __init__.py +│ │ │ ├── config_utils.py +│ │ │ ├── tenant_aware_client_factory.py +│ │ │ └── tenant_aware_mongo_client.py +│ │ └── tenant_cache_utils.py +│ ├── devops_scripts +│ │ ├── __init__.py +│ │ ├── commitlint +│ │ │ ├── __init__.py +│ │ │ └── conventional_commit_lint.py +│ │ ├── data_fix +│ │ │ ├── __init__.py +│ │ │ ├── data_fix_docs +│ │ │ │ ├── DATA_FIX_README.md +│ │ │ │ ├── elasticsearch_scripts_guide.md +│ │ │ │ ├── milvus_scripts_guide.md +│ │ │ │ └── mongodb_scripts_guide.md +│ │ │ ├── es_rebuild_index.py +│ │ │ ├── es_sync_docs.py +│ │ │ ├── es_sync_episodic_memory_docs.py +│ │ │ ├── milvus_rebuild_collection.py +│ │ │ ├── milvus_sync_docs.py +│ │ │ ├── milvus_sync_episodic_memory_docs.py +│ │ │ ├── mongo_add_timestamp_shard.py +│ │ │ └── mongo_fix_episodic_memory_missing_vector.py +│ │ ├── i18n +│ │ │ ├── __init__.py +│ │ │ └── i18n_tool.py +│ │ ├── mongo_migrate.py +│ │ └── sensitive_info +│ │ ├── __init__.py +│ │ └── sensitive_info_tool.py +│ ├── infra_layer +│ │ ├── __init__.py +│ │ ├── adapters +│ │ │ ├── __init__.py +│ │ │ ├── input +│ │ │ │ ├── __init__.py +│ │ │ │ ├── api +│ │ │ │ │ ├── __init__.py +│ │ │ │ │ ├── dto +│ │ │ │ │ │ ├── global_user_profile_dto.py +│ │ │ │ │ │ ├── memory_dto.py +│ │ │ │ │ │ └── status_dto.py +│ │ │ │ │ ├── global_user_profile +│ │ │ │ │ │ ├── __init__.py +│ │ │ │ │ │ └── global_user_profile_controller.py +│ │ │ │ │ ├── health +│ │ │ │ │ │ └── health_controller.py +│ │ │ │ │ ├── mapper +│ │ │ │ │ │ ├── __init__.py +│ │ │ │ │ │ └── group_chat_converter.py +│ │ │ │ │ ├── memory +│ │ │ │ │ │ └── memory_controller.py +│ │ │ │ │ └── status +│ │ │ │ │ ├── __init__.py +│ │ │ │ │ └── status_controller.py +│ │ │ │ ├── jobs +│ │ │ │ │ └── __init__.py +│ │ │ │ ├── mcp +│ │ │ │ │ └── __init__.py +│ │ │ │ └── mq +│ │ │ │ ├── __init__.py +│ │ │ │ └── mapper +│ │ │ │ └── __init__.py +│ │ │ └── out +│ │ │ ├── event +│ │ │ │ ├── __init__.py +│ │ │ │ └── memcell_created_event.py +│ │ │ ├── persistence +│ │ │ │ ├── document +│ │ │ │ │ ├── __init__.py +│ │ │ │ │ ├── memory +│ │ │ │ │ │ ├── __init__.py +│ │ │ │ │ │ ├── behavior_history.py +│ │ │ │ │ │ ├── cluster_state.py +│ │ │ │ │ │ ├── conversation_meta.py +│ │ │ │ │ │ ├── conversation_status.py +│ │ │ │ │ │ ├── core_memory.py +│ │ │ │ │ │ ├── entity.py +│ │ │ │ │ │ ├── episodic_memory.py +│ │ │ │ │ │ ├── event_log_record.py +│ │ │ │ │ │ ├── foresight_record.py +│ │ │ │ │ │ ├── global_user_profile.py +│ │ │ │ │ │ ├── group_profile.py +│ │ │ │ │ │ ├── group_user_profile_memory.py +│ │ │ │ │ │ ├── memcell.py +│ │ │ │ │ │ ├── relationship.py +│ │ │ │ │ │ └── user_profile.py +│ │ │ │ │ └── request +│ │ │ │ │ ├── __init__.py +│ │ │ │ │ └── memory_request_log.py +│ │ │ │ ├── mapper +│ │ │ │ │ ├── __init__.py +│ │ │ │ │ └── memory_request_log_mapper.py +│ │ │ │ └── repository +│ │ │ │ ├── __init__.py +│ │ │ │ ├── behavior_history_raw_repository.py +│ │ │ │ ├── cluster_state_raw_repository.py +│ │ │ │ ├── conversation_data_raw_repository.py +│ │ │ │ ├── conversation_meta_raw_repository.py +│ │ │ │ ├── conversation_status_raw_repository.py +│ │ │ │ ├── core_memory_raw_repository.py +│ │ │ │ ├── entity_raw_repository.py +│ │ │ │ ├── episodic_memory_raw_repository.py +│ │ │ │ ├── event_log_record_raw_repository.py +│ │ │ │ ├── foresight_record_repository.py +│ │ │ │ ├── global_user_profile_raw_repository.py +│ │ │ │ ├── group_profile_raw_repository.py +│ │ │ │ ├── group_user_profile_memory_raw_repository.py +│ │ │ │ ├── memcell_raw_repository.py +│ │ │ │ ├── memory_request_log_repository.py +│ │ │ │ ├── relationship_raw_repository.py +│ │ │ │ └── user_profile_raw_repository.py +│ │ │ └── search +│ │ │ ├── __init__.py +│ │ │ ├── elasticsearch +│ │ │ │ ├── __init__.py +│ │ │ │ ├── converter +│ │ │ │ │ ├── __init__.py +│ │ │ │ │ ├── episodic_memory_converter.py +│ │ │ │ │ ├── event_log_converter.py +│ │ │ │ │ └── foresight_converter.py +│ │ │ │ └── memory +│ │ │ │ ├── __init__.py +│ │ │ │ ├── episodic_memory.py +│ │ │ │ ├── event_log.py +│ │ │ │ └── foresight.py +│ │ │ ├── milvus +│ │ │ │ ├── __init__.py +│ │ │ │ ├── converter +│ │ │ │ │ ├── __init__.py +│ │ │ │ │ ├── episodic_memory_milvus_converter.py +│ │ │ │ │ ├── event_log_milvus_converter.py +│ │ │ │ │ └── foresight_milvus_converter.py +│ │ │ │ └── memory +│ │ │ │ ├── __init__.py +│ │ │ │ ├── episodic_memory_collection.py +│ │ │ │ ├── event_log_collection.py +│ │ │ │ └── foresight_collection.py +│ │ │ └── repository +│ │ │ ├── __init__.py +│ │ │ ├── episodic_memory_es_repository.py +│ │ │ ├── episodic_memory_milvus_repository.py +│ │ │ ├── event_log_es_repository.py +│ │ │ ├── event_log_milvus_repository.py +│ │ │ ├── foresight_es_repository.py +│ │ │ └── foresight_milvus_repository.py +│ │ └── scripts +│ │ ├── __init__.py +│ │ └── migrations +│ │ └── __init__.py +│ ├── manage.py +│ ├── memory_layer +│ │ ├── __init__.py +│ │ ├── cluster_manager +│ │ │ ├── __init__.py +│ │ │ ├── config.py +│ │ │ └── manager.py +│ │ ├── constants.py +│ │ ├── llm +│ │ │ ├── __init__.py +│ │ │ ├── config.py +│ │ │ ├── llm_provider.py +│ │ │ ├── openai_provider.py +│ │ │ └── protocol.py +│ │ ├── memcell_extractor +│ │ │ ├── base_memcell_extractor.py +│ │ │ └── conv_memcell_extractor.py +│ │ ├── memory_extractor +│ │ │ ├── base_memory_extractor.py +│ │ │ ├── episode_memory_extractor.py +│ │ │ ├── event_log_extractor.py +│ │ │ ├── foresight_extractor.py +│ │ │ ├── group_profile +│ │ │ │ ├── __init__.py +│ │ │ │ ├── data_processor.py +│ │ │ │ ├── llm_handler.py +│ │ │ │ ├── role_processor.py +│ │ │ │ └── topic_processor.py +│ │ │ ├── group_profile_memory_extractor.py +│ │ │ ├── profile_memory +│ │ │ │ ├── __init__.py +│ │ │ │ ├── conversation.py +│ │ │ │ ├── data_normalize.py +│ │ │ │ ├── empty_evidence_completion.py +│ │ │ │ ├── evidence_utils.py +│ │ │ │ ├── extractor.py +│ │ │ │ ├── merger.py +│ │ │ │ ├── profile_helpers.py +│ │ │ │ ├── project_helpers.py +│ │ │ │ ├── skill_helpers.py +│ │ │ │ ├── types.py +│ │ │ │ └── value_helpers.py +│ │ │ ├── profile_memory_extractor.py +│ │ │ └── profile_memory_life +│ │ │ ├── __init__.py +│ │ │ ├── extractor.py +│ │ │ ├── id_mapper.py +│ │ │ └── types.py +│ │ ├── memory_manager.py +│ │ ├── profile_manager +│ │ │ ├── README.md +│ │ │ ├── __init__.py +│ │ │ ├── config.py +│ │ │ ├── discriminator.py +│ │ │ └── manager.py +│ │ └── prompts +│ │ ├── __init__.py +│ │ ├── en +│ │ │ ├── __init__.py +│ │ │ ├── conv_prompts.py +│ │ │ ├── episode_mem_prompts.py +│ │ │ ├── event_log_prompts.py +│ │ │ ├── foresight_prompts.py +│ │ │ ├── group_profile_merge_prompts.py +│ │ │ ├── group_profile_prompts.py +│ │ │ ├── profile_mem_evidence_completion_prompt.py +│ │ │ ├── profile_mem_life_prompts.py +│ │ │ ├── profile_mem_part1_prompts.py +│ │ │ ├── profile_mem_part2_prompts.py +│ │ │ ├── profile_mem_part3_prompts.py +│ │ │ └── profile_mem_prompts.py +│ │ └── zh +│ │ ├── __init__.py +│ │ ├── conv_prompts.py +│ │ ├── episode_mem_prompts.py +│ │ ├── event_log_prompts.py +│ │ ├── foresight_prompts.py +│ │ ├── group_profile_merge_prompts.py +│ │ ├── group_profile_prompts.py +│ │ ├── profile_mem_evidence_completion_prompt.py +│ │ ├── profile_mem_life_prompts.py +│ │ ├── profile_mem_part1_prompts.py +│ │ ├── profile_mem_part2_prompts.py +│ │ ├── profile_mem_part3_prompts.py +│ │ └── profile_mem_prompts.py +│ ├── migrations +│ │ ├── __init__.py +│ │ ├── mongodb +│ │ │ └── __init__.py +│ │ └── postgresql +│ │ └── __init__.py +│ ├── project_meta.py +│ ├── run.py +│ ├── run_memorize.py +│ ├── service +│ │ ├── __init__.py +│ │ ├── conversation_meta_service.py +│ │ ├── global_user_profile_service.py +│ │ ├── memcell_delete_service.py +│ │ ├── memory_request_log_service.py +│ │ └── request_status_service.py +│ └── task.py +├── tests +│ ├── test_business_lifespan_shutdown.py +│ ├── test_conv_memcell_extractor.py +│ ├── test_conversation_data_raw_repository.py +│ ├── test_conversation_meta.py +│ ├── test_conversation_status_raw_repository.py +│ ├── test_core_memory_raw_repository.py +│ ├── test_datetime_utils_to_iso_format.py +│ ├── test_embedding_reranker_providers.py +│ ├── test_episodic_memory_es_repository.py +│ ├── test_episodic_memory_milvus_repository.py +│ ├── test_global_user_profile_controller.py +│ ├── test_group_profile_datetime_check.py +│ ├── test_group_profile_raw_repository.py +│ ├── test_group_profile_simple.py +│ ├── test_group_user_profile_memory_raw_repository.py +│ ├── test_integration_vectorize_rerank.py +│ ├── test_keyword_vocabulary_milvus_repository.py +│ ├── test_memcell_raw_repository.py +│ ├── test_memory_controller.py +│ ├── test_memory_controller_request_params.py +│ ├── test_migrate_user_goal_to_work_responsibility.py +│ ├── test_msg_group_queue_manager.py +│ ├── test_pickle_size_analysis.py +│ ├── test_rate_limiter_quick.py +│ ├── test_rawdata_json_serialization.py +│ ├── test_redis_distributed_lock.py +│ ├── test_redis_length_cache.py +│ ├── test_redis_windows_cache.py +│ ├── test_request_converter.py +│ ├── test_retrieval_utils_similarity.py +│ ├── test_smart_text_parser.py +│ ├── test_stability_database.py +│ ├── test_stability_integration.py +│ ├── test_task_cancel.py +│ ├── test_task_cancel_2.py +│ ├── test_tokenizer_factory.py +│ └── test_wait_for.py +└── uv.lock \ No newline at end of file From d9463820e300f1dca99104ceb8f97d0c264f0ab4 Mon Sep 17 00:00:00 2001 From: Exploreunive Date: Fri, 20 Mar 2026 10:22:04 +0800 Subject: [PATCH 3/5] chore: remove tree_less.txt from tracking --- tree_less.txt | 809 -------------------------------------------------- 1 file changed, 809 deletions(-) delete mode 100644 tree_less.txt diff --git a/tree_less.txt b/tree_less.txt deleted file mode 100644 index f155718e..00000000 --- a/tree_less.txt +++ /dev/null @@ -1,809 +0,0 @@ -EverMemOS -├── AGENTS.md -├── CLAUDE.md -├── CONTRIBUTING.md -├── Dockerfile -├── LICENSE -├── Makefile -├── NOTICE -├── README.md -├── README.zh.md -├── SECURITY.md -├── config.json -├── data -│ ├── README.md -│ ├── assistant_chat_en.json -│ ├── assistant_chat_zh.json -│ ├── group_chat_en.json -│ ├── group_chat_zh.json -│ └── locomo10.json -├── data_format -│ ├── __init__.py -│ └── group_chat -│ ├── __init__.py -│ ├── group_chat_format.md -│ └── group_chat_format.py -├── demo -│ ├── README.md -│ ├── __init__.py -│ ├── chat -│ │ ├── __init__.py -│ │ ├── orchestrator.py -│ │ ├── selectors.py -│ │ ├── session.py -│ │ └── ui.py -│ ├── chat_with_memory.py -│ ├── config -│ │ ├── __init__.py -│ │ └── memory_config.py -│ ├── extract_memory.py -│ ├── simple_demo.py -│ ├── tools -│ │ ├── clear_all_data.py -│ │ ├── debug_view_databases.py -│ │ ├── reset_databases.py -│ │ ├── resync_memcells.py -│ │ ├── resync_personal_memories.py -│ │ ├── test_retrieval_comprehensive.py -│ │ └── test_v1api_search.py -│ ├── ui -│ │ ├── __init__.py -│ │ └── i18n_texts.py -│ └── utils -│ ├── __init__.py -│ ├── memory_utils.py -│ └── simple_memory_manager.py -├── docker-compose.yaml -├── docs -│ ├── ACKNOWLEDGMENTS.md -│ ├── ARCHITECTURE.md -│ ├── CHANGELOG.md -│ ├── CITATION.md -│ ├── OVERVIEW.md -│ ├── README.md -│ ├── STARTER_KIT.md -│ ├── advanced -│ │ ├── GROUP_CHAT_GUIDE.md -│ │ ├── METADATA_CONTROL.md -│ │ └── RETRIEVAL_STRATEGIES.md -│ ├── api_docs -│ │ └── memory_api.md -│ ├── dev_docs -│ │ ├── agentic_retrieval_guide.md -│ │ ├── agentic_retrieve_testing.md -│ │ ├── api_usage_guide.md -│ │ ├── bootstrap_usage.md -│ │ ├── development_guide.md -│ │ ├── development_standards.md -│ │ ├── getting_started.md -│ │ ├── memory_types_guide.md -│ │ ├── metrics_library_design.md -│ │ └── run_memorize_usage.md -│ ├── installation -│ │ ├── DOCKER_SETUP.md -│ │ └── SETUP.md -│ └── usage -│ ├── BATCH_OPERATIONS.md -│ ├── CONFIGURATION_GUIDE.md -│ ├── DEMOS.md -│ ├── MONGODB_GUIDE.md -│ └── USAGE_EXAMPLES.md -├── env.template -├── evaluation -│ ├── README.md -│ ├── __init__.py -│ ├── cli.py -│ ├── config -│ │ ├── datasets -│ │ │ ├── evermembench.yaml -│ │ │ ├── locomo.yaml -│ │ │ ├── longmemeval.yaml -│ │ │ └── personamem.yaml -│ │ ├── prompts.yaml -│ │ └── systems -│ │ ├── evermemos.yaml -│ │ ├── evermemos_cloud_api.yaml -│ │ ├── evermemos_local_api.yaml -│ │ ├── mem0.yaml -│ │ ├── memos.yaml -│ │ ├── memu.yaml -│ │ └── zep.yaml -│ ├── data -│ │ ├── evermembench -│ │ ├── locomo -│ │ │ └── locomo10.json -│ │ ├── longmemeval -│ │ └── personamem -│ └── src -│ ├── __init__.py -│ ├── adapters -│ │ ├── __init__.py -│ │ ├── base.py -│ │ ├── evermemos -│ │ │ ├── README.md -│ │ │ ├── __init__.py -│ │ │ ├── config.py -│ │ │ ├── prompts -│ │ │ │ ├── __init__.py -│ │ │ │ ├── answer_prompts.py -│ │ │ │ ├── multi_query_prompts.py -│ │ │ │ ├── refined_query_prompts.py -│ │ │ │ └── sufficiency_check_prompts.py -│ │ │ ├── stage1_memcells_extraction.py -│ │ │ ├── stage2_index_building.py -│ │ │ ├── stage3_memory_retrivel.py -│ │ │ ├── stage4_response.py -│ │ │ ├── stage5_eval.py -│ │ │ └── tools -│ │ │ ├── __init__.py -│ │ │ ├── agentic_utils.py -│ │ │ ├── compute_acc.py -│ │ │ ├── in_memory_cluster_storage.py -│ │ │ └── in_memory_profile_storage.py -│ │ ├── evermemos_adapter.py -│ │ ├── evermemos_api_adapter.py -│ │ ├── mem0_adapter.py -│ │ ├── memos_adapter.py -│ │ ├── memu_adapter.py -│ │ ├── online_base.py -│ │ ├── registry.py -│ │ └── zep_adapter.py -│ ├── converters -│ │ ├── __init__.py -│ │ ├── base.py -│ │ ├── longmemeval_converter.py -│ │ ├── personamem_converter.py -│ │ └── registry.py -│ ├── core -│ │ ├── __init__.py -│ │ ├── data_models.py -│ │ ├── loaders.py -│ │ ├── pipeline.py -│ │ └── stages -│ │ ├── __init__.py -│ │ ├── add_stage.py -│ │ ├── answer_stage.py -│ │ ├── evaluate_stage.py -│ │ └── search_stage.py -│ ├── evaluators -│ │ ├── __init__.py -│ │ ├── base.py -│ │ ├── exact_match.py -│ │ ├── hybrid.py -│ │ ├── llm_judge.py -│ │ └── registry.py -│ └── utils -│ ├── __init__.py -│ ├── checkpoint.py -│ ├── cleaner.py -│ ├── config.py -│ ├── logger.py -│ ├── prompts.py -│ └── saver.py -├── examples -│ └── openclaw-plugin -│ ├── README.md -│ ├── README.zh.md -│ ├── SKILL.md -│ ├── bin -│ │ └── install.js -│ ├── index.js -│ ├── openclaw.plugin.json -│ ├── package.json -│ └── src -│ ├── api.js -│ ├── config.js -│ ├── convert.js -│ ├── engine.js -│ ├── http.js -│ ├── messages.js -│ ├── prompt.js -│ ├── subagent-assembler.js -│ ├── subagent-tracker.js -│ └── types.js -├── figs -├── pyproject.toml -├── pyrightconfig.json -├── pytest.ini -├── src -│ ├── __init__.py -│ ├── addon.py -│ ├── agentic_layer -│ │ ├── __init__.py -│ │ ├── agentic_utils.py -│ │ ├── fetch_mem_service.py -│ │ ├── memory_manager.py -│ │ ├── metrics -│ │ │ ├── __init__.py -│ │ │ ├── memorize_metrics.py -│ │ │ ├── rerank_metrics.py -│ │ │ ├── retrieve_metrics.py -│ │ │ └── vectorize_metrics.py -│ │ ├── rerank_deepinfra.py -│ │ ├── rerank_interface.py -│ │ ├── rerank_service.py -│ │ ├── rerank_vllm.py -│ │ ├── retrieval_utils.py -│ │ ├── vectorize_base.py -│ │ ├── vectorize_deepinfra.py -│ │ ├── vectorize_interface.py -│ │ ├── vectorize_service.py -│ │ └── vectorize_vllm.py -│ ├── api_specs -│ │ ├── __init__.py -│ │ ├── dtos -│ │ │ ├── __init__.py -│ │ │ ├── base.py -│ │ │ ├── conversation_meta.py -│ │ │ └── memory.py -│ │ ├── memory_models.py -│ │ ├── memory_types.py -│ │ └── request_converter.py -│ ├── app.py -│ ├── application_startup.py -│ ├── base_app.py -│ ├── biz_layer -│ │ ├── mem_db_operations.py -│ │ ├── mem_memorize.py -│ │ ├── mem_sync.py -│ │ └── memorize_config.py -│ ├── bootstrap.py -│ ├── common_utils -│ │ ├── __init__.py -│ │ ├── app_meta.py -│ │ ├── base62_utils.py -│ │ ├── cli_ui.py -│ │ ├── datetime_utils.py -│ │ ├── language_utils.py -│ │ ├── load_env.py -│ │ ├── project_path.py -│ │ ├── text_utils.py -│ │ └── url_extractor.py -│ ├── config -│ │ ├── __init__.py -│ │ ├── llm_backends.yaml -│ │ └── stopwords -│ │ └── hit_stopwords.txt -│ ├── core -│ │ ├── addons -│ │ │ ├── __init__.py -│ │ │ ├── addon_registry.py -│ │ │ ├── addonize -│ │ │ │ ├── __init__.py -│ │ │ │ ├── addon_bean_order_strategy.py -│ │ │ │ ├── asynctasks_setup.py -│ │ │ │ ├── di_setup.py -│ │ │ │ └── tests -│ │ │ │ ├── __init__.py -│ │ │ │ └── test_addon_bean_order_strategy.py -│ │ │ ├── addons_registry.py -│ │ │ └── introduction.md -│ │ ├── asynctasks -│ │ │ ├── __init__.py -│ │ │ ├── examples -│ │ │ │ └── hello_word_job.py -│ │ │ ├── task_manager.py -│ │ │ └── task_scan_registry.py -│ │ ├── authorize -│ │ │ ├── __init__.py -│ │ │ ├── decorators.py -│ │ │ ├── enums.py -│ │ │ ├── interfaces.py -│ │ │ └── strategies.py -│ │ ├── cache -│ │ │ ├── __init__.py -│ │ │ └── redis_cache_queue -│ │ │ ├── __init__.py -│ │ │ ├── redis_data_processor.py -│ │ │ ├── redis_length_cache_manager.py -│ │ │ └── redis_windows_cache_manager.py -│ │ ├── capability -│ │ │ ├── __init__.py -│ │ │ ├── app_capability.py -│ │ │ ├── configuration -│ │ │ │ └── __init__.py -│ │ │ └── logging -│ │ │ └── __init__.py -│ │ ├── class_annotations -│ │ │ ├── __init__.py -│ │ │ ├── decorator.py -│ │ │ ├── types.py -│ │ │ └── utils.py -│ │ ├── component -│ │ │ ├── __init__.py -│ │ │ ├── auth_provider.py -│ │ │ ├── config_provider.py -│ │ │ ├── database_connection_provider.py -│ │ │ ├── database_session_provider.py -│ │ │ ├── elasticsearch_client_factory.py -│ │ │ ├── kafka_consumer_factory.py -│ │ │ ├── kafka_producer_factory.py -│ │ │ ├── llm -│ │ │ │ ├── llm_adapter -│ │ │ │ │ ├── __init__.py -│ │ │ │ │ ├── anthropic_adapter.py -│ │ │ │ │ ├── completion.py -│ │ │ │ │ ├── gemini_adapter.py -│ │ │ │ │ ├── gemini_client.py -│ │ │ │ │ ├── llm_backend_adapter.py -│ │ │ │ │ ├── message.py -│ │ │ │ │ └── openai_adapter.py -│ │ │ │ └── tokenizer -│ │ │ │ ├── __init__.py -│ │ │ │ └── tokenizer_factory.py -│ │ │ ├── milvus_client_factory.py -│ │ │ ├── mongodb_client_factory.py -│ │ │ ├── openai_compatible_client.py -│ │ │ └── redis_provider.py -│ │ ├── config -│ │ │ └── __init__.py -│ │ ├── constants -│ │ │ ├── __init__.py -│ │ │ ├── errors.py -│ │ │ └── exceptions.py -│ │ ├── context -│ │ │ ├── __init__.py -│ │ │ ├── context.py -│ │ │ └── context_manager.py -│ │ ├── di -│ │ │ ├── __init__.py -│ │ │ ├── bean_definition.py -│ │ │ ├── bean_order_strategy.py -│ │ │ ├── container.py -│ │ │ ├── decorators.py -│ │ │ ├── exceptions.py -│ │ │ ├── introduction.md -│ │ │ ├── scan_context.py -│ │ │ ├── scan_path_registry.py -│ │ │ ├── scanner.py -│ │ │ ├── tests -│ │ │ │ ├── __init__.py -│ │ │ │ ├── test_bean_order_strategy.py -│ │ │ │ ├── test_di_container.py -│ │ │ │ ├── test_di_scanner.py -│ │ │ │ └── test_fixtures.py -│ │ │ └── utils.py -│ │ ├── events -│ │ │ ├── __init__.py -│ │ │ ├── base_event.py -│ │ │ ├── event_listener.py -│ │ │ └── event_publisher.py -│ │ ├── interface -│ │ │ ├── __init__.py -│ │ │ ├── controller -│ │ │ │ ├── __init__.py -│ │ │ │ ├── base_controller.py -│ │ │ │ └── debug -│ │ │ │ ├── __init__.py -│ │ │ │ └── debug_controller.py -│ │ │ └── decorator -│ │ │ ├── __init__.py -│ │ │ └── require_tenant.py -│ │ ├── lifespan -│ │ │ ├── __init__.py -│ │ │ ├── business_lifespan.py -│ │ │ ├── database_lifespan.py -│ │ │ ├── elasticsearch_lifespan.py -│ │ │ ├── lifespan_factory.py -│ │ │ ├── lifespan_interface.py -│ │ │ ├── longjob_lifespan.py -│ │ │ ├── metrics_lifespan.py -│ │ │ ├── milvus_lifespan.py -│ │ │ └── mongodb_lifespan.py -│ │ ├── lock -│ │ │ ├── __init__.py -│ │ │ └── redis_distributed_lock.py -│ │ ├── longjob -│ │ │ ├── __init__.py -│ │ │ ├── interfaces.py -│ │ │ ├── longjob_error.py -│ │ │ ├── longjob_runner.py -│ │ │ └── recycle_consumer_base.py -│ │ ├── middleware -│ │ │ ├── __init__.py -│ │ │ ├── app_logic_middleware.py -│ │ │ ├── database_session_middleware.py -│ │ │ ├── global_exception_handler.py -│ │ │ ├── hmac_signature_middleware.py -│ │ │ ├── profile_middleware.py -│ │ │ ├── prometheus_middleware.py -│ │ │ ├── sse_exception_middleware.py -│ │ │ └── user_context_middleware.py -│ │ ├── nlp -│ │ │ ├── __init__.py -│ │ │ └── stopwords_utils.py -│ │ ├── observation -│ │ │ ├── logger.py -│ │ │ ├── metrics -│ │ │ │ ├── __init__.py -│ │ │ │ ├── counter.py -│ │ │ │ ├── gauge.py -│ │ │ │ ├── histogram.py -│ │ │ │ ├── registry.py -│ │ │ │ └── server.py -│ │ │ └── tracing -│ │ │ ├── __init__.py -│ │ │ └── decorators.py -│ │ ├── oxm -│ │ │ ├── __init__.py -│ │ │ ├── constants.py -│ │ │ ├── es -│ │ │ │ ├── __init__.py -│ │ │ │ ├── analyzer.py -│ │ │ │ ├── base_converter.py -│ │ │ │ ├── base_repository.py -│ │ │ │ ├── doc_base.py -│ │ │ │ ├── es_utils.py -│ │ │ │ ├── mapping_templates.py -│ │ │ │ └── migration -│ │ │ │ ├── __init__.py -│ │ │ │ └── utils.py -│ │ │ ├── milvus -│ │ │ │ ├── __init__.py -│ │ │ │ ├── async_collection.py -│ │ │ │ ├── base_converter.py -│ │ │ │ ├── base_repository.py -│ │ │ │ ├── migration -│ │ │ │ │ └── utils.py -│ │ │ │ └── milvus_collection_base.py -│ │ │ ├── mongo -│ │ │ │ ├── __init__.py -│ │ │ │ ├── audit_base.py -│ │ │ │ ├── base_repository.py -│ │ │ │ ├── constant -│ │ │ │ │ └── annotations.py -│ │ │ │ ├── document_base.py -│ │ │ │ ├── document_base_with_soft_delete.py -│ │ │ │ ├── migration -│ │ │ │ │ ├── __init__.py -│ │ │ │ │ ├── cli.py -│ │ │ │ │ └── manager.py -│ │ │ │ └── mongo_utils.py -│ │ │ └── pg -│ │ │ ├── __init__.py -│ │ │ ├── audit_base.py -│ │ │ └── base_repository.py -│ │ ├── queue -│ │ │ ├── __init__.py -│ │ │ ├── msg_group_queue -│ │ │ │ ├── __init__.py -│ │ │ │ ├── msg_group_queue_manager.py -│ │ │ │ └── msg_group_queue_manager_factory.py -│ │ │ └── redis_group_queue -│ │ │ ├── __init__.py -│ │ │ ├── kafka_consumer_record_item.py -│ │ │ ├── redis_group_queue_item.py -│ │ │ ├── redis_group_queue_lua_scripts.py -│ │ │ ├── redis_msg_group_queue_manager.py -│ │ │ └── redis_msg_group_queue_manager_factory.py -│ │ ├── rate_limit -│ │ │ ├── __init__.py -│ │ │ └── rate_limiter.py -│ │ ├── request -│ │ │ ├── __init__.py -│ │ │ ├── app_logic_provider.py -│ │ │ ├── request_history_config.py -│ │ │ ├── request_history_decorator.py -│ │ │ ├── request_history_event.py -│ │ │ └── timeout_background.py -│ │ └── tenants -│ │ ├── __init__.py -│ │ ├── init_tenant_all.py -│ │ ├── request_tenant_provider.py -│ │ ├── tenant_config.py -│ │ ├── tenant_contextvar.py -│ │ ├── tenant_info_provider.py -│ │ ├── tenant_models.py -│ │ ├── tenant_switch.py -│ │ └── tenantize -│ │ ├── __init__.py -│ │ ├── kv -│ │ │ ├── __init__.py -│ │ │ └── redis -│ │ │ ├── __init__.py -│ │ │ └── tenant_key_utils.py -│ │ ├── oxm -│ │ │ ├── __init__.py -│ │ │ ├── es -│ │ │ │ ├── __init__.py -│ │ │ │ ├── config_utils.py -│ │ │ │ └── tenant_aware_async_document.py -│ │ │ ├── milvus -│ │ │ │ ├── __init__.py -│ │ │ │ ├── config_utils.py -│ │ │ │ ├── tenant_aware_collection.py -│ │ │ │ └── tenant_aware_collection_with_suffix.py -│ │ │ └── mongo -│ │ │ ├── __init__.py -│ │ │ ├── config_utils.py -│ │ │ ├── tenant_aware_client_factory.py -│ │ │ └── tenant_aware_mongo_client.py -│ │ └── tenant_cache_utils.py -│ ├── devops_scripts -│ │ ├── __init__.py -│ │ ├── commitlint -│ │ │ ├── __init__.py -│ │ │ └── conventional_commit_lint.py -│ │ ├── data_fix -│ │ │ ├── __init__.py -│ │ │ ├── data_fix_docs -│ │ │ │ ├── DATA_FIX_README.md -│ │ │ │ ├── elasticsearch_scripts_guide.md -│ │ │ │ ├── milvus_scripts_guide.md -│ │ │ │ └── mongodb_scripts_guide.md -│ │ │ ├── es_rebuild_index.py -│ │ │ ├── es_sync_docs.py -│ │ │ ├── es_sync_episodic_memory_docs.py -│ │ │ ├── milvus_rebuild_collection.py -│ │ │ ├── milvus_sync_docs.py -│ │ │ ├── milvus_sync_episodic_memory_docs.py -│ │ │ ├── mongo_add_timestamp_shard.py -│ │ │ └── mongo_fix_episodic_memory_missing_vector.py -│ │ ├── i18n -│ │ │ ├── __init__.py -│ │ │ └── i18n_tool.py -│ │ ├── mongo_migrate.py -│ │ └── sensitive_info -│ │ ├── __init__.py -│ │ └── sensitive_info_tool.py -│ ├── infra_layer -│ │ ├── __init__.py -│ │ ├── adapters -│ │ │ ├── __init__.py -│ │ │ ├── input -│ │ │ │ ├── __init__.py -│ │ │ │ ├── api -│ │ │ │ │ ├── __init__.py -│ │ │ │ │ ├── dto -│ │ │ │ │ │ ├── global_user_profile_dto.py -│ │ │ │ │ │ ├── memory_dto.py -│ │ │ │ │ │ └── status_dto.py -│ │ │ │ │ ├── global_user_profile -│ │ │ │ │ │ ├── __init__.py -│ │ │ │ │ │ └── global_user_profile_controller.py -│ │ │ │ │ ├── health -│ │ │ │ │ │ └── health_controller.py -│ │ │ │ │ ├── mapper -│ │ │ │ │ │ ├── __init__.py -│ │ │ │ │ │ └── group_chat_converter.py -│ │ │ │ │ ├── memory -│ │ │ │ │ │ └── memory_controller.py -│ │ │ │ │ └── status -│ │ │ │ │ ├── __init__.py -│ │ │ │ │ └── status_controller.py -│ │ │ │ ├── jobs -│ │ │ │ │ └── __init__.py -│ │ │ │ ├── mcp -│ │ │ │ │ └── __init__.py -│ │ │ │ └── mq -│ │ │ │ ├── __init__.py -│ │ │ │ └── mapper -│ │ │ │ └── __init__.py -│ │ │ └── out -│ │ │ ├── event -│ │ │ │ ├── __init__.py -│ │ │ │ └── memcell_created_event.py -│ │ │ ├── persistence -│ │ │ │ ├── document -│ │ │ │ │ ├── __init__.py -│ │ │ │ │ ├── memory -│ │ │ │ │ │ ├── __init__.py -│ │ │ │ │ │ ├── behavior_history.py -│ │ │ │ │ │ ├── cluster_state.py -│ │ │ │ │ │ ├── conversation_meta.py -│ │ │ │ │ │ ├── conversation_status.py -│ │ │ │ │ │ ├── core_memory.py -│ │ │ │ │ │ ├── entity.py -│ │ │ │ │ │ ├── episodic_memory.py -│ │ │ │ │ │ ├── event_log_record.py -│ │ │ │ │ │ ├── foresight_record.py -│ │ │ │ │ │ ├── global_user_profile.py -│ │ │ │ │ │ ├── group_profile.py -│ │ │ │ │ │ ├── group_user_profile_memory.py -│ │ │ │ │ │ ├── memcell.py -│ │ │ │ │ │ ├── relationship.py -│ │ │ │ │ │ └── user_profile.py -│ │ │ │ │ └── request -│ │ │ │ │ ├── __init__.py -│ │ │ │ │ └── memory_request_log.py -│ │ │ │ ├── mapper -│ │ │ │ │ ├── __init__.py -│ │ │ │ │ └── memory_request_log_mapper.py -│ │ │ │ └── repository -│ │ │ │ ├── __init__.py -│ │ │ │ ├── behavior_history_raw_repository.py -│ │ │ │ ├── cluster_state_raw_repository.py -│ │ │ │ ├── conversation_data_raw_repository.py -│ │ │ │ ├── conversation_meta_raw_repository.py -│ │ │ │ ├── conversation_status_raw_repository.py -│ │ │ │ ├── core_memory_raw_repository.py -│ │ │ │ ├── entity_raw_repository.py -│ │ │ │ ├── episodic_memory_raw_repository.py -│ │ │ │ ├── event_log_record_raw_repository.py -│ │ │ │ ├── foresight_record_repository.py -│ │ │ │ ├── global_user_profile_raw_repository.py -│ │ │ │ ├── group_profile_raw_repository.py -│ │ │ │ ├── group_user_profile_memory_raw_repository.py -│ │ │ │ ├── memcell_raw_repository.py -│ │ │ │ ├── memory_request_log_repository.py -│ │ │ │ ├── relationship_raw_repository.py -│ │ │ │ └── user_profile_raw_repository.py -│ │ │ └── search -│ │ │ ├── __init__.py -│ │ │ ├── elasticsearch -│ │ │ │ ├── __init__.py -│ │ │ │ ├── converter -│ │ │ │ │ ├── __init__.py -│ │ │ │ │ ├── episodic_memory_converter.py -│ │ │ │ │ ├── event_log_converter.py -│ │ │ │ │ └── foresight_converter.py -│ │ │ │ └── memory -│ │ │ │ ├── __init__.py -│ │ │ │ ├── episodic_memory.py -│ │ │ │ ├── event_log.py -│ │ │ │ └── foresight.py -│ │ │ ├── milvus -│ │ │ │ ├── __init__.py -│ │ │ │ ├── converter -│ │ │ │ │ ├── __init__.py -│ │ │ │ │ ├── episodic_memory_milvus_converter.py -│ │ │ │ │ ├── event_log_milvus_converter.py -│ │ │ │ │ └── foresight_milvus_converter.py -│ │ │ │ └── memory -│ │ │ │ ├── __init__.py -│ │ │ │ ├── episodic_memory_collection.py -│ │ │ │ ├── event_log_collection.py -│ │ │ │ └── foresight_collection.py -│ │ │ └── repository -│ │ │ ├── __init__.py -│ │ │ ├── episodic_memory_es_repository.py -│ │ │ ├── episodic_memory_milvus_repository.py -│ │ │ ├── event_log_es_repository.py -│ │ │ ├── event_log_milvus_repository.py -│ │ │ ├── foresight_es_repository.py -│ │ │ └── foresight_milvus_repository.py -│ │ └── scripts -│ │ ├── __init__.py -│ │ └── migrations -│ │ └── __init__.py -│ ├── manage.py -│ ├── memory_layer -│ │ ├── __init__.py -│ │ ├── cluster_manager -│ │ │ ├── __init__.py -│ │ │ ├── config.py -│ │ │ └── manager.py -│ │ ├── constants.py -│ │ ├── llm -│ │ │ ├── __init__.py -│ │ │ ├── config.py -│ │ │ ├── llm_provider.py -│ │ │ ├── openai_provider.py -│ │ │ └── protocol.py -│ │ ├── memcell_extractor -│ │ │ ├── base_memcell_extractor.py -│ │ │ └── conv_memcell_extractor.py -│ │ ├── memory_extractor -│ │ │ ├── base_memory_extractor.py -│ │ │ ├── episode_memory_extractor.py -│ │ │ ├── event_log_extractor.py -│ │ │ ├── foresight_extractor.py -│ │ │ ├── group_profile -│ │ │ │ ├── __init__.py -│ │ │ │ ├── data_processor.py -│ │ │ │ ├── llm_handler.py -│ │ │ │ ├── role_processor.py -│ │ │ │ └── topic_processor.py -│ │ │ ├── group_profile_memory_extractor.py -│ │ │ ├── profile_memory -│ │ │ │ ├── __init__.py -│ │ │ │ ├── conversation.py -│ │ │ │ ├── data_normalize.py -│ │ │ │ ├── empty_evidence_completion.py -│ │ │ │ ├── evidence_utils.py -│ │ │ │ ├── extractor.py -│ │ │ │ ├── merger.py -│ │ │ │ ├── profile_helpers.py -│ │ │ │ ├── project_helpers.py -│ │ │ │ ├── skill_helpers.py -│ │ │ │ ├── types.py -│ │ │ │ └── value_helpers.py -│ │ │ ├── profile_memory_extractor.py -│ │ │ └── profile_memory_life -│ │ │ ├── __init__.py -│ │ │ ├── extractor.py -│ │ │ ├── id_mapper.py -│ │ │ └── types.py -│ │ ├── memory_manager.py -│ │ ├── profile_manager -│ │ │ ├── README.md -│ │ │ ├── __init__.py -│ │ │ ├── config.py -│ │ │ ├── discriminator.py -│ │ │ └── manager.py -│ │ └── prompts -│ │ ├── __init__.py -│ │ ├── en -│ │ │ ├── __init__.py -│ │ │ ├── conv_prompts.py -│ │ │ ├── episode_mem_prompts.py -│ │ │ ├── event_log_prompts.py -│ │ │ ├── foresight_prompts.py -│ │ │ ├── group_profile_merge_prompts.py -│ │ │ ├── group_profile_prompts.py -│ │ │ ├── profile_mem_evidence_completion_prompt.py -│ │ │ ├── profile_mem_life_prompts.py -│ │ │ ├── profile_mem_part1_prompts.py -│ │ │ ├── profile_mem_part2_prompts.py -│ │ │ ├── profile_mem_part3_prompts.py -│ │ │ └── profile_mem_prompts.py -│ │ └── zh -│ │ ├── __init__.py -│ │ ├── conv_prompts.py -│ │ ├── episode_mem_prompts.py -│ │ ├── event_log_prompts.py -│ │ ├── foresight_prompts.py -│ │ ├── group_profile_merge_prompts.py -│ │ ├── group_profile_prompts.py -│ │ ├── profile_mem_evidence_completion_prompt.py -│ │ ├── profile_mem_life_prompts.py -│ │ ├── profile_mem_part1_prompts.py -│ │ ├── profile_mem_part2_prompts.py -│ │ ├── profile_mem_part3_prompts.py -│ │ └── profile_mem_prompts.py -│ ├── migrations -│ │ ├── __init__.py -│ │ ├── mongodb -│ │ │ └── __init__.py -│ │ └── postgresql -│ │ └── __init__.py -│ ├── project_meta.py -│ ├── run.py -│ ├── run_memorize.py -│ ├── service -│ │ ├── __init__.py -│ │ ├── conversation_meta_service.py -│ │ ├── global_user_profile_service.py -│ │ ├── memcell_delete_service.py -│ │ ├── memory_request_log_service.py -│ │ └── request_status_service.py -│ └── task.py -├── tests -│ ├── test_business_lifespan_shutdown.py -│ ├── test_conv_memcell_extractor.py -│ ├── test_conversation_data_raw_repository.py -│ ├── test_conversation_meta.py -│ ├── test_conversation_status_raw_repository.py -│ ├── test_core_memory_raw_repository.py -│ ├── test_datetime_utils_to_iso_format.py -│ ├── test_embedding_reranker_providers.py -│ ├── test_episodic_memory_es_repository.py -│ ├── test_episodic_memory_milvus_repository.py -│ ├── test_global_user_profile_controller.py -│ ├── test_group_profile_datetime_check.py -│ ├── test_group_profile_raw_repository.py -│ ├── test_group_profile_simple.py -│ ├── test_group_user_profile_memory_raw_repository.py -│ ├── test_integration_vectorize_rerank.py -│ ├── test_keyword_vocabulary_milvus_repository.py -│ ├── test_memcell_raw_repository.py -│ ├── test_memory_controller.py -│ ├── test_memory_controller_request_params.py -│ ├── test_migrate_user_goal_to_work_responsibility.py -│ ├── test_msg_group_queue_manager.py -│ ├── test_pickle_size_analysis.py -│ ├── test_rate_limiter_quick.py -│ ├── test_rawdata_json_serialization.py -│ ├── test_redis_distributed_lock.py -│ ├── test_redis_length_cache.py -│ ├── test_redis_windows_cache.py -│ ├── test_request_converter.py -│ ├── test_retrieval_utils_similarity.py -│ ├── test_smart_text_parser.py -│ ├── test_stability_database.py -│ ├── test_stability_integration.py -│ ├── test_task_cancel.py -│ ├── test_task_cancel_2.py -│ ├── test_tokenizer_factory.py -│ └── test_wait_for.py -└── uv.lock \ No newline at end of file From 63cb3d2709d5c91802e2f0b9619de38b07f60be2 Mon Sep 17 00:00:00 2001 From: Exploreunive Date: Fri, 20 Mar 2026 10:26:38 +0800 Subject: [PATCH 4/5] chore: add tree_less.txt to .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 686575d8..9ec274ff 100755 --- a/.gitignore +++ b/.gitignore @@ -219,4 +219,4 @@ evaluation/locomo_evaluation/results_ref/demo/results/ # i18n translation progress .translation_progress.json -.review_progress.json \ No newline at end of file +.review_progress.jsontree_less.txt From 39a8e0c6e4d78de63d9f7bf4a80e329fa6570af5 Mon Sep 17 00:00:00 2001 From: Exploreunive Date: Fri, 20 Mar 2026 10:38:24 +0800 Subject: [PATCH 5/5] feat: add POST /memories/restore endpoint for soft-deleted memory recovery --- src/api_specs/dtos/memory.py | 45 +++++++ .../input/api/memory/memory_controller.py | 120 ++++++++++++++++++ src/service/memcell_restore_service.py | 95 ++++++++++++++ 3 files changed, 260 insertions(+) create mode 100644 src/service/memcell_restore_service.py diff --git a/src/api_specs/dtos/memory.py b/src/api_specs/dtos/memory.py index 4b5a6f57..81b26f90 100644 --- a/src/api_specs/dtos/memory.py +++ b/src/api_specs/dtos/memory.py @@ -922,3 +922,48 @@ class DeleteMemoriesResponse(BaseApiResponse[DeleteMemoriesResult]): ] } } + + +class RestoreMemoriesResult(BaseModel): + """Restore soft-deleted memories result data""" + + filters: List[str] = Field( + default_factory=list, + description="List of filter types used for restoration", + examples=[["event_id"], ["user_id"]], + ) + count: int = Field( + default=0, description="Number of memories restored", examples=[1, 25] + ) + + +class RestoreMemoriesResponse(BaseApiResponse[RestoreMemoriesResult]): + """Restore soft-deleted memories API response + + Response for POST /api/v1/memories/restore endpoint. + """ + + result: RestoreMemoriesResult = Field(description="Restore operation result") + + model_config = { + "json_schema_extra": { + "examples": [ + { + "summary": "Restore by event_id", + "value": { + "status": "ok", + "message": "Successfully restored 1 memory", + "result": {"filters": ["event_id"], "count": 1}, + }, + }, + { + "summary": "Restore by user_id", + "value": { + "status": "ok", + "message": "Successfully restored 25 memories", + "result": {"filters": ["user_id"], "count": 25}, + }, + }, + ] + } + } diff --git a/src/infra_layer/adapters/input/api/memory/memory_controller.py b/src/infra_layer/adapters/input/api/memory/memory_controller.py index a4a4d57e..36da4cfb 100644 --- a/src/infra_layer/adapters/input/api/memory/memory_controller.py +++ b/src/infra_layer/adapters/input/api/memory/memory_controller.py @@ -50,12 +50,14 @@ SaveConversationMetaResponse, PatchConversationMetaResponse, DeleteMemoriesResponse, + RestoreMemoriesResponse, ) from core.request.timeout_background import timeout_to_background from core.request import log_request from core.component.redis_provider import RedisProvider from service.memory_request_log_service import MemoryRequestLogService from service.memcell_delete_service import MemCellDeleteService +from service.memcell_restore_service import MemCellRestoreService from service.conversation_meta_service import ConversationMetaService from api_specs.memory_types import RawDataType from agentic_layer.metrics.memorize_metrics import ( @@ -1120,3 +1122,121 @@ async def delete_memories( status_code=500, detail="Failed to delete memories, please try again later", ) from e + + @post( + "/restore", + response_model=RestoreMemoriesResponse, + summary="Restore soft-deleted memories", + description=""" + Restore soft-deleted memory records based on filter criteria + + ## Functionality: + - Restore previously soft-deleted memories + - Supports restore by event_id (single memory) or user_id (batch) + - At least one filter must be specified + + ## Filter Parameters (provide one): + - **event_id**: Restore a specific memory by its event_id + - **user_id**: Restore all soft-deleted memories of a user + + ## Use Cases: + - Undo accidental deletion + - Recover user data + - Data restoration after testing + """, + responses={ + 400: { + "description": "Request parameter error", + "content": { + "application/json": { + "example": { + "status": ErrorStatus.FAILED.value, + "code": ErrorCode.INVALID_PARAMETER.value, + "message": "At least one of event_id or user_id must be provided", + } + } + }, + }, + 500: { + "description": "Internal server error", + "content": { + "application/json": { + "example": { + "status": ErrorStatus.FAILED.value, + "code": ErrorCode.SYSTEM_ERROR.value, + "message": "Failed to restore memories, please try again later", + } + } + }, + }, + }, + ) + async def restore_memories( + self, + fastapi_request: FastAPIRequest, + request_body=None, + ) -> RestoreMemoriesResponse: + """ + Restore soft-deleted memory data based on filter criteria + """ + del request_body + + try: + from core.oxm.constants import MAGIC_ALL + + params = await self._collect_request_params(fastapi_request) + + event_id = params.get("event_id", MAGIC_ALL) + user_id = params.get("user_id", MAGIC_ALL) + + # Validate: at least one filter required + if (not event_id or event_id == MAGIC_ALL) and ( + not user_id or user_id == MAGIC_ALL + ): + raise HTTPException( + status_code=400, + detail="At least one of event_id or user_id must be provided", + ) + + logger.info( + "Received restore request: event_id=%s, user_id=%s", + event_id, + user_id, + ) + + # Get restore service + restore_service = get_bean_by_type(MemCellRestoreService) + + # Execute restore + result = await restore_service.restore_by_combined_criteria( + event_id=event_id, + user_id=user_id, + ) + + if not result["success"]: + error_msg = result.get( + "error", "No soft-deleted memories found matching the criteria" + ) + logger.warning("Restore operation returned no results: %s", result) + raise HTTPException(status_code=404, detail=error_msg) + + logger.info( + "Restore request completed successfully: filters=%s, count=%d", + result["filters"], + result["count"], + ) + + return { + "status": ErrorStatus.OK.value, + "message": f"Successfully restored {result['count']} {'memory' if result['count'] == 1 else 'memories'}", + "result": {"filters": result["filters"], "count": result["count"]}, + } + + except HTTPException: + raise + except Exception as e: + logger.error("Restore request processing failed: %s", e, exc_info=True) + raise HTTPException( + status_code=500, + detail="Failed to restore memories, please try again later", + ) from e diff --git a/src/service/memcell_restore_service.py b/src/service/memcell_restore_service.py new file mode 100644 index 00000000..31489064 --- /dev/null +++ b/src/service/memcell_restore_service.py @@ -0,0 +1,95 @@ +""" +MemCell Restore Service - Handle restore logic for soft-deleted MemCells + +Provides multiple restoration methods: +- Restore by single event_id +- Batch restore by user_id +- Batch restore by combined criteria +""" + +from typing import Optional +from core.di.decorators import component +from core.observation.logger import get_logger +from infra_layer.adapters.out.persistence.repository.memcell_raw_repository import ( + MemCellRawRepository, +) + +logger = get_logger(__name__) + + +@component("memcell_restore_service") +class MemCellRestoreService: + """MemCell restore service for soft-deleted records""" + + def __init__(self, memcell_repository: MemCellRawRepository): + self.memcell_repository = memcell_repository + logger.info("MemCellRestoreService initialized") + + async def restore_by_combined_criteria( + self, + event_id: Optional[str] = None, + user_id: Optional[str] = None, + ) -> dict: + """ + Restore soft-deleted MemCells based on combined criteria + + Args: + event_id: The event_id of MemCell + user_id: User ID (batch restore all deleted memories of a user) + + Returns: + dict: Dictionary containing restoration results + - filters: List of filter conditions used + - count: Number of restored records + - success: Whether the operation succeeded + """ + from core.oxm.constants import MAGIC_ALL + + filters_used = [] + + # Restore by event_id + if event_id and event_id != MAGIC_ALL: + filters_used.append("event_id") + try: + success = await self.memcell_repository.restore_by_event_id(event_id) + return { + "filters": filters_used, + "count": 1 if success else 0, + "success": success, + } + except Exception as e: + logger.error("Failed to restore by event_id: %s", e) + return { + "filters": filters_used, + "count": 0, + "success": False, + "error": str(e), + } + + # Restore by user_id + if user_id and user_id != MAGIC_ALL: + filters_used.append("user_id") + try: + count = await self.memcell_repository.restore_by_user_id(user_id) + return { + "filters": filters_used, + "count": count, + "success": count > 0, + } + except Exception as e: + logger.error("Failed to restore by user_id: %s", e) + return { + "filters": filters_used, + "count": 0, + "success": False, + "error": str(e), + } + + # No filter conditions provided + logger.warning("No restore criteria provided") + return { + "filters": [], + "count": 0, + "success": False, + "error": "At least one of event_id or user_id must be provided", + }