MemoryOS 2.0.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- memoryos-2.0.3.dist-info/METADATA +418 -0
- memoryos-2.0.3.dist-info/RECORD +315 -0
- memoryos-2.0.3.dist-info/WHEEL +4 -0
- memoryos-2.0.3.dist-info/entry_points.txt +3 -0
- memoryos-2.0.3.dist-info/licenses/LICENSE +201 -0
- memos/__init__.py +20 -0
- memos/api/client.py +571 -0
- memos/api/config.py +1018 -0
- memos/api/context/dependencies.py +50 -0
- memos/api/exceptions.py +53 -0
- memos/api/handlers/__init__.py +62 -0
- memos/api/handlers/add_handler.py +158 -0
- memos/api/handlers/base_handler.py +194 -0
- memos/api/handlers/chat_handler.py +1401 -0
- memos/api/handlers/component_init.py +388 -0
- memos/api/handlers/config_builders.py +190 -0
- memos/api/handlers/feedback_handler.py +93 -0
- memos/api/handlers/formatters_handler.py +237 -0
- memos/api/handlers/memory_handler.py +316 -0
- memos/api/handlers/scheduler_handler.py +497 -0
- memos/api/handlers/search_handler.py +222 -0
- memos/api/handlers/suggestion_handler.py +117 -0
- memos/api/mcp_serve.py +614 -0
- memos/api/middleware/request_context.py +101 -0
- memos/api/product_api.py +38 -0
- memos/api/product_models.py +1206 -0
- memos/api/routers/__init__.py +1 -0
- memos/api/routers/product_router.py +477 -0
- memos/api/routers/server_router.py +394 -0
- memos/api/server_api.py +44 -0
- memos/api/start_api.py +433 -0
- memos/chunkers/__init__.py +4 -0
- memos/chunkers/base.py +24 -0
- memos/chunkers/charactertext_chunker.py +41 -0
- memos/chunkers/factory.py +24 -0
- memos/chunkers/markdown_chunker.py +62 -0
- memos/chunkers/sentence_chunker.py +54 -0
- memos/chunkers/simple_chunker.py +50 -0
- memos/cli.py +113 -0
- memos/configs/__init__.py +0 -0
- memos/configs/base.py +82 -0
- memos/configs/chunker.py +59 -0
- memos/configs/embedder.py +88 -0
- memos/configs/graph_db.py +236 -0
- memos/configs/internet_retriever.py +100 -0
- memos/configs/llm.py +151 -0
- memos/configs/mem_agent.py +54 -0
- memos/configs/mem_chat.py +81 -0
- memos/configs/mem_cube.py +105 -0
- memos/configs/mem_os.py +83 -0
- memos/configs/mem_reader.py +91 -0
- memos/configs/mem_scheduler.py +385 -0
- memos/configs/mem_user.py +70 -0
- memos/configs/memory.py +324 -0
- memos/configs/parser.py +38 -0
- memos/configs/reranker.py +18 -0
- memos/configs/utils.py +8 -0
- memos/configs/vec_db.py +80 -0
- memos/context/context.py +355 -0
- memos/dependency.py +52 -0
- memos/deprecation.py +262 -0
- memos/embedders/__init__.py +0 -0
- memos/embedders/ark.py +95 -0
- memos/embedders/base.py +106 -0
- memos/embedders/factory.py +29 -0
- memos/embedders/ollama.py +77 -0
- memos/embedders/sentence_transformer.py +49 -0
- memos/embedders/universal_api.py +51 -0
- memos/exceptions.py +30 -0
- memos/graph_dbs/__init__.py +0 -0
- memos/graph_dbs/base.py +274 -0
- memos/graph_dbs/factory.py +27 -0
- memos/graph_dbs/item.py +46 -0
- memos/graph_dbs/nebular.py +1794 -0
- memos/graph_dbs/neo4j.py +1942 -0
- memos/graph_dbs/neo4j_community.py +1058 -0
- memos/graph_dbs/polardb.py +5446 -0
- memos/hello_world.py +97 -0
- memos/llms/__init__.py +0 -0
- memos/llms/base.py +25 -0
- memos/llms/deepseek.py +13 -0
- memos/llms/factory.py +38 -0
- memos/llms/hf.py +443 -0
- memos/llms/hf_singleton.py +114 -0
- memos/llms/ollama.py +135 -0
- memos/llms/openai.py +222 -0
- memos/llms/openai_new.py +198 -0
- memos/llms/qwen.py +13 -0
- memos/llms/utils.py +14 -0
- memos/llms/vllm.py +218 -0
- memos/log.py +237 -0
- memos/mem_agent/base.py +19 -0
- memos/mem_agent/deepsearch_agent.py +391 -0
- memos/mem_agent/factory.py +36 -0
- memos/mem_chat/__init__.py +0 -0
- memos/mem_chat/base.py +30 -0
- memos/mem_chat/factory.py +21 -0
- memos/mem_chat/simple.py +200 -0
- memos/mem_cube/__init__.py +0 -0
- memos/mem_cube/base.py +30 -0
- memos/mem_cube/general.py +240 -0
- memos/mem_cube/navie.py +172 -0
- memos/mem_cube/utils.py +169 -0
- memos/mem_feedback/base.py +15 -0
- memos/mem_feedback/feedback.py +1192 -0
- memos/mem_feedback/simple_feedback.py +40 -0
- memos/mem_feedback/utils.py +230 -0
- memos/mem_os/client.py +5 -0
- memos/mem_os/core.py +1203 -0
- memos/mem_os/main.py +582 -0
- memos/mem_os/product.py +1608 -0
- memos/mem_os/product_server.py +455 -0
- memos/mem_os/utils/default_config.py +359 -0
- memos/mem_os/utils/format_utils.py +1403 -0
- memos/mem_os/utils/reference_utils.py +162 -0
- memos/mem_reader/__init__.py +0 -0
- memos/mem_reader/base.py +47 -0
- memos/mem_reader/factory.py +53 -0
- memos/mem_reader/memory.py +298 -0
- memos/mem_reader/multi_modal_struct.py +965 -0
- memos/mem_reader/read_multi_modal/__init__.py +43 -0
- memos/mem_reader/read_multi_modal/assistant_parser.py +311 -0
- memos/mem_reader/read_multi_modal/base.py +273 -0
- memos/mem_reader/read_multi_modal/file_content_parser.py +826 -0
- memos/mem_reader/read_multi_modal/image_parser.py +359 -0
- memos/mem_reader/read_multi_modal/multi_modal_parser.py +252 -0
- memos/mem_reader/read_multi_modal/string_parser.py +139 -0
- memos/mem_reader/read_multi_modal/system_parser.py +327 -0
- memos/mem_reader/read_multi_modal/text_content_parser.py +131 -0
- memos/mem_reader/read_multi_modal/tool_parser.py +210 -0
- memos/mem_reader/read_multi_modal/user_parser.py +218 -0
- memos/mem_reader/read_multi_modal/utils.py +358 -0
- memos/mem_reader/simple_struct.py +912 -0
- memos/mem_reader/strategy_struct.py +163 -0
- memos/mem_reader/utils.py +157 -0
- memos/mem_scheduler/__init__.py +0 -0
- memos/mem_scheduler/analyzer/__init__.py +0 -0
- memos/mem_scheduler/analyzer/api_analyzer.py +714 -0
- memos/mem_scheduler/analyzer/eval_analyzer.py +219 -0
- memos/mem_scheduler/analyzer/mos_for_test_scheduler.py +571 -0
- memos/mem_scheduler/analyzer/scheduler_for_eval.py +280 -0
- memos/mem_scheduler/base_scheduler.py +1319 -0
- memos/mem_scheduler/general_modules/__init__.py +0 -0
- memos/mem_scheduler/general_modules/api_misc.py +137 -0
- memos/mem_scheduler/general_modules/base.py +80 -0
- memos/mem_scheduler/general_modules/init_components_for_scheduler.py +425 -0
- memos/mem_scheduler/general_modules/misc.py +313 -0
- memos/mem_scheduler/general_modules/scheduler_logger.py +389 -0
- memos/mem_scheduler/general_modules/task_threads.py +315 -0
- memos/mem_scheduler/general_scheduler.py +1495 -0
- memos/mem_scheduler/memory_manage_modules/__init__.py +5 -0
- memos/mem_scheduler/memory_manage_modules/memory_filter.py +306 -0
- memos/mem_scheduler/memory_manage_modules/retriever.py +547 -0
- memos/mem_scheduler/monitors/__init__.py +0 -0
- memos/mem_scheduler/monitors/dispatcher_monitor.py +366 -0
- memos/mem_scheduler/monitors/general_monitor.py +394 -0
- memos/mem_scheduler/monitors/task_schedule_monitor.py +254 -0
- memos/mem_scheduler/optimized_scheduler.py +410 -0
- memos/mem_scheduler/orm_modules/__init__.py +0 -0
- memos/mem_scheduler/orm_modules/api_redis_model.py +518 -0
- memos/mem_scheduler/orm_modules/base_model.py +729 -0
- memos/mem_scheduler/orm_modules/monitor_models.py +261 -0
- memos/mem_scheduler/orm_modules/redis_model.py +699 -0
- memos/mem_scheduler/scheduler_factory.py +23 -0
- memos/mem_scheduler/schemas/__init__.py +0 -0
- memos/mem_scheduler/schemas/analyzer_schemas.py +52 -0
- memos/mem_scheduler/schemas/api_schemas.py +233 -0
- memos/mem_scheduler/schemas/general_schemas.py +55 -0
- memos/mem_scheduler/schemas/message_schemas.py +173 -0
- memos/mem_scheduler/schemas/monitor_schemas.py +406 -0
- memos/mem_scheduler/schemas/task_schemas.py +132 -0
- memos/mem_scheduler/task_schedule_modules/__init__.py +0 -0
- memos/mem_scheduler/task_schedule_modules/dispatcher.py +740 -0
- memos/mem_scheduler/task_schedule_modules/local_queue.py +247 -0
- memos/mem_scheduler/task_schedule_modules/orchestrator.py +74 -0
- memos/mem_scheduler/task_schedule_modules/redis_queue.py +1385 -0
- memos/mem_scheduler/task_schedule_modules/task_queue.py +162 -0
- memos/mem_scheduler/utils/__init__.py +0 -0
- memos/mem_scheduler/utils/api_utils.py +77 -0
- memos/mem_scheduler/utils/config_utils.py +100 -0
- memos/mem_scheduler/utils/db_utils.py +50 -0
- memos/mem_scheduler/utils/filter_utils.py +176 -0
- memos/mem_scheduler/utils/metrics.py +125 -0
- memos/mem_scheduler/utils/misc_utils.py +290 -0
- memos/mem_scheduler/utils/monitor_event_utils.py +67 -0
- memos/mem_scheduler/utils/status_tracker.py +229 -0
- memos/mem_scheduler/webservice_modules/__init__.py +0 -0
- memos/mem_scheduler/webservice_modules/rabbitmq_service.py +485 -0
- memos/mem_scheduler/webservice_modules/redis_service.py +380 -0
- memos/mem_user/factory.py +94 -0
- memos/mem_user/mysql_persistent_user_manager.py +271 -0
- memos/mem_user/mysql_user_manager.py +502 -0
- memos/mem_user/persistent_factory.py +98 -0
- memos/mem_user/persistent_user_manager.py +260 -0
- memos/mem_user/redis_persistent_user_manager.py +225 -0
- memos/mem_user/user_manager.py +488 -0
- memos/memories/__init__.py +0 -0
- memos/memories/activation/__init__.py +0 -0
- memos/memories/activation/base.py +42 -0
- memos/memories/activation/item.py +56 -0
- memos/memories/activation/kv.py +292 -0
- memos/memories/activation/vllmkv.py +219 -0
- memos/memories/base.py +19 -0
- memos/memories/factory.py +42 -0
- memos/memories/parametric/__init__.py +0 -0
- memos/memories/parametric/base.py +19 -0
- memos/memories/parametric/item.py +11 -0
- memos/memories/parametric/lora.py +41 -0
- memos/memories/textual/__init__.py +0 -0
- memos/memories/textual/base.py +92 -0
- memos/memories/textual/general.py +236 -0
- memos/memories/textual/item.py +304 -0
- memos/memories/textual/naive.py +187 -0
- memos/memories/textual/prefer_text_memory/__init__.py +0 -0
- memos/memories/textual/prefer_text_memory/adder.py +504 -0
- memos/memories/textual/prefer_text_memory/config.py +106 -0
- memos/memories/textual/prefer_text_memory/extractor.py +221 -0
- memos/memories/textual/prefer_text_memory/factory.py +85 -0
- memos/memories/textual/prefer_text_memory/retrievers.py +177 -0
- memos/memories/textual/prefer_text_memory/spliter.py +132 -0
- memos/memories/textual/prefer_text_memory/utils.py +93 -0
- memos/memories/textual/preference.py +344 -0
- memos/memories/textual/simple_preference.py +161 -0
- memos/memories/textual/simple_tree.py +69 -0
- memos/memories/textual/tree.py +459 -0
- memos/memories/textual/tree_text_memory/__init__.py +0 -0
- memos/memories/textual/tree_text_memory/organize/__init__.py +0 -0
- memos/memories/textual/tree_text_memory/organize/handler.py +184 -0
- memos/memories/textual/tree_text_memory/organize/manager.py +518 -0
- memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py +238 -0
- memos/memories/textual/tree_text_memory/organize/reorganizer.py +622 -0
- memos/memories/textual/tree_text_memory/retrieve/__init__.py +0 -0
- memos/memories/textual/tree_text_memory/retrieve/advanced_searcher.py +364 -0
- memos/memories/textual/tree_text_memory/retrieve/bm25_util.py +186 -0
- memos/memories/textual/tree_text_memory/retrieve/bochasearch.py +419 -0
- memos/memories/textual/tree_text_memory/retrieve/internet_retriever.py +270 -0
- memos/memories/textual/tree_text_memory/retrieve/internet_retriever_factory.py +102 -0
- memos/memories/textual/tree_text_memory/retrieve/reasoner.py +61 -0
- memos/memories/textual/tree_text_memory/retrieve/recall.py +497 -0
- memos/memories/textual/tree_text_memory/retrieve/reranker.py +111 -0
- memos/memories/textual/tree_text_memory/retrieve/retrieval_mid_structs.py +16 -0
- memos/memories/textual/tree_text_memory/retrieve/retrieve_utils.py +472 -0
- memos/memories/textual/tree_text_memory/retrieve/searcher.py +848 -0
- memos/memories/textual/tree_text_memory/retrieve/task_goal_parser.py +135 -0
- memos/memories/textual/tree_text_memory/retrieve/utils.py +54 -0
- memos/memories/textual/tree_text_memory/retrieve/xinyusearch.py +387 -0
- memos/memos_tools/dinding_report_bot.py +453 -0
- memos/memos_tools/lockfree_dict.py +120 -0
- memos/memos_tools/notification_service.py +44 -0
- memos/memos_tools/notification_utils.py +142 -0
- memos/memos_tools/singleton.py +174 -0
- memos/memos_tools/thread_safe_dict.py +310 -0
- memos/memos_tools/thread_safe_dict_segment.py +382 -0
- memos/multi_mem_cube/__init__.py +0 -0
- memos/multi_mem_cube/composite_cube.py +86 -0
- memos/multi_mem_cube/single_cube.py +874 -0
- memos/multi_mem_cube/views.py +54 -0
- memos/parsers/__init__.py +0 -0
- memos/parsers/base.py +15 -0
- memos/parsers/factory.py +21 -0
- memos/parsers/markitdown.py +28 -0
- memos/reranker/__init__.py +4 -0
- memos/reranker/base.py +25 -0
- memos/reranker/concat.py +103 -0
- memos/reranker/cosine_local.py +102 -0
- memos/reranker/factory.py +72 -0
- memos/reranker/http_bge.py +324 -0
- memos/reranker/http_bge_strategy.py +327 -0
- memos/reranker/noop.py +19 -0
- memos/reranker/strategies/__init__.py +4 -0
- memos/reranker/strategies/base.py +61 -0
- memos/reranker/strategies/concat_background.py +94 -0
- memos/reranker/strategies/concat_docsource.py +110 -0
- memos/reranker/strategies/dialogue_common.py +109 -0
- memos/reranker/strategies/factory.py +31 -0
- memos/reranker/strategies/single_turn.py +107 -0
- memos/reranker/strategies/singleturn_outmem.py +98 -0
- memos/settings.py +10 -0
- memos/templates/__init__.py +0 -0
- memos/templates/advanced_search_prompts.py +211 -0
- memos/templates/cloud_service_prompt.py +107 -0
- memos/templates/instruction_completion.py +66 -0
- memos/templates/mem_agent_prompts.py +85 -0
- memos/templates/mem_feedback_prompts.py +822 -0
- memos/templates/mem_reader_prompts.py +1096 -0
- memos/templates/mem_reader_strategy_prompts.py +238 -0
- memos/templates/mem_scheduler_prompts.py +626 -0
- memos/templates/mem_search_prompts.py +93 -0
- memos/templates/mos_prompts.py +403 -0
- memos/templates/prefer_complete_prompt.py +735 -0
- memos/templates/tool_mem_prompts.py +139 -0
- memos/templates/tree_reorganize_prompts.py +230 -0
- memos/types/__init__.py +34 -0
- memos/types/general_types.py +151 -0
- memos/types/openai_chat_completion_types/__init__.py +15 -0
- memos/types/openai_chat_completion_types/chat_completion_assistant_message_param.py +56 -0
- memos/types/openai_chat_completion_types/chat_completion_content_part_image_param.py +27 -0
- memos/types/openai_chat_completion_types/chat_completion_content_part_input_audio_param.py +23 -0
- memos/types/openai_chat_completion_types/chat_completion_content_part_param.py +43 -0
- memos/types/openai_chat_completion_types/chat_completion_content_part_refusal_param.py +16 -0
- memos/types/openai_chat_completion_types/chat_completion_content_part_text_param.py +16 -0
- memos/types/openai_chat_completion_types/chat_completion_message_custom_tool_call_param.py +27 -0
- memos/types/openai_chat_completion_types/chat_completion_message_function_tool_call_param.py +32 -0
- memos/types/openai_chat_completion_types/chat_completion_message_param.py +18 -0
- memos/types/openai_chat_completion_types/chat_completion_message_tool_call_union_param.py +15 -0
- memos/types/openai_chat_completion_types/chat_completion_system_message_param.py +36 -0
- memos/types/openai_chat_completion_types/chat_completion_tool_message_param.py +30 -0
- memos/types/openai_chat_completion_types/chat_completion_user_message_param.py +34 -0
- memos/utils.py +123 -0
- memos/vec_dbs/__init__.py +0 -0
- memos/vec_dbs/base.py +117 -0
- memos/vec_dbs/factory.py +23 -0
- memos/vec_dbs/item.py +50 -0
- memos/vec_dbs/milvus.py +654 -0
- memos/vec_dbs/qdrant.py +355 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
from memos.configs.mem_scheduler import BaseSchedulerConfig
|
|
2
|
+
from memos.llms.base import BaseLLM
|
|
3
|
+
from memos.log import get_logger
|
|
4
|
+
from memos.mem_scheduler.general_modules.base import BaseSchedulerModule
|
|
5
|
+
from memos.mem_scheduler.utils.misc_utils import extract_json_obj
|
|
6
|
+
from memos.memories.textual.tree import TextualMemoryItem
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
logger = get_logger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MemoryFilter(BaseSchedulerModule):
|
|
13
|
+
def __init__(self, process_llm: BaseLLM, config: BaseSchedulerConfig):
|
|
14
|
+
super().__init__()
|
|
15
|
+
self.config: BaseSchedulerConfig = config
|
|
16
|
+
self.process_llm = process_llm
|
|
17
|
+
|
|
18
|
+
def filter_unrelated_memories(
|
|
19
|
+
self,
|
|
20
|
+
query_history: list[str],
|
|
21
|
+
memories: list[TextualMemoryItem],
|
|
22
|
+
) -> (list[TextualMemoryItem], bool):
|
|
23
|
+
"""
|
|
24
|
+
Filter out memories that are completely unrelated to the query history using LLM.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
query_history: List of query strings to determine relevance
|
|
28
|
+
memories: List of TextualMemoryItem objects to be filtered
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Tuple of (filtered_memories, success_flag)
|
|
32
|
+
- filtered_memories: List of TextualMemoryItem objects that are relevant to queries
|
|
33
|
+
- success_flag: Boolean indicating if LLM filtering was successful
|
|
34
|
+
|
|
35
|
+
Note:
|
|
36
|
+
If LLM filtering fails, returns all memories (conservative approach)
|
|
37
|
+
"""
|
|
38
|
+
success_flag = False
|
|
39
|
+
|
|
40
|
+
if not memories:
|
|
41
|
+
logger.info("No memories to filter - returning empty list")
|
|
42
|
+
return [], True
|
|
43
|
+
|
|
44
|
+
if not query_history:
|
|
45
|
+
logger.info("No query history provided - keeping all memories")
|
|
46
|
+
return memories, True
|
|
47
|
+
|
|
48
|
+
logger.info(
|
|
49
|
+
f"Starting memory filtering for {len(memories)} memories against {len(query_history)} queries"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Extract memory texts for LLM processing
|
|
53
|
+
memory_texts = [mem.memory for mem in memories]
|
|
54
|
+
|
|
55
|
+
# Build LLM prompt for memory filtering
|
|
56
|
+
prompt = self.build_prompt(
|
|
57
|
+
"memory_filtering",
|
|
58
|
+
query_history=[f"[{i}] {query}" for i, query in enumerate(query_history)],
|
|
59
|
+
memories=[f"[{i}] {mem}" for i, mem in enumerate(memory_texts)],
|
|
60
|
+
)
|
|
61
|
+
logger.debug(f"Generated filtering prompt: {prompt[:200]}...") # Log first 200 chars
|
|
62
|
+
|
|
63
|
+
# Get LLM response
|
|
64
|
+
response = self.process_llm.generate([{"role": "user", "content": prompt}])
|
|
65
|
+
logger.debug(f"Received LLM filtering response: {response[:200]}...") # Log first 200 chars
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
# Parse JSON response
|
|
69
|
+
response = extract_json_obj(response)
|
|
70
|
+
logger.debug(f"Parsed JSON response: {response}")
|
|
71
|
+
relevant_indices = response["relevant_memories"]
|
|
72
|
+
filtered_count = response["filtered_count"]
|
|
73
|
+
reasoning = response["reasoning"]
|
|
74
|
+
|
|
75
|
+
# Validate indices
|
|
76
|
+
if not isinstance(relevant_indices, list):
|
|
77
|
+
raise ValueError("relevant_memories must be a list")
|
|
78
|
+
|
|
79
|
+
# Filter memories based on relevant indices
|
|
80
|
+
filtered_memories = []
|
|
81
|
+
for idx in relevant_indices:
|
|
82
|
+
if isinstance(idx, int) and 0 <= idx < len(memories):
|
|
83
|
+
filtered_memories.append(memories[idx])
|
|
84
|
+
else:
|
|
85
|
+
logger.warning(f"Invalid memory index {idx} - skipping")
|
|
86
|
+
|
|
87
|
+
logger.info(
|
|
88
|
+
f"Successfully filtered memories. Kept {len(filtered_memories)} out of {len(memories)} memories. "
|
|
89
|
+
f"Filtered out {filtered_count} unrelated memories. "
|
|
90
|
+
f"Filtering reasoning: {reasoning}"
|
|
91
|
+
)
|
|
92
|
+
success_flag = True
|
|
93
|
+
|
|
94
|
+
except Exception as e:
|
|
95
|
+
logger.error(
|
|
96
|
+
f"Failed to filter memories with LLM. Exception: {e}. Raw response: {response}",
|
|
97
|
+
exc_info=True,
|
|
98
|
+
)
|
|
99
|
+
# Conservative approach: keep all memories if filtering fails
|
|
100
|
+
filtered_memories = memories
|
|
101
|
+
success_flag = False
|
|
102
|
+
|
|
103
|
+
return filtered_memories, success_flag
|
|
104
|
+
|
|
105
|
+
def filter_redundant_memories(
|
|
106
|
+
self,
|
|
107
|
+
query_history: list[str],
|
|
108
|
+
memories: list[TextualMemoryItem],
|
|
109
|
+
) -> (list[TextualMemoryItem], bool):
|
|
110
|
+
"""
|
|
111
|
+
Filter out redundant memories using LLM analysis.
|
|
112
|
+
|
|
113
|
+
This function removes redundant memories by keeping the most informative
|
|
114
|
+
version when multiple memories contain similar information relevant to queries.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
query_history: List of query strings to determine relevance and value
|
|
118
|
+
memories: List of TextualMemoryItem objects to be filtered
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
Tuple of (filtered_memories, success_flag)
|
|
122
|
+
- filtered_memories: List of TextualMemoryItem objects after redundancy filtering
|
|
123
|
+
- success_flag: Boolean indicating if LLM filtering was successful
|
|
124
|
+
|
|
125
|
+
Note:
|
|
126
|
+
If LLM filtering fails, returns all memories (conservative approach)
|
|
127
|
+
"""
|
|
128
|
+
success_flag = False
|
|
129
|
+
|
|
130
|
+
if not memories:
|
|
131
|
+
logger.info("No memories to filter for redundancy - returning empty list")
|
|
132
|
+
return [], True
|
|
133
|
+
|
|
134
|
+
if not query_history:
|
|
135
|
+
logger.info("No query history provided - keeping all memories")
|
|
136
|
+
return memories, True
|
|
137
|
+
|
|
138
|
+
if len(memories) <= 1:
|
|
139
|
+
logger.info("Only one memory - no redundancy to filter")
|
|
140
|
+
return memories, True
|
|
141
|
+
|
|
142
|
+
logger.info(
|
|
143
|
+
f"Starting redundancy filtering for {len(memories)} memories against {len(query_history)} queries"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
# Extract memory texts for LLM processing
|
|
147
|
+
memory_texts = [mem.memory for mem in memories]
|
|
148
|
+
|
|
149
|
+
# Build LLM prompt for redundancy filtering
|
|
150
|
+
prompt = self.build_prompt(
|
|
151
|
+
"memory_redundancy_filtering",
|
|
152
|
+
query_history=[f"[{i}] {query}" for i, query in enumerate(query_history)],
|
|
153
|
+
memories=[f"[{i}] {mem}" for i, mem in enumerate(memory_texts)],
|
|
154
|
+
)
|
|
155
|
+
logger.debug(
|
|
156
|
+
f"Generated redundancy filtering prompt: {prompt[:200]}..."
|
|
157
|
+
) # Log first 200 chars
|
|
158
|
+
|
|
159
|
+
# Get LLM response
|
|
160
|
+
response = self.process_llm.generate([{"role": "user", "content": prompt}])
|
|
161
|
+
logger.debug(
|
|
162
|
+
f"Received LLM redundancy filtering response: {response[:200]}..."
|
|
163
|
+
) # Log first 200 chars
|
|
164
|
+
|
|
165
|
+
try:
|
|
166
|
+
# Parse JSON response
|
|
167
|
+
response = extract_json_obj(response)
|
|
168
|
+
logger.debug(f"Parsed JSON response: {response}")
|
|
169
|
+
kept_indices = response["kept_memories"]
|
|
170
|
+
redundant_groups = response.get("redundant_groups", [])
|
|
171
|
+
reasoning = response["reasoning"]
|
|
172
|
+
|
|
173
|
+
# Validate indices
|
|
174
|
+
if not isinstance(kept_indices, list):
|
|
175
|
+
raise ValueError("kept_memories must be a list")
|
|
176
|
+
|
|
177
|
+
# Filter memories based on kept indices
|
|
178
|
+
filtered_memories = []
|
|
179
|
+
for idx in kept_indices:
|
|
180
|
+
if isinstance(idx, int) and 0 <= idx < len(memories):
|
|
181
|
+
filtered_memories.append(memories[idx])
|
|
182
|
+
else:
|
|
183
|
+
logger.warning(f"Invalid memory index {idx} - skipping")
|
|
184
|
+
|
|
185
|
+
logger.info(
|
|
186
|
+
f"Successfully filtered redundant memories. "
|
|
187
|
+
f"Kept {len(filtered_memories)} out of {len(memories)} memories. "
|
|
188
|
+
f"Removed {len(memories) - len(filtered_memories)} redundant memories. "
|
|
189
|
+
f"Redundant groups identified: {len(redundant_groups)}. "
|
|
190
|
+
f"Filtering reasoning: {reasoning}"
|
|
191
|
+
)
|
|
192
|
+
success_flag = True
|
|
193
|
+
|
|
194
|
+
except Exception as e:
|
|
195
|
+
logger.error(
|
|
196
|
+
f"Failed to filter redundant memories with LLM. Exception: {e}. Raw response: {response}",
|
|
197
|
+
exc_info=True,
|
|
198
|
+
)
|
|
199
|
+
# Conservative approach: keep all memories if filtering fails
|
|
200
|
+
filtered_memories = memories
|
|
201
|
+
success_flag = False
|
|
202
|
+
|
|
203
|
+
return filtered_memories, success_flag
|
|
204
|
+
|
|
205
|
+
def filter_unrelated_and_redundant_memories(
|
|
206
|
+
self,
|
|
207
|
+
query_history: list[str],
|
|
208
|
+
memories: list[TextualMemoryItem],
|
|
209
|
+
) -> (list[TextualMemoryItem], bool):
|
|
210
|
+
"""
|
|
211
|
+
Filter out both unrelated and redundant memories using LLM analysis.
|
|
212
|
+
|
|
213
|
+
This function performs two types of filtering in sequence:
|
|
214
|
+
1. Remove memories that are completely unrelated to the query history
|
|
215
|
+
2. Remove redundant memories by keeping the most informative version
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
query_history: List of query strings to determine relevance and value
|
|
219
|
+
memories: List of TextualMemoryItem objects to be filtered
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
Tuple of (filtered_memories, success_flag)
|
|
223
|
+
- filtered_memories: List of TextualMemoryItem objects after both filtering steps
|
|
224
|
+
- success_flag: Boolean indicating if LLM filtering was successful
|
|
225
|
+
|
|
226
|
+
Note:
|
|
227
|
+
If LLM filtering fails, returns all memories (conservative approach)
|
|
228
|
+
"""
|
|
229
|
+
if not memories:
|
|
230
|
+
logger.info("No memories to filter for unrelated and redundant - returning empty list")
|
|
231
|
+
return [], True
|
|
232
|
+
|
|
233
|
+
if not query_history:
|
|
234
|
+
logger.info("No query history provided - keeping all memories")
|
|
235
|
+
return memories, True
|
|
236
|
+
|
|
237
|
+
if len(memories) <= 1:
|
|
238
|
+
logger.info("Only one memory - no filtering needed")
|
|
239
|
+
return memories, True
|
|
240
|
+
|
|
241
|
+
logger.info(
|
|
242
|
+
f"Starting combined unrelated and redundant filtering for {len(memories)} memories against {len(query_history)} queries"
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
# Extract memory texts for LLM processing
|
|
246
|
+
memory_texts = [mem.memory for mem in memories]
|
|
247
|
+
|
|
248
|
+
# Build LLM prompt for combined filtering
|
|
249
|
+
prompt = self.build_prompt(
|
|
250
|
+
"memory_combined_filtering",
|
|
251
|
+
query_history=[f"[{i}] {query}" for i, query in enumerate(query_history)],
|
|
252
|
+
memories=[f"[{i}] {mem}" for i, mem in enumerate(memory_texts)],
|
|
253
|
+
)
|
|
254
|
+
logger.debug(
|
|
255
|
+
f"Generated combined filtering prompt: {prompt[:200]}..."
|
|
256
|
+
) # Log first 200 chars
|
|
257
|
+
|
|
258
|
+
# Get LLM response
|
|
259
|
+
response = self.process_llm.generate([{"role": "user", "content": prompt}])
|
|
260
|
+
logger.debug(
|
|
261
|
+
f"Received LLM combined filtering response: {response[:200]}..."
|
|
262
|
+
) # Log first 200 chars
|
|
263
|
+
|
|
264
|
+
try:
|
|
265
|
+
# Parse JSON response
|
|
266
|
+
response = extract_json_obj(response)
|
|
267
|
+
logger.debug(f"Parsed JSON response: {response}")
|
|
268
|
+
kept_indices = response["kept_memories"]
|
|
269
|
+
unrelated_removed_count = response.get("unrelated_removed_count", 0)
|
|
270
|
+
redundant_removed_count = response.get("redundant_removed_count", 0)
|
|
271
|
+
redundant_groups = response.get("redundant_groups", [])
|
|
272
|
+
reasoning = response["reasoning"]
|
|
273
|
+
|
|
274
|
+
# Validate indices
|
|
275
|
+
if not isinstance(kept_indices, list):
|
|
276
|
+
raise ValueError("kept_memories must be a list")
|
|
277
|
+
|
|
278
|
+
# Filter memories based on kept indices
|
|
279
|
+
filtered_memories = []
|
|
280
|
+
for idx in kept_indices:
|
|
281
|
+
if isinstance(idx, int) and 0 <= idx < len(memories):
|
|
282
|
+
filtered_memories.append(memories[idx])
|
|
283
|
+
else:
|
|
284
|
+
logger.warning(f"Invalid memory index {idx} - skipping")
|
|
285
|
+
|
|
286
|
+
logger.info(
|
|
287
|
+
f"Successfully filtered unrelated and redundant memories. "
|
|
288
|
+
f"Kept {len(filtered_memories)} out of {len(memories)} memories. "
|
|
289
|
+
f"Removed {len(memories) - len(filtered_memories)} memories total. "
|
|
290
|
+
f"Unrelated removed: {unrelated_removed_count}. "
|
|
291
|
+
f"Redundant removed: {redundant_removed_count}. "
|
|
292
|
+
f"Redundant groups identified: {len(redundant_groups)}. "
|
|
293
|
+
f"Filtering reasoning: {reasoning}"
|
|
294
|
+
)
|
|
295
|
+
success_flag = True
|
|
296
|
+
|
|
297
|
+
except Exception as e:
|
|
298
|
+
logger.error(
|
|
299
|
+
f"Failed to filter unrelated and redundant memories with LLM. Exception: {e}. Raw response: {response}",
|
|
300
|
+
exc_info=True,
|
|
301
|
+
)
|
|
302
|
+
# Conservative approach: keep all memories if filtering fails
|
|
303
|
+
filtered_memories = memories
|
|
304
|
+
success_flag = False
|
|
305
|
+
|
|
306
|
+
return filtered_memories, success_flag
|