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.
Files changed (315) hide show
  1. memoryos-2.0.3.dist-info/METADATA +418 -0
  2. memoryos-2.0.3.dist-info/RECORD +315 -0
  3. memoryos-2.0.3.dist-info/WHEEL +4 -0
  4. memoryos-2.0.3.dist-info/entry_points.txt +3 -0
  5. memoryos-2.0.3.dist-info/licenses/LICENSE +201 -0
  6. memos/__init__.py +20 -0
  7. memos/api/client.py +571 -0
  8. memos/api/config.py +1018 -0
  9. memos/api/context/dependencies.py +50 -0
  10. memos/api/exceptions.py +53 -0
  11. memos/api/handlers/__init__.py +62 -0
  12. memos/api/handlers/add_handler.py +158 -0
  13. memos/api/handlers/base_handler.py +194 -0
  14. memos/api/handlers/chat_handler.py +1401 -0
  15. memos/api/handlers/component_init.py +388 -0
  16. memos/api/handlers/config_builders.py +190 -0
  17. memos/api/handlers/feedback_handler.py +93 -0
  18. memos/api/handlers/formatters_handler.py +237 -0
  19. memos/api/handlers/memory_handler.py +316 -0
  20. memos/api/handlers/scheduler_handler.py +497 -0
  21. memos/api/handlers/search_handler.py +222 -0
  22. memos/api/handlers/suggestion_handler.py +117 -0
  23. memos/api/mcp_serve.py +614 -0
  24. memos/api/middleware/request_context.py +101 -0
  25. memos/api/product_api.py +38 -0
  26. memos/api/product_models.py +1206 -0
  27. memos/api/routers/__init__.py +1 -0
  28. memos/api/routers/product_router.py +477 -0
  29. memos/api/routers/server_router.py +394 -0
  30. memos/api/server_api.py +44 -0
  31. memos/api/start_api.py +433 -0
  32. memos/chunkers/__init__.py +4 -0
  33. memos/chunkers/base.py +24 -0
  34. memos/chunkers/charactertext_chunker.py +41 -0
  35. memos/chunkers/factory.py +24 -0
  36. memos/chunkers/markdown_chunker.py +62 -0
  37. memos/chunkers/sentence_chunker.py +54 -0
  38. memos/chunkers/simple_chunker.py +50 -0
  39. memos/cli.py +113 -0
  40. memos/configs/__init__.py +0 -0
  41. memos/configs/base.py +82 -0
  42. memos/configs/chunker.py +59 -0
  43. memos/configs/embedder.py +88 -0
  44. memos/configs/graph_db.py +236 -0
  45. memos/configs/internet_retriever.py +100 -0
  46. memos/configs/llm.py +151 -0
  47. memos/configs/mem_agent.py +54 -0
  48. memos/configs/mem_chat.py +81 -0
  49. memos/configs/mem_cube.py +105 -0
  50. memos/configs/mem_os.py +83 -0
  51. memos/configs/mem_reader.py +91 -0
  52. memos/configs/mem_scheduler.py +385 -0
  53. memos/configs/mem_user.py +70 -0
  54. memos/configs/memory.py +324 -0
  55. memos/configs/parser.py +38 -0
  56. memos/configs/reranker.py +18 -0
  57. memos/configs/utils.py +8 -0
  58. memos/configs/vec_db.py +80 -0
  59. memos/context/context.py +355 -0
  60. memos/dependency.py +52 -0
  61. memos/deprecation.py +262 -0
  62. memos/embedders/__init__.py +0 -0
  63. memos/embedders/ark.py +95 -0
  64. memos/embedders/base.py +106 -0
  65. memos/embedders/factory.py +29 -0
  66. memos/embedders/ollama.py +77 -0
  67. memos/embedders/sentence_transformer.py +49 -0
  68. memos/embedders/universal_api.py +51 -0
  69. memos/exceptions.py +30 -0
  70. memos/graph_dbs/__init__.py +0 -0
  71. memos/graph_dbs/base.py +274 -0
  72. memos/graph_dbs/factory.py +27 -0
  73. memos/graph_dbs/item.py +46 -0
  74. memos/graph_dbs/nebular.py +1794 -0
  75. memos/graph_dbs/neo4j.py +1942 -0
  76. memos/graph_dbs/neo4j_community.py +1058 -0
  77. memos/graph_dbs/polardb.py +5446 -0
  78. memos/hello_world.py +97 -0
  79. memos/llms/__init__.py +0 -0
  80. memos/llms/base.py +25 -0
  81. memos/llms/deepseek.py +13 -0
  82. memos/llms/factory.py +38 -0
  83. memos/llms/hf.py +443 -0
  84. memos/llms/hf_singleton.py +114 -0
  85. memos/llms/ollama.py +135 -0
  86. memos/llms/openai.py +222 -0
  87. memos/llms/openai_new.py +198 -0
  88. memos/llms/qwen.py +13 -0
  89. memos/llms/utils.py +14 -0
  90. memos/llms/vllm.py +218 -0
  91. memos/log.py +237 -0
  92. memos/mem_agent/base.py +19 -0
  93. memos/mem_agent/deepsearch_agent.py +391 -0
  94. memos/mem_agent/factory.py +36 -0
  95. memos/mem_chat/__init__.py +0 -0
  96. memos/mem_chat/base.py +30 -0
  97. memos/mem_chat/factory.py +21 -0
  98. memos/mem_chat/simple.py +200 -0
  99. memos/mem_cube/__init__.py +0 -0
  100. memos/mem_cube/base.py +30 -0
  101. memos/mem_cube/general.py +240 -0
  102. memos/mem_cube/navie.py +172 -0
  103. memos/mem_cube/utils.py +169 -0
  104. memos/mem_feedback/base.py +15 -0
  105. memos/mem_feedback/feedback.py +1192 -0
  106. memos/mem_feedback/simple_feedback.py +40 -0
  107. memos/mem_feedback/utils.py +230 -0
  108. memos/mem_os/client.py +5 -0
  109. memos/mem_os/core.py +1203 -0
  110. memos/mem_os/main.py +582 -0
  111. memos/mem_os/product.py +1608 -0
  112. memos/mem_os/product_server.py +455 -0
  113. memos/mem_os/utils/default_config.py +359 -0
  114. memos/mem_os/utils/format_utils.py +1403 -0
  115. memos/mem_os/utils/reference_utils.py +162 -0
  116. memos/mem_reader/__init__.py +0 -0
  117. memos/mem_reader/base.py +47 -0
  118. memos/mem_reader/factory.py +53 -0
  119. memos/mem_reader/memory.py +298 -0
  120. memos/mem_reader/multi_modal_struct.py +965 -0
  121. memos/mem_reader/read_multi_modal/__init__.py +43 -0
  122. memos/mem_reader/read_multi_modal/assistant_parser.py +311 -0
  123. memos/mem_reader/read_multi_modal/base.py +273 -0
  124. memos/mem_reader/read_multi_modal/file_content_parser.py +826 -0
  125. memos/mem_reader/read_multi_modal/image_parser.py +359 -0
  126. memos/mem_reader/read_multi_modal/multi_modal_parser.py +252 -0
  127. memos/mem_reader/read_multi_modal/string_parser.py +139 -0
  128. memos/mem_reader/read_multi_modal/system_parser.py +327 -0
  129. memos/mem_reader/read_multi_modal/text_content_parser.py +131 -0
  130. memos/mem_reader/read_multi_modal/tool_parser.py +210 -0
  131. memos/mem_reader/read_multi_modal/user_parser.py +218 -0
  132. memos/mem_reader/read_multi_modal/utils.py +358 -0
  133. memos/mem_reader/simple_struct.py +912 -0
  134. memos/mem_reader/strategy_struct.py +163 -0
  135. memos/mem_reader/utils.py +157 -0
  136. memos/mem_scheduler/__init__.py +0 -0
  137. memos/mem_scheduler/analyzer/__init__.py +0 -0
  138. memos/mem_scheduler/analyzer/api_analyzer.py +714 -0
  139. memos/mem_scheduler/analyzer/eval_analyzer.py +219 -0
  140. memos/mem_scheduler/analyzer/mos_for_test_scheduler.py +571 -0
  141. memos/mem_scheduler/analyzer/scheduler_for_eval.py +280 -0
  142. memos/mem_scheduler/base_scheduler.py +1319 -0
  143. memos/mem_scheduler/general_modules/__init__.py +0 -0
  144. memos/mem_scheduler/general_modules/api_misc.py +137 -0
  145. memos/mem_scheduler/general_modules/base.py +80 -0
  146. memos/mem_scheduler/general_modules/init_components_for_scheduler.py +425 -0
  147. memos/mem_scheduler/general_modules/misc.py +313 -0
  148. memos/mem_scheduler/general_modules/scheduler_logger.py +389 -0
  149. memos/mem_scheduler/general_modules/task_threads.py +315 -0
  150. memos/mem_scheduler/general_scheduler.py +1495 -0
  151. memos/mem_scheduler/memory_manage_modules/__init__.py +5 -0
  152. memos/mem_scheduler/memory_manage_modules/memory_filter.py +306 -0
  153. memos/mem_scheduler/memory_manage_modules/retriever.py +547 -0
  154. memos/mem_scheduler/monitors/__init__.py +0 -0
  155. memos/mem_scheduler/monitors/dispatcher_monitor.py +366 -0
  156. memos/mem_scheduler/monitors/general_monitor.py +394 -0
  157. memos/mem_scheduler/monitors/task_schedule_monitor.py +254 -0
  158. memos/mem_scheduler/optimized_scheduler.py +410 -0
  159. memos/mem_scheduler/orm_modules/__init__.py +0 -0
  160. memos/mem_scheduler/orm_modules/api_redis_model.py +518 -0
  161. memos/mem_scheduler/orm_modules/base_model.py +729 -0
  162. memos/mem_scheduler/orm_modules/monitor_models.py +261 -0
  163. memos/mem_scheduler/orm_modules/redis_model.py +699 -0
  164. memos/mem_scheduler/scheduler_factory.py +23 -0
  165. memos/mem_scheduler/schemas/__init__.py +0 -0
  166. memos/mem_scheduler/schemas/analyzer_schemas.py +52 -0
  167. memos/mem_scheduler/schemas/api_schemas.py +233 -0
  168. memos/mem_scheduler/schemas/general_schemas.py +55 -0
  169. memos/mem_scheduler/schemas/message_schemas.py +173 -0
  170. memos/mem_scheduler/schemas/monitor_schemas.py +406 -0
  171. memos/mem_scheduler/schemas/task_schemas.py +132 -0
  172. memos/mem_scheduler/task_schedule_modules/__init__.py +0 -0
  173. memos/mem_scheduler/task_schedule_modules/dispatcher.py +740 -0
  174. memos/mem_scheduler/task_schedule_modules/local_queue.py +247 -0
  175. memos/mem_scheduler/task_schedule_modules/orchestrator.py +74 -0
  176. memos/mem_scheduler/task_schedule_modules/redis_queue.py +1385 -0
  177. memos/mem_scheduler/task_schedule_modules/task_queue.py +162 -0
  178. memos/mem_scheduler/utils/__init__.py +0 -0
  179. memos/mem_scheduler/utils/api_utils.py +77 -0
  180. memos/mem_scheduler/utils/config_utils.py +100 -0
  181. memos/mem_scheduler/utils/db_utils.py +50 -0
  182. memos/mem_scheduler/utils/filter_utils.py +176 -0
  183. memos/mem_scheduler/utils/metrics.py +125 -0
  184. memos/mem_scheduler/utils/misc_utils.py +290 -0
  185. memos/mem_scheduler/utils/monitor_event_utils.py +67 -0
  186. memos/mem_scheduler/utils/status_tracker.py +229 -0
  187. memos/mem_scheduler/webservice_modules/__init__.py +0 -0
  188. memos/mem_scheduler/webservice_modules/rabbitmq_service.py +485 -0
  189. memos/mem_scheduler/webservice_modules/redis_service.py +380 -0
  190. memos/mem_user/factory.py +94 -0
  191. memos/mem_user/mysql_persistent_user_manager.py +271 -0
  192. memos/mem_user/mysql_user_manager.py +502 -0
  193. memos/mem_user/persistent_factory.py +98 -0
  194. memos/mem_user/persistent_user_manager.py +260 -0
  195. memos/mem_user/redis_persistent_user_manager.py +225 -0
  196. memos/mem_user/user_manager.py +488 -0
  197. memos/memories/__init__.py +0 -0
  198. memos/memories/activation/__init__.py +0 -0
  199. memos/memories/activation/base.py +42 -0
  200. memos/memories/activation/item.py +56 -0
  201. memos/memories/activation/kv.py +292 -0
  202. memos/memories/activation/vllmkv.py +219 -0
  203. memos/memories/base.py +19 -0
  204. memos/memories/factory.py +42 -0
  205. memos/memories/parametric/__init__.py +0 -0
  206. memos/memories/parametric/base.py +19 -0
  207. memos/memories/parametric/item.py +11 -0
  208. memos/memories/parametric/lora.py +41 -0
  209. memos/memories/textual/__init__.py +0 -0
  210. memos/memories/textual/base.py +92 -0
  211. memos/memories/textual/general.py +236 -0
  212. memos/memories/textual/item.py +304 -0
  213. memos/memories/textual/naive.py +187 -0
  214. memos/memories/textual/prefer_text_memory/__init__.py +0 -0
  215. memos/memories/textual/prefer_text_memory/adder.py +504 -0
  216. memos/memories/textual/prefer_text_memory/config.py +106 -0
  217. memos/memories/textual/prefer_text_memory/extractor.py +221 -0
  218. memos/memories/textual/prefer_text_memory/factory.py +85 -0
  219. memos/memories/textual/prefer_text_memory/retrievers.py +177 -0
  220. memos/memories/textual/prefer_text_memory/spliter.py +132 -0
  221. memos/memories/textual/prefer_text_memory/utils.py +93 -0
  222. memos/memories/textual/preference.py +344 -0
  223. memos/memories/textual/simple_preference.py +161 -0
  224. memos/memories/textual/simple_tree.py +69 -0
  225. memos/memories/textual/tree.py +459 -0
  226. memos/memories/textual/tree_text_memory/__init__.py +0 -0
  227. memos/memories/textual/tree_text_memory/organize/__init__.py +0 -0
  228. memos/memories/textual/tree_text_memory/organize/handler.py +184 -0
  229. memos/memories/textual/tree_text_memory/organize/manager.py +518 -0
  230. memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py +238 -0
  231. memos/memories/textual/tree_text_memory/organize/reorganizer.py +622 -0
  232. memos/memories/textual/tree_text_memory/retrieve/__init__.py +0 -0
  233. memos/memories/textual/tree_text_memory/retrieve/advanced_searcher.py +364 -0
  234. memos/memories/textual/tree_text_memory/retrieve/bm25_util.py +186 -0
  235. memos/memories/textual/tree_text_memory/retrieve/bochasearch.py +419 -0
  236. memos/memories/textual/tree_text_memory/retrieve/internet_retriever.py +270 -0
  237. memos/memories/textual/tree_text_memory/retrieve/internet_retriever_factory.py +102 -0
  238. memos/memories/textual/tree_text_memory/retrieve/reasoner.py +61 -0
  239. memos/memories/textual/tree_text_memory/retrieve/recall.py +497 -0
  240. memos/memories/textual/tree_text_memory/retrieve/reranker.py +111 -0
  241. memos/memories/textual/tree_text_memory/retrieve/retrieval_mid_structs.py +16 -0
  242. memos/memories/textual/tree_text_memory/retrieve/retrieve_utils.py +472 -0
  243. memos/memories/textual/tree_text_memory/retrieve/searcher.py +848 -0
  244. memos/memories/textual/tree_text_memory/retrieve/task_goal_parser.py +135 -0
  245. memos/memories/textual/tree_text_memory/retrieve/utils.py +54 -0
  246. memos/memories/textual/tree_text_memory/retrieve/xinyusearch.py +387 -0
  247. memos/memos_tools/dinding_report_bot.py +453 -0
  248. memos/memos_tools/lockfree_dict.py +120 -0
  249. memos/memos_tools/notification_service.py +44 -0
  250. memos/memos_tools/notification_utils.py +142 -0
  251. memos/memos_tools/singleton.py +174 -0
  252. memos/memos_tools/thread_safe_dict.py +310 -0
  253. memos/memos_tools/thread_safe_dict_segment.py +382 -0
  254. memos/multi_mem_cube/__init__.py +0 -0
  255. memos/multi_mem_cube/composite_cube.py +86 -0
  256. memos/multi_mem_cube/single_cube.py +874 -0
  257. memos/multi_mem_cube/views.py +54 -0
  258. memos/parsers/__init__.py +0 -0
  259. memos/parsers/base.py +15 -0
  260. memos/parsers/factory.py +21 -0
  261. memos/parsers/markitdown.py +28 -0
  262. memos/reranker/__init__.py +4 -0
  263. memos/reranker/base.py +25 -0
  264. memos/reranker/concat.py +103 -0
  265. memos/reranker/cosine_local.py +102 -0
  266. memos/reranker/factory.py +72 -0
  267. memos/reranker/http_bge.py +324 -0
  268. memos/reranker/http_bge_strategy.py +327 -0
  269. memos/reranker/noop.py +19 -0
  270. memos/reranker/strategies/__init__.py +4 -0
  271. memos/reranker/strategies/base.py +61 -0
  272. memos/reranker/strategies/concat_background.py +94 -0
  273. memos/reranker/strategies/concat_docsource.py +110 -0
  274. memos/reranker/strategies/dialogue_common.py +109 -0
  275. memos/reranker/strategies/factory.py +31 -0
  276. memos/reranker/strategies/single_turn.py +107 -0
  277. memos/reranker/strategies/singleturn_outmem.py +98 -0
  278. memos/settings.py +10 -0
  279. memos/templates/__init__.py +0 -0
  280. memos/templates/advanced_search_prompts.py +211 -0
  281. memos/templates/cloud_service_prompt.py +107 -0
  282. memos/templates/instruction_completion.py +66 -0
  283. memos/templates/mem_agent_prompts.py +85 -0
  284. memos/templates/mem_feedback_prompts.py +822 -0
  285. memos/templates/mem_reader_prompts.py +1096 -0
  286. memos/templates/mem_reader_strategy_prompts.py +238 -0
  287. memos/templates/mem_scheduler_prompts.py +626 -0
  288. memos/templates/mem_search_prompts.py +93 -0
  289. memos/templates/mos_prompts.py +403 -0
  290. memos/templates/prefer_complete_prompt.py +735 -0
  291. memos/templates/tool_mem_prompts.py +139 -0
  292. memos/templates/tree_reorganize_prompts.py +230 -0
  293. memos/types/__init__.py +34 -0
  294. memos/types/general_types.py +151 -0
  295. memos/types/openai_chat_completion_types/__init__.py +15 -0
  296. memos/types/openai_chat_completion_types/chat_completion_assistant_message_param.py +56 -0
  297. memos/types/openai_chat_completion_types/chat_completion_content_part_image_param.py +27 -0
  298. memos/types/openai_chat_completion_types/chat_completion_content_part_input_audio_param.py +23 -0
  299. memos/types/openai_chat_completion_types/chat_completion_content_part_param.py +43 -0
  300. memos/types/openai_chat_completion_types/chat_completion_content_part_refusal_param.py +16 -0
  301. memos/types/openai_chat_completion_types/chat_completion_content_part_text_param.py +16 -0
  302. memos/types/openai_chat_completion_types/chat_completion_message_custom_tool_call_param.py +27 -0
  303. memos/types/openai_chat_completion_types/chat_completion_message_function_tool_call_param.py +32 -0
  304. memos/types/openai_chat_completion_types/chat_completion_message_param.py +18 -0
  305. memos/types/openai_chat_completion_types/chat_completion_message_tool_call_union_param.py +15 -0
  306. memos/types/openai_chat_completion_types/chat_completion_system_message_param.py +36 -0
  307. memos/types/openai_chat_completion_types/chat_completion_tool_message_param.py +30 -0
  308. memos/types/openai_chat_completion_types/chat_completion_user_message_param.py +34 -0
  309. memos/utils.py +123 -0
  310. memos/vec_dbs/__init__.py +0 -0
  311. memos/vec_dbs/base.py +117 -0
  312. memos/vec_dbs/factory.py +23 -0
  313. memos/vec_dbs/item.py +50 -0
  314. memos/vec_dbs/milvus.py +654 -0
  315. 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