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,222 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Search handler for memory search functionality (Class-based version).
|
|
3
|
+
|
|
4
|
+
This module provides a class-based implementation of search handlers,
|
|
5
|
+
using dependency injection for better modularity and testability.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import time
|
|
9
|
+
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from memos.api.handlers.base_handler import BaseHandler, HandlerDependencies
|
|
13
|
+
from memos.api.handlers.formatters_handler import rerank_knowledge_mem
|
|
14
|
+
from memos.api.product_models import APISearchRequest, SearchResponse
|
|
15
|
+
from memos.log import get_logger
|
|
16
|
+
from memos.memories.textual.tree_text_memory.retrieve.retrieve_utils import (
|
|
17
|
+
cosine_similarity_matrix,
|
|
18
|
+
)
|
|
19
|
+
from memos.multi_mem_cube.composite_cube import CompositeCubeView
|
|
20
|
+
from memos.multi_mem_cube.single_cube import SingleCubeView
|
|
21
|
+
from memos.multi_mem_cube.views import MemCubeView
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
logger = get_logger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class SearchHandler(BaseHandler):
|
|
28
|
+
"""
|
|
29
|
+
Handler for memory search operations.
|
|
30
|
+
|
|
31
|
+
Provides fast, fine-grained, and mixture-based search modes.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(self, dependencies: HandlerDependencies):
|
|
35
|
+
"""
|
|
36
|
+
Initialize search handler.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
dependencies: HandlerDependencies instance
|
|
40
|
+
"""
|
|
41
|
+
super().__init__(dependencies)
|
|
42
|
+
self._validate_dependencies(
|
|
43
|
+
"naive_mem_cube", "mem_scheduler", "searcher", "deepsearch_agent"
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
def handle_search_memories(self, search_req: APISearchRequest) -> SearchResponse:
|
|
47
|
+
"""
|
|
48
|
+
Main handler for search memories endpoint.
|
|
49
|
+
|
|
50
|
+
Orchestrates the search process based on the requested search mode,
|
|
51
|
+
supporting both text and preference memory searches.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
search_req: Search request containing query and parameters
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
SearchResponse with formatted results
|
|
58
|
+
"""
|
|
59
|
+
self.logger.info(f"[SearchHandler] Search Req is: {search_req}")
|
|
60
|
+
|
|
61
|
+
# Increase recall pool if deduplication is enabled to ensure diversity
|
|
62
|
+
original_top_k = search_req.top_k
|
|
63
|
+
if search_req.dedup == "sim":
|
|
64
|
+
search_req.top_k = original_top_k * 5
|
|
65
|
+
|
|
66
|
+
cube_view = self._build_cube_view(search_req)
|
|
67
|
+
|
|
68
|
+
results = cube_view.search_memories(search_req)
|
|
69
|
+
if search_req.dedup == "sim":
|
|
70
|
+
results = self._dedup_text_memories(results, original_top_k)
|
|
71
|
+
self._strip_embeddings(results)
|
|
72
|
+
# Restore original top_k for downstream logic or response metadata
|
|
73
|
+
search_req.top_k = original_top_k
|
|
74
|
+
|
|
75
|
+
start_time = time.time()
|
|
76
|
+
text_mem = results["text_mem"]
|
|
77
|
+
results["text_mem"] = rerank_knowledge_mem(
|
|
78
|
+
self.reranker,
|
|
79
|
+
query=search_req.query,
|
|
80
|
+
text_mem=text_mem,
|
|
81
|
+
top_k=original_top_k,
|
|
82
|
+
file_mem_proportion=0.5,
|
|
83
|
+
)
|
|
84
|
+
rerank_time = time.time() - start_time
|
|
85
|
+
|
|
86
|
+
self.logger.info(f"[Knowledge_replace_memory_time] Rerank time: {rerank_time} seconds")
|
|
87
|
+
self.logger.info(
|
|
88
|
+
f"[SearchHandler] Final search results: count={len(results)} results={results}"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
return SearchResponse(
|
|
92
|
+
message="Search completed successfully",
|
|
93
|
+
data=results,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
def _dedup_text_memories(self, results: dict[str, Any], target_top_k: int) -> dict[str, Any]:
|
|
97
|
+
buckets = results.get("text_mem", [])
|
|
98
|
+
if not buckets:
|
|
99
|
+
return results
|
|
100
|
+
|
|
101
|
+
flat: list[tuple[int, dict[str, Any], float]] = []
|
|
102
|
+
for bucket_idx, bucket in enumerate(buckets):
|
|
103
|
+
for mem in bucket.get("memories", []):
|
|
104
|
+
score = mem.get("metadata", {}).get("relativity", 0.0)
|
|
105
|
+
flat.append((bucket_idx, mem, score))
|
|
106
|
+
|
|
107
|
+
if len(flat) <= 1:
|
|
108
|
+
return results
|
|
109
|
+
|
|
110
|
+
embeddings = self._extract_embeddings([mem for _, mem, _ in flat])
|
|
111
|
+
if embeddings is None:
|
|
112
|
+
documents = [mem.get("memory", "") for _, mem, _ in flat]
|
|
113
|
+
embeddings = self.searcher.embedder.embed(documents)
|
|
114
|
+
|
|
115
|
+
similarity_matrix = cosine_similarity_matrix(embeddings)
|
|
116
|
+
|
|
117
|
+
indices_by_bucket: dict[int, list[int]] = {i: [] for i in range(len(buckets))}
|
|
118
|
+
for flat_index, (bucket_idx, _, _) in enumerate(flat):
|
|
119
|
+
indices_by_bucket[bucket_idx].append(flat_index)
|
|
120
|
+
|
|
121
|
+
selected_global: list[int] = []
|
|
122
|
+
selected_by_bucket: dict[int, list[int]] = {i: [] for i in range(len(buckets))}
|
|
123
|
+
|
|
124
|
+
ordered_indices = sorted(range(len(flat)), key=lambda idx: flat[idx][2], reverse=True)
|
|
125
|
+
for idx in ordered_indices:
|
|
126
|
+
bucket_idx = flat[idx][0]
|
|
127
|
+
if len(selected_by_bucket[bucket_idx]) >= target_top_k:
|
|
128
|
+
continue
|
|
129
|
+
# Use 0.92 threshold strictly
|
|
130
|
+
if self._is_unrelated(idx, selected_global, similarity_matrix, 0.92):
|
|
131
|
+
selected_by_bucket[bucket_idx].append(idx)
|
|
132
|
+
selected_global.append(idx)
|
|
133
|
+
|
|
134
|
+
# Removed the 'filling' logic that was pulling back similar items.
|
|
135
|
+
# Now it will only return items that truly pass the 0.92 threshold,
|
|
136
|
+
# up to target_top_k.
|
|
137
|
+
|
|
138
|
+
for bucket_idx, bucket in enumerate(buckets):
|
|
139
|
+
selected_indices = selected_by_bucket.get(bucket_idx, [])
|
|
140
|
+
bucket["memories"] = [flat[i][1] for i in selected_indices]
|
|
141
|
+
return results
|
|
142
|
+
|
|
143
|
+
@staticmethod
|
|
144
|
+
def _is_unrelated(
|
|
145
|
+
index: int,
|
|
146
|
+
selected_indices: list[int],
|
|
147
|
+
similarity_matrix: list[list[float]],
|
|
148
|
+
similarity_threshold: float,
|
|
149
|
+
) -> bool:
|
|
150
|
+
return all(similarity_matrix[index][j] <= similarity_threshold for j in selected_indices)
|
|
151
|
+
|
|
152
|
+
@staticmethod
|
|
153
|
+
def _max_similarity(
|
|
154
|
+
index: int, selected_indices: list[int], similarity_matrix: list[list[float]]
|
|
155
|
+
) -> float:
|
|
156
|
+
if not selected_indices:
|
|
157
|
+
return 0.0
|
|
158
|
+
return max(similarity_matrix[index][j] for j in selected_indices)
|
|
159
|
+
|
|
160
|
+
@staticmethod
|
|
161
|
+
def _extract_embeddings(memories: list[dict[str, Any]]) -> list[list[float]] | None:
|
|
162
|
+
embeddings: list[list[float]] = []
|
|
163
|
+
for mem in memories:
|
|
164
|
+
embedding = mem.get("metadata", {}).get("embedding")
|
|
165
|
+
if not embedding:
|
|
166
|
+
return None
|
|
167
|
+
embeddings.append(embedding)
|
|
168
|
+
return embeddings
|
|
169
|
+
|
|
170
|
+
@staticmethod
|
|
171
|
+
def _strip_embeddings(results: dict[str, Any]) -> None:
|
|
172
|
+
for bucket in results.get("text_mem", []):
|
|
173
|
+
for mem in bucket.get("memories", []):
|
|
174
|
+
metadata = mem.get("metadata", {})
|
|
175
|
+
if "embedding" in metadata:
|
|
176
|
+
metadata["embedding"] = []
|
|
177
|
+
for bucket in results.get("tool_mem", []):
|
|
178
|
+
for mem in bucket.get("memories", []):
|
|
179
|
+
metadata = mem.get("metadata", {})
|
|
180
|
+
if "embedding" in metadata:
|
|
181
|
+
metadata["embedding"] = []
|
|
182
|
+
|
|
183
|
+
def _resolve_cube_ids(self, search_req: APISearchRequest) -> list[str]:
|
|
184
|
+
"""
|
|
185
|
+
Normalize target cube ids from search_req.
|
|
186
|
+
Priority:
|
|
187
|
+
1) readable_cube_ids (deprecated mem_cube_id is converted to this in model validator)
|
|
188
|
+
2) fallback to user_id
|
|
189
|
+
"""
|
|
190
|
+
if search_req.readable_cube_ids:
|
|
191
|
+
return list(dict.fromkeys(search_req.readable_cube_ids))
|
|
192
|
+
|
|
193
|
+
return [search_req.user_id]
|
|
194
|
+
|
|
195
|
+
def _build_cube_view(self, search_req: APISearchRequest) -> MemCubeView:
|
|
196
|
+
cube_ids = self._resolve_cube_ids(search_req)
|
|
197
|
+
|
|
198
|
+
if len(cube_ids) == 1:
|
|
199
|
+
cube_id = cube_ids[0]
|
|
200
|
+
return SingleCubeView(
|
|
201
|
+
cube_id=cube_id,
|
|
202
|
+
naive_mem_cube=self.naive_mem_cube,
|
|
203
|
+
mem_reader=self.mem_reader,
|
|
204
|
+
mem_scheduler=self.mem_scheduler,
|
|
205
|
+
logger=self.logger,
|
|
206
|
+
searcher=self.searcher,
|
|
207
|
+
deepsearch_agent=self.deepsearch_agent,
|
|
208
|
+
)
|
|
209
|
+
else:
|
|
210
|
+
single_views = [
|
|
211
|
+
SingleCubeView(
|
|
212
|
+
cube_id=cube_id,
|
|
213
|
+
naive_mem_cube=self.naive_mem_cube,
|
|
214
|
+
mem_reader=self.mem_reader,
|
|
215
|
+
mem_scheduler=self.mem_scheduler,
|
|
216
|
+
logger=self.logger,
|
|
217
|
+
searcher=self.searcher,
|
|
218
|
+
deepsearch_agent=self.deepsearch_agent,
|
|
219
|
+
)
|
|
220
|
+
for cube_id in cube_ids
|
|
221
|
+
]
|
|
222
|
+
return CompositeCubeView(cube_views=single_views, logger=self.logger)
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Suggestion handler for generating suggestion queries.
|
|
3
|
+
|
|
4
|
+
This module handles suggestion query generation based on user's recent memories
|
|
5
|
+
or further suggestions from chat history.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from memos.api.product_models import SuggestionResponse
|
|
13
|
+
from memos.log import get_logger
|
|
14
|
+
from memos.mem_os.utils.format_utils import clean_json_response
|
|
15
|
+
from memos.templates.mos_prompts import (
|
|
16
|
+
FURTHER_SUGGESTION_PROMPT,
|
|
17
|
+
SUGGESTION_QUERY_PROMPT_EN,
|
|
18
|
+
SUGGESTION_QUERY_PROMPT_ZH,
|
|
19
|
+
)
|
|
20
|
+
from memos.types import MessageList
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
logger = get_logger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _get_further_suggestion(
|
|
27
|
+
llm: Any,
|
|
28
|
+
message: MessageList,
|
|
29
|
+
) -> list[str]:
|
|
30
|
+
"""
|
|
31
|
+
Get further suggestion based on recent dialogue.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
llm: LLM instance for generating suggestions
|
|
35
|
+
message: Recent chat messages
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
List of suggestion queries
|
|
39
|
+
"""
|
|
40
|
+
try:
|
|
41
|
+
dialogue_info = "\n".join([f"{msg['role']}: {msg['content']}" for msg in message[-2:]])
|
|
42
|
+
further_suggestion_prompt = FURTHER_SUGGESTION_PROMPT.format(dialogue=dialogue_info)
|
|
43
|
+
message_list = [{"role": "system", "content": further_suggestion_prompt}]
|
|
44
|
+
response = llm.generate(message_list)
|
|
45
|
+
clean_response = clean_json_response(response)
|
|
46
|
+
response_json = json.loads(clean_response)
|
|
47
|
+
return response_json["query"]
|
|
48
|
+
except Exception as e:
|
|
49
|
+
logger.error(f"Error getting further suggestion: {e}", exc_info=True)
|
|
50
|
+
return []
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def handle_get_suggestion_queries(
|
|
54
|
+
user_id: str,
|
|
55
|
+
language: str,
|
|
56
|
+
message: MessageList | None,
|
|
57
|
+
llm: Any,
|
|
58
|
+
naive_mem_cube: Any,
|
|
59
|
+
) -> SuggestionResponse:
|
|
60
|
+
"""
|
|
61
|
+
Main handler for suggestion queries endpoint.
|
|
62
|
+
|
|
63
|
+
Generates suggestion queries based on user's recent memories or chat history.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
user_id: User ID
|
|
67
|
+
language: Language preference ("zh" or "en")
|
|
68
|
+
message: Optional chat message list for further suggestions
|
|
69
|
+
llm: LLM instance
|
|
70
|
+
naive_mem_cube: Memory cube instance
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
SuggestionResponse with generated queries
|
|
74
|
+
"""
|
|
75
|
+
try:
|
|
76
|
+
# If message is provided, get further suggestions based on dialogue
|
|
77
|
+
if message:
|
|
78
|
+
suggestions = _get_further_suggestion(llm, message)
|
|
79
|
+
return SuggestionResponse(
|
|
80
|
+
message="Suggestions retrieved successfully",
|
|
81
|
+
data={"query": suggestions},
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# Otherwise, generate suggestions based on recent memories
|
|
85
|
+
if language == "zh":
|
|
86
|
+
suggestion_prompt = SUGGESTION_QUERY_PROMPT_ZH
|
|
87
|
+
else: # English
|
|
88
|
+
suggestion_prompt = SUGGESTION_QUERY_PROMPT_EN
|
|
89
|
+
|
|
90
|
+
# Search for recent memories
|
|
91
|
+
text_mem_results = naive_mem_cube.text_mem.search(
|
|
92
|
+
query="my recently memories",
|
|
93
|
+
user_name=user_id,
|
|
94
|
+
top_k=3,
|
|
95
|
+
mode="fast",
|
|
96
|
+
info={"user_id": user_id},
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Extract memory content
|
|
100
|
+
memories = ""
|
|
101
|
+
if text_mem_results:
|
|
102
|
+
memories = "\n".join([m.memory[:200] for m in text_mem_results])
|
|
103
|
+
|
|
104
|
+
# Generate suggestions using LLM
|
|
105
|
+
message_list = [{"role": "system", "content": suggestion_prompt.format(memories=memories)}]
|
|
106
|
+
response = llm.generate(message_list)
|
|
107
|
+
clean_response = clean_json_response(response)
|
|
108
|
+
response_json = json.loads(clean_response)
|
|
109
|
+
|
|
110
|
+
return SuggestionResponse(
|
|
111
|
+
message="Suggestions retrieved successfully",
|
|
112
|
+
data={"query": response_json["query"]},
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
except Exception as e:
|
|
116
|
+
logger.error(f"Failed to get suggestions: {e}", exc_info=True)
|
|
117
|
+
raise
|