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,61 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Any
3
+
4
+ from memos.memories.textual.item import TextualMemoryItem
5
+
6
+ from .dialogue_common import DialogueRankingTracker
7
+
8
+
9
+ class BaseRerankerStrategy(ABC):
10
+ """Abstract interface for memory rerankers with concatenation strategy."""
11
+
12
+ @abstractmethod
13
+ def prepare_documents(
14
+ self,
15
+ query: str,
16
+ graph_results: list[TextualMemoryItem],
17
+ top_k: int,
18
+ **kwargs,
19
+ ) -> tuple[DialogueRankingTracker, dict[str, Any], list[str]]:
20
+ """
21
+ Prepare documents for ranking based on the strategy.
22
+
23
+ Args:
24
+ query: The search query
25
+ graph_results: List of TextualMemoryItem objects to process
26
+ top_k: Maximum number of items to return
27
+ **kwargs: Additional strategy-specific parameters
28
+
29
+ Returns:
30
+ tuple[DialogueRankingTracker, dict[str, Any], list[str]]:
31
+ - Tracker: DialogueRankingTracker instance
32
+ - original_items: Dict mapping memory_id to original TextualMemoryItem
33
+ - documents: List of text documents ready for ranking
34
+ """
35
+ raise NotImplementedError
36
+
37
+ @abstractmethod
38
+ def reconstruct_items(
39
+ self,
40
+ ranked_indices: list[int],
41
+ scores: list[float],
42
+ tracker: DialogueRankingTracker,
43
+ original_items: dict[str, Any],
44
+ top_k: int,
45
+ **kwargs,
46
+ ) -> list[tuple[TextualMemoryItem, float]]:
47
+ """
48
+ Reconstruct TextualMemoryItem objects from ranked results.
49
+
50
+ Args:
51
+ ranked_indices: List of indices sorted by relevance
52
+ scores: Corresponding relevance scores
53
+ tracker: DialogueRankingTracker instance
54
+ original_items: Dict mapping memory_id to original TextualMemoryItem
55
+ top_k: Maximum number of items to return
56
+ **kwargs: Additional strategy-specific parameters
57
+
58
+ Returns:
59
+ List of (reconstructed_memory_item, aggregated_score) tuples
60
+ """
61
+ raise NotImplementedError
@@ -0,0 +1,94 @@
1
+ # memos/reranker/strategies/single_turn.py
2
+ from __future__ import annotations
3
+
4
+ import re
5
+
6
+ from typing import Any
7
+
8
+ from .base import BaseRerankerStrategy
9
+ from .dialogue_common import DialogueRankingTracker
10
+
11
+
12
+ _TAG1 = re.compile(r"^\s*\[[^\]]*\]\s*")
13
+
14
+
15
+ class ConcatBackgroundStrategy(BaseRerankerStrategy):
16
+ """
17
+ Concat background strategy.
18
+
19
+ This strategy processes dialogue pairs by concatenating background and
20
+ user and assistant messages into single strings for ranking. Each dialogue pair becomes a
21
+ separate document for ranking.
22
+ """
23
+
24
+ def prepare_documents(
25
+ self,
26
+ query: str,
27
+ graph_results: list,
28
+ top_k: int,
29
+ **kwargs,
30
+ ) -> tuple[DialogueRankingTracker, dict[str, Any], list[str]]:
31
+ """
32
+ Prepare documents based on single turn concatenation strategy.
33
+
34
+ Args:
35
+ query: The search query
36
+ graph_results: List of graph results
37
+ top_k: Maximum number of items to return
38
+
39
+ Returns:
40
+ tuple[DialogueRankingTracker, dict[str, Any], list[str]]:
41
+ - Tracker: DialogueRankingTracker instance
42
+ - original_items: Dict mapping memory_id to original TextualMemoryItem
43
+ - documents: List of text documents ready for ranking
44
+ """
45
+
46
+ original_items = {}
47
+ tracker = DialogueRankingTracker()
48
+ documents = []
49
+ for item in graph_results:
50
+ memory = getattr(item, "memory", None)
51
+ if isinstance(memory, str):
52
+ memory = _TAG1.sub("", memory)
53
+
54
+ background = ""
55
+ if hasattr(item, "metadata") and hasattr(item.metadata, "background"):
56
+ background = getattr(item.metadata, "background", "")
57
+ if not isinstance(background, str):
58
+ background = ""
59
+
60
+ documents.append(f"{memory}\n{background}")
61
+ return tracker, original_items, documents
62
+
63
+ def reconstruct_items(
64
+ self,
65
+ ranked_indices: list[int],
66
+ scores: list[float],
67
+ tracker: DialogueRankingTracker,
68
+ original_items: dict[str, Any],
69
+ top_k: int,
70
+ **kwargs,
71
+ ) -> list[tuple[Any, float]]:
72
+ """
73
+ Reconstruct TextualMemoryItem objects from ranked dialogue pairs.
74
+
75
+ Args:
76
+ ranked_indices: List of dialogue pair indices sorted by relevance
77
+ scores: Corresponding relevance scores
78
+ tracker: DialogueRankingTracker instance
79
+ original_items: Dict mapping memory_id to original TextualMemoryItem
80
+ top_k: Maximum number of items to return
81
+
82
+ Returns:
83
+ List of (reconstructed_memory_item, aggregated_score) tuples
84
+ """
85
+ graph_results = kwargs.get("graph_results")
86
+ documents = kwargs.get("documents")
87
+ reconstructed_items = []
88
+ for idx in ranked_indices:
89
+ item = graph_results[idx]
90
+ item.memory = f"{item.memory}\n{documents[idx]}"
91
+ reconstructed_items.append((item, scores[idx]))
92
+
93
+ reconstructed_items.sort(key=lambda x: x[1], reverse=True)
94
+ return reconstructed_items[:top_k]
@@ -0,0 +1,110 @@
1
+ # memos/reranker/strategies/single_turn.py
2
+ from __future__ import annotations
3
+
4
+ import re
5
+
6
+ from typing import Any
7
+
8
+ from .base import BaseRerankerStrategy
9
+ from .dialogue_common import DialogueRankingTracker
10
+
11
+
12
+ _TAG1 = re.compile(r"^\s*\[[^\]]*\]\s*")
13
+
14
+
15
+ class ConcatDocSourceStrategy(BaseRerankerStrategy):
16
+ """
17
+ Concat background strategy.
18
+
19
+ This strategy processes dialogue pairs by concatenating background and
20
+ user and assistant messages into single strings for ranking. Each dialogue pair becomes a
21
+ separate document for ranking.
22
+ """
23
+
24
+ """
25
+ Concat background strategy.
26
+
27
+ This strategy processes dialogue pairs by concatenating background and
28
+ user and assistant messages into single strings for ranking. Each dialogue pair becomes a
29
+ separate document for ranking.
30
+ """
31
+
32
+ def prepare_documents(
33
+ self,
34
+ query: str,
35
+ graph_results: list,
36
+ top_k: int,
37
+ **kwargs,
38
+ ) -> tuple[DialogueRankingTracker, dict[str, Any], list[str]]:
39
+ """
40
+ Prepare documents based on single turn concatenation strategy.
41
+
42
+ Args:
43
+ query: The search query
44
+ graph_results: List of graph results
45
+ top_k: Maximum number of items to return
46
+
47
+ Returns:
48
+ tuple[DialogueRankingTracker, dict[str, Any], list[str]]:
49
+ - Tracker: DialogueRankingTracker instance
50
+ - original_items: Dict mapping memory_id to original TextualMemoryItem
51
+ - documents: List of text documents ready for ranking
52
+ """
53
+
54
+ original_items = {}
55
+ tracker = DialogueRankingTracker()
56
+ documents = []
57
+ documents_set = set()
58
+ for item in graph_results:
59
+ memory = getattr(item, "memory", None)
60
+ if isinstance(memory, str):
61
+ memory = _TAG1.sub("", memory)
62
+
63
+ chunk_text = ""
64
+ if hasattr(item, "metadata") and hasattr(item.metadata, "sources"):
65
+ sources = getattr(item.metadata, "sources", [])
66
+ for source in sources:
67
+ if source.type == "file":
68
+ chunk_text += source.content
69
+ if chunk_text:
70
+ if chunk_text in documents_set:
71
+ continue
72
+ else:
73
+ documents_set.add(chunk_text)
74
+ documents.append(f"{memory}\n\n[Sources]:\n{chunk_text}")
75
+ else:
76
+ documents.append(memory)
77
+ return tracker, original_items, documents
78
+
79
+ def reconstruct_items(
80
+ self,
81
+ ranked_indices: list[int],
82
+ scores: list[float],
83
+ tracker: DialogueRankingTracker,
84
+ original_items: dict[str, Any],
85
+ top_k: int,
86
+ **kwargs,
87
+ ) -> list[tuple[Any, float]]:
88
+ """
89
+ Reconstruct TextualMemoryItem objects from ranked dialogue pairs.
90
+
91
+ Args:
92
+ ranked_indices: List of dialogue pair indices sorted by relevance
93
+ scores: Corresponding relevance scores
94
+ tracker: DialogueRankingTracker instance
95
+ original_items: Dict mapping memory_id to original TextualMemoryItem
96
+ top_k: Maximum number of items to return
97
+
98
+ Returns:
99
+ List of (reconstructed_memory_item, aggregated_score) tuples
100
+ """
101
+ graph_results = kwargs.get("graph_results")
102
+ documents = kwargs.get("documents")
103
+ reconstructed_items = []
104
+ for idx in ranked_indices:
105
+ item = graph_results[idx]
106
+ item.memory = f"{documents[idx]}"
107
+ reconstructed_items.append((item, scores[idx]))
108
+
109
+ reconstructed_items.sort(key=lambda x: x[1], reverse=True)
110
+ return reconstructed_items[:top_k]
@@ -0,0 +1,109 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+
5
+ from typing import Any, Literal
6
+
7
+ from pydantic import BaseModel
8
+
9
+ from memos.memories.textual.item import SourceMessage, TextualMemoryItem
10
+
11
+
12
+ # Strip a leading "[...]" tag (e.g., "[2025-09-01] ..." or "[meta] ...")
13
+ # before sending text to the reranker. This keeps inputs clean and
14
+ # avoids misleading the model with bracketed prefixes.
15
+ _TAG1 = re.compile(r"^\s*\[[^\]]*\]\s*")
16
+
17
+
18
+ def strip_memory_tags(item: TextualMemoryItem) -> str:
19
+ """Strip leading tags from memory text."""
20
+ memory = _TAG1.sub("", m) if isinstance((m := getattr(item, "memory", None)), str) else m
21
+ return memory
22
+
23
+
24
+ def extract_content(msg: dict[str, Any] | str) -> str:
25
+ """Extract content from message, handling both string and dict formats."""
26
+ if isinstance(msg, dict):
27
+ return msg.get("content", str(msg))
28
+ if isinstance(msg, SourceMessage):
29
+ return msg.content
30
+ return str(msg)
31
+
32
+
33
+ class DialoguePair(BaseModel):
34
+ """Represents a single dialogue pair extracted from sources."""
35
+
36
+ pair_id: str # Unique identifier for this dialogue pair
37
+ memory_id: str # ID of the source TextualMemoryItem
38
+ memory: str
39
+ pair_index: int # Index of this pair within the source memory's dialogue
40
+ user_msg: str | dict[str, Any] | SourceMessage # User message content
41
+ assistant_msg: str | dict[str, Any] | SourceMessage # Assistant message content
42
+ combined_text: str # The concatenated text used for ranking
43
+ chat_time: str | None = None
44
+
45
+ @property
46
+ def user_content(self) -> str:
47
+ """Get user message content as string."""
48
+ return extract_content(self.user_msg)
49
+
50
+ @property
51
+ def assistant_content(self) -> str:
52
+ """Get assistant message content as string."""
53
+ return extract_content(self.assistant_msg)
54
+
55
+
56
+ class DialogueRankingTracker:
57
+ """Tracks dialogue pairs and their rankings for memory reconstruction."""
58
+
59
+ def __init__(self):
60
+ self.dialogue_pairs: list[DialoguePair] = []
61
+
62
+ def add_dialogue_pair(
63
+ self,
64
+ memory_id: str,
65
+ pair_index: int,
66
+ user_msg: str | dict[str, Any],
67
+ assistant_msg: str | dict[str, Any],
68
+ memory: str,
69
+ chat_time: str | None = None,
70
+ concat_format: Literal["user_assistant", "user_only"] = "user_assistant",
71
+ ) -> str:
72
+ """Add a dialogue pair and return its unique ID."""
73
+ user_content = extract_content(user_msg)
74
+ assistant_content = extract_content(assistant_msg)
75
+ if concat_format == "user_assistant":
76
+ combined_text = f"[{chat_time}]: \nuser: {user_content}\nassistant: {assistant_content}"
77
+ elif concat_format == "user_only":
78
+ combined_text = f"[{chat_time}]: \nuser: {user_content}"
79
+ else:
80
+ raise ValueError(f"Invalid concat format: {concat_format}")
81
+
82
+ pair_id = f"{memory_id}_{pair_index}"
83
+
84
+ dialogue_pair = DialoguePair(
85
+ pair_id=pair_id,
86
+ memory_id=memory_id,
87
+ pair_index=pair_index,
88
+ user_msg=user_msg,
89
+ assistant_msg=assistant_msg,
90
+ combined_text=combined_text,
91
+ memory=memory,
92
+ chat_time=chat_time,
93
+ )
94
+
95
+ self.dialogue_pairs.append(dialogue_pair)
96
+ return pair_id
97
+
98
+ def get_documents_for_ranking(self, concat_memory: bool = True) -> list[str]:
99
+ """Get the combined text documents for ranking."""
100
+ if concat_memory:
101
+ return [(pair.memory + "\n\n" + pair.combined_text) for pair in self.dialogue_pairs]
102
+ else:
103
+ return [pair.combined_text for pair in self.dialogue_pairs]
104
+
105
+ def get_dialogue_pair_by_index(self, index: int) -> DialoguePair | None:
106
+ """Get dialogue pair by its index in the ranking results."""
107
+ if 0 <= index < len(self.dialogue_pairs):
108
+ return self.dialogue_pairs[index]
109
+ return None
@@ -0,0 +1,31 @@
1
+ # memos/reranker/factory.py
2
+ from __future__ import annotations
3
+
4
+ from typing import TYPE_CHECKING, Any, ClassVar
5
+
6
+ from .concat_background import ConcatBackgroundStrategy
7
+ from .concat_docsource import ConcatDocSourceStrategy
8
+ from .single_turn import SingleTurnStrategy
9
+ from .singleturn_outmem import SingleTurnOutMemStrategy
10
+
11
+
12
+ if TYPE_CHECKING:
13
+ from .base import BaseRerankerStrategy
14
+
15
+
16
+ class RerankerStrategyFactory:
17
+ """Factory class for creating reranker strategy instances."""
18
+
19
+ backend_to_class: ClassVar[dict[str, Any]] = {
20
+ "single_turn": SingleTurnStrategy,
21
+ "concat_background": ConcatBackgroundStrategy,
22
+ "singleturn_outmem": SingleTurnOutMemStrategy,
23
+ "concat_docsource": ConcatDocSourceStrategy,
24
+ }
25
+
26
+ @classmethod
27
+ def from_config(cls, config_factory: str = "single_turn") -> BaseRerankerStrategy:
28
+ if config_factory not in cls.backend_to_class:
29
+ raise ValueError(f"Invalid backend: {config_factory}")
30
+ strategy_class = cls.backend_to_class[config_factory]
31
+ return strategy_class()
@@ -0,0 +1,107 @@
1
+ # memos/reranker/strategies/single_turn.py
2
+ from __future__ import annotations
3
+
4
+ from copy import deepcopy
5
+ from typing import Any
6
+
7
+ from .base import BaseRerankerStrategy
8
+ from .dialogue_common import DialogueRankingTracker, extract_content, strip_memory_tags
9
+
10
+
11
+ class SingleTurnStrategy(BaseRerankerStrategy):
12
+ """
13
+ Single turn dialogue strategy.
14
+
15
+ This strategy processes dialogue pairs by concatenating user and assistant
16
+ messages into single strings for ranking. Each dialogue pair becomes a
17
+ separate document for ranking.
18
+ example:
19
+ >>> documents = ["chat_time: 2025-01-01 12:00:00\nuser: hello\nassistant: hi there"]
20
+ >>> output memory item: ["Memory:xxx \n\n chat_time: 2025-01-01 12:00:00\nuser: hello\nassistant: hi there"]
21
+ """
22
+
23
+ def prepare_documents(
24
+ self,
25
+ query: str,
26
+ graph_results: list,
27
+ top_k: int,
28
+ **kwargs,
29
+ ) -> tuple[DialogueRankingTracker, dict[str, Any], list[str]]:
30
+ """
31
+ Prepare documents based on single turn concatenation strategy.
32
+
33
+ Args:
34
+ query: The search query
35
+ graph_results: List of graph results
36
+ top_k: Maximum number of items to return
37
+
38
+ Returns:
39
+ tuple[DialogueRankingTracker, dict[str, Any], list[str]]:
40
+ - Tracker: DialogueRankingTracker instance
41
+ - original_items: Dict mapping memory_id to original TextualMemoryItem
42
+ - documents: List of text documents ready for ranking
43
+ """
44
+
45
+ original_items = {}
46
+ tracker = DialogueRankingTracker()
47
+ for item in graph_results:
48
+ memory = strip_memory_tags(item)
49
+ sources = getattr(item.metadata, "sources", [])
50
+ original_items[item.id] = item
51
+
52
+ # Group messages into pairs and concatenate
53
+ for i in range(0, len(sources), 2):
54
+ user_msg = sources[i] if i < len(sources) else {}
55
+ assistant_msg = sources[i + 1] if i + 1 < len(sources) else {}
56
+
57
+ user_content = extract_content(user_msg)
58
+ assistant_content = extract_content(assistant_msg)
59
+ chat_time = getattr(user_msg, "chat_time", "")
60
+
61
+ if user_content or assistant_content: # Only add non-empty pairs
62
+ pair_index = i // 2
63
+ tracker.add_dialogue_pair(
64
+ item.id, pair_index, user_msg, assistant_msg, memory or "", chat_time
65
+ )
66
+
67
+ documents = tracker.get_documents_for_ranking()
68
+ return tracker, original_items, documents
69
+
70
+ def reconstruct_items(
71
+ self,
72
+ ranked_indices: list[int],
73
+ scores: list[float],
74
+ tracker: DialogueRankingTracker,
75
+ original_items: dict[str, Any],
76
+ top_k: int,
77
+ **kwargs,
78
+ ) -> list[tuple[Any, float]]:
79
+ """
80
+ Reconstruct TextualMemoryItem objects from ranked dialogue pairs.
81
+
82
+ Args:
83
+ ranked_indices: List of dialogue pair indices sorted by relevance
84
+ scores: Corresponding relevance scores
85
+ tracker: DialogueRankingTracker instance
86
+ original_items: Dict mapping memory_id to original TextualMemoryItem
87
+ top_k: Maximum number of items to return
88
+
89
+ Returns:
90
+ List of (reconstructed_memory_item, aggregated_score) tuples
91
+ """
92
+ reconstructed_items = []
93
+ for idx, score in zip(ranked_indices, scores, strict=False):
94
+ dialogue_pair = tracker.get_dialogue_pair_by_index(idx)
95
+ if dialogue_pair and (dialogue_pair.memory_id in original_items):
96
+ original_item = original_items[dialogue_pair.memory_id]
97
+ reconstructed_item = deepcopy(original_item)
98
+ reconstructed_item.memory = (
99
+ dialogue_pair.memory
100
+ + "\n\nsources-dialogue-pairs"
101
+ + dialogue_pair.combined_text
102
+ )
103
+ reconstructed_items.append((reconstructed_item, score))
104
+
105
+ # Sort by aggregated score and return top_k
106
+ reconstructed_items.sort(key=lambda x: x[1], reverse=True)
107
+ return reconstructed_items[:top_k]
@@ -0,0 +1,98 @@
1
+ # memos/reranker/strategies/single_turn.py
2
+ from __future__ import annotations
3
+
4
+ from collections import defaultdict
5
+ from typing import TYPE_CHECKING, Any
6
+
7
+ from .dialogue_common import DialogueRankingTracker
8
+ from .single_turn import SingleTurnStrategy
9
+
10
+
11
+ if TYPE_CHECKING:
12
+ from .dialogue_common import DialogueRankingTracker
13
+
14
+
15
+ class SingleTurnOutMemStrategy(SingleTurnStrategy):
16
+ """
17
+ Single turn dialogue strategy.
18
+
19
+ This strategy processes dialogue pairs by concatenating user and assistant
20
+ messages into single strings for ranking. Each dialogue pair becomes a
21
+ separate document for ranking.
22
+ example:
23
+ >>> documents = ["chat_time: 2025-01-01 12:00:00\nuser: hello\nassistant: hi there"]
24
+ >>> output memory item: ["Memory:xxx \n\n chat_time: 2025-01-01 12:00:00\nuser: hello\nassistant: hi there"]
25
+ """
26
+
27
+ def prepare_documents(
28
+ self,
29
+ query: str,
30
+ graph_results: list,
31
+ top_k: int,
32
+ **kwargs,
33
+ ) -> tuple[DialogueRankingTracker, dict[str, Any], list[str]]:
34
+ """
35
+ Prepare documents based on single turn concatenation strategy.
36
+
37
+ Args:
38
+ query: The search query
39
+ graph_results: List of graph results
40
+ top_k: Maximum number of items to return
41
+
42
+ Returns:
43
+ tuple[DialogueRankingTracker, dict[str, Any], list[str]]:
44
+ - Tracker: DialogueRankingTracker instance
45
+ - original_items: Dict mapping memory_id to original TextualMemoryItem
46
+ - documents: List of text documents ready for ranking
47
+ """
48
+ return super().prepare_documents(query, graph_results, top_k, **kwargs)
49
+
50
+ def reconstruct_items(
51
+ self,
52
+ ranked_indices: list[int],
53
+ scores: list[float],
54
+ tracker: DialogueRankingTracker,
55
+ original_items: dict[str, Any],
56
+ top_k: int,
57
+ **kwargs,
58
+ ) -> list[tuple[Any, float]]:
59
+ """
60
+ Reconstruct TextualMemoryItem objects from ranked dialogue pairs.
61
+
62
+ Args:
63
+ ranked_indices: List of dialogue pair indices sorted by relevance
64
+ scores: Corresponding relevance scores
65
+ tracker: DialogueRankingTracker instance
66
+ original_items: Dict mapping memory_id to original TextualMemoryItem
67
+ top_k: Maximum number of items to return
68
+
69
+ Returns:
70
+ List of (reconstructed_memory_item, aggregated_score) tuples
71
+ """
72
+ # Group ranked pairs by memory_id
73
+ memory_groups = defaultdict(list)
74
+ memory_scores = defaultdict(list)
75
+
76
+ for idx, score in zip(ranked_indices, scores, strict=False):
77
+ dialogue_pair = tracker.get_dialogue_pair_by_index(idx)
78
+ if dialogue_pair:
79
+ memory_groups[dialogue_pair.memory_id].append(dialogue_pair)
80
+ memory_scores[dialogue_pair.memory_id].append(score)
81
+
82
+ reconstructed_items = []
83
+
84
+ for memory_id, _pairs in memory_groups.items():
85
+ if memory_id not in original_items:
86
+ continue
87
+ original_item = original_items[memory_id]
88
+
89
+ # Calculate aggregated score (e.g., max, mean, or weighted average)
90
+ pair_scores = memory_scores[memory_id]
91
+
92
+ aggregated_score = max(pair_scores) if pair_scores else 0.0
93
+
94
+ reconstructed_items.append((original_item, aggregated_score))
95
+
96
+ # Sort by aggregated score and return top_k
97
+ reconstructed_items.sort(key=lambda x: x[1], reverse=True)
98
+ return reconstructed_items[:top_k]
memos/settings.py ADDED
@@ -0,0 +1,10 @@
1
+ import os
2
+
3
+ from pathlib import Path
4
+
5
+
6
+ MEMOS_DIR = Path(os.getenv("MEMOS_BASE_PATH", Path.cwd())) / ".memos"
7
+ DEBUG = False
8
+
9
+ # "memos" or "memos.submodules" ... to filter logs from specific packages
10
+ LOG_FILTER_TREE_PREFIX = ""
File without changes