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,571 @@
1
+ from datetime import datetime
2
+
3
+ from memos.configs.mem_os import MOSConfig
4
+ from memos.log import get_logger
5
+ from memos.mem_os.main import MOS
6
+ from memos.mem_scheduler.schemas.general_schemas import (
7
+ MONITOR_WORKING_MEMORY_TYPE,
8
+ )
9
+ from memos.mem_scheduler.schemas.message_schemas import ScheduleMessageItem
10
+ from memos.mem_scheduler.schemas.task_schemas import (
11
+ ANSWER_TASK_LABEL,
12
+ QUERY_TASK_LABEL,
13
+ )
14
+
15
+
16
+ logger = get_logger(__name__)
17
+
18
+
19
+ class MOSForTestScheduler(MOS):
20
+ """This class is only to test abilities of mem scheduler with enhanced monitoring"""
21
+
22
+ def __init__(self, config: MOSConfig):
23
+ super().__init__(config)
24
+ self.memory_helpfulness_analysis = []
25
+
26
+ def _str_memories(self, memories: list[str]) -> str:
27
+ """Format memories for display."""
28
+ if not memories:
29
+ return "No memories."
30
+ return "\n".join(f"{i + 1}. {memory}" for i, memory in enumerate(memories))
31
+
32
+ def _analyze_memory_helpfulness(
33
+ self,
34
+ query: str,
35
+ working_memories_before: list,
36
+ working_memories_after: list,
37
+ scheduler_memories: list,
38
+ ):
39
+ """Analyze how helpful each memory is for answering the current query."""
40
+ print("\n" + "=" * 80)
41
+ print("🧠 MEMORY HELPFULNESS ANALYSIS FOR QUERY")
42
+ print("=" * 80)
43
+
44
+ print(f"šŸ“ Query: {query}")
45
+ print(f"šŸ“Š Working Memories Before Scheduler: {len(working_memories_before)}")
46
+ print(f"šŸ“Š Working Memories After Scheduler: {len(working_memories_after)}")
47
+ print(f"šŸ“Š Working Memories from Monitor: {len(scheduler_memories)}")
48
+
49
+ # Display working memories before scheduler (first 5 only)
50
+ if working_memories_before:
51
+ print("\nšŸ”„ WORKING MEMORIES BEFORE SCHEDULER (first 5):")
52
+ for i, mem in enumerate(working_memories_before[:5]):
53
+ print(f" {i + 1}. {mem}")
54
+
55
+ # Display working memories after scheduler (first 5 only)
56
+ if working_memories_after:
57
+ print("\nšŸ”„ WORKING MEMORIES AFTER SCHEDULER (first 5):")
58
+ for i, mem in enumerate(working_memories_after[:5]):
59
+ print(f" {i + 1}. {mem}")
60
+
61
+ # Display scheduler memories from monitor (first 5 only)
62
+ if scheduler_memories:
63
+ print("\nšŸ”„ WORKING MEMORIES FROM MONITOR (first 5):")
64
+ for i, mem in enumerate(scheduler_memories[:5]):
65
+ print(f" {i + 1}. {mem}")
66
+
67
+ # Batch assess working memory helpfulness before scheduler
68
+ if working_memories_before:
69
+ print(
70
+ f"\nšŸ”„ WORKING MEMORY HELPFULNESS BEFORE SCHEDULER ({len(working_memories_before)}):"
71
+ )
72
+ before_assessment = self._batch_assess_memories(
73
+ query, working_memories_before[:5], "before scheduler"
74
+ )
75
+ for i, (_mem, score, reason) in enumerate(before_assessment):
76
+ print(f" {i + 1}. Helpfulness: {score}/10 - {reason}")
77
+
78
+ # Batch assess working memory helpfulness after scheduler
79
+ if working_memories_after:
80
+ print(
81
+ f"\nšŸ”„ WORKING MEMORY HELPFULNESS AFTER SCHEDULER ({len(working_memories_after)}):"
82
+ )
83
+ after_assessment = self._batch_assess_memories(
84
+ query, working_memories_after[:5], "after scheduler"
85
+ )
86
+ for i, (_mem, score, reason) in enumerate(after_assessment):
87
+ print(f" {i + 1}. Helpfulness: {score}/10 - {reason}")
88
+
89
+ # Batch assess scheduler memories from monitor
90
+ if scheduler_memories:
91
+ print(f"\nšŸ”„ WORKINGMEMORIES FROM MONITOR HELPFULNESS ({len(scheduler_memories)}):")
92
+ scheduler_assessment = self._batch_assess_memories(
93
+ query, scheduler_memories[:5], "from monitor"
94
+ )
95
+ for i, (_mem, score, reason) in enumerate(scheduler_assessment):
96
+ print(f" {i + 1}. Helpfulness: {score}/10 - {reason}")
97
+
98
+ # Overall assessment - compare before vs after vs scheduler
99
+ print("\nšŸ’” OVERALL ASSESSMENT:")
100
+ if working_memories_before and working_memories_after:
101
+ before_scores = (
102
+ [score for _, score, _ in before_assessment]
103
+ if "before_assessment" in locals()
104
+ else []
105
+ )
106
+ after_scores = (
107
+ [score for _, score, _ in after_assessment]
108
+ if "after_assessment" in locals()
109
+ else []
110
+ )
111
+ scheduler_scores = (
112
+ [score for _, score, _ in scheduler_assessment]
113
+ if "scheduler_assessment" in locals()
114
+ else []
115
+ )
116
+
117
+ avg_before_helpfulness = sum(before_scores) / len(before_scores)
118
+ avg_after_helpfulness = sum(after_scores) / len(after_scores)
119
+
120
+ print(f" Average Helpfulness Before Scheduler: {avg_before_helpfulness:.1f}/10")
121
+ print(f" Average Helpfulness After Scheduler: {avg_after_helpfulness:.1f}/10")
122
+ print(f" Improvement: {avg_after_helpfulness - avg_before_helpfulness:+.1f}")
123
+
124
+ if avg_after_helpfulness > avg_before_helpfulness:
125
+ print(" āœ… Scheduler improved working memory quality")
126
+ elif avg_after_helpfulness < avg_before_helpfulness:
127
+ print(" āŒ Scheduler decreased working memory quality")
128
+ else:
129
+ print(" āš–ļø Scheduler maintained working memory quality")
130
+
131
+ # Compare scheduler memories vs working memories
132
+
133
+ avg_scheduler_helpfulness = sum(scheduler_scores) / len(scheduler_scores)
134
+ print(
135
+ f" Average Helpfulness of Memories from Monitors: {avg_scheduler_helpfulness:.1f}/10"
136
+ )
137
+
138
+ if avg_scheduler_helpfulness > avg_after_helpfulness:
139
+ print(" šŸŽÆ Memories from Monitors are more helpful than working memories")
140
+ elif avg_scheduler_helpfulness < avg_after_helpfulness:
141
+ print(" āš ļø Working memories are more helpful than Memories from Monitors")
142
+ else:
143
+ print(
144
+ " āš–ļø WORKING Memories from Monitors and working memories have similar helpfulness"
145
+ )
146
+
147
+ # Record analysis results
148
+ self.memory_helpfulness_analysis.append(
149
+ {
150
+ "query": query,
151
+ "working_memories_before_count": len(working_memories_before),
152
+ "working_memories_after_count": len(working_memories_after),
153
+ "scheduler_memories_count": len(scheduler_memories),
154
+ "working_helpfulness_before": [score for _, score, _ in before_assessment]
155
+ if "before_assessment" in locals()
156
+ else [],
157
+ "working_helpfulness_after": [score for _, score, _ in after_assessment]
158
+ if "after_assessment" in locals()
159
+ else [],
160
+ "scheduler_helpfulness": [score for _, score, _ in scheduler_assessment]
161
+ if "scheduler_assessment" in locals()
162
+ else [],
163
+ }
164
+ )
165
+
166
+ print("=" * 80 + "\n")
167
+
168
+ def _batch_assess_memories(self, query: str, memories: list, context: str) -> list:
169
+ """Use LLM to assess multiple memories at once and compare their quality."""
170
+ try:
171
+ # Create prompt for batch assessment
172
+ memories_text = "\n".join([f"{i + 1}. {mem}" for i, mem in enumerate(memories)])
173
+
174
+ assessment_prompt = f"""
175
+ Task: Assess and compare the helpfulness of multiple memories for answering a query.
176
+
177
+ Query: "{query}"
178
+
179
+ Context: These are working memories {context}.
180
+
181
+ Memories to assess:
182
+ {memories_text}
183
+
184
+ Please provide:
185
+ 1. A helpfulness score from 1-10 for each memory (where 10 = extremely helpful, 1 = not helpful at all)
186
+ 2. A brief reason for each score
187
+ 3. Rank the memories from most helpful to least helpful
188
+
189
+ Format your response as:
190
+ Memory 1: Score [number] - [reason]
191
+ Memory 2: Score [number] - [reason]
192
+ Memory 3: Score [number] - [reason]
193
+ Memory 4: Score [number] - [reason]
194
+ Memory 5: Score [number] - [reason]
195
+
196
+ Ranking: [memory numbers in order from most to least helpful]
197
+
198
+ Consider:
199
+ - Direct relevance to the query
200
+ - Information completeness
201
+ - How directly it answers the question
202
+ - Whether it provides useful context or background
203
+ - Compare memories against each other for relative quality
204
+ """
205
+
206
+ # Use the chat LLM to get batch assessment
207
+ messages = [{"role": "user", "content": assessment_prompt}]
208
+ response = self.chat_llm.generate(messages)
209
+
210
+ # Parse the response to extract scores and reasons
211
+ assessment_results = []
212
+ lines = response.strip().split("\n")
213
+
214
+ for i, mem in enumerate(memories):
215
+ score = 5 # Default score
216
+ reason = "LLM assessment failed, using default score"
217
+
218
+ # Look for the corresponding memory line
219
+ for line in lines:
220
+ if line.startswith(f"Memory {i + 1}:"):
221
+ try:
222
+ # Extract score and reason from line like "Memory 1: Score 8 - Highly relevant"
223
+ parts = line.split("Score ")[1].split(" - ", 1)
224
+ score = int(parts[0])
225
+ score = max(1, min(10, score)) # Ensure score is 1-10
226
+ reason = parts[1] if len(parts) > 1 else "No reason provided"
227
+ except Exception:
228
+ pass
229
+ break
230
+
231
+ assessment_results.append((mem, score, reason))
232
+
233
+ return assessment_results
234
+
235
+ except Exception as e:
236
+ logger.warning(f"LLM batch assessment failed: {e}, using fallback scoring")
237
+ # Fallback to individual assessment if batch fails
238
+ return [
239
+ (
240
+ mem,
241
+ self._assess_memory_helpfulness(query, mem)["score"],
242
+ self._assess_memory_helpfulness(query, mem)["reason"],
243
+ )
244
+ for mem in memories
245
+ ]
246
+
247
+ def _assess_memory_helpfulness(self, query: str, memory: str) -> dict:
248
+ """Use LLM to assess how helpful a memory is for answering the current query (1-10 scale)"""
249
+ try:
250
+ # Create prompt for LLM assessment
251
+ assessment_prompt = f"""
252
+ Task: Rate how helpful this memory is for answering the given query on a scale of 1-10.
253
+
254
+ Query: "{query}"
255
+
256
+ Memory: "{memory}"
257
+
258
+ Please provide:
259
+ 1. A score from 1-10 (where 10 = extremely helpful, 1 = not helpful at all)
260
+ 2. A brief reason for your score
261
+
262
+ Format your response as:
263
+ Score: [number]
264
+ Reason: [your explanation]
265
+
266
+ Consider:
267
+ - Direct relevance to the query
268
+ - Information completeness
269
+ - How directly it answers the question
270
+ - Whether it provides useful context or background
271
+ """
272
+
273
+ # Use the chat LLM to get assessment
274
+ messages = [{"role": "user", "content": assessment_prompt}]
275
+ response = self.chat_llm.generate(messages)
276
+
277
+ # Parse the response to extract score and reason
278
+ lines = response.strip().split("\n")
279
+ score = 5 # Default score
280
+ reason = "LLM assessment failed, using default score"
281
+
282
+ for line in lines:
283
+ if line.startswith("Score:"):
284
+ try:
285
+ score_text = line.split(":")[1].strip()
286
+ score = int(score_text)
287
+ score = max(1, min(10, score)) # Ensure score is 1-10
288
+ except Exception:
289
+ pass
290
+ elif line.startswith("Reason:"):
291
+ reason = line.split(":", 1)[1].strip()
292
+
293
+ return {"score": score, "reason": reason}
294
+
295
+ except Exception as e:
296
+ logger.warning(f"LLM assessment failed: {e}, using fallback scoring")
297
+ # Fallback to simple keyword matching if LLM fails
298
+ return self._fallback_memory_assessment(query, memory)
299
+
300
+ def _fallback_memory_assessment(self, query: str, memory: str) -> dict:
301
+ """Fallback assessment method using keyword matching if LLM fails"""
302
+ query_lower = query.lower()
303
+ memory_lower = memory.lower()
304
+
305
+ # Keyword matching
306
+ query_words = set(query_lower.split())
307
+ memory_words = set(memory_lower.split())
308
+ common_words = query_words.intersection(memory_words)
309
+
310
+ # Semantic relevance scoring
311
+ score = 0
312
+
313
+ # Exact keyword matches (highest weight)
314
+ if len(common_words) > 0:
315
+ score += min(len(common_words) * 2, 6)
316
+
317
+ # Partial matches (medium weight)
318
+ partial_matches = sum(
319
+ 1 for qw in query_words for mw in memory_words if qw in mw or mw in qw
320
+ )
321
+ if partial_matches > 0:
322
+ score += min(partial_matches, 3)
323
+
324
+ # Topic relevance (through common topic words)
325
+ topic_words = [
326
+ "problem",
327
+ "solution",
328
+ "answer",
329
+ "method",
330
+ "reason",
331
+ "result",
332
+ "analysis",
333
+ "compare",
334
+ "explain",
335
+ ]
336
+ topic_matches = sum(1 for topic in topic_words if topic in memory_lower)
337
+ score += topic_matches
338
+
339
+ # Ensure score is 1-10
340
+ score = max(1, min(10, score))
341
+
342
+ # Determine helpfulness level
343
+ if score >= 8:
344
+ reason = "Highly relevant, directly answers the query"
345
+ elif score >= 6:
346
+ reason = "Relevant, provides useful information"
347
+ elif score >= 4:
348
+ reason = "Partially relevant, somewhat helpful"
349
+ elif score >= 2:
350
+ reason = "Low relevance, limited help"
351
+ else:
352
+ reason = "Very low relevance, minimal help"
353
+
354
+ return {"score": score, "reason": reason}
355
+
356
+ def _assess_ranking_quality(self, rank: int, helpfulness: int) -> str:
357
+ """Use LLM to assess whether the memory ranking is reasonable"""
358
+ try:
359
+ # Create prompt for LLM ranking assessment
360
+ ranking_prompt = f"""
361
+ Task: Assess whether this memory ranking is reasonable.
362
+
363
+ Context: A memory with helpfulness score {helpfulness}/10 is ranked at position {rank}.
364
+
365
+ Please evaluate if this ranking makes sense and provide a brief assessment.
366
+
367
+ Consider:
368
+ - Higher helpfulness scores should generally rank higher
369
+ - Rank 1 should typically have the highest helpfulness
370
+ - The relationship between rank and helpfulness
371
+
372
+ Provide a brief assessment in one sentence.
373
+ """
374
+
375
+ # Use the chat LLM to get assessment
376
+ messages = [{"role": "user", "content": ranking_prompt}]
377
+ response = self.chat_llm.generate(messages)
378
+
379
+ return response.strip()
380
+
381
+ except Exception as e:
382
+ logger.warning(f"LLM ranking assessment failed: {e}, using fallback assessment")
383
+ # Fallback assessment
384
+ if rank == 1 and helpfulness >= 8:
385
+ return "āœ… Ranking is reasonable - most helpful memory ranked first"
386
+ elif rank == 1 and helpfulness <= 4:
387
+ return "āŒ Ranking is unreasonable - first ranked memory has low helpfulness"
388
+ elif rank <= 3 and helpfulness >= 6:
389
+ return "āœ… Ranking is reasonable - high helpfulness memory ranked high"
390
+ elif rank <= 3 and helpfulness <= 3:
391
+ return "āš ļø Ranking may be unreasonable - low helpfulness memory ranked high"
392
+ elif rank > 3 and helpfulness >= 7:
393
+ return "āš ļø Ranking may be unreasonable - high helpfulness memory ranked low"
394
+ else:
395
+ return "🟔 Ranking is acceptable - helpfulness and rank generally match"
396
+
397
+ def chat(self, query: str, user_id: str | None = None) -> str:
398
+ """
399
+ Chat with the MOS with memory helpfulness analysis.
400
+
401
+ Args:
402
+ query (str): The user's query.
403
+ user_id (str | None): The user ID.
404
+
405
+ Returns:
406
+ str: The response from the MOS.
407
+ """
408
+ target_user_id = user_id if user_id is not None else self.user_id
409
+ accessible_cubes = self.user_manager.get_user_cubes(target_user_id)
410
+ user_cube_ids = [cube.cube_id for cube in accessible_cubes]
411
+
412
+ if target_user_id not in self.chat_history_manager:
413
+ self._register_chat_history(target_user_id)
414
+
415
+ chat_history = self.chat_history_manager[target_user_id]
416
+ topk_for_scheduler = 2
417
+
418
+ if self.config.enable_textual_memory and self.mem_cubes:
419
+ memories_all = []
420
+ for mem_cube_id, mem_cube in self.mem_cubes.items():
421
+ if mem_cube_id not in user_cube_ids:
422
+ continue
423
+ if not mem_cube.text_mem:
424
+ continue
425
+
426
+ # Get working memories BEFORE scheduler
427
+ working_memories_before = [m.memory for m in mem_cube.text_mem.get_working_memory()]
428
+
429
+ message_item = ScheduleMessageItem(
430
+ user_id=target_user_id,
431
+ mem_cube_id=mem_cube_id,
432
+ label=QUERY_TASK_LABEL,
433
+ content=query,
434
+ timestamp=datetime.now(),
435
+ )
436
+
437
+ print(f"\nšŸš€ Starting Scheduler for {mem_cube_id}...")
438
+
439
+ # Force scheduler to run immediately
440
+ self.mem_scheduler.monitor.query_trigger_interval = 0
441
+ self.mem_scheduler._query_message_consumer(messages=[message_item])
442
+
443
+ # Get scheduler memories
444
+ scheduler_memories = self.mem_scheduler.monitor.get_monitor_memories(
445
+ user_id=target_user_id,
446
+ mem_cube_id=mem_cube_id,
447
+ memory_type=MONITOR_WORKING_MEMORY_TYPE,
448
+ top_k=20,
449
+ )
450
+
451
+ # Get working memories AFTER scheduler
452
+ working_memories_after = [m.memory for m in mem_cube.text_mem.get_working_memory()]
453
+
454
+ # Get mem_cube memories for response generation
455
+ memories = mem_cube.text_mem.search(
456
+ query,
457
+ top_k=self.config.top_k - topk_for_scheduler,
458
+ info={
459
+ "user_id": target_user_id,
460
+ "session_id": self.session_id,
461
+ "chat_history": chat_history.chat_history,
462
+ },
463
+ )
464
+ text_memories = [m.memory for m in memories]
465
+
466
+ # Analyze memory helpfulness - compare before vs after vs scheduler
467
+ self._analyze_memory_helpfulness(
468
+ query, working_memories_before, working_memories_after, scheduler_memories
469
+ )
470
+
471
+ # Combine all memories for response generation
472
+ memories_all.extend(scheduler_memories[:topk_for_scheduler])
473
+ memories_all.extend(text_memories)
474
+ memories_all = list(set(memories_all))
475
+
476
+ logger.info(f"🧠 [Memory] Searched memories:\n{self._str_memories(memories_all)}\n")
477
+ system_prompt = self._build_system_prompt(memories_all)
478
+ else:
479
+ system_prompt = self._build_system_prompt()
480
+
481
+ current_messages = [
482
+ {"role": "system", "content": system_prompt},
483
+ *chat_history.chat_history,
484
+ {"role": "user", "content": query},
485
+ ]
486
+ past_key_values = None
487
+
488
+ if self.config.enable_activation_memory:
489
+ if self.config.chat_model.backend != "huggingface":
490
+ logger.error(
491
+ "Activation memory only used for huggingface backend. Skipping activation memory."
492
+ )
493
+ else:
494
+ # TODO this only one cubes
495
+ for mem_cube_id, mem_cube in self.mem_cubes.items():
496
+ if mem_cube_id not in user_cube_ids:
497
+ continue
498
+ if mem_cube.act_mem:
499
+ kv_cache = next(iter(mem_cube.act_mem.get_all()), None)
500
+ past_key_values = (
501
+ kv_cache.memory if (kv_cache and hasattr(kv_cache, "memory")) else None
502
+ )
503
+ break
504
+ # Generate response
505
+ response = self.chat_llm.generate(current_messages, past_key_values=past_key_values)
506
+ else:
507
+ response = self.chat_llm.generate(current_messages)
508
+
509
+ logger.info(f"šŸ¤– [Assistant] {response}\n")
510
+ chat_history.chat_history.append({"role": "user", "content": query})
511
+ chat_history.chat_history.append({"role": "assistant", "content": response})
512
+ self.chat_history_manager[user_id] = chat_history
513
+
514
+ # Submit message to scheduler for answer processing
515
+ for accessible_mem_cube in accessible_cubes:
516
+ mem_cube_id = accessible_mem_cube.cube_id
517
+ mem_cube = self.mem_cubes[mem_cube_id]
518
+ if self.enable_mem_scheduler and self.mem_scheduler is not None:
519
+ message_item = ScheduleMessageItem(
520
+ user_id=target_user_id,
521
+ mem_cube_id=mem_cube_id,
522
+ label=ANSWER_TASK_LABEL,
523
+ content=response,
524
+ timestamp=datetime.now(),
525
+ )
526
+ self.mem_scheduler.submit_messages(messages=[message_item])
527
+
528
+ return response
529
+
530
+ def get_memory_helpfulness_summary(self) -> dict:
531
+ """Get summary of memory helpfulness analysis."""
532
+ if not self.memory_helpfulness_analysis:
533
+ return {"message": "No memory helpfulness analysis data available"}
534
+
535
+ total_queries = len(self.memory_helpfulness_analysis)
536
+
537
+ # Calculate average helpfulness for working memories before scheduler
538
+ before_scores = []
539
+ for analysis in self.memory_helpfulness_analysis:
540
+ before_scores.extend(analysis["working_helpfulness_before"])
541
+
542
+ # Calculate average helpfulness for working memories after scheduler
543
+ after_scores = []
544
+ for analysis in self.memory_helpfulness_analysis:
545
+ after_scores.extend(analysis["working_helpfulness_after"])
546
+
547
+ # Calculate average helpfulness for scheduler memories from monitor
548
+ scheduler_scores = []
549
+ for analysis in self.memory_helpfulness_analysis:
550
+ scheduler_scores.extend(analysis["scheduler_helpfulness"])
551
+
552
+ avg_before_helpfulness = sum(before_scores) / len(before_scores) if before_scores else 0
553
+ avg_after_helpfulness = sum(after_scores) / len(after_scores) if after_scores else 0
554
+ avg_scheduler_helpfulness = (
555
+ sum(scheduler_scores) / len(scheduler_scores) if scheduler_scores else 0
556
+ )
557
+
558
+ return {
559
+ "total_queries": total_queries,
560
+ "working_memories_before_analyzed": len(before_scores),
561
+ "working_memories_after_analyzed": len(after_scores),
562
+ "scheduler_memories_analyzed": len(scheduler_scores),
563
+ "average_helpfulness_before_scheduler": f"{avg_before_helpfulness:.1f}/10",
564
+ "average_helpfulness_after_scheduler": f"{avg_after_helpfulness:.1f}/10",
565
+ "average_helpfulness_scheduler_memories": f"{avg_scheduler_helpfulness:.1f}/10",
566
+ "overall_improvement": f"{avg_after_helpfulness - avg_before_helpfulness:+.1f}",
567
+ "improvement_percentage": f"{((avg_after_helpfulness - avg_before_helpfulness) / avg_before_helpfulness * 100):+.1f}%"
568
+ if avg_before_helpfulness > 0
569
+ else "N/A",
570
+ "scheduler_vs_working_comparison": f"{avg_scheduler_helpfulness - avg_after_helpfulness:+.1f}",
571
+ }