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,292 @@
1
+ import os
2
+ import pickle
3
+
4
+ from datetime import datetime
5
+
6
+ from transformers import DynamicCache
7
+
8
+ from memos.configs.memory import KVCacheMemoryConfig
9
+ from memos.dependency import require_python_package
10
+ from memos.llms.factory import LLMFactory
11
+ from memos.memories.activation.base import BaseActMemory
12
+ from memos.memories.activation.item import KVCacheItem
13
+ from memos.memories.textual.item import TextualMemoryItem
14
+
15
+
16
+ class KVCacheMemory(BaseActMemory):
17
+ """
18
+ Key-Value Cache Memory for activation memories.
19
+ This memory type is designed to store and retrieve key-value caches.
20
+ """
21
+
22
+ @require_python_package(
23
+ import_name="torch",
24
+ install_link="https://pytorch.org/get-started/locally/",
25
+ )
26
+ def __init__(self, config: KVCacheMemoryConfig) -> None:
27
+ """Initialize the KV Cache Memory with a configuration."""
28
+ self.config = config
29
+ self.llm = LLMFactory.from_config(config.extractor_llm)
30
+ self.kv_cache_memories: dict[str, KVCacheItem] = {}
31
+
32
+ def extract(self, text: str) -> KVCacheItem:
33
+ """Extract memory based on the text.
34
+
35
+ Uses the LLM to build KV caches from the provided text.
36
+
37
+ Args:
38
+ text: Input text to extract memory from
39
+
40
+ Returns:
41
+ Extracted memory item
42
+ """
43
+ # Build KV cache from the text using the LLM
44
+ kv_cache = self.llm.build_kv_cache(text)
45
+
46
+ # Create a KVCacheItem with the extracted cache
47
+ cache_item = KVCacheItem(
48
+ memory=kv_cache,
49
+ metadata={"source_text": text, "extracted_at": datetime.now().isoformat()},
50
+ )
51
+
52
+ return cache_item
53
+
54
+ def add(self, memories: list[KVCacheItem]) -> None:
55
+ """Add memories to the KV cache memory.
56
+
57
+ Args:
58
+ memories: List of KVCacheItem to add
59
+ """
60
+ for memory in memories:
61
+ self.kv_cache_memories[memory.id] = memory
62
+
63
+ def get_cache(self, cache_ids: list[str]) -> DynamicCache | None:
64
+ """Merge multiple KV caches into a single cache.
65
+
66
+ Args:
67
+ cache_ids: List of cache IDs to merge
68
+
69
+ Returns:
70
+ Merged DynamicCache or None if no caches found
71
+ """
72
+ caches_to_merge = []
73
+ for cache_id in cache_ids:
74
+ cache_item = self.kv_cache_memories.get(cache_id)
75
+ if cache_item and cache_item.memory:
76
+ caches_to_merge.append(cache_item.memory)
77
+
78
+ if not caches_to_merge:
79
+ return None
80
+
81
+ return self._concat_caches(caches_to_merge)
82
+
83
+ def get(self, memory_id: str) -> KVCacheItem | None:
84
+ """Get a memory by its ID.
85
+
86
+ Args:
87
+ memory_id: ID of the memory to retrieve
88
+
89
+ Returns:
90
+ Memory dictionary or None if not found
91
+ """
92
+ return self.kv_cache_memories.get(memory_id)
93
+
94
+ def get_by_ids(self, memory_ids: list[str]) -> list[KVCacheItem | None]:
95
+ """Get memories by their IDs.
96
+
97
+ Args:
98
+ memory_ids: List of memory IDs to retrieve
99
+
100
+ Returns:
101
+ List of memory dictionaries or None for missing ones
102
+ """
103
+ results = []
104
+ for memory_id in memory_ids:
105
+ memory = self.get(memory_id)
106
+ results.append(memory)
107
+ return results
108
+
109
+ def get_all(self) -> list[KVCacheItem]:
110
+ """Get all memories.
111
+
112
+ Returns:
113
+ List of all KVCacheItems in the memory
114
+ """
115
+ return list(self.kv_cache_memories.values())
116
+
117
+ def delete(self, memory_ids: list[str]) -> None:
118
+ """Delete memories by their IDs.
119
+
120
+ Args:
121
+ memory_ids: List of memory IDs to delete
122
+ """
123
+ for memory_id in memory_ids:
124
+ self.kv_cache_memories.pop(memory_id, None)
125
+
126
+ def delete_all(self) -> None:
127
+ """Delete all memories."""
128
+ self.kv_cache_memories.clear()
129
+
130
+ def from_textual_memory(self, mem: TextualMemoryItem) -> KVCacheItem:
131
+ """
132
+ Convert a TextualMemoryItem to a KVCacheItem.
133
+ This method extracts the key-value cache from the textual memory.
134
+ """
135
+ # Build KV cache from the textual memory content
136
+ kv_cache = self.llm.build_kv_cache(mem.memory)
137
+ return KVCacheItem(memory=kv_cache, metadata=mem.metadata.model_dump())
138
+
139
+ def load(self, dir: str) -> None:
140
+ """Load memories from os.path.join(dir, self.config.memory_filename)
141
+
142
+ Args:
143
+ dir (str): The directory containing the memory files.
144
+ """
145
+ import torch
146
+
147
+ file_path = os.path.join(dir, self.config.memory_filename)
148
+
149
+ if not os.path.exists(file_path):
150
+ # If file doesn't exist, start with empty memories
151
+ return
152
+
153
+ try:
154
+ # Allow loading DynamicCache and KVCacheItem types
155
+ torch.serialization.add_safe_globals([DynamicCache, KVCacheItem])
156
+
157
+ with open(file_path, "rb") as f:
158
+ data = pickle.load(f)
159
+
160
+ if isinstance(data, dict):
161
+ # Load memories, handle both old and new formats
162
+ if "kv_cache_memories" in data:
163
+ memories = data["kv_cache_memories"]
164
+ if isinstance(memories, list):
165
+ # Convert list to dict format
166
+ self.kv_cache_memories = {item.id: item for item in memories}
167
+ else:
168
+ self.kv_cache_memories = memories
169
+ else:
170
+ # Reset to empty if no memories in data
171
+ self.kv_cache_memories = {}
172
+ elif isinstance(data, list):
173
+ # Backward compatibility: convert list to dict
174
+ self.kv_cache_memories = {item.id: item for item in data}
175
+ else:
176
+ # Reset to empty if data format is unexpected
177
+ self.kv_cache_memories = {}
178
+
179
+ except (EOFError, pickle.UnpicklingError, Exception):
180
+ # If loading fails, start with empty memories
181
+ self.kv_cache_memories = {}
182
+
183
+ def dump(self, dir: str) -> None:
184
+ """Dump memories to os.path.join(dir, self.config.memory_filename)
185
+
186
+ Args:
187
+ dir (str): The directory where the memory files will be saved.
188
+ """
189
+ file_path = os.path.join(dir, self.config.memory_filename)
190
+
191
+ # Create directory if it doesn't exist
192
+ os.makedirs(dir, exist_ok=True)
193
+
194
+ # Prepare data to save (only memories)
195
+ data = {"kv_cache_memories": self.kv_cache_memories}
196
+
197
+ with open(file_path, "wb") as f:
198
+ pickle.dump(data, f, protocol=pickle.HIGHEST_PROTOCOL)
199
+
200
+ def _concat_caches(self, caches: list[DynamicCache]) -> DynamicCache:
201
+ """
202
+ Faster concat merge: for each layer, gather all caches' tensors
203
+ and do a single torch.cat per layer.
204
+ """
205
+ import torch
206
+
207
+ assert caches, "Need at least one cache"
208
+ if len(caches) == 1:
209
+ return caches[0]
210
+
211
+ merged = DynamicCache()
212
+
213
+ # Check for new structure (layers)
214
+ if hasattr(caches[0], "layers"):
215
+ num_layers = len(caches[0].layers)
216
+
217
+ # Ensure merged has layers attribute and populate it
218
+ if not hasattr(merged, "layers"):
219
+ merged.layers = []
220
+
221
+ if num_layers > 0:
222
+ # Get the class of the layer from the first cache
223
+ # We assume all caches use the same layer class
224
+ layer_cls = type(caches[0].layers[0])
225
+
226
+ # Populate merged.layers
227
+ while len(merged.layers) < num_layers:
228
+ merged.layers.append(layer_cls())
229
+
230
+ for layer in range(num_layers):
231
+ # gather all K and V for this layer
232
+ keys = [c.layers[layer].keys for c in caches]
233
+ vals = [c.layers[layer].values for c in caches]
234
+ # single concat per layer
235
+ merged.layers[layer].keys = torch.cat(keys, dim=-2)
236
+ merged.layers[layer].values = torch.cat(vals, dim=-2)
237
+
238
+ # Check for old structure (key_cache)
239
+ elif hasattr(caches[0], "key_cache"):
240
+ num_layers = len(caches[0].key_cache)
241
+
242
+ for layer in range(num_layers):
243
+ # gather all K and V for this layer
244
+ keys = [c.key_cache[layer] for c in caches]
245
+ vals = [c.value_cache[layer] for c in caches]
246
+ # single concat per layer
247
+ merged.key_cache.append(torch.cat(keys, dim=-2))
248
+ merged.value_cache.append(torch.cat(vals, dim=-2))
249
+
250
+ else:
251
+ raise AttributeError(
252
+ "DynamicCache object has neither 'layers' nor 'key_cache' attributes"
253
+ )
254
+
255
+ return merged
256
+
257
+
258
+ def move_dynamic_cache_htod(dynamic_cache: DynamicCache, device: str) -> DynamicCache:
259
+ """
260
+ Move DynamicCache from CPU to GPU device.
261
+ Compatible with both old and new transformers versions.
262
+
263
+ In SimpleMemChat.run(), if self.config.enable_activation_memory is enabled,
264
+ we load serialized kv cache from a [class KVCacheMemory] object, which has a kv_cache_memories on CPU.
265
+ So before inferring with DynamicCache, we should move it to GPU in-place first.
266
+ """
267
+ # Handle compatibility between old and new transformers versions
268
+ if hasattr(dynamic_cache, "layers"):
269
+ # New version: use layers attribute
270
+ for layer in dynamic_cache.layers:
271
+ if hasattr(layer, "key_cache") and layer.key_cache is not None:
272
+ layer.key_cache = layer.key_cache.to(device, non_blocking=True)
273
+ if hasattr(layer, "value_cache") and layer.value_cache is not None:
274
+ layer.value_cache = layer.value_cache.to(device, non_blocking=True)
275
+ elif hasattr(layer, "keys") and hasattr(layer, "values"):
276
+ # Alternative attribute names in some versions
277
+ if layer.keys is not None:
278
+ layer.keys = layer.keys.to(device, non_blocking=True)
279
+ if layer.values is not None:
280
+ layer.values = layer.values.to(device, non_blocking=True)
281
+ elif hasattr(dynamic_cache, "key_cache") and hasattr(dynamic_cache, "value_cache"):
282
+ # Old version: use key_cache and value_cache attributes
283
+ for i in range(len(dynamic_cache.key_cache)):
284
+ if dynamic_cache.key_cache[i] is not None:
285
+ dynamic_cache.key_cache[i] = dynamic_cache.key_cache[i].to(
286
+ device, non_blocking=True
287
+ )
288
+ if dynamic_cache.value_cache[i] is not None:
289
+ dynamic_cache.value_cache[i] = dynamic_cache.value_cache[i].to(
290
+ device, non_blocking=True
291
+ )
292
+ return dynamic_cache
@@ -0,0 +1,219 @@
1
+ import os
2
+ import pickle
3
+
4
+ from datetime import datetime
5
+
6
+ from memos.configs.memory import KVCacheMemoryConfig
7
+ from memos.dependency import require_python_package
8
+ from memos.llms.factory import LLMFactory
9
+ from memos.memories.activation.base import BaseActMemory
10
+ from memos.memories.activation.item import VLLMKVCacheItem
11
+ from memos.memories.textual.item import TextualMemoryItem
12
+
13
+
14
+ class VLLMKVCacheMemory(BaseActMemory):
15
+ """
16
+ VLLM Key-Value Cache Memory for activation memories.
17
+ This memory type is designed to store and retrieve prompt strings for vLLM KV cache preloading.
18
+ Unlike traditional KV cache that stores DynamicCache objects, vLLM handles cache on server side.
19
+ """
20
+
21
+ @require_python_package(
22
+ import_name="torch",
23
+ install_link="https://pytorch.org/get-started/locally/",
24
+ )
25
+ def __init__(self, config: KVCacheMemoryConfig) -> None:
26
+ """Initialize the VLLM KV Cache Memory with a configuration."""
27
+ self.config = config
28
+ self.llm = LLMFactory.from_config(config.extractor_llm)
29
+ self.kv_cache_memories: dict[str, VLLMKVCacheItem] = {}
30
+
31
+ def extract(self, text: str) -> VLLMKVCacheItem:
32
+ """Extract memory based on the text.
33
+
34
+ Uses the LLM to build vLLM KV cache from the provided text.
35
+ For vLLM, this means preloading the KV cache on the server side.
36
+
37
+ Args:
38
+ text: Input text to extract memory from
39
+
40
+ Returns:
41
+ Extracted VLLM KV cache item with prompt string
42
+ """
43
+ # Build vLLM KV cache from the text using the LLM
44
+ # This preloads the cache on the vLLM server and returns the prompt
45
+ prompt = self.llm.build_vllm_kv_cache(text)
46
+
47
+ # Create a VLLMKVCacheItem with the extracted prompt
48
+ cache_item = VLLMKVCacheItem(
49
+ memory=prompt,
50
+ metadata={"source_text": text, "extracted_at": datetime.now().isoformat()},
51
+ )
52
+
53
+ return cache_item
54
+
55
+ def add(self, memories: list[VLLMKVCacheItem]) -> None:
56
+ """Add memories to the VLLM KV cache memory.
57
+
58
+ Args:
59
+ memories: List of VLLMKVCacheItem to add
60
+ """
61
+ for memory in memories:
62
+ self.kv_cache_memories[memory.id] = memory
63
+
64
+ def get_cache(self, cache_ids: list[str]) -> str | None:
65
+ """Get the prompt string for the most recent cache.
66
+
67
+ Since vLLM handles KV cache on server side, we return the prompt string
68
+ that can be used for generation. For multiple caches, we return the most recent one.
69
+
70
+ Args:
71
+ cache_ids: List of cache IDs to consider
72
+
73
+ Returns:
74
+ Prompt string for the most recent cache or None if no caches found
75
+ """
76
+ if not cache_ids:
77
+ return None
78
+
79
+ # For vLLM, we typically want the most recent cache
80
+ # Return the prompt from the last cache ID in the list
81
+ latest_cache_id = cache_ids[-1]
82
+ cache_item = self.kv_cache_memories.get(latest_cache_id)
83
+
84
+ if cache_item and cache_item.memory:
85
+ return cache_item.memory
86
+
87
+ return None
88
+
89
+ def get(self, memory_id: str) -> VLLMKVCacheItem | None:
90
+ """Get a memory by its ID.
91
+
92
+ Args:
93
+ memory_id: ID of the memory to retrieve
94
+
95
+ Returns:
96
+ VLLMKVCacheItem or None if not found
97
+ """
98
+ return self.kv_cache_memories.get(memory_id)
99
+
100
+ def get_by_ids(self, memory_ids: list[str]) -> list[VLLMKVCacheItem | None]:
101
+ """Get memories by their IDs.
102
+
103
+ Args:
104
+ memory_ids: List of memory IDs to retrieve
105
+
106
+ Returns:
107
+ List of VLLMKVCacheItem or None for missing ones
108
+ """
109
+ results = []
110
+ for memory_id in memory_ids:
111
+ memory = self.get(memory_id)
112
+ results.append(memory)
113
+ return results
114
+
115
+ def get_all(self) -> list[VLLMKVCacheItem]:
116
+ """Get all memories.
117
+
118
+ Returns:
119
+ List of all VLLMKVCacheItems in the memory
120
+ """
121
+ return list(self.kv_cache_memories.values())
122
+
123
+ def delete(self, memory_ids: list[str]) -> None:
124
+ """Delete memories by their IDs.
125
+
126
+ Args:
127
+ memory_ids: List of memory IDs to delete
128
+ """
129
+ for memory_id in memory_ids:
130
+ self.kv_cache_memories.pop(memory_id, None)
131
+
132
+ def delete_all(self) -> None:
133
+ """Delete all memories."""
134
+ self.kv_cache_memories.clear()
135
+
136
+ def from_textual_memory(self, mem: TextualMemoryItem) -> VLLMKVCacheItem:
137
+ """
138
+ Convert a TextualMemoryItem to a VLLMKVCacheItem.
139
+ This method extracts the prompt string from the textual memory.
140
+ """
141
+ # Build vLLM KV cache from the textual memory content
142
+ prompt = self.llm.build_vllm_kv_cache(mem.memory)
143
+ return VLLMKVCacheItem(memory=prompt, metadata=mem.metadata.model_dump())
144
+
145
+ def load(self, dir: str) -> None:
146
+ """Load memories from os.path.join(dir, self.config.memory_filename)
147
+
148
+ Args:
149
+ dir (str): The directory containing the memory files.
150
+ """
151
+ file_path = os.path.join(dir, self.config.memory_filename)
152
+
153
+ if not os.path.exists(file_path):
154
+ # If file doesn't exist, start with empty memories
155
+ return
156
+
157
+ try:
158
+ # Allow loading VLLMKVCacheItem types
159
+ import torch
160
+
161
+ torch.serialization.add_safe_globals([VLLMKVCacheItem])
162
+
163
+ with open(file_path, "rb") as f:
164
+ data = pickle.load(f)
165
+
166
+ if isinstance(data, dict):
167
+ # Load memories, handle both old and new formats
168
+ if "kv_cache_memories" in data:
169
+ memories = data["kv_cache_memories"]
170
+ if isinstance(memories, list):
171
+ # Convert list to dict format
172
+ self.kv_cache_memories = {item.id: item for item in memories}
173
+ else:
174
+ self.kv_cache_memories = memories
175
+ else:
176
+ # Reset to empty if no memories in data
177
+ self.kv_cache_memories = {}
178
+ elif isinstance(data, list):
179
+ # Backward compatibility: convert list to dict
180
+ self.kv_cache_memories = {item.id: item for item in data}
181
+ else:
182
+ # Reset to empty if data format is unexpected
183
+ self.kv_cache_memories = {}
184
+
185
+ except (EOFError, pickle.UnpicklingError, Exception):
186
+ # If loading fails, start with empty memories
187
+ self.kv_cache_memories = {}
188
+
189
+ def dump(self, dir: str) -> None:
190
+ """Dump memories to os.path.join(dir, self.config.memory_filename)
191
+
192
+ Args:
193
+ dir (str): The directory where the memory files will be saved.
194
+ """
195
+ file_path = os.path.join(dir, self.config.memory_filename)
196
+
197
+ # Create directory if it doesn't exist
198
+ os.makedirs(dir, exist_ok=True)
199
+
200
+ # Prepare data to save (only memories)
201
+ data = {"kv_cache_memories": self.kv_cache_memories}
202
+
203
+ with open(file_path, "wb") as f:
204
+ pickle.dump(data, f, protocol=pickle.HIGHEST_PROTOCOL)
205
+
206
+ def preload_kv_cache(self, cache_ids: list[str]) -> None:
207
+ """
208
+ Preload KV cache on vLLM server for the given cache IDs.
209
+ This method calls build_vllm_kv_cache for each cache to ensure
210
+ the KV cache is loaded on the server side.
211
+
212
+ Args:
213
+ cache_ids: List of cache IDs to preload
214
+ """
215
+ for cache_id in cache_ids:
216
+ cache_item = self.kv_cache_memories.get(cache_id)
217
+ if cache_item and cache_item.memory:
218
+ # Re-preload the KV cache on the server
219
+ self.llm.build_vllm_kv_cache(cache_item.memory)
memos/memories/base.py ADDED
@@ -0,0 +1,19 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+
4
+ class BaseMemory(ABC):
5
+ """Base class for all memory implementations."""
6
+
7
+ @abstractmethod
8
+ def load(self, dir: str) -> None:
9
+ """Load memories from os.path.join(dir, self.config.memory_filename)
10
+ Args:
11
+ dir (str): The directory containing the memory files.
12
+ """
13
+
14
+ @abstractmethod
15
+ def dump(self, dir: str) -> None:
16
+ """Dump memories to os.path.join(dir, self.config.memory_filename)
17
+ Args:
18
+ dir (str): The directory where the memory files will be saved.
19
+ """
@@ -0,0 +1,42 @@
1
+ from typing import Any, ClassVar
2
+
3
+ from memos.configs.memory import MemoryConfigFactory
4
+ from memos.memories.activation.base import BaseActMemory
5
+ from memos.memories.activation.kv import KVCacheMemory
6
+ from memos.memories.activation.vllmkv import VLLMKVCacheMemory
7
+ from memos.memories.base import BaseMemory
8
+ from memos.memories.parametric.base import BaseParaMemory
9
+ from memos.memories.parametric.lora import LoRAMemory
10
+ from memos.memories.textual.base import BaseTextMemory
11
+ from memos.memories.textual.general import GeneralTextMemory
12
+ from memos.memories.textual.naive import NaiveTextMemory
13
+ from memos.memories.textual.preference import PreferenceTextMemory
14
+ from memos.memories.textual.simple_preference import SimplePreferenceTextMemory
15
+ from memos.memories.textual.simple_tree import SimpleTreeTextMemory
16
+ from memos.memories.textual.tree import TreeTextMemory
17
+
18
+
19
+ class MemoryFactory(BaseMemory):
20
+ """Factory class for creating memory instances."""
21
+
22
+ backend_to_class: ClassVar[dict[str, Any]] = {
23
+ "naive_text": NaiveTextMemory,
24
+ "general_text": GeneralTextMemory,
25
+ "tree_text": TreeTextMemory,
26
+ "simple_tree_text": SimpleTreeTextMemory,
27
+ "pref_text": PreferenceTextMemory,
28
+ "simple_pref_text": SimplePreferenceTextMemory,
29
+ "kv_cache": KVCacheMemory,
30
+ "vllm_kv_cache": VLLMKVCacheMemory,
31
+ "lora": LoRAMemory,
32
+ }
33
+
34
+ @classmethod
35
+ def from_config(
36
+ cls, config_factory: MemoryConfigFactory
37
+ ) -> BaseTextMemory | BaseActMemory | BaseParaMemory:
38
+ backend = config_factory.backend
39
+ if backend not in cls.backend_to_class:
40
+ raise ValueError(f"Invalid backend: {backend}")
41
+ memory_class = cls.backend_to_class[backend]
42
+ return memory_class(config_factory.config)
File without changes
@@ -0,0 +1,19 @@
1
+ ################################################################
2
+ # TODO:
3
+ # This file currently serves as a placeholder.
4
+ # The actual implementation will be added here in the future.
5
+ # Please do not use this as a functional module yet.
6
+ ################################################################
7
+
8
+ from abc import abstractmethod
9
+
10
+ from memos.configs.memory import BaseParaMemoryConfig
11
+ from memos.memories.base import BaseMemory
12
+
13
+
14
+ class BaseParaMemory(BaseMemory):
15
+ """Base class for all parametric memory implementations."""
16
+
17
+ @abstractmethod
18
+ def __init__(self, config: BaseParaMemoryConfig):
19
+ """Initialize memory with the given configuration."""
@@ -0,0 +1,11 @@
1
+ import uuid
2
+
3
+ from typing import Any
4
+
5
+ from pydantic import BaseModel, Field
6
+
7
+
8
+ class ParametricMemoryItem(BaseModel):
9
+ id: str = Field(default_factory=lambda: str(uuid.uuid4()))
10
+ memory: Any
11
+ metadata: dict = {}
@@ -0,0 +1,41 @@
1
+ ################################################################
2
+ # TODO:
3
+ # This file currently serves as a placeholder.
4
+ # The actual implementation will be added here in the future.
5
+ # Please do not use this as a functional module yet.
6
+ ################################################################
7
+
8
+ import os
9
+
10
+ from memos.configs.memory import LoRAMemoryConfig
11
+ from memos.memories.parametric.base import BaseParaMemory
12
+
13
+
14
+ class LoRAMemory(BaseParaMemory):
15
+ """
16
+ LoRA Memory for parametric memories.
17
+ This memory type is designed to store and retrieve low-rank adaptation (LoRA) parameters.
18
+ """
19
+
20
+ def __init__(self, config: LoRAMemoryConfig) -> None:
21
+ """Initialize the LoRA Memory with a configuration."""
22
+ self.config = config
23
+
24
+ def load(self, dir: str) -> None:
25
+ """Load memories from os.path.join(dir, self.config.memory_filename)
26
+
27
+ Args:
28
+ dir (str): The directory containing the memory files.
29
+ """
30
+
31
+ def dump(self, dir: str) -> None:
32
+ """Dump memories to os.path.join(dir, self.config.memory_filename)
33
+
34
+ Args:
35
+ dir (str): The directory where the memory files will be saved.
36
+ """
37
+ path = os.path.join(dir, self.config.memory_filename)
38
+ if not os.path.exists(dir):
39
+ os.makedirs(dir, exist_ok=True)
40
+ with open(path, "wb") as f:
41
+ f.write(b"Placeholder")
File without changes