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,50 @@
1
+ class SimpleTextSplitter:
2
+ """Simple text splitter wrapper."""
3
+
4
+ def __init__(self, chunk_size: int, chunk_overlap: int):
5
+ self.chunk_size = chunk_size
6
+ self.chunk_overlap = chunk_overlap
7
+
8
+ def chunk(self, text: str, **kwargs) -> list[str]:
9
+ return self._simple_split_text(text, self.chunk_size, self.chunk_overlap)
10
+
11
+ def _simple_split_text(self, text: str, chunk_size: int, chunk_overlap: int) -> list[str]:
12
+ """
13
+ Simple text splitter as fallback when langchain is not available.
14
+
15
+ Args:
16
+ text: Text to split
17
+ chunk_size: Maximum size of chunks
18
+ chunk_overlap: Overlap between chunks
19
+
20
+ Returns:
21
+ List of text chunks
22
+ """
23
+ if not text or len(text) <= chunk_size:
24
+ return [text] if text.strip() else []
25
+
26
+ chunks = []
27
+ start = 0
28
+ text_len = len(text)
29
+
30
+ while start < text_len:
31
+ # Calculate end position
32
+ end = min(start + chunk_size, text_len)
33
+
34
+ # If not the last chunk, try to break at a good position
35
+ if end < text_len:
36
+ # Try to break at newline, sentence end, or space
37
+ for separator in ["\n\n", "\n", "。", "!", "?", ". ", "! ", "? ", " "]:
38
+ last_sep = text.rfind(separator, start, end)
39
+ if last_sep != -1:
40
+ end = last_sep + len(separator)
41
+ break
42
+
43
+ chunk = text[start:end].strip()
44
+ if chunk:
45
+ chunks.append(chunk)
46
+
47
+ # Move start position with overlap
48
+ start = max(start + 1, end - chunk_overlap)
49
+
50
+ return chunks
memos/cli.py ADDED
@@ -0,0 +1,113 @@
1
+ """
2
+ MemOS CLI Tool
3
+ This script provides command-line interface for MemOS operations.
4
+ """
5
+
6
+ import argparse
7
+ import json
8
+ import os
9
+ import zipfile
10
+
11
+ from io import BytesIO
12
+
13
+
14
+ def export_openapi(output: str) -> bool:
15
+ """Export OpenAPI schema to JSON file."""
16
+ from memos.api.server_api import app
17
+
18
+ # Create directory if it doesn't exist
19
+ if os.path.dirname(output):
20
+ os.makedirs(os.path.dirname(output), exist_ok=True)
21
+
22
+ with open(output, "w") as f:
23
+ json.dump(app.openapi(), f, indent=2)
24
+ f.write("\n")
25
+
26
+ print(f"✅ OpenAPI schema exported to: {output}")
27
+ return True
28
+
29
+
30
+ def download_examples(dest: str) -> bool:
31
+ import requests
32
+
33
+ """Download examples from the MemOS repository."""
34
+ zip_url = "https://github.com/MemTensor/MemOS/archive/refs/heads/main.zip"
35
+ print(f"📥 Downloading examples from {zip_url}...")
36
+
37
+ try:
38
+ response = requests.get(zip_url)
39
+ response.raise_for_status()
40
+
41
+ with zipfile.ZipFile(BytesIO(response.content)) as z:
42
+ extracted_files = []
43
+ for file in z.namelist():
44
+ if "MemOS-main/examples/" in file and not file.endswith("/"):
45
+ # Remove the prefix and extract to dest
46
+ relative_path = file.replace("MemOS-main/examples/", "")
47
+ extract_path = os.path.join(dest, relative_path)
48
+
49
+ # Create directory if it doesn't exist
50
+ os.makedirs(os.path.dirname(extract_path), exist_ok=True)
51
+
52
+ # Extract the file
53
+ with z.open(file) as source, open(extract_path, "wb") as target:
54
+ target.write(source.read())
55
+ extracted_files.append(extract_path)
56
+
57
+ print(f"✅ Examples downloaded to: {dest}")
58
+ print(f"📁 {len(extracted_files)} files extracted")
59
+
60
+ except requests.RequestException as e:
61
+ print(f"❌ Error downloading examples: {e}")
62
+ return False
63
+ except Exception as e:
64
+ print(f"❌ Error extracting examples: {e}")
65
+ return False
66
+
67
+ return True
68
+
69
+
70
+ def main():
71
+ """Main CLI entry point."""
72
+ parser = argparse.ArgumentParser(
73
+ prog="memos",
74
+ description="MemOS Command Line Interface",
75
+ )
76
+
77
+ # Create subparsers for different commands
78
+ subparsers = parser.add_subparsers(dest="command", help="Available commands")
79
+
80
+ # Download examples command
81
+ examples_parser = subparsers.add_parser("download_examples", help="Download example files")
82
+ examples_parser.add_argument(
83
+ "--dest",
84
+ type=str,
85
+ default="./examples",
86
+ help="Destination directory for examples (default: ./examples)",
87
+ )
88
+
89
+ # Export API command
90
+ api_parser = subparsers.add_parser("export_openapi", help="Export OpenAPI schema to JSON file")
91
+ api_parser.add_argument(
92
+ "--output",
93
+ type=str,
94
+ default="openapi.json",
95
+ help="Output path for OpenAPI schema (default: openapi.json)",
96
+ )
97
+
98
+ # Parse arguments
99
+ args = parser.parse_args()
100
+
101
+ # Handle commands
102
+ if args.command == "download_examples":
103
+ success = download_examples(args.dest)
104
+ exit(0 if success else 1)
105
+ elif args.command == "export_openapi":
106
+ success = export_openapi(args.output)
107
+ exit(0 if success else 1)
108
+ else:
109
+ parser.print_help()
110
+
111
+
112
+ if __name__ == "__main__":
113
+ main()
File without changes
memos/configs/base.py ADDED
@@ -0,0 +1,82 @@
1
+ import os
2
+
3
+ from typing import Any
4
+
5
+ import yaml
6
+
7
+ from pydantic import BaseModel, ConfigDict, Field, model_validator
8
+
9
+ from memos.log import get_logger
10
+
11
+
12
+ logger = get_logger(__name__)
13
+
14
+
15
+ class BaseConfig(BaseModel):
16
+ """Base configuration.
17
+
18
+ All configurations should inherit from this class.
19
+ This class uses Pydantic's ConfigDict to enforce strict validation
20
+ and forbids extra fields."""
21
+
22
+ model_schema: str = Field(
23
+ "NOT_SET",
24
+ description="Schema for configuration. This value will be automatically set.",
25
+ exclude=True,
26
+ )
27
+
28
+ model_config = ConfigDict(extra="forbid", strict=True)
29
+
30
+ @model_validator(mode="after")
31
+ def set_default_schema(self) -> "BaseConfig":
32
+ dot_path_schema = self.__module__ + "." + self.__class__.__name__
33
+ if self.model_schema == dot_path_schema:
34
+ return self
35
+ if self.model_schema != "NOT_SET":
36
+ logger.warning(
37
+ f"Schema is set to {self.model_schema}, but it should be {dot_path_schema}. "
38
+ "Changing schema to the default value."
39
+ )
40
+ self.model_schema = dot_path_schema
41
+ return self
42
+
43
+ @classmethod
44
+ def from_json_file(cls, json_path: str) -> Any:
45
+ """Load configuration from a JSON file."""
46
+ with open(json_path, encoding="utf-8") as f:
47
+ data = f.read()
48
+ return cls.model_validate_json(data)
49
+
50
+ def to_json_file(self, json_path: str) -> None:
51
+ """Dump configuration to a JSON file."""
52
+ dir_path = os.path.dirname(json_path)
53
+ if dir_path:
54
+ os.makedirs(dir_path, exist_ok=True)
55
+ with open(json_path, "w", encoding="utf-8") as f:
56
+ f.write(self.model_dump_json(indent=2, warnings="none"))
57
+
58
+ @classmethod
59
+ def from_yaml_file(cls, yaml_path: str) -> Any:
60
+ """Load configuration from a YAML file."""
61
+ with open(yaml_path, encoding="utf-8") as f:
62
+ data = yaml.safe_load(f)
63
+ return cls.model_validate(data)
64
+
65
+ def to_yaml_file(self, yaml_path: str) -> None:
66
+ """Dump configuration to a YAML file."""
67
+
68
+ dir_path = os.path.dirname(yaml_path)
69
+ if dir_path:
70
+ os.makedirs(dir_path, exist_ok=True)
71
+
72
+ with open(yaml_path, "w", encoding="utf-8") as f:
73
+ yaml.safe_dump(
74
+ self.model_dump(mode="json", warnings="none"),
75
+ f,
76
+ default_flow_style=False,
77
+ allow_unicode=True,
78
+ indent=2,
79
+ )
80
+
81
+ def get(self, key, default=None):
82
+ return getattr(self, key, default)
@@ -0,0 +1,59 @@
1
+ from typing import Any, ClassVar
2
+
3
+ from pydantic import Field, field_validator, model_validator
4
+
5
+ from memos.configs.base import BaseConfig
6
+
7
+
8
+ class BaseChunkerConfig(BaseConfig):
9
+ """Base configuration class for chunkers."""
10
+
11
+ tokenizer_or_token_counter: str = Field(
12
+ default="gpt2", description="Tokenizer model name or a token counting function"
13
+ )
14
+ chunk_size: int = Field(default=512, description="Maximum tokens per chunk")
15
+ chunk_overlap: int = Field(default=128, description="Overlap between chunks")
16
+ min_sentences_per_chunk: int = Field(default=1, description="Minimum sentences in each chunk")
17
+
18
+
19
+ class SentenceChunkerConfig(BaseChunkerConfig):
20
+ """Configuration for sentence-based text chunker."""
21
+
22
+
23
+ class MarkdownChunkerConfig(BaseChunkerConfig):
24
+ """Configuration for markdown-based text chunker."""
25
+
26
+ headers_to_split_on: list[tuple[str, str]] = Field(
27
+ default=[("#", "Header 1"), ("##", "Header 2"), ("###", "Header 3")],
28
+ description="Headers to split on",
29
+ )
30
+ strip_headers: bool = Field(default=True, description="Strip headers from the text")
31
+ recursive: bool = Field(
32
+ default=False, description="Whether to use recursive character text splitter"
33
+ )
34
+
35
+
36
+ class ChunkerConfigFactory(BaseConfig):
37
+ """Factory class for creating chunker configurations."""
38
+
39
+ backend: str = Field(..., description="Backend for chunker")
40
+ config: dict[str, Any] = Field(..., description="Configuration for the chunker backend")
41
+
42
+ backend_to_class: ClassVar[dict[str, Any]] = {
43
+ "sentence": SentenceChunkerConfig,
44
+ "markdown": MarkdownChunkerConfig,
45
+ }
46
+
47
+ @field_validator("backend")
48
+ @classmethod
49
+ def validate_backend(cls, backend: str) -> str:
50
+ """Validate the backend field."""
51
+ if backend not in cls.backend_to_class:
52
+ raise ValueError(f"Invalid backend: {backend}")
53
+ return backend
54
+
55
+ @model_validator(mode="after")
56
+ def create_config(self) -> "ChunkerConfigFactory":
57
+ config_class = self.backend_to_class[self.backend]
58
+ self.config = config_class(**self.config)
59
+ return self
@@ -0,0 +1,88 @@
1
+ from typing import Any, ClassVar
2
+
3
+ from pydantic import Field, field_validator, model_validator
4
+
5
+ from memos.configs.base import BaseConfig
6
+
7
+
8
+ class BaseEmbedderConfig(BaseConfig):
9
+ """Base configuration class for embedding models."""
10
+
11
+ model_name_or_path: str = Field(..., description="Model name or path")
12
+ embedding_dims: int | None = Field(
13
+ default=None, description="Number of dimensions for the embedding"
14
+ )
15
+ max_tokens: int | None = Field(
16
+ default=8192,
17
+ description="Maximum number of tokens per text. Texts exceeding this limit will be automatically truncated. Set to None to disable truncation.",
18
+ )
19
+ headers_extra: dict[str, Any] | None = Field(
20
+ default=None,
21
+ description="Extra headers for the embedding model, only for universal_api backend",
22
+ )
23
+
24
+
25
+ class OllamaEmbedderConfig(BaseEmbedderConfig):
26
+ api_base: str = Field(default="http://localhost:11434", description="Base URL for Ollama API")
27
+
28
+
29
+ class ArkEmbedderConfig(BaseEmbedderConfig):
30
+ api_key: str = Field(..., description="Ark API key")
31
+ api_base: str = Field(
32
+ default="https://ark.cn-beijing.volces.com/api/v3/", description="Base URL for Ark API"
33
+ )
34
+ chunk_size: int = Field(default=1, description="Chunk size for Ark API")
35
+ multi_modal: bool = Field(
36
+ default=False,
37
+ description="Whether to use multi-modal embedding (text + image) with Ark",
38
+ )
39
+
40
+
41
+ class SenTranEmbedderConfig(BaseEmbedderConfig):
42
+ """Configuration class for Sentence Transformer embeddings."""
43
+
44
+ trust_remote_code: bool = Field(
45
+ default=True,
46
+ description="Whether to trust remote code when loading the model",
47
+ )
48
+
49
+
50
+ class UniversalAPIEmbedderConfig(BaseEmbedderConfig):
51
+ """
52
+ Configuration class for universal API embedding providers, e.g.,
53
+ OpenAI, etc.
54
+ """
55
+
56
+ provider: str = Field(..., description="Provider name, e.g., 'openai'")
57
+ api_key: str = Field(..., description="API key for the embedding provider")
58
+ base_url: str | None = Field(
59
+ default=None, description="Optional base URL for custom or proxied endpoint"
60
+ )
61
+
62
+
63
+ class EmbedderConfigFactory(BaseConfig):
64
+ """Factory class for creating embedder configurations."""
65
+
66
+ backend: str = Field(..., description="Backend for embedding model")
67
+ config: dict[str, Any] = Field(..., description="Configuration for the embedding model backend")
68
+
69
+ backend_to_class: ClassVar[dict[str, Any]] = {
70
+ "ollama": OllamaEmbedderConfig,
71
+ "sentence_transformer": SenTranEmbedderConfig,
72
+ "ark": ArkEmbedderConfig,
73
+ "universal_api": UniversalAPIEmbedderConfig,
74
+ }
75
+
76
+ @field_validator("backend")
77
+ @classmethod
78
+ def validate_backend(cls, backend: str) -> str:
79
+ """Validate the backend field."""
80
+ if backend not in cls.backend_to_class:
81
+ raise ValueError(f"Invalid backend: {backend}")
82
+ return backend
83
+
84
+ @model_validator(mode="after")
85
+ def create_config(self) -> "EmbedderConfigFactory":
86
+ config_class = self.backend_to_class[self.backend]
87
+ self.config = config_class(**self.config)
88
+ return self
@@ -0,0 +1,236 @@
1
+ from typing import Any, ClassVar
2
+
3
+ from pydantic import BaseModel, Field, field_validator, model_validator
4
+
5
+ from memos.configs.base import BaseConfig
6
+ from memos.configs.vec_db import VectorDBConfigFactory
7
+
8
+
9
+ class BaseGraphDBConfig(BaseConfig):
10
+ """Base class for all graph database configurations."""
11
+
12
+ uri: str | list
13
+ user: str
14
+ password: str
15
+
16
+
17
+ class Neo4jGraphDBConfig(BaseGraphDBConfig):
18
+ """
19
+ Neo4j-specific configuration.
20
+
21
+ This config supports:
22
+ 1) Physical isolation (multi-db) — each user gets a dedicated Neo4j database.
23
+ 2) Logical isolation (single-db) — all users share one or more databases, but each node is tagged with `user_name`.
24
+
25
+ How to use:
26
+ - If `use_multi_db=True`, then `db_name` should usually be the same as `user_name`.
27
+ Each user gets a separate database for physical isolation.
28
+ Example: db_name = "alice", user_name = None or "alice".
29
+
30
+ - If `use_multi_db=False`, then `db_name` is your shared database (e.g., "neo4j" or "shared_db").
31
+ You must provide `user_name` to logically isolate each user's data.
32
+ All nodes and queries must respect this tag.
33
+
34
+ Example configs:
35
+ ---
36
+ # Physical isolation:
37
+ db_name = "alice"
38
+ use_multi_db = True
39
+ user_name = None
40
+
41
+ # Logical isolation:
42
+ db_name = "shared_db_student_group"
43
+ use_multi_db = False
44
+ user_name = "alice"
45
+ """
46
+
47
+ db_name: str = Field(..., description="The name of the target Neo4j database")
48
+ auto_create: bool = Field(
49
+ default=False,
50
+ description="If True, automatically create the target db_name in multi-db mode if it does not exist.",
51
+ )
52
+
53
+ use_multi_db: bool = Field(
54
+ default=True,
55
+ description=(
56
+ "If True: use Neo4j's multi-database feature for physical isolation; "
57
+ "each user typically gets a separate database. "
58
+ "If False: use a single shared database with logical isolation by user_name."
59
+ ),
60
+ )
61
+
62
+ user_name: str | None = Field(
63
+ default=None,
64
+ description=(
65
+ "Logical user or tenant ID for data isolation. "
66
+ "Required if use_multi_db is False. "
67
+ "All nodes must be tagged with this and all queries must filter by this."
68
+ ),
69
+ )
70
+
71
+ embedding_dimension: int = Field(default=768, description="Dimension of vector embedding")
72
+
73
+ @model_validator(mode="after")
74
+ def validate_config(self):
75
+ """Validate logical constraints to avoid misconfiguration."""
76
+ if not self.use_multi_db and not self.user_name:
77
+ raise ValueError(
78
+ "In single-database mode (use_multi_db=False), `user_name` must be provided for logical isolation."
79
+ )
80
+ return self
81
+
82
+
83
+ class Neo4jCommunityGraphDBConfig(Neo4jGraphDBConfig):
84
+ """
85
+ Community edition config for Neo4j.
86
+
87
+ Notes:
88
+ - Must set `use_multi_db = False`
89
+ - Must provide `user_name` for logical isolation
90
+ - Embedding vector DB config is required
91
+ """
92
+
93
+ vec_config: VectorDBConfigFactory = Field(
94
+ ..., description="Vector DB config for embedding search"
95
+ )
96
+
97
+ @model_validator(mode="after")
98
+ def validate_community(self):
99
+ if self.use_multi_db:
100
+ raise ValueError("Neo4j Community Edition does not support use_multi_db=True.")
101
+ if not self.user_name:
102
+ raise ValueError("Neo4j Community config requires user_name for logical isolation.")
103
+ return self
104
+
105
+
106
+ class NebulaGraphDBConfig(BaseGraphDBConfig):
107
+ """
108
+ NebulaGraph-specific configuration.
109
+
110
+ Key concepts:
111
+ - `space`: Equivalent to a database or namespace. All tag/edge/schema live within a space.
112
+ - `user_name`: Used for logical tenant isolation if needed.
113
+ - `auto_create`: Whether to automatically create the target space if it does not exist.
114
+
115
+ Example:
116
+ ---
117
+ hosts = ["127.0.0.1:9669"]
118
+ user = "root"
119
+ password = "nebula"
120
+ space = "shared_graph"
121
+ user_name = "alice"
122
+ """
123
+
124
+ space: str = Field(
125
+ ..., description="The name of the target NebulaGraph space (like a database)"
126
+ )
127
+ user_name: str | None = Field(
128
+ default=None,
129
+ description="Logical user or tenant ID for data isolation (optional, used in metadata tagging)",
130
+ )
131
+ auto_create: bool = Field(
132
+ default=False,
133
+ description="Whether to auto-create the space if it does not exist",
134
+ )
135
+ use_multi_db: bool = Field(
136
+ default=True,
137
+ description=(
138
+ "If True: use Neo4j's multi-database feature for physical isolation; "
139
+ "each user typically gets a separate database. "
140
+ "If False: use a single shared database with logical isolation by user_name."
141
+ ),
142
+ )
143
+ max_client: int = Field(
144
+ default=1000,
145
+ description=("max_client"),
146
+ )
147
+ embedding_dimension: int = Field(default=3072, description="Dimension of vector embedding")
148
+
149
+ @model_validator(mode="after")
150
+ def validate_config(self):
151
+ """Validate config."""
152
+ if not self.space:
153
+ raise ValueError("`space` must be provided")
154
+ return self
155
+
156
+
157
+ class PolarDBGraphDBConfig(BaseConfig):
158
+ """
159
+ PolarDB-specific configuration.
160
+
161
+ Key concepts:
162
+ - `db_name`: The name of the target PolarDB database
163
+ - `user_name`: Used for logical tenant isolation if needed
164
+ - `auto_create`: Whether to automatically create the target database if it does not exist
165
+ - `use_multi_db`: Whether to use multi-database mode for physical isolation
166
+
167
+ Example:
168
+ ---
169
+ host = "localhost"
170
+ port = 5432
171
+ user = "postgres"
172
+ password = "password"
173
+ db_name = "memos_db"
174
+ user_name = "alice"
175
+ use_multi_db = True
176
+ auto_create = True
177
+ """
178
+
179
+ host: str = Field(..., description="Database host")
180
+ port: int = Field(default=5432, description="Database port")
181
+ user: str = Field(..., description="Database user")
182
+ password: str = Field(..., description="Database password")
183
+ db_name: str = Field(..., description="The name of the target PolarDB database")
184
+ user_name: str | None = Field(
185
+ default=None,
186
+ description="Logical user or tenant ID for data isolation (optional, used in metadata tagging)",
187
+ )
188
+ auto_create: bool = Field(
189
+ default=False,
190
+ description="Whether to auto-create the database if it does not exist",
191
+ )
192
+ use_multi_db: bool = Field(
193
+ default=True,
194
+ description=(
195
+ "If True: use multi-database mode for physical isolation; "
196
+ "each tenant typically gets a separate database. "
197
+ "If False: use a single shared database with logical isolation by user_name."
198
+ ),
199
+ )
200
+ embedding_dimension: int = Field(default=1024, description="Dimension of vector embedding")
201
+ maxconn: int = Field(
202
+ default=100,
203
+ description="Maximum number of connections in the connection pool",
204
+ )
205
+
206
+ @model_validator(mode="after")
207
+ def validate_config(self):
208
+ """Validate config."""
209
+ if not self.db_name:
210
+ raise ValueError("`db_name` must be provided")
211
+ return self
212
+
213
+
214
+ class GraphDBConfigFactory(BaseModel):
215
+ backend: str = Field(..., description="Backend for graph database")
216
+ config: dict[str, Any] = Field(..., description="Configuration for the graph database backend")
217
+
218
+ backend_to_class: ClassVar[dict[str, Any]] = {
219
+ "neo4j": Neo4jGraphDBConfig,
220
+ "neo4j-community": Neo4jCommunityGraphDBConfig,
221
+ "nebular": NebulaGraphDBConfig,
222
+ "polardb": PolarDBGraphDBConfig,
223
+ }
224
+
225
+ @field_validator("backend")
226
+ @classmethod
227
+ def validate_backend(cls, backend: str) -> str:
228
+ if backend not in cls.backend_to_class:
229
+ raise ValueError(f"Unsupported graph db backend: {backend}")
230
+ return backend
231
+
232
+ @model_validator(mode="after")
233
+ def instantiate_config(self):
234
+ config_class = self.backend_to_class[self.backend]
235
+ self.config = config_class(**self.config)
236
+ return self