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,237 @@
1
+ """
2
+ Data formatting utilities for server handlers.
3
+
4
+ This module provides utility functions for formatting and transforming data
5
+ structures for API responses, including memory items and preferences.
6
+ """
7
+
8
+ from typing import Any
9
+
10
+ from memos.log import get_logger
11
+ from memos.templates.instruction_completion import instruct_completion
12
+
13
+
14
+ logger = get_logger(__name__)
15
+
16
+
17
+ def to_iter(running: Any) -> list[Any]:
18
+ """
19
+ Normalize running tasks to a list of task objects.
20
+
21
+ Handles different input types and converts them to a consistent list format.
22
+
23
+ Args:
24
+ running: Running tasks, can be None, dict, or iterable
25
+
26
+ Returns:
27
+ List of task objects
28
+ """
29
+ if running is None:
30
+ return []
31
+ if isinstance(running, dict):
32
+ return list(running.values())
33
+ return list(running) if running else []
34
+
35
+
36
+ def format_memory_item(
37
+ memory_data: Any, include_embedding: bool = False, save_sources: bool = True
38
+ ) -> dict[str, Any]:
39
+ """
40
+ Format a single memory item for API response.
41
+
42
+ Transforms a memory object into a dictionary with metadata properly
43
+ structured for API consumption.
44
+
45
+ Args:
46
+ memory_data: Memory object to format
47
+
48
+ Returns:
49
+ Formatted memory dictionary with ref_id and metadata
50
+ """
51
+ memory = memory_data.model_dump()
52
+ memory_id = memory["id"]
53
+ ref_id = f"[{memory_id.split('-')[0]}]"
54
+
55
+ memory["ref_id"] = ref_id
56
+ if not include_embedding:
57
+ memory["metadata"]["embedding"] = []
58
+ if not save_sources:
59
+ memory["metadata"]["sources"] = []
60
+ memory["metadata"]["usage"] = []
61
+ memory["metadata"]["ref_id"] = ref_id
62
+ memory["metadata"]["id"] = memory_id
63
+ memory["metadata"]["memory"] = memory["memory"]
64
+
65
+ return memory
66
+
67
+
68
+ def post_process_pref_mem(
69
+ memories_result: dict[str, Any],
70
+ pref_formatted_mem: list[dict[str, Any]],
71
+ mem_cube_id: str,
72
+ include_preference: bool,
73
+ ) -> dict[str, Any]:
74
+ """
75
+ Post-process preference memory results.
76
+
77
+ Adds formatted preference memories to the result dictionary and generates
78
+ instruction completion strings if preferences are included.
79
+
80
+ Args:
81
+ memories_result: Result dictionary to update
82
+ pref_formatted_mem: List of formatted preference memories
83
+ mem_cube_id: Memory cube ID
84
+ include_preference: Whether to include preferences in result
85
+
86
+ Returns:
87
+ Updated memories_result dictionary
88
+ """
89
+ if include_preference:
90
+ memories_result["pref_mem"].append(
91
+ {
92
+ "cube_id": mem_cube_id,
93
+ "memories": pref_formatted_mem,
94
+ "total_nodes": len(pref_formatted_mem),
95
+ }
96
+ )
97
+ pref_instruction, pref_note = instruct_completion(pref_formatted_mem)
98
+ memories_result["pref_string"] = pref_instruction
99
+ memories_result["pref_note"] = pref_note
100
+
101
+ return memories_result
102
+
103
+
104
+ def post_process_textual_mem(
105
+ memories_result: dict[str, Any],
106
+ text_formatted_mem: list[dict[str, Any]],
107
+ mem_cube_id: str,
108
+ ) -> dict[str, Any]:
109
+ """
110
+ Post-process text and tool memory results.
111
+ """
112
+ fact_mem = [
113
+ mem
114
+ for mem in text_formatted_mem
115
+ if mem["metadata"]["memory_type"] not in ["ToolSchemaMemory", "ToolTrajectoryMemory"]
116
+ ]
117
+ tool_mem = [
118
+ mem
119
+ for mem in text_formatted_mem
120
+ if mem["metadata"]["memory_type"] in ["ToolSchemaMemory", "ToolTrajectoryMemory"]
121
+ ]
122
+
123
+ memories_result["text_mem"].append(
124
+ {
125
+ "cube_id": mem_cube_id,
126
+ "memories": fact_mem,
127
+ "total_nodes": len(fact_mem),
128
+ }
129
+ )
130
+ memories_result["tool_mem"].append(
131
+ {
132
+ "cube_id": mem_cube_id,
133
+ "memories": tool_mem,
134
+ "total_nodes": len(tool_mem),
135
+ }
136
+ )
137
+ return memories_result
138
+
139
+
140
+ def separate_knowledge_and_conversation_mem(memories: list[dict[str, Any]]):
141
+ """
142
+ Separate knowledge and conversation memories from retrieval results.
143
+ """
144
+ knowledge_mem = []
145
+ conversation_mem = []
146
+ for item in memories:
147
+ sources = item.get("metadata", {}).get("sources", [])
148
+ if (
149
+ len(sources) > 0
150
+ and "type" in sources[0]
151
+ and sources[0]["type"] == "file"
152
+ and "content" in sources[0]
153
+ and sources[0]["content"] != ""
154
+ ): # TODO change to memory_type
155
+ knowledge_mem.append(item)
156
+ else:
157
+ conversation_mem.append(item)
158
+
159
+ logger.info(
160
+ f"Retrieval results number of knowledge_mem: {len(knowledge_mem)}, conversation_mem: {len(conversation_mem)}"
161
+ )
162
+ return knowledge_mem, conversation_mem
163
+
164
+
165
+ def rerank_knowledge_mem(
166
+ reranker: Any,
167
+ query: str,
168
+ text_mem: list[dict[str, Any]],
169
+ top_k: int,
170
+ file_mem_proportion: float = 0.5,
171
+ ) -> list[dict[str, Any]]:
172
+ """
173
+ Rerank knowledge memories and keep conversation memories.
174
+ """
175
+ memid2cubeid = {}
176
+ memories_list = []
177
+ for memory_group in text_mem:
178
+ cube_id = memory_group["cube_id"]
179
+ memories = memory_group["memories"]
180
+ memories_list.extend(memories)
181
+ for memory in memories:
182
+ memid2cubeid[memory["id"]] = cube_id
183
+
184
+ knowledge_mem, conversation_mem = separate_knowledge_and_conversation_mem(memories_list)
185
+ knowledge_mem_top_k = max(int(top_k * file_mem_proportion), int(top_k - len(conversation_mem)))
186
+ # rerank set unuse
187
+ reranked_knowledge_mem = knowledge_mem
188
+
189
+ # Sort by relativity in descending order
190
+ reranked_knowledge_mem = sorted(
191
+ reranked_knowledge_mem,
192
+ key=lambda item: item.get("metadata", {}).get("relativity", 0.0),
193
+ reverse=True,
194
+ )
195
+
196
+ # TODO revoke sources replace memory value
197
+ for item in reranked_knowledge_mem:
198
+ item["memory"] = item["metadata"]["sources"][0]["content"]
199
+ item["metadata"]["sources"] = []
200
+
201
+ for item in conversation_mem:
202
+ item.setdefault("metadata", {})["sources"] = []
203
+
204
+ # deduplicate: remove items with duplicate memory content
205
+ original_count = len(reranked_knowledge_mem)
206
+ seen_memories = set[Any]()
207
+ deduplicated_knowledge_mem = []
208
+ for item in reranked_knowledge_mem:
209
+ memory_content = item.get("memory", "")
210
+ if memory_content and memory_content not in seen_memories:
211
+ seen_memories.add(memory_content)
212
+ deduplicated_knowledge_mem.append(item)
213
+ deduplicated_count = len(deduplicated_knowledge_mem)
214
+ logger.info(
215
+ f"After filtering duplicate knowledge base text from sources, count changed from {original_count} to {deduplicated_count}"
216
+ )
217
+
218
+ reranked_knowledge_mem = deduplicated_knowledge_mem[:knowledge_mem_top_k]
219
+ conversation_mem_top_k = top_k - len(reranked_knowledge_mem)
220
+ cubeid2memories = {}
221
+ text_mem_res = []
222
+
223
+ for memory in reranked_knowledge_mem + conversation_mem[:conversation_mem_top_k]:
224
+ cube_id = memid2cubeid[memory["id"]]
225
+ if cube_id not in cubeid2memories:
226
+ cubeid2memories[cube_id] = []
227
+ cubeid2memories[cube_id].append(memory)
228
+
229
+ for cube_id, memories in cubeid2memories.items():
230
+ text_mem_res.append(
231
+ {
232
+ "cube_id": cube_id,
233
+ "memories": memories,
234
+ }
235
+ )
236
+
237
+ return text_mem_res
@@ -0,0 +1,316 @@
1
+ """
2
+ Memory handler for retrieving and managing memories.
3
+
4
+ This module handles retrieving all memories or specific subgraphs based on queries.
5
+ """
6
+
7
+ from typing import TYPE_CHECKING, Any, Literal
8
+
9
+ from memos.api.handlers.formatters_handler import (
10
+ format_memory_item,
11
+ post_process_pref_mem,
12
+ post_process_textual_mem,
13
+ )
14
+ from memos.api.product_models import (
15
+ DeleteMemoryRequest,
16
+ DeleteMemoryResponse,
17
+ GetMemoryRequest,
18
+ GetMemoryResponse,
19
+ MemoryResponse,
20
+ )
21
+ from memos.log import get_logger
22
+ from memos.mem_cube.navie import NaiveMemCube
23
+ from memos.mem_os.utils.format_utils import (
24
+ convert_graph_to_tree_forworkmem,
25
+ ensure_unique_tree_ids,
26
+ filter_nodes_by_tree_ids,
27
+ remove_embedding_recursive,
28
+ sort_children_by_memory_type,
29
+ )
30
+
31
+
32
+ if TYPE_CHECKING:
33
+ from memos.memories.textual.preference import TextualMemoryItem
34
+
35
+
36
+ logger = get_logger(__name__)
37
+
38
+
39
+ def handle_get_all_memories(
40
+ user_id: str,
41
+ mem_cube_id: str,
42
+ memory_type: Literal["text_mem", "act_mem", "param_mem", "para_mem"],
43
+ naive_mem_cube: Any,
44
+ ) -> MemoryResponse:
45
+ """
46
+ Main handler for getting all memories.
47
+
48
+ Retrieves all memories of specified type for a user and formats them appropriately.
49
+
50
+ Args:
51
+ user_id: User ID
52
+ mem_cube_id: Memory cube ID
53
+ memory_type: Type of memory to retrieve
54
+ naive_mem_cube: Memory cube instance
55
+
56
+ Returns:
57
+ MemoryResponse with formatted memory data
58
+ """
59
+ try:
60
+ reformat_memory_list = []
61
+
62
+ if memory_type == "text_mem":
63
+ # Get all text memories from the graph database
64
+ memories = naive_mem_cube.text_mem.get_all(user_name=mem_cube_id)
65
+
66
+ # Format and convert to tree structure
67
+ memories_cleaned = remove_embedding_recursive(memories)
68
+ custom_type_ratios = {
69
+ "WorkingMemory": 0.20,
70
+ "LongTermMemory": 0.40,
71
+ "UserMemory": 0.40,
72
+ }
73
+ tree_result, node_type_count = convert_graph_to_tree_forworkmem(
74
+ memories_cleaned, target_node_count=200, type_ratios=custom_type_ratios
75
+ )
76
+ # Ensure all node IDs are unique in the tree structure
77
+ tree_result = ensure_unique_tree_ids(tree_result)
78
+ memories_filtered = filter_nodes_by_tree_ids(tree_result, memories_cleaned)
79
+ children = tree_result["children"]
80
+ children_sort = sort_children_by_memory_type(children)
81
+ tree_result["children"] = children_sort
82
+ memories_filtered["tree_structure"] = tree_result
83
+
84
+ reformat_memory_list.append(
85
+ {
86
+ "cube_id": mem_cube_id,
87
+ "memories": [memories_filtered],
88
+ "memory_statistics": node_type_count,
89
+ }
90
+ )
91
+
92
+ elif memory_type == "act_mem":
93
+ logger.warning("Activity memory retrieval not implemented yet.")
94
+ elif memory_type == "para_mem":
95
+ logger.warning("Parameter memory retrieval not implemented yet.")
96
+ return MemoryResponse(
97
+ message="Memories retrieved successfully",
98
+ data=reformat_memory_list,
99
+ )
100
+
101
+ except Exception as e:
102
+ logger.error(f"Failed to get all memories: {e}", exc_info=True)
103
+ raise
104
+
105
+
106
+ def handle_get_subgraph(
107
+ user_id: str,
108
+ mem_cube_id: str,
109
+ query: str,
110
+ top_k: int,
111
+ naive_mem_cube: Any,
112
+ ) -> MemoryResponse:
113
+ """
114
+ Main handler for getting memory subgraph based on query.
115
+
116
+ Retrieves relevant memory subgraph and formats it as a tree structure.
117
+
118
+ Args:
119
+ user_id: User ID
120
+ mem_cube_id: Memory cube ID
121
+ query: Search query
122
+ top_k: Number of top results to return
123
+ naive_mem_cube: Memory cube instance
124
+
125
+ Returns:
126
+ MemoryResponse with formatted subgraph data
127
+ """
128
+ try:
129
+ # Get relevant subgraph from text memory
130
+ memories = naive_mem_cube.text_mem.get_relevant_subgraph(
131
+ query, top_k=top_k, user_name=mem_cube_id
132
+ )
133
+
134
+ # Format and convert to tree structure
135
+ memories_cleaned = remove_embedding_recursive(memories)
136
+ custom_type_ratios = {
137
+ "WorkingMemory": 0.20,
138
+ "LongTermMemory": 0.40,
139
+ "UserMemory": 0.40,
140
+ }
141
+ tree_result, node_type_count = convert_graph_to_tree_forworkmem(
142
+ memories_cleaned, target_node_count=150, type_ratios=custom_type_ratios
143
+ )
144
+ # Ensure all node IDs are unique in the tree structure
145
+ tree_result = ensure_unique_tree_ids(tree_result)
146
+ memories_filtered = filter_nodes_by_tree_ids(tree_result, memories_cleaned)
147
+ children = tree_result["children"]
148
+ children_sort = sort_children_by_memory_type(children)
149
+ tree_result["children"] = children_sort
150
+ memories_filtered["tree_structure"] = tree_result
151
+
152
+ reformat_memory_list = [
153
+ {
154
+ "cube_id": mem_cube_id,
155
+ "memories": [memories_filtered],
156
+ "memory_statistics": node_type_count,
157
+ }
158
+ ]
159
+
160
+ return MemoryResponse(
161
+ message="Memories retrieved successfully",
162
+ data=reformat_memory_list,
163
+ )
164
+
165
+ except Exception as e:
166
+ logger.error(f"Failed to get subgraph: {e}", exc_info=True)
167
+ raise
168
+
169
+
170
+ def handle_get_memory(memory_id: str, naive_mem_cube: NaiveMemCube) -> GetMemoryResponse:
171
+ """
172
+ Handler for getting a single memory by its ID.
173
+
174
+ Tries to retrieve from text memory first, then preference memory if not found.
175
+
176
+ Args:
177
+ memory_id: The ID of the memory to retrieve
178
+ naive_mem_cube: Memory cube instance
179
+
180
+ Returns:
181
+ GetMemoryResponse with the memory data
182
+ """
183
+
184
+ try:
185
+ memory = naive_mem_cube.text_mem.get(memory_id)
186
+ except Exception:
187
+ memory = None
188
+
189
+ # If not found in text memory, try preference memory
190
+ pref = None
191
+ if memory is None and naive_mem_cube.pref_mem is not None:
192
+ collection_names = ["explicit_preference", "implicit_preference"]
193
+ for collection_name in collection_names:
194
+ try:
195
+ pref = naive_mem_cube.pref_mem.get_with_collection_name(collection_name, memory_id)
196
+ if pref is not None:
197
+ break
198
+ except Exception:
199
+ continue
200
+
201
+ # Get the data from whichever memory source succeeded
202
+ data = (memory or pref).model_dump() if (memory or pref) else None
203
+
204
+ return GetMemoryResponse(
205
+ message="Memory retrieved successfully"
206
+ if data
207
+ else f"Memory with ID {memory_id} not found",
208
+ code=200,
209
+ data=data,
210
+ )
211
+
212
+
213
+ def handle_get_memories(
214
+ get_mem_req: GetMemoryRequest, naive_mem_cube: NaiveMemCube
215
+ ) -> GetMemoryResponse:
216
+ results: dict[str, Any] = {"text_mem": [], "pref_mem": [], "tool_mem": []}
217
+ memories = naive_mem_cube.text_mem.get_all(
218
+ user_name=get_mem_req.mem_cube_id,
219
+ user_id=get_mem_req.user_id,
220
+ page=get_mem_req.page,
221
+ page_size=get_mem_req.page_size,
222
+ filter=get_mem_req.filter,
223
+ )["nodes"]
224
+
225
+ results = post_process_textual_mem(results, memories, get_mem_req.mem_cube_id)
226
+
227
+ if not get_mem_req.include_tool_memory:
228
+ results["tool_mem"] = []
229
+
230
+ preferences: list[TextualMemoryItem] = []
231
+
232
+ format_preferences = []
233
+ if get_mem_req.include_preference and naive_mem_cube.pref_mem is not None:
234
+ filter_params: dict[str, Any] = {}
235
+ if get_mem_req.user_id is not None:
236
+ filter_params["user_id"] = get_mem_req.user_id
237
+ if get_mem_req.mem_cube_id is not None:
238
+ filter_params["mem_cube_id"] = get_mem_req.mem_cube_id
239
+ if get_mem_req.filter is not None:
240
+ # Check and remove user_id/mem_cube_id from filter if present
241
+ filter_copy = get_mem_req.filter.copy()
242
+ removed_fields = []
243
+
244
+ if "user_id" in filter_copy:
245
+ filter_copy.pop("user_id")
246
+ removed_fields.append("user_id")
247
+ if "mem_cube_id" in filter_copy:
248
+ filter_copy.pop("mem_cube_id")
249
+ removed_fields.append("mem_cube_id")
250
+
251
+ if removed_fields:
252
+ logger.warning(
253
+ f"Fields {removed_fields} found in filter will be ignored. "
254
+ f"Use request-level user_id/mem_cube_id parameters instead."
255
+ )
256
+
257
+ filter_params.update(filter_copy)
258
+
259
+ preferences, _ = naive_mem_cube.pref_mem.get_memory_by_filter(
260
+ filter_params, page=get_mem_req.page, page_size=get_mem_req.page_size
261
+ )
262
+ format_preferences = [format_memory_item(item, save_sources=False) for item in preferences]
263
+
264
+ results = post_process_pref_mem(
265
+ results, format_preferences, get_mem_req.mem_cube_id, get_mem_req.include_preference
266
+ )
267
+
268
+ # Filter to only keep text_mem, pref_mem, tool_mem
269
+ filtered_results = {
270
+ "text_mem": results.get("text_mem", []),
271
+ "pref_mem": results.get("pref_mem", []),
272
+ "tool_mem": results.get("tool_mem", []),
273
+ }
274
+
275
+ return GetMemoryResponse(message="Memories retrieved successfully", data=filtered_results)
276
+
277
+
278
+ def handle_delete_memories(delete_mem_req: DeleteMemoryRequest, naive_mem_cube: NaiveMemCube):
279
+ logger.info(
280
+ f"[Delete memory request] writable_cube_ids: {delete_mem_req.writable_cube_ids}, memory_ids: {delete_mem_req.memory_ids}"
281
+ )
282
+ # Validate that only one of memory_ids, file_ids, or filter is provided
283
+ provided_params = [
284
+ delete_mem_req.memory_ids is not None,
285
+ delete_mem_req.file_ids is not None,
286
+ delete_mem_req.filter is not None,
287
+ ]
288
+ if sum(provided_params) != 1:
289
+ return DeleteMemoryResponse(
290
+ message="Exactly one of memory_ids, file_ids, or filter must be provided",
291
+ data={"status": "failure"},
292
+ )
293
+
294
+ try:
295
+ if delete_mem_req.memory_ids is not None:
296
+ naive_mem_cube.text_mem.delete_by_memory_ids(delete_mem_req.memory_ids)
297
+ if naive_mem_cube.pref_mem is not None:
298
+ naive_mem_cube.pref_mem.delete(delete_mem_req.memory_ids)
299
+ elif delete_mem_req.file_ids is not None:
300
+ naive_mem_cube.text_mem.delete_by_filter(
301
+ writable_cube_ids=delete_mem_req.writable_cube_ids, file_ids=delete_mem_req.file_ids
302
+ )
303
+ elif delete_mem_req.filter is not None:
304
+ naive_mem_cube.text_mem.delete_by_filter(filter=delete_mem_req.filter)
305
+ if naive_mem_cube.pref_mem is not None:
306
+ naive_mem_cube.pref_mem.delete_by_filter(filter=delete_mem_req.filter)
307
+ except Exception as e:
308
+ logger.error(f"Failed to delete memories: {e}", exc_info=True)
309
+ return DeleteMemoryResponse(
310
+ message="Failed to delete memories",
311
+ data={"status": "failure"},
312
+ )
313
+ return DeleteMemoryResponse(
314
+ message="Memories deleted successfully",
315
+ data={"status": "success"},
316
+ )