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,488 @@
1
+ """User management system for MemOS.
2
+
3
+ This module provides user authentication, authorization, and cube management
4
+ functionality using SQLAlchemy and SQLite.
5
+ """
6
+
7
+ import uuid
8
+
9
+ from datetime import datetime
10
+ from enum import Enum
11
+ from pathlib import Path
12
+
13
+ from sqlalchemy import (
14
+ Boolean,
15
+ Column,
16
+ DateTime,
17
+ ForeignKey,
18
+ String,
19
+ Table,
20
+ create_engine,
21
+ )
22
+ from sqlalchemy import (
23
+ Enum as SQLEnum,
24
+ )
25
+ from sqlalchemy.exc import IntegrityError
26
+ from sqlalchemy.orm import Session, declarative_base, relationship, sessionmaker
27
+
28
+ from memos import settings
29
+ from memos.log import get_logger
30
+
31
+
32
+ logger = get_logger(__name__)
33
+
34
+ Base = declarative_base()
35
+
36
+
37
+ class UserRole(Enum):
38
+ """User roles enumeration."""
39
+
40
+ ROOT = "ROOT"
41
+ ADMIN = "ADMIN"
42
+ USER = "USER"
43
+ GUEST = "GUEST"
44
+
45
+
46
+ # Association table for many-to-many relationship between users and cubes
47
+ user_cube_association = Table(
48
+ "user_cube_association",
49
+ Base.metadata,
50
+ Column("user_id", String, ForeignKey("users.user_id"), primary_key=True),
51
+ Column("cube_id", String, ForeignKey("cubes.cube_id"), primary_key=True),
52
+ Column("created_at", DateTime, default=datetime.now),
53
+ )
54
+
55
+
56
+ class User(Base):
57
+ """User model for the database."""
58
+
59
+ __tablename__ = "users"
60
+
61
+ user_id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
62
+ user_name = Column(String, unique=True, nullable=False)
63
+ role = Column(SQLEnum(UserRole), default=UserRole.USER, nullable=False)
64
+ created_at = Column(DateTime, default=datetime.now, nullable=False)
65
+ updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now, nullable=False)
66
+ is_active = Column(Boolean, default=True, nullable=False)
67
+
68
+ # Relationship with cubes
69
+ cubes = relationship("Cube", secondary=user_cube_association, back_populates="users")
70
+ owned_cubes = relationship("Cube", back_populates="owner", cascade="all, delete-orphan")
71
+
72
+ def __repr__(self):
73
+ return f"<User(user_id='{self.user_id}', user_name='{self.user_name}', role='{self.role.value}')>"
74
+
75
+
76
+ class Cube(Base):
77
+ """Cube model for the database."""
78
+
79
+ __tablename__ = "cubes"
80
+
81
+ cube_id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
82
+ cube_name = Column(String, nullable=False)
83
+ cube_path = Column(String, nullable=True) # Local path or remote repo
84
+ owner_id = Column(String, ForeignKey("users.user_id"), nullable=False)
85
+ created_at = Column(DateTime, default=datetime.now, nullable=False)
86
+ updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now, nullable=False)
87
+ is_active = Column(Boolean, default=True, nullable=False)
88
+
89
+ # Relationships
90
+ owner = relationship("User", back_populates="owned_cubes")
91
+ users = relationship("User", secondary=user_cube_association, back_populates="cubes")
92
+
93
+ def __repr__(self):
94
+ return f"<Cube(cube_id='{self.cube_id}', cube_name='{self.cube_name}', owner_id='{self.owner_id}')>"
95
+
96
+
97
+ class UserManager:
98
+ """User management system for MemOS."""
99
+
100
+ def __init__(self, db_path: str | None = None, user_id: str = "root"):
101
+ """Initialize the user manager with database connection.
102
+
103
+ Args:
104
+ db_path (str, optional): Path to the SQLite database file.
105
+ If None, uses default path in MEMOS_DIR.
106
+ user_id (str, optional): User ID. If None, uses default user ID.
107
+ """
108
+ if db_path is None:
109
+ db_path = str(settings.MEMOS_DIR / "memos_users.db")
110
+
111
+ # Ensure the directory exists
112
+ Path(db_path).parent.mkdir(parents=True, exist_ok=True)
113
+
114
+ self.db_path = db_path
115
+ self.engine = create_engine(f"sqlite:///{db_path}", echo=False)
116
+ self.SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=self.engine)
117
+
118
+ # Create tables
119
+ Base.metadata.create_all(bind=self.engine)
120
+
121
+ # Initialize with root user if no users exist
122
+ self._init_root_user(user_id)
123
+
124
+ logger.info(f"UserManager initialized with database at {db_path}")
125
+
126
+ def _get_session(self) -> Session:
127
+ """Get a database session."""
128
+ return self.SessionLocal()
129
+
130
+ def _init_root_user(self, user_id: str) -> None:
131
+ """Initialize the root user if no users exist."""
132
+ session = self._get_session()
133
+ try:
134
+ # Check if any users exist
135
+ user_count = session.query(User).count()
136
+ if user_count == 0:
137
+ root_user = User(user_id=user_id, user_name=user_id, role=UserRole.ROOT)
138
+ session.add(root_user)
139
+ session.commit()
140
+ logger.info("Root user created successfully")
141
+ else:
142
+ self.create_user(user_name=user_id, user_id=user_id, role=UserRole.ROOT)
143
+ except Exception as e:
144
+ session.rollback()
145
+ logger.error(f"Failed to create {user_id} user: {e}")
146
+ finally:
147
+ session.close()
148
+
149
+ def create_user(
150
+ self, user_name: str, role: UserRole = UserRole.USER, user_id: str | None = None
151
+ ) -> str:
152
+ """Create a new user.
153
+
154
+ Args:
155
+ user_name (str): Name of the user.
156
+ role (UserRole): Role of the user.
157
+ user_id (str, optional): Custom user ID. If None, generates UUID.
158
+
159
+ Returns:
160
+ str: The created user ID.
161
+
162
+ Raises:
163
+ ValueError: If user_name already exists.
164
+ """
165
+ session = self._get_session()
166
+ try:
167
+ # Check if user_name already exists
168
+ existing_user = session.query(User).filter(User.user_name == user_name).first()
169
+ if existing_user:
170
+ logger.info(f"User with name '{user_name}' already exists")
171
+ return existing_user.user_id
172
+ user = User(user_name=user_name, role=role, user_id=user_id or str(uuid.uuid4()))
173
+ session.add(user)
174
+ session.commit()
175
+ logger.info(f"User '{user_name}' created with ID: {user.user_id}")
176
+ return user.user_id
177
+ except IntegrityError:
178
+ session.rollback()
179
+ logger.info(f"failed to create user with name '{user_name}' already exists")
180
+ except Exception as e:
181
+ session.rollback()
182
+ logger.error(f"Error creating user: {e}")
183
+ raise
184
+ finally:
185
+ session.close()
186
+
187
+ def get_user(self, user_id: str) -> User | None:
188
+ """Get user by ID.
189
+
190
+ Args:
191
+ user_id (str): The user ID.
192
+
193
+ Returns:
194
+ User: The user object or None if not found.
195
+ """
196
+ session = self._get_session()
197
+ try:
198
+ return session.query(User).filter(User.user_id == user_id).first()
199
+ finally:
200
+ session.close()
201
+
202
+ def get_user_by_name(self, user_name: str) -> User | None:
203
+ """Get user by name.
204
+
205
+ Args:
206
+ user_name (str): The user name.
207
+
208
+ Returns:
209
+ User: The user object or None if not found.
210
+ """
211
+ session = self._get_session()
212
+ try:
213
+ return session.query(User).filter(User.user_name == user_name).first()
214
+ finally:
215
+ session.close()
216
+
217
+ def validate_user(self, user_id: str) -> bool:
218
+ """Validate if a user exists and is active.
219
+
220
+ Args:
221
+ user_id (str): The user ID to validate.
222
+
223
+ Returns:
224
+ bool: True if user exists and is active, False otherwise.
225
+ """
226
+ user = self.get_user(user_id)
227
+ return user is not None and user.is_active
228
+
229
+ def list_users(self) -> list[User]:
230
+ """List all active users.
231
+
232
+ Returns:
233
+ list[User]: List of all active users.
234
+ """
235
+ session = self._get_session()
236
+ try:
237
+ return session.query(User).filter(User.is_active).all()
238
+ finally:
239
+ session.close()
240
+
241
+ def create_cube(
242
+ self,
243
+ cube_name: str,
244
+ owner_id: str,
245
+ cube_path: str | None = None,
246
+ cube_id: str | None = None,
247
+ ) -> str:
248
+ """Create a new cube.
249
+
250
+ Args:
251
+ cube_name (str): Name of the cube.
252
+ owner_id (str): ID of the cube owner.
253
+ cube_path (str, optional): Path to the cube.
254
+ cube_id (str, optional): Custom cube ID. If None, generates UUID.
255
+
256
+ Returns:
257
+ str: The created cube ID.
258
+
259
+ Raises:
260
+ ValueError: If owner doesn't exist.
261
+ """
262
+ session = self._get_session()
263
+ try:
264
+ # Validate owner exists
265
+ owner = session.query(User).filter(User.user_id == owner_id).first()
266
+ if not owner:
267
+ raise ValueError(f"User with ID '{owner_id}' does not exist")
268
+
269
+ cube = Cube(
270
+ cube_name=cube_name,
271
+ owner_id=owner_id,
272
+ cube_path=cube_path,
273
+ cube_id=cube_id or str(uuid.uuid4()),
274
+ )
275
+ session.add(cube)
276
+
277
+ # Add owner to cube users
278
+ cube.users.append(owner)
279
+
280
+ session.commit()
281
+ logger.info(f"Cube '{cube_name}' created with ID: {cube.cube_id}")
282
+ return cube.cube_id
283
+ except Exception as e:
284
+ session.rollback()
285
+ logger.error(f"Error creating cube: {e}")
286
+ raise
287
+ finally:
288
+ session.close()
289
+
290
+ def get_cube(self, cube_id: str) -> Cube | None:
291
+ """Get cube by ID.
292
+
293
+ Args:
294
+ cube_id (str): The cube ID.
295
+
296
+ Returns:
297
+ Cube: The cube object or None if not found.
298
+ """
299
+ session = self._get_session()
300
+ try:
301
+ return session.query(Cube).filter(Cube.cube_id == cube_id).first()
302
+ finally:
303
+ session.close()
304
+
305
+ def validate_user_cube_access(self, user_id: str, cube_id: str) -> bool:
306
+ """Validate if a user has access to a cube.
307
+
308
+ Args:
309
+ user_id (str): The user ID.
310
+ cube_id (str): The cube ID.
311
+
312
+ Returns:
313
+ bool: True if user has access to cube, False otherwise.
314
+ """
315
+ session = self._get_session()
316
+ try:
317
+ # Check if user exists and is active
318
+ user = session.query(User).filter(User.user_id == user_id, User.is_active).first()
319
+ if not user:
320
+ return False
321
+
322
+ # Check if cube exists and is active
323
+ cube = session.query(Cube).filter(Cube.cube_id == cube_id, Cube.is_active).first()
324
+ if not cube:
325
+ return False
326
+
327
+ # Check if user has access to cube (owner or in users list)
328
+ if cube.owner_id == user_id:
329
+ return True
330
+
331
+ # Check many-to-many relationship
332
+ return user in cube.users
333
+ finally:
334
+ session.close()
335
+
336
+ def get_user_cubes(self, user_id: str) -> list[Cube]:
337
+ """Get all cubes accessible by a user.
338
+
339
+ Args:
340
+ user_id (str): The user ID.
341
+
342
+ Returns:
343
+ list[Cube]: List of cubes accessible by the user.
344
+ """
345
+ session = self._get_session()
346
+ try:
347
+ user = session.query(User).filter(User.user_id == user_id).first()
348
+ if not user:
349
+ return []
350
+
351
+ active_cubes = [cube for cube in user.cubes if cube.is_active]
352
+ return sorted(active_cubes, key=lambda cube: cube.created_at, reverse=True)
353
+ finally:
354
+ session.close()
355
+
356
+ def add_user_to_cube(self, user_id: str, cube_id: str) -> bool:
357
+ """Add a user to a cube's access list.
358
+
359
+ Args:
360
+ user_id (str): The user ID.
361
+ cube_id (str): The cube ID.
362
+
363
+ Returns:
364
+ bool: True if successful, False otherwise.
365
+ """
366
+ session = self._get_session()
367
+ try:
368
+ user = session.query(User).filter(User.user_id == user_id).first()
369
+ cube = session.query(Cube).filter(Cube.cube_id == cube_id).first()
370
+
371
+ if not user or not cube:
372
+ return False
373
+
374
+ if user not in cube.users:
375
+ cube.users.append(user)
376
+ session.commit()
377
+ logger.info(f"User '{user_id}' added to cube '{cube_id}'")
378
+
379
+ return True
380
+ except Exception as e:
381
+ session.rollback()
382
+ logger.error(f"Error adding user to cube: {e}")
383
+ return False
384
+ finally:
385
+ session.close()
386
+
387
+ def remove_user_from_cube(self, user_id: str, cube_id: str) -> bool:
388
+ """Remove a user from a cube's access list.
389
+
390
+ Args:
391
+ user_id (str): The user ID.
392
+ cube_id (str): The cube ID.
393
+
394
+ Returns:
395
+ bool: True if successful, False otherwise.
396
+ """
397
+ session = self._get_session()
398
+ try:
399
+ user = session.query(User).filter(User.user_id == user_id).first()
400
+ cube = session.query(Cube).filter(Cube.cube_id == cube_id).first()
401
+
402
+ if not user or not cube:
403
+ return False
404
+
405
+ # Don't remove owner
406
+ if cube.owner_id == user_id:
407
+ logger.warning(f"Cannot remove owner '{user_id}' from cube '{cube_id}'")
408
+ return False
409
+
410
+ if user in cube.users:
411
+ cube.users.remove(user)
412
+ session.commit()
413
+ logger.info(f"User '{user_id}' removed from cube '{cube_id}'")
414
+
415
+ return True
416
+ except Exception as e:
417
+ session.rollback()
418
+ logger.error(f"Error removing user from cube: {e}")
419
+ return False
420
+ finally:
421
+ session.close()
422
+
423
+ def delete_user(self, user_id: str) -> bool:
424
+ """Soft delete a user (set is_active to False).
425
+
426
+ Args:
427
+ user_id (str): The user ID.
428
+
429
+ Returns:
430
+ bool: True if successful, False otherwise.
431
+ """
432
+ session = self._get_session()
433
+ try:
434
+ user = session.query(User).filter(User.user_id == user_id).first()
435
+ if not user:
436
+ return False
437
+
438
+ # Don't delete root user
439
+ if user.role == UserRole.ROOT:
440
+ logger.warning("Cannot delete root user")
441
+ return False
442
+
443
+ user.is_active = False
444
+ session.commit()
445
+ logger.info(f"User '{user_id}' deactivated")
446
+ return True
447
+ except Exception as e:
448
+ session.rollback()
449
+ logger.error(f"Error deleting user: {e}")
450
+ return False
451
+ finally:
452
+ session.close()
453
+
454
+ def delete_cube(self, cube_id: str) -> bool:
455
+ """Soft delete a cube (set is_active to False).
456
+
457
+ Args:
458
+ cube_id (str): The cube ID.
459
+
460
+ Returns:
461
+ bool: True if successful, False otherwise.
462
+ """
463
+ session = self._get_session()
464
+ try:
465
+ cube = session.query(Cube).filter(Cube.cube_id == cube_id).first()
466
+ if not cube:
467
+ return False
468
+
469
+ cube.is_active = False
470
+ session.commit()
471
+ logger.info(f"Cube '{cube_id}' deactivated")
472
+ return True
473
+ except Exception as e:
474
+ session.rollback()
475
+ logger.error(f"Error deleting cube: {e}")
476
+ return False
477
+ finally:
478
+ session.close()
479
+
480
+ def close(self) -> None:
481
+ """Close the database engine and dispose of all connections.
482
+
483
+ This method should be called when the UserManager is no longer needed
484
+ to ensure proper cleanup of database connections.
485
+ """
486
+ if hasattr(self, "engine"):
487
+ self.engine.dispose()
488
+ logger.info("UserManager database connections closed")
File without changes
File without changes
@@ -0,0 +1,42 @@
1
+ from abc import abstractmethod
2
+ from typing import Any
3
+
4
+ from memos.configs.memory import BaseActMemoryConfig
5
+ from memos.memories.base import BaseMemory
6
+
7
+
8
+ class BaseActMemory(BaseMemory):
9
+ @abstractmethod
10
+ def __init__(self, config: BaseActMemoryConfig) -> None:
11
+ """Initialize the activation memory with a configuration."""
12
+
13
+ @abstractmethod
14
+ def extract(self, text: str) -> Any:
15
+ """Extract memory based on the texts."""
16
+
17
+ @abstractmethod
18
+ def add(self, memories: list) -> None:
19
+ """Add memories."""
20
+
21
+ @abstractmethod
22
+ def get(self, memory_id: str) -> Any | None:
23
+ """Get a memory by its ID."""
24
+
25
+ @abstractmethod
26
+ def get_by_ids(self, memory_ids: list[str]) -> list[Any | None]:
27
+ """Get memories by their IDs."""
28
+
29
+ @abstractmethod
30
+ def get_all(self) -> list[Any]:
31
+ """Get all memories."""
32
+
33
+ @abstractmethod
34
+ def delete(self, memory_ids: list[str]) -> None:
35
+ """Delete memories.
36
+ Args:
37
+ memory_ids (list[str]): List of memory IDs to delete.
38
+ """
39
+
40
+ @abstractmethod
41
+ def delete_all(self) -> None:
42
+ """Delete all memories."""
@@ -0,0 +1,56 @@
1
+ import uuid
2
+
3
+ from datetime import datetime
4
+ from typing import Any
5
+
6
+ from pydantic import BaseModel, ConfigDict, Field
7
+ from transformers import DynamicCache
8
+
9
+ from memos.mem_scheduler.utils.db_utils import get_utc_now
10
+
11
+
12
+ class ActivationMemoryItem(BaseModel):
13
+ id: str = Field(default_factory=lambda: str(uuid.uuid4()))
14
+ memory: Any
15
+ metadata: dict = {}
16
+
17
+
18
+ class KVCacheRecords(BaseModel):
19
+ text_memories: list[str] = Field(
20
+ default=[],
21
+ description="The list of text memories transformed to the activation memory.",
22
+ )
23
+ composed_text_memory: str = Field(
24
+ default="",
25
+ description="Single string combining all text_memories using assembly template",
26
+ )
27
+ timestamp: datetime = Field(
28
+ default_factory=get_utc_now, description="submit time for schedule_messages"
29
+ )
30
+
31
+
32
+ class KVCacheItem(ActivationMemoryItem):
33
+ id: str = Field(default_factory=lambda: str(uuid.uuid4()))
34
+ memory: DynamicCache = Field(
35
+ default_factory=DynamicCache,
36
+ description="Dynamic cache for storing key-value pairs in the memory.",
37
+ )
38
+ metadata: dict = Field(
39
+ default_factory=dict, description="Metadata associated with the KV cache item."
40
+ )
41
+
42
+ model_config = ConfigDict(arbitrary_types_allowed=True) # To allow DynamicCache as a field type
43
+ records: KVCacheRecords = KVCacheRecords()
44
+
45
+
46
+ class VLLMKVCacheItem(KVCacheItem):
47
+ """
48
+ VLLM KV Cache Item that stores prompt strings instead of DynamicCache objects.
49
+ This is because vLLM handles KV cache on the server side via preloading.
50
+ """
51
+
52
+ # Override memory field to store prompt string instead of DynamicCache
53
+ memory: str = Field(
54
+ default="",
55
+ description="Prompt string used to preload KV cache in vLLM server",
56
+ )