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,385 @@
1
+ import logging
2
+ import os
3
+
4
+ from pathlib import Path
5
+ from typing import Any, ClassVar
6
+
7
+ from pydantic import ConfigDict, Field, field_validator, model_validator
8
+
9
+ from memos.configs.base import BaseConfig
10
+ from memos.mem_scheduler.general_modules.misc import DictConversionMixin, EnvConfigMixin
11
+ from memos.mem_scheduler.schemas.general_schemas import (
12
+ BASE_DIR,
13
+ DEFAULT_ACT_MEM_DUMP_PATH,
14
+ DEFAULT_ACTIVATION_MEM_MONITOR_SIZE_LIMIT,
15
+ DEFAULT_CONSUME_BATCH,
16
+ DEFAULT_CONSUME_INTERVAL_SECONDS,
17
+ DEFAULT_CONTEXT_WINDOW_SIZE,
18
+ DEFAULT_MAX_INTERNAL_MESSAGE_QUEUE_SIZE,
19
+ DEFAULT_MULTI_TASK_RUNNING_TIMEOUT,
20
+ DEFAULT_SCHEDULER_RETRIEVER_BATCH_SIZE,
21
+ DEFAULT_SCHEDULER_RETRIEVER_RETRIES,
22
+ DEFAULT_THREAD_POOL_MAX_WORKERS,
23
+ DEFAULT_TOP_K,
24
+ DEFAULT_USE_REDIS_QUEUE,
25
+ DEFAULT_WORKING_MEM_MONITOR_SIZE_LIMIT,
26
+ )
27
+
28
+
29
+ class BaseSchedulerConfig(BaseConfig):
30
+ """Base configuration class for mem_scheduler."""
31
+
32
+ top_k: int = Field(
33
+ default=DEFAULT_TOP_K,
34
+ description="Number of top candidates to consider in initial retrieval",
35
+ )
36
+ enable_parallel_dispatch: bool = Field(
37
+ default=True, description="Whether to enable parallel message processing using thread pool"
38
+ )
39
+ thread_pool_max_workers: int = Field(
40
+ default=DEFAULT_THREAD_POOL_MAX_WORKERS,
41
+ gt=1,
42
+ description=f"Maximum worker threads in pool (default: {DEFAULT_THREAD_POOL_MAX_WORKERS})",
43
+ )
44
+ consume_interval_seconds: float = Field(
45
+ default=DEFAULT_CONSUME_INTERVAL_SECONDS,
46
+ gt=0,
47
+ description=f"Interval for consuming messages from queue in seconds (default: {DEFAULT_CONSUME_INTERVAL_SECONDS})",
48
+ )
49
+ consume_batch: int = Field(
50
+ default=DEFAULT_CONSUME_BATCH,
51
+ gt=0,
52
+ description=f"Number of messages to consume in each batch (default: {DEFAULT_CONSUME_BATCH})",
53
+ )
54
+ auth_config_path: str | None = Field(
55
+ default=None,
56
+ description="Path to the authentication configuration file containing private credentials",
57
+ )
58
+ # Redis queue configuration
59
+ use_redis_queue: bool = Field(
60
+ default=DEFAULT_USE_REDIS_QUEUE,
61
+ description="Whether to use Redis queue instead of local memory queue",
62
+ )
63
+ redis_config: dict[str, Any] = Field(
64
+ default_factory=lambda: {"host": "localhost", "port": 6379, "db": 0},
65
+ description="Redis connection configuration",
66
+ )
67
+ max_internal_message_queue_size: int = Field(
68
+ default=DEFAULT_MAX_INTERNAL_MESSAGE_QUEUE_SIZE,
69
+ description="Maximum size of internal message queue when not using Redis",
70
+ )
71
+ multi_task_running_timeout: int = Field(
72
+ default=DEFAULT_MULTI_TASK_RUNNING_TIMEOUT,
73
+ description="Default timeout for multi-task running operations in seconds",
74
+ )
75
+
76
+
77
+ class GeneralSchedulerConfig(BaseSchedulerConfig):
78
+ model_config = ConfigDict(extra="ignore", strict=True)
79
+ act_mem_update_interval: int | None = Field(
80
+ default=300, description="Interval in seconds for updating activation memory"
81
+ )
82
+ context_window_size: int | None = Field(
83
+ default=DEFAULT_CONTEXT_WINDOW_SIZE,
84
+ description="Size of the context window for conversation history",
85
+ )
86
+ act_mem_dump_path: str | None = Field(
87
+ default=DEFAULT_ACT_MEM_DUMP_PATH, # Replace with DEFAULT_ACT_MEM_DUMP_PATH
88
+ description="File path for dumping activation memory",
89
+ )
90
+ enable_activation_memory: bool = Field(
91
+ default=False, description="Whether to enable automatic activation memory updates"
92
+ )
93
+ working_mem_monitor_capacity: int = Field(
94
+ default=DEFAULT_WORKING_MEM_MONITOR_SIZE_LIMIT,
95
+ description="Capacity of the working memory monitor",
96
+ )
97
+ activation_mem_monitor_capacity: int = Field(
98
+ default=DEFAULT_ACTIVATION_MEM_MONITOR_SIZE_LIMIT,
99
+ description="Capacity of the activation memory monitor",
100
+ )
101
+
102
+ # Memory enhancement concurrency & retries configuration
103
+ enhance_batch_size: int | None = Field(
104
+ default=DEFAULT_SCHEDULER_RETRIEVER_BATCH_SIZE,
105
+ description="Batch size for concurrent memory enhancement; None or <=1 disables batching",
106
+ )
107
+ enhance_retries: int = Field(
108
+ default=DEFAULT_SCHEDULER_RETRIEVER_RETRIES,
109
+ ge=0,
110
+ description="Number of retry attempts per enhancement batch",
111
+ )
112
+
113
+ # Database configuration for ORM persistence
114
+ db_path: str | None = Field(
115
+ default=None,
116
+ description="Path to SQLite database file for ORM persistence. If None, uses default scheduler_orm.db",
117
+ )
118
+ db_url: str | None = Field(
119
+ default=None,
120
+ description="Database URL for ORM persistence (e.g., mysql://user:pass@host/db). Takes precedence over db_path",
121
+ )
122
+ enable_orm_persistence: bool = Field(
123
+ default=True, description="Whether to enable ORM-based persistence for monitors"
124
+ )
125
+
126
+
127
+ class OptimizedSchedulerConfig(GeneralSchedulerConfig):
128
+ """Configuration for the optimized scheduler.
129
+
130
+ This class inherits all fields from `GeneralSchedulerConfig`
131
+ and is used to distinguish optimized scheduling logic via type.
132
+ """
133
+
134
+
135
+ class SchedulerConfigFactory(BaseConfig):
136
+ """Factory class for creating scheduler configurations."""
137
+
138
+ backend: str = Field(..., description="Backend for scheduler")
139
+ config: dict[str, Any] = Field(..., description="Configuration for the scheduler backend")
140
+
141
+ model_config = ConfigDict(extra="forbid", strict=True)
142
+ backend_to_class: ClassVar[dict[str, Any]] = {
143
+ "general_scheduler": GeneralSchedulerConfig,
144
+ "optimized_scheduler": OptimizedSchedulerConfig, # optimized_scheduler uses same config as general_scheduler
145
+ }
146
+
147
+ @field_validator("backend")
148
+ @classmethod
149
+ def validate_backend(cls, backend: str) -> str:
150
+ """Validate the backend field."""
151
+ if backend not in cls.backend_to_class:
152
+ raise ValueError(f"Invalid backend: {backend}")
153
+ return backend
154
+
155
+ @model_validator(mode="after")
156
+ def create_config(self) -> "SchedulerConfigFactory":
157
+ config_class = self.backend_to_class[self.backend]
158
+ self.config = config_class(**self.config)
159
+ return self
160
+
161
+
162
+ # ************************* Auth *************************
163
+ class RabbitMQConfig(
164
+ BaseConfig,
165
+ DictConversionMixin,
166
+ EnvConfigMixin,
167
+ ):
168
+ host_name: str = Field(default="", description="Endpoint for RabbitMQ instance access")
169
+ user_name: str = Field(default="", description="Static username for RabbitMQ instance")
170
+ password: str = Field(default="", description="Password for the static username")
171
+ virtual_host: str = Field(default="", description="Vhost name for RabbitMQ instance")
172
+ erase_on_connect: bool = Field(
173
+ default=True, description="Whether to clear connection state or buffers upon connecting"
174
+ )
175
+ port: int = Field(
176
+ default=5672,
177
+ description="Port number for RabbitMQ instance access",
178
+ ge=1, # Port must be >= 1
179
+ le=65535, # Port must be <= 65535
180
+ )
181
+ exchange_name: str = Field(
182
+ default="memos-fanout",
183
+ description="Exchange name for RabbitMQ (e.g., memos-fanout, memos-memory-change)",
184
+ )
185
+ exchange_type: str = Field(
186
+ default="fanout", description="Exchange type for RabbitMQ (fanout or direct)"
187
+ )
188
+
189
+
190
+ class GraphDBAuthConfig(BaseConfig, DictConversionMixin, EnvConfigMixin):
191
+ uri: str = Field(
192
+ default="bolt://localhost:7687",
193
+ description="URI for graph database access (e.g., bolt://host:port)",
194
+ )
195
+ user: str = Field(default="neo4j", description="Username for graph database authentication")
196
+ password: str = Field(
197
+ default="",
198
+ description="Password for graph database authentication",
199
+ min_length=8, # Recommended minimum password length
200
+ )
201
+ db_name: str = Field(default="neo4j", description="Database name to connect to")
202
+ auto_create: bool = Field(
203
+ default=True, description="Whether to automatically create the database if it doesn't exist"
204
+ )
205
+
206
+
207
+ class OpenAIConfig(BaseConfig, DictConversionMixin, EnvConfigMixin):
208
+ api_key: str = Field(default="", description="API key for OpenAI service")
209
+ base_url: str = Field(default="", description="Base URL for API endpoint")
210
+ default_model: str = Field(default="", description="Default model to use")
211
+
212
+
213
+ class AuthConfig(BaseConfig, DictConversionMixin):
214
+ rabbitmq: RabbitMQConfig | None = None
215
+ openai: OpenAIConfig | None = None
216
+ graph_db: GraphDBAuthConfig | None = None
217
+ default_config_path: ClassVar[str] = (
218
+ f"{BASE_DIR}/examples/data/config/mem_scheduler/scheduler_auth.yaml"
219
+ )
220
+
221
+ @model_validator(mode="after")
222
+ def validate_partial_initialization(self) -> "AuthConfig":
223
+ """
224
+ Validate that at least one configuration component is successfully initialized.
225
+ Log warnings for any failed initializations but allow partial success.
226
+ """
227
+ logger = logging.getLogger(__name__)
228
+
229
+ initialized_components = []
230
+ failed_components = []
231
+
232
+ if self.rabbitmq is not None:
233
+ initialized_components.append("rabbitmq")
234
+ else:
235
+ failed_components.append("rabbitmq")
236
+
237
+ if self.openai is not None:
238
+ initialized_components.append("openai")
239
+ else:
240
+ failed_components.append("openai")
241
+
242
+ if self.graph_db is not None:
243
+ initialized_components.append("graph_db")
244
+ else:
245
+ failed_components.append("graph_db")
246
+
247
+ # Allow all components to be None for flexibility, but log a warning
248
+ if not initialized_components:
249
+ logger.warning(
250
+ "All configuration components are None. This may indicate missing environment variables or configuration files."
251
+ )
252
+ elif failed_components:
253
+ logger.warning(
254
+ f"Failed to initialize components: {', '.join(failed_components)}. Successfully initialized: {', '.join(initialized_components)}"
255
+ )
256
+
257
+ return self
258
+
259
+ @classmethod
260
+ def from_local_config(cls, config_path: str | Path | None = None) -> "AuthConfig":
261
+ """
262
+ Load configuration from either a YAML or JSON file based on file extension.
263
+
264
+ Automatically detects file type (YAML or JSON) from the file extension
265
+ and uses the appropriate parser. If no path is provided, uses the default
266
+ configuration path (YAML) or its JSON counterpart.
267
+
268
+ Args:
269
+ config_path: Optional path to configuration file.
270
+ If not provided, uses default configuration path.
271
+
272
+ Returns:
273
+ AuthConfig instance populated with data from the configuration file.
274
+
275
+ Raises:
276
+ FileNotFoundError: If the specified or default configuration file does not exist.
277
+ ValueError: If file extension is not .yaml/.yml or .json, or if parsing fails.
278
+ """
279
+ # Determine config path
280
+ if config_path is None:
281
+ config_path = cls.default_config_path
282
+
283
+ # Validate file existence
284
+ config_path_obj = Path(config_path)
285
+ if not config_path_obj.exists():
286
+ raise FileNotFoundError(f"Configuration file not found: {config_path}")
287
+
288
+ # Get file extension and determine parser
289
+ file_ext = config_path_obj.suffix.lower()
290
+
291
+ if file_ext in (".yaml", ".yml"):
292
+ return cls.from_yaml_file(yaml_path=str(config_path_obj))
293
+ elif file_ext == ".json":
294
+ return cls.from_json_file(json_path=str(config_path_obj))
295
+ else:
296
+ raise ValueError(
297
+ f"Unsupported file format: {file_ext}. "
298
+ "Please use YAML (.yaml, .yml) or JSON (.json) files."
299
+ )
300
+
301
+ @classmethod
302
+ def from_local_env(cls) -> "AuthConfig":
303
+ """Creates an AuthConfig instance by loading configuration from environment variables.
304
+
305
+ This method loads configuration for all nested components (RabbitMQ, OpenAI, GraphDB)
306
+ from their respective environment variables using each component's specific prefix.
307
+ If any component fails to initialize, it will be set to None and a warning will be logged.
308
+
309
+ Returns:
310
+ AuthConfig: Configured instance with values from environment variables
311
+
312
+ Raises:
313
+ ValueError: If all components fail to initialize
314
+ """
315
+ logger = logging.getLogger(__name__)
316
+
317
+ rabbitmq_config = None
318
+ openai_config = None
319
+ graph_db_config = None
320
+
321
+ # Try to initialize RabbitMQ config - check if any RabbitMQ env vars exist
322
+ try:
323
+ rabbitmq_prefix = RabbitMQConfig.get_env_prefix()
324
+ has_rabbitmq_env = any(key.startswith(rabbitmq_prefix) for key in os.environ)
325
+ if has_rabbitmq_env:
326
+ rabbitmq_config = RabbitMQConfig.from_env()
327
+ logger.info("Successfully initialized RabbitMQ configuration")
328
+ else:
329
+ logger.info(
330
+ "No RabbitMQ environment variables found, skipping RabbitMQ initialization"
331
+ )
332
+ except (ValueError, Exception) as e:
333
+ logger.warning(f"Failed to initialize RabbitMQ config from environment: {e}")
334
+
335
+ # Try to initialize OpenAI config - check if any OpenAI env vars exist
336
+ try:
337
+ openai_prefix = OpenAIConfig.get_env_prefix()
338
+ has_openai_env = any(key.startswith(openai_prefix) for key in os.environ)
339
+ if has_openai_env:
340
+ openai_config = OpenAIConfig.from_env()
341
+ logger.info("Successfully initialized OpenAI configuration")
342
+ else:
343
+ logger.info("No OpenAI environment variables found, skipping OpenAI initialization")
344
+ except (ValueError, Exception) as e:
345
+ logger.warning(f"Failed to initialize OpenAI config from environment: {e}")
346
+
347
+ # Try to initialize GraphDB config - check if any GraphDB env vars exist
348
+ try:
349
+ graphdb_prefix = GraphDBAuthConfig.get_env_prefix()
350
+ has_graphdb_env = any(key.startswith(graphdb_prefix) for key in os.environ)
351
+ if has_graphdb_env:
352
+ graph_db_config = GraphDBAuthConfig.from_env()
353
+ logger.info("Successfully initialized GraphDB configuration")
354
+ else:
355
+ logger.info(
356
+ "No GraphDB environment variables found, skipping GraphDB initialization"
357
+ )
358
+ except (ValueError, Exception) as e:
359
+ logger.warning(f"Failed to initialize GraphDB config from environment: {e}")
360
+
361
+ return cls(
362
+ rabbitmq=rabbitmq_config,
363
+ openai=openai_config,
364
+ graph_db=graph_db_config,
365
+ )
366
+
367
+ def set_openai_config_to_environment(self):
368
+ # Set environment variables only if openai config is available
369
+ if self.openai is not None:
370
+ os.environ["OPENAI_API_KEY"] = self.openai.api_key
371
+ os.environ["OPENAI_BASE_URL"] = self.openai.base_url
372
+ os.environ["MODEL"] = self.openai.default_model
373
+ else:
374
+ logger = logging.getLogger(__name__)
375
+ logger.warning("OpenAI config is not available, skipping environment variable setup")
376
+
377
+ @classmethod
378
+ def default_config_exists(cls) -> bool:
379
+ """
380
+ Check if the default configuration file exists.
381
+
382
+ Returns:
383
+ bool: True if the default config file exists, False otherwise
384
+ """
385
+ return Path(cls.default_config_path).exists()
@@ -0,0 +1,70 @@
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
+
7
+
8
+ class BaseUserManagerConfig(BaseConfig):
9
+ """Base configuration class for user managers."""
10
+
11
+ user_id: str = Field(default="root", description="Default user ID for initialization")
12
+
13
+
14
+ class SQLiteUserManagerConfig(BaseUserManagerConfig):
15
+ """SQLite user manager configuration."""
16
+
17
+ db_path: str | None = Field(
18
+ default=None,
19
+ description="Path to SQLite database file. If None, uses default path in MEMOS_DIR",
20
+ )
21
+
22
+
23
+ class MySQLUserManagerConfig(BaseUserManagerConfig):
24
+ """MySQL user manager configuration."""
25
+
26
+ host: str = Field(default="localhost", description="MySQL server host")
27
+ port: int = Field(default=3306, description="MySQL server port")
28
+ username: str = Field(default="root", description="MySQL username")
29
+ password: str = Field(default="", description="MySQL password")
30
+ database: str = Field(default="memos_users", description="MySQL database name")
31
+ charset: str = Field(default="utf8mb4", description="MySQL charset")
32
+
33
+
34
+ class RedisUserManagerConfig(BaseUserManagerConfig):
35
+ """Redis user manager configuration."""
36
+
37
+ host: str = Field(default="localhost", description="Redis server host")
38
+ port: int = Field(default=6379, description="Redis server port")
39
+ username: str = Field(default="root", description="Redis username")
40
+ password: str = Field(default="", description="Redis password")
41
+ database: str = Field(default="memos_users", description="Redis database name")
42
+ charset: str = Field(default="utf8mb4", description="Redis charset")
43
+
44
+
45
+ class UserManagerConfigFactory(BaseModel):
46
+ """Factory for user manager configurations."""
47
+
48
+ backend: str = Field(default="sqlite", description="Backend for user manager")
49
+ config: dict[str, Any] = Field(
50
+ default_factory=dict, description="Configuration for the user manager backend"
51
+ )
52
+
53
+ backend_to_class: ClassVar[dict[str, Any]] = {
54
+ "sqlite": SQLiteUserManagerConfig,
55
+ "mysql": MySQLUserManagerConfig,
56
+ "redis": RedisUserManagerConfig,
57
+ }
58
+
59
+ @field_validator("backend")
60
+ @classmethod
61
+ def validate_backend(cls, backend: str) -> str:
62
+ if backend not in cls.backend_to_class:
63
+ raise ValueError(f"Unsupported user manager backend: {backend}")
64
+ return backend
65
+
66
+ @model_validator(mode="after")
67
+ def instantiate_config(self):
68
+ config_class = self.backend_to_class[self.backend]
69
+ self.config = config_class(**self.config)
70
+ return self