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,382 @@
1
+ import threading
2
+ import time
3
+
4
+ from collections.abc import Iterator
5
+ from contextlib import contextmanager
6
+ from typing import Any, Generic, TypeVar
7
+
8
+
9
+ K = TypeVar("K")
10
+ V = TypeVar("V")
11
+
12
+
13
+ class FastReadWriteLock:
14
+ """Read-write lock optimized for FastAPI scenarios:
15
+ reader priority with writer starvation prevention"""
16
+
17
+ def __init__(self):
18
+ self._readers = 0
19
+ self._writers = 0
20
+ self._waiting_writers = 0
21
+ self._lock = threading.RLock()
22
+ self._read_ready = threading.Condition(self._lock)
23
+ self._write_ready = threading.Condition(self._lock)
24
+ # Writer starvation detection
25
+ self._last_write_time = 0
26
+ self._write_starvation_threshold = 0.1 # 100ms
27
+
28
+ def acquire_read(self) -> bool:
29
+ """Fast read lock acquisition"""
30
+ with self._lock:
31
+ # Check if writers are starving
32
+ current_time = time.time()
33
+ write_starving = (
34
+ self._waiting_writers > 0
35
+ and current_time - self._last_write_time > self._write_starvation_threshold
36
+ )
37
+
38
+ # If no writers are active and no starvation, allow readers to continue
39
+ if self._writers == 0 and not write_starving:
40
+ self._readers += 1
41
+ return True
42
+
43
+ # Otherwise wait
44
+ while self._writers > 0 or write_starving:
45
+ self._read_ready.wait()
46
+ current_time = time.time()
47
+ write_starving = (
48
+ self._waiting_writers > 0
49
+ and current_time - self._last_write_time > self._write_starvation_threshold
50
+ )
51
+
52
+ self._readers += 1
53
+ return True
54
+
55
+ def release_read(self):
56
+ """Release read lock"""
57
+ with self._lock:
58
+ self._readers -= 1
59
+ if self._readers == 0:
60
+ self._write_ready.notify()
61
+
62
+ def acquire_write(self) -> bool:
63
+ """Write lock acquisition"""
64
+ with self._lock:
65
+ self._waiting_writers += 1
66
+ try:
67
+ while self._readers > 0 or self._writers > 0:
68
+ self._write_ready.wait()
69
+
70
+ self._writers = 1
71
+ self._waiting_writers -= 1
72
+ self._last_write_time = time.time()
73
+ return True
74
+ except:
75
+ self._waiting_writers -= 1
76
+ raise
77
+
78
+ def release_write(self):
79
+ """Release write lock"""
80
+ with self._lock:
81
+ self._writers = 0
82
+ # Prioritize notifying readers (reader priority strategy)
83
+ self._read_ready.notify_all()
84
+ self._write_ready.notify()
85
+
86
+
87
+ class SegmentedLock:
88
+ """Segmented lock, segments based on key hash"""
89
+
90
+ def __init__(self, segment_count: int = 64):
91
+ self.segment_count = segment_count
92
+ self.locks = [FastReadWriteLock() for _ in range(segment_count)]
93
+
94
+ def get_lock(self, key: K) -> FastReadWriteLock:
95
+ """Get the corresponding lock based on key"""
96
+ segment = hash(key) % self.segment_count
97
+ return self.locks[segment]
98
+
99
+ @contextmanager
100
+ def read_lock(self, key: K):
101
+ """Read lock context manager"""
102
+ lock = self.get_lock(key)
103
+ lock.acquire_read()
104
+ try:
105
+ yield
106
+ finally:
107
+ lock.release_read()
108
+
109
+ @contextmanager
110
+ def write_lock(self, key: K):
111
+ """Write lock context manager"""
112
+ lock = self.get_lock(key)
113
+ lock.acquire_write()
114
+ try:
115
+ yield
116
+ finally:
117
+ lock.release_write()
118
+
119
+
120
+ class OptimizedThreadSafeDict(Generic[K, V]):
121
+ """
122
+ Thread-safe dictionary optimized for FastAPI scenarios:
123
+ - Segmented locks to reduce contention
124
+ - Reader priority with writer starvation prevention
125
+ - Support for large object storage
126
+ - Strong consistency guarantee
127
+ """
128
+
129
+ def __init__(
130
+ self, initial_dict: dict[K, V] | None = None, segment_count: int = 128
131
+ ): # More segments for high concurrency
132
+ self._segments: list[dict[K, V]] = [{} for _ in range(segment_count)]
133
+ self._segment_count = segment_count
134
+ self._segmented_lock = SegmentedLock(segment_count)
135
+
136
+ # Initialize data
137
+ if initial_dict:
138
+ for k, v in initial_dict.items():
139
+ segment_idx = self._get_segment(k)
140
+ self._segments[segment_idx][k] = v
141
+
142
+ def _get_segment(self, key: K) -> int:
143
+ """Calculate the segment corresponding to the key"""
144
+ return hash(key) % self._segment_count
145
+
146
+ def __getitem__(self, key: K) -> V:
147
+ """Get element"""
148
+ segment_idx = self._get_segment(key)
149
+ with self._segmented_lock.read_lock(key):
150
+ return self._segments[segment_idx][key]
151
+
152
+ def __setitem__(self, key: K, value: V) -> None:
153
+ """Set element - key optimization point"""
154
+ segment_idx = self._get_segment(key)
155
+ with self._segmented_lock.write_lock(key):
156
+ self._segments[segment_idx][key] = value
157
+
158
+ def __delitem__(self, key: K) -> None:
159
+ """Delete element"""
160
+ segment_idx = self._get_segment(key)
161
+ with self._segmented_lock.write_lock(key):
162
+ del self._segments[segment_idx][key]
163
+
164
+ def __contains__(self, key: K) -> bool:
165
+ """Check if key is contained"""
166
+ segment_idx = self._get_segment(key)
167
+ with self._segmented_lock.read_lock(key):
168
+ return key in self._segments[segment_idx]
169
+
170
+ def get(self, key: K, default: V | None = None) -> V | None:
171
+ """Safely get element"""
172
+ segment_idx = self._get_segment(key)
173
+ with self._segmented_lock.read_lock(key):
174
+ return self._segments[segment_idx].get(key, default)
175
+
176
+ def pop(self, key: K, *args) -> V:
177
+ """Pop element"""
178
+ segment_idx = self._get_segment(key)
179
+ with self._segmented_lock.write_lock(key):
180
+ return self._segments[segment_idx].pop(key, *args)
181
+
182
+ def setdefault(self, key: K, default: V | None = None) -> V:
183
+ """Set default value"""
184
+ segment_idx = self._get_segment(key)
185
+ with self._segmented_lock.write_lock(key):
186
+ return self._segments[segment_idx].setdefault(key, default)
187
+
188
+ def update(self, other=None, **kwargs) -> None:
189
+ """Batch update - optimized batch operation"""
190
+ items = (other.items() if hasattr(other, "items") else other) if other is not None else []
191
+
192
+ # Group update items by segment
193
+ segment_updates: dict[int, list[tuple[K, V]]] = {}
194
+
195
+ for k, v in items:
196
+ segment_idx = self._get_segment(k)
197
+ if segment_idx not in segment_updates:
198
+ segment_updates[segment_idx] = []
199
+ segment_updates[segment_idx].append((k, v))
200
+
201
+ for k, v in kwargs.items():
202
+ segment_idx = self._get_segment(k)
203
+ if segment_idx not in segment_updates:
204
+ segment_updates[segment_idx] = []
205
+ segment_updates[segment_idx].append((k, v))
206
+
207
+ # Update segment by segment to reduce lock holding time
208
+ for segment_idx, updates in segment_updates.items():
209
+ # Use the first key to get the lock (all keys in the same segment map to the same lock)
210
+ first_key = updates[0][0]
211
+ with self._segmented_lock.write_lock(first_key):
212
+ for k, v in updates:
213
+ self._segments[segment_idx][k] = v
214
+
215
+ def clear(self) -> None:
216
+ """Clear all elements - need to acquire all locks"""
217
+ # Acquire all locks in order to avoid deadlock
218
+ acquired_locks = []
219
+ try:
220
+ for i in range(self._segment_count):
221
+ lock = self._segmented_lock.locks[i]
222
+ lock.acquire_write()
223
+ acquired_locks.append(lock)
224
+
225
+ # Clear all segments
226
+ for segment in self._segments:
227
+ segment.clear()
228
+
229
+ finally:
230
+ # Release locks in reverse order
231
+ for lock in reversed(acquired_locks):
232
+ lock.release_write()
233
+
234
+ def __len__(self) -> int:
235
+ """Get total length - snapshot read"""
236
+ total = 0
237
+ acquired_locks = []
238
+ try:
239
+ # Acquire all read locks
240
+ for i in range(self._segment_count):
241
+ lock = self._segmented_lock.locks[i]
242
+ lock.acquire_read()
243
+ acquired_locks.append(lock)
244
+
245
+ # Calculate total length
246
+ for segment in self._segments:
247
+ total += len(segment)
248
+
249
+ return total
250
+
251
+ finally:
252
+ # Release all read locks
253
+ for lock in reversed(acquired_locks):
254
+ lock.release_read()
255
+
256
+ def __bool__(self) -> bool:
257
+ """Check if empty"""
258
+ return len(self) > 0
259
+
260
+ def keys(self) -> list[K]:
261
+ """Get snapshot of all keys"""
262
+ all_keys = []
263
+ acquired_locks = []
264
+
265
+ try:
266
+ # Acquire all read locks
267
+ for i in range(self._segment_count):
268
+ lock = self._segmented_lock.locks[i]
269
+ lock.acquire_read()
270
+ acquired_locks.append(lock)
271
+
272
+ # Collect all keys
273
+ for segment in self._segments:
274
+ all_keys.extend(segment.keys())
275
+
276
+ return all_keys
277
+
278
+ finally:
279
+ for lock in reversed(acquired_locks):
280
+ lock.release_read()
281
+
282
+ def values(self) -> list[V]:
283
+ """Get snapshot of all values"""
284
+ all_values = []
285
+ acquired_locks = []
286
+
287
+ try:
288
+ for i in range(self._segment_count):
289
+ lock = self._segmented_lock.locks[i]
290
+ lock.acquire_read()
291
+ acquired_locks.append(lock)
292
+
293
+ for segment in self._segments:
294
+ all_values.extend(segment.values())
295
+
296
+ return all_values
297
+
298
+ finally:
299
+ for lock in reversed(acquired_locks):
300
+ lock.release_read()
301
+
302
+ def items(self) -> list[tuple[K, V]]:
303
+ """Get snapshot of all items"""
304
+ all_items = []
305
+ acquired_locks = []
306
+
307
+ try:
308
+ for i in range(self._segment_count):
309
+ lock = self._segmented_lock.locks[i]
310
+ lock.acquire_read()
311
+ acquired_locks.append(lock)
312
+
313
+ for segment in self._segments:
314
+ all_items.extend(segment.items())
315
+
316
+ return all_items
317
+
318
+ finally:
319
+ for lock in reversed(acquired_locks):
320
+ lock.release_read()
321
+
322
+ def copy(self) -> dict[K, V]:
323
+ """Create dictionary copy"""
324
+ result = {}
325
+ acquired_locks = []
326
+
327
+ try:
328
+ for i in range(self._segment_count):
329
+ lock = self._segmented_lock.locks[i]
330
+ lock.acquire_read()
331
+ acquired_locks.append(lock)
332
+
333
+ for segment in self._segments:
334
+ result.update(segment)
335
+
336
+ return result
337
+
338
+ finally:
339
+ for lock in reversed(acquired_locks):
340
+ lock.release_read()
341
+
342
+ def __iter__(self) -> Iterator[K]:
343
+ """Iterator - returns snapshot"""
344
+ return iter(self.keys())
345
+
346
+ def __repr__(self) -> str:
347
+ """String representation"""
348
+ return f"OptimizedThreadSafeDict({dict(self.items())})"
349
+
350
+ def stats(self) -> dict[str, Any]:
351
+ """Get statistics"""
352
+ segment_sizes = []
353
+ total_items = 0
354
+
355
+ acquired_locks = []
356
+ try:
357
+ for i in range(self._segment_count):
358
+ lock = self._segmented_lock.locks[i]
359
+ lock.acquire_read()
360
+ acquired_locks.append(lock)
361
+
362
+ for segment in self._segments:
363
+ size = len(segment)
364
+ segment_sizes.append(size)
365
+ total_items += size
366
+
367
+ avg_size = total_items / self._segment_count if self._segment_count > 0 else 0
368
+ max_size = max(segment_sizes) if segment_sizes else 0
369
+ min_size = min(segment_sizes) if segment_sizes else 0
370
+
371
+ return {
372
+ "total_items": total_items,
373
+ "segment_count": self._segment_count,
374
+ "avg_segment_size": avg_size,
375
+ "max_segment_size": max_size,
376
+ "min_segment_size": min_size,
377
+ "load_balance_ratio": min_size / max_size if max_size > 0 else 1.0,
378
+ }
379
+
380
+ finally:
381
+ for lock in reversed(acquired_locks):
382
+ lock.release_read()
File without changes
@@ -0,0 +1,86 @@
1
+ from __future__ import annotations
2
+
3
+ from concurrent.futures import as_completed
4
+ from dataclasses import dataclass
5
+ from typing import TYPE_CHECKING, Any
6
+
7
+ from memos.context.context import ContextThreadPoolExecutor
8
+ from memos.multi_mem_cube.views import MemCubeView
9
+
10
+
11
+ if TYPE_CHECKING:
12
+ from memos.api.product_models import APIADDRequest, APIFeedbackRequest, APISearchRequest
13
+ from memos.multi_mem_cube.single_cube import SingleCubeView
14
+
15
+
16
+ @dataclass
17
+ class CompositeCubeView(MemCubeView):
18
+ """
19
+ A composite view over multiple logical cubes.
20
+
21
+ For now (fast mode), it simply fan-out writes to all cubes;
22
+ later we can add smarter routing / slow mode here.
23
+ """
24
+
25
+ cube_views: list[SingleCubeView]
26
+ logger: Any
27
+
28
+ def add_memories(self, add_req: APIADDRequest) -> list[dict[str, Any]]:
29
+ all_results: list[dict[str, Any]] = []
30
+
31
+ # fast mode: for each cube view, add memories
32
+ # maybe add more strategies in add_req.async_mode
33
+ for view in self.cube_views:
34
+ self.logger.info(f"[CompositeCubeView] fan-out add to cube={view.cube_id}")
35
+ results = view.add_memories(add_req)
36
+ all_results.extend(results)
37
+
38
+ return all_results
39
+
40
+ def search_memories(self, search_req: APISearchRequest) -> dict[str, Any]:
41
+ # aggregated MOSSearchResult
42
+ merged_results: dict[str, Any] = {
43
+ "text_mem": [],
44
+ "act_mem": [],
45
+ "para_mem": [],
46
+ "pref_mem": [],
47
+ "pref_note": "",
48
+ "tool_mem": [],
49
+ }
50
+
51
+ def _search_single_cube(view: SingleCubeView) -> dict[str, Any]:
52
+ self.logger.info(f"[CompositeCubeView] fan-out search to cube={view.cube_id}")
53
+ return view.search_memories(search_req)
54
+
55
+ # parallel search for each cube
56
+ with ContextThreadPoolExecutor(max_workers=2) as executor:
57
+ future_to_view = {
58
+ executor.submit(_search_single_cube, view): view for view in self.cube_views
59
+ }
60
+
61
+ for future in as_completed(future_to_view):
62
+ cube_result = future.result()
63
+ merged_results["text_mem"].extend(cube_result.get("text_mem", []))
64
+ merged_results["act_mem"].extend(cube_result.get("act_mem", []))
65
+ merged_results["para_mem"].extend(cube_result.get("para_mem", []))
66
+ merged_results["pref_mem"].extend(cube_result.get("pref_mem", []))
67
+ merged_results["tool_mem"].extend(cube_result.get("tool_mem", []))
68
+
69
+ note = cube_result.get("pref_note")
70
+ if note:
71
+ if merged_results["pref_note"]:
72
+ merged_results["pref_note"] += " | " + note
73
+ else:
74
+ merged_results["pref_note"] = note
75
+
76
+ return merged_results
77
+
78
+ def feedback_memories(self, feedback_req: APIFeedbackRequest) -> list[dict[str, Any]]:
79
+ all_results: list[dict[str, Any]] = []
80
+
81
+ for view in self.cube_views:
82
+ self.logger.info(f"[CompositeCubeView] fan-out add to cube={view.cube_id}")
83
+ results = view.feedback_memories(feedback_req)
84
+ all_results.extend(results)
85
+
86
+ return all_results