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,355 @@
1
+ """
2
+ Global request context management for trace_id and request-scoped data.
3
+
4
+ This module provides optional trace_id functionality that can be enabled
5
+ when using the API components. It uses ContextVar to ensure thread safety
6
+ and request isolation.
7
+ """
8
+
9
+ import functools
10
+ import os
11
+ import threading
12
+
13
+ from collections.abc import Callable
14
+ from concurrent.futures import ThreadPoolExecutor
15
+ from contextvars import ContextVar
16
+ from typing import Any, TypeVar
17
+
18
+
19
+ T = TypeVar("T")
20
+
21
+ # Global context variable for request-scoped data
22
+ _request_context: ContextVar[dict[str, Any] | None] = ContextVar("request_context", default=None)
23
+
24
+
25
+ class RequestContext:
26
+ """
27
+ Request-scoped context object that holds trace_id and other request data.
28
+
29
+ This provides a Flask g-like object for FastAPI applications.
30
+ """
31
+
32
+ def __init__(
33
+ self,
34
+ trace_id: str | None = None,
35
+ api_path: str | None = None,
36
+ env: str | None = None,
37
+ user_type: str | None = None,
38
+ user_name: str | None = None,
39
+ source: str | None = None,
40
+ ):
41
+ self.trace_id = trace_id or "trace-id"
42
+ self.api_path = api_path
43
+ self.env = env
44
+ self.user_type = user_type
45
+ self.user_name = user_name
46
+ self.source = source
47
+ self._data: dict[str, Any] = {}
48
+
49
+ def set(self, key: str, value: Any) -> None:
50
+ """Set a value in the context."""
51
+ self._data[key] = value
52
+
53
+ def get(self, key: str, default: Any | None = None) -> Any:
54
+ """Get a value from the context."""
55
+ return self._data.get(key, default)
56
+
57
+ def __setattr__(self, name: str, value: Any) -> None:
58
+ if name.startswith("_") or name in (
59
+ "trace_id",
60
+ "api_path",
61
+ "env",
62
+ "user_type",
63
+ "user_name",
64
+ "source",
65
+ ):
66
+ super().__setattr__(name, value)
67
+ else:
68
+ if not hasattr(self, "_data"):
69
+ super().__setattr__(name, value)
70
+ else:
71
+ self._data[name] = value
72
+
73
+ def __getattr__(self, name: str) -> Any:
74
+ if hasattr(self, "_data") and name in self._data:
75
+ return self._data[name]
76
+ raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")
77
+
78
+ def to_dict(self) -> dict[str, Any]:
79
+ """Convert context to dictionary."""
80
+ return {
81
+ "trace_id": self.trace_id,
82
+ "api_path": self.api_path,
83
+ "env": self.env,
84
+ "user_type": self.user_type,
85
+ "user_name": self.user_name,
86
+ "source": self.source,
87
+ "data": self._data.copy(),
88
+ }
89
+
90
+
91
+ def set_request_context(context: RequestContext | None) -> None:
92
+ """
93
+ Set the current request context.
94
+
95
+ This is typically called by the API dependency injection system.
96
+ """
97
+ if context:
98
+ _request_context.set(context.to_dict())
99
+ else:
100
+ _request_context.set(None)
101
+
102
+
103
+ def get_current_trace_id() -> str | None:
104
+ """
105
+ Get the current request's trace_id.
106
+
107
+ Returns:
108
+ The trace_id if available, None otherwise.
109
+ """
110
+ context = _request_context.get()
111
+ if context:
112
+ return context.get("trace_id")
113
+ return None
114
+
115
+
116
+ def get_current_api_path() -> str | None:
117
+ """
118
+ Get the current request's api path.
119
+ """
120
+ context = _request_context.get()
121
+ if context:
122
+ return context.get("api_path")
123
+ return None
124
+
125
+
126
+ def get_current_env() -> str | None:
127
+ """
128
+ Get the current request's env.
129
+ """
130
+ context = _request_context.get()
131
+ if context:
132
+ return context.get("env")
133
+ return "prod"
134
+
135
+
136
+ def get_current_user_type() -> str | None:
137
+ """
138
+ Get the current request's user type.
139
+ """
140
+ context = _request_context.get()
141
+ if context:
142
+ return context.get("user_type")
143
+ return "opensource"
144
+
145
+
146
+ def get_current_user_name() -> str | None:
147
+ """
148
+ Get the current request's user name.
149
+ """
150
+ context = _request_context.get()
151
+ if context:
152
+ return context.get("user_name")
153
+ return "memos"
154
+
155
+
156
+ def get_current_source() -> str | None:
157
+ """
158
+ Get the current request's source (e.g., 'product_api' or 'server_api').
159
+ """
160
+ context = _request_context.get()
161
+ if context:
162
+ return context.get("source")
163
+ return None
164
+
165
+
166
+ def get_current_context() -> RequestContext | None:
167
+ """
168
+ Get the current request context.
169
+
170
+ Returns:
171
+ The current RequestContext if available, None otherwise.
172
+ """
173
+ context_dict = _request_context.get()
174
+ if context_dict:
175
+ ctx = RequestContext(
176
+ trace_id=context_dict.get("trace_id"),
177
+ api_path=context_dict.get("api_path"),
178
+ env=context_dict.get("env"),
179
+ user_type=context_dict.get("user_type"),
180
+ user_name=context_dict.get("user_name"),
181
+ source=context_dict.get("source"),
182
+ )
183
+ ctx._data = context_dict.get("data", {}).copy()
184
+ return ctx
185
+ return None
186
+
187
+
188
+ def require_context() -> RequestContext:
189
+ """
190
+ Get the current request context, raising an error if not available.
191
+
192
+ Returns:
193
+ The current RequestContext.
194
+
195
+ Raises:
196
+ RuntimeError: If called outside of a request context.
197
+ """
198
+ context = get_current_context()
199
+ if context is None:
200
+ raise RuntimeError(
201
+ "No request context available. This function must be called within a request handler."
202
+ )
203
+ return context
204
+
205
+
206
+ class ContextThread(threading.Thread):
207
+ """
208
+ Thread class that automatically propagates the main thread's trace_id to child threads.
209
+ """
210
+
211
+ def __init__(self, target, args=(), kwargs=None, **thread_kwargs):
212
+ super().__init__(**thread_kwargs)
213
+ self.target = target
214
+ self.args = args
215
+ self.kwargs = kwargs or {}
216
+
217
+ self.main_trace_id = get_current_trace_id()
218
+ self.main_api_path = get_current_api_path()
219
+ self.main_env = get_current_env()
220
+ self.main_user_type = get_current_user_type()
221
+ self.main_user_name = get_current_user_name()
222
+ self.main_context = get_current_context()
223
+
224
+ def run(self):
225
+ # Create a new RequestContext with the main thread's trace_id
226
+ if self.main_context:
227
+ # Copy the context data
228
+ child_context = RequestContext(
229
+ trace_id=self.main_trace_id,
230
+ api_path=self.main_api_path,
231
+ env=self.main_env,
232
+ user_type=self.main_user_type,
233
+ user_name=self.main_user_name,
234
+ )
235
+ child_context._data = self.main_context._data.copy()
236
+
237
+ # Set the context in the child thread
238
+ set_request_context(child_context)
239
+
240
+ # Run the target function
241
+ self.target(*self.args, **self.kwargs)
242
+
243
+
244
+ class ContextThreadPoolExecutor(ThreadPoolExecutor):
245
+ """
246
+ ThreadPoolExecutor that automatically propagates the main thread's trace_id to worker threads.
247
+ """
248
+
249
+ def submit(self, fn: Callable[..., T], *args: Any, **kwargs: Any) -> Any:
250
+ """
251
+ Submit a callable to be executed with the given arguments.
252
+ Automatically propagates the current thread's context to the worker thread.
253
+ """
254
+ main_trace_id = get_current_trace_id()
255
+ main_api_path = get_current_api_path()
256
+ main_env = get_current_env()
257
+ main_user_type = get_current_user_type()
258
+ main_user_name = get_current_user_name()
259
+ main_context = get_current_context()
260
+
261
+ @functools.wraps(fn)
262
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
263
+ if main_context:
264
+ # Create and set new context in worker thread
265
+ child_context = RequestContext(
266
+ trace_id=main_trace_id,
267
+ api_path=main_api_path,
268
+ env=main_env,
269
+ user_type=main_user_type,
270
+ user_name=main_user_name,
271
+ )
272
+ child_context._data = main_context._data.copy()
273
+ set_request_context(child_context)
274
+
275
+ return fn(*args, **kwargs)
276
+
277
+ return super().submit(wrapper, *args, **kwargs)
278
+
279
+ def map(
280
+ self,
281
+ fn: Callable[..., T],
282
+ *iterables: Any,
283
+ timeout: float | None = None,
284
+ chunksize: int = 1,
285
+ ) -> Any:
286
+ """
287
+ Returns an iterator equivalent to map(fn, iter).
288
+ Automatically propagates the current thread's context to worker threads.
289
+ """
290
+ main_trace_id = get_current_trace_id()
291
+ main_api_path = get_current_api_path()
292
+ main_env = get_current_env()
293
+ main_user_type = get_current_user_type()
294
+ main_user_name = get_current_user_name()
295
+ main_context = get_current_context()
296
+
297
+ @functools.wraps(fn)
298
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
299
+ if main_context:
300
+ # Create and set new context in worker thread
301
+ child_context = RequestContext(
302
+ trace_id=main_trace_id,
303
+ api_path=main_api_path,
304
+ env=main_env,
305
+ user_type=main_user_type,
306
+ user_name=main_user_name,
307
+ )
308
+ child_context._data = main_context._data.copy()
309
+ set_request_context(child_context)
310
+
311
+ return fn(*args, **kwargs)
312
+
313
+ return super().map(wrapper, *iterables, timeout=timeout, chunksize=chunksize)
314
+
315
+
316
+ # Type for trace_id getter function
317
+ TraceIdGetter = Callable[[], str | None]
318
+
319
+ # Global variable to hold the trace_id getter function
320
+ _trace_id_getter: TraceIdGetter | None = None
321
+
322
+
323
+ def generate_trace_id() -> str:
324
+ """Generate a random trace_id."""
325
+ return os.urandom(16).hex()
326
+
327
+
328
+ def set_trace_id_getter(getter: TraceIdGetter) -> None:
329
+ """
330
+ Set a custom trace_id getter function.
331
+
332
+ This allows the logging system to retrieve trace_id without importing
333
+ API-specific general_modules.
334
+ """
335
+ global _trace_id_getter
336
+ _trace_id_getter = getter
337
+
338
+
339
+ def get_trace_id_for_logging() -> str | None:
340
+ """
341
+ Get trace_id for logging purposes.
342
+
343
+ This function is used by the logging system and will use either
344
+ the custom getter function or fall back to the default context.
345
+ """
346
+ if _trace_id_getter:
347
+ try:
348
+ return _trace_id_getter()
349
+ except Exception:
350
+ pass
351
+ return get_current_trace_id()
352
+
353
+
354
+ # Initialize the default trace_id getter
355
+ set_trace_id_getter(get_current_trace_id)
memos/dependency.py ADDED
@@ -0,0 +1,52 @@
1
+ """
2
+ This utility provides tools for managing dependencies in MemOS.
3
+ """
4
+
5
+ import functools
6
+ import importlib
7
+
8
+
9
+ def require_python_package(
10
+ import_name: str, install_command: str | None = None, install_link: str | None = None
11
+ ):
12
+ """Check if a package is available and provide installation hints on import failure.
13
+
14
+ Args:
15
+ import_name (str): The top-level importable module name a package provides.
16
+ install_command (str, optional): Installation command.
17
+ install_link (str, optional): URL link to installation guide.
18
+
19
+ Returns:
20
+ Callable: A decorator function that wraps the target function with package availability check.
21
+
22
+ Raises:
23
+ ImportError: When the specified package is not available, with installation
24
+ instructions included in the error message.
25
+
26
+ Example:
27
+ >>> @require_python_package(
28
+ ... import_name='faiss',
29
+ ... install_command='pip install faiss-cpu',
30
+ ... install_link='https://github.com/facebookresearch/faiss/blob/main/INSTALL.md'
31
+ ... )
32
+ ... def create_faiss_index():
33
+ ... from faiss import IndexFlatL2 # Actual import in function
34
+ ... return IndexFlatL2(128)
35
+ """
36
+
37
+ def decorator(func):
38
+ @functools.wraps(func)
39
+ def wrapper(*args, **kwargs):
40
+ try:
41
+ importlib.import_module(import_name)
42
+ except ImportError:
43
+ error_msg = f"Missing required module - '{import_name}'\n"
44
+ error_msg += f"💡 Install command: {install_command}\n" if install_command else ""
45
+ error_msg += f"💡 Install guide: {install_link}\n" if install_link else ""
46
+
47
+ raise ImportError(error_msg) from None
48
+ return func(*args, **kwargs)
49
+
50
+ return wrapper
51
+
52
+ return decorator
memos/deprecation.py ADDED
@@ -0,0 +1,262 @@
1
+ """
2
+ This module provides utilities for marking functions, classes, and parameters
3
+ as deprecated. It includes decorators for deprecation, a function to issue
4
+ warnings, and utilities to check deprecation status.
5
+ """
6
+
7
+ import functools
8
+ import warnings
9
+
10
+ from collections.abc import Callable
11
+ from typing import Any, TypeVar
12
+
13
+
14
+ warnings.simplefilter("default", DeprecationWarning)
15
+
16
+
17
+ F = TypeVar("F", bound=Callable[..., Any])
18
+ C = TypeVar("C", bound=type)
19
+
20
+
21
+ def deprecated(
22
+ reason: str | None = None,
23
+ version: str | None = None,
24
+ alternative: str | None = None,
25
+ category: type[Warning] = DeprecationWarning,
26
+ stacklevel: int = 2,
27
+ ) -> Callable[[F], F]:
28
+ """
29
+ Decorator to mark functions as deprecated.
30
+
31
+ Args:
32
+ reason: Optional reason for deprecation
33
+ version: Version when the function was deprecated
34
+ alternative: Suggested alternative function/method
35
+ category: Warning category to use
36
+ stacklevel: Stack level for the warning
37
+
38
+ Example:
39
+ @deprecated(reason="Use new_function instead", version="1.2.0")
40
+ def old_function():
41
+ pass
42
+ """
43
+
44
+ def decorator(func: F) -> F:
45
+ @functools.wraps(func)
46
+ def wrapper(*args, **kwargs):
47
+ # Build deprecation message
48
+ msg_parts = [f"Function '{func.__name__}' is deprecated"]
49
+
50
+ if version:
51
+ msg_parts.append(f"since version {version}")
52
+
53
+ if reason:
54
+ msg_parts.append(f"- {reason}")
55
+
56
+ if alternative:
57
+ msg_parts.append(f"Use '{alternative}' instead")
58
+
59
+ message = ". ".join(msg_parts) + "."
60
+
61
+ warnings.warn(message, category=category, stacklevel=stacklevel)
62
+ return func(*args, **kwargs)
63
+
64
+ # Mark the wrapper as deprecated for introspection
65
+ wrapper.__deprecated__ = True
66
+ wrapper.__deprecation_info__ = {
67
+ "reason": reason,
68
+ "version": version,
69
+ "alternative": alternative,
70
+ "category": category,
71
+ }
72
+
73
+ return wrapper
74
+
75
+ return decorator
76
+
77
+
78
+ def deprecated_class(
79
+ reason: str | None = None,
80
+ version: str | None = None,
81
+ alternative: str | None = None,
82
+ category: type[Warning] = DeprecationWarning,
83
+ stacklevel: int = 2,
84
+ ) -> Callable[[C], C]:
85
+ """
86
+ Decorator to mark classes as deprecated.
87
+
88
+ Args:
89
+ reason: Optional reason for deprecation
90
+ version: Version when the class was deprecated
91
+ alternative: Suggested alternative class
92
+ category: Warning category to use
93
+ stacklevel: Stack level for the warning
94
+
95
+ Example:
96
+ @deprecated_class(reason="Use NewClass instead", version="1.2.0")
97
+ class OldClass:
98
+ pass
99
+ """
100
+
101
+ def decorator(cls: C) -> C:
102
+ # Store original __init__
103
+ original_init = cls.__init__
104
+
105
+ @functools.wraps(original_init)
106
+ def new_init(self, *args, **kwargs):
107
+ # Build deprecation message
108
+ msg_parts = [f"Class '{cls.__name__}' is deprecated"]
109
+
110
+ if version:
111
+ msg_parts.append(f"since version {version}")
112
+
113
+ if reason:
114
+ msg_parts.append(f"- {reason}")
115
+
116
+ if alternative:
117
+ msg_parts.append(f"Use '{alternative}' instead")
118
+
119
+ message = ". ".join(msg_parts) + "."
120
+
121
+ warnings.warn(message, category=category, stacklevel=stacklevel)
122
+ original_init(self, *args, **kwargs)
123
+
124
+ # Replace __init__
125
+ cls.__init__ = new_init
126
+
127
+ # Mark the class as deprecated for introspection
128
+ cls.__deprecated__ = True
129
+ cls.__deprecation_info__ = {
130
+ "reason": reason,
131
+ "version": version,
132
+ "alternative": alternative,
133
+ "category": category,
134
+ }
135
+
136
+ return cls
137
+
138
+ return decorator
139
+
140
+
141
+ def deprecated_parameter(
142
+ parameter_name: str,
143
+ reason: str | None = None,
144
+ version: str | None = None,
145
+ alternative: str | None = None,
146
+ category: type[Warning] = DeprecationWarning,
147
+ stacklevel: int = 2,
148
+ ) -> Callable[[F], F]:
149
+ """
150
+ Decorator to mark specific parameters as deprecated.
151
+
152
+ Args:
153
+ parameter_name: Name of the deprecated parameter
154
+ reason: Optional reason for deprecation
155
+ version: Version when the parameter was deprecated
156
+ alternative: Suggested alternative parameter
157
+ category: Warning category to use
158
+ stacklevel: Stack level for the warning
159
+
160
+ Example:
161
+ @deprecated_parameter("old_param", alternative="new_param", version="1.2.0")
162
+ def my_function(new_param=None, old_param=None):
163
+ pass
164
+ """
165
+
166
+ def decorator(func: F) -> F:
167
+ @functools.wraps(func)
168
+ def wrapper(*args, **kwargs):
169
+ # Check if deprecated parameter is used
170
+ if parameter_name in kwargs:
171
+ # Build deprecation message
172
+ msg_parts = [
173
+ f"Parameter '{parameter_name}' in function '{func.__name__}' is deprecated"
174
+ ]
175
+
176
+ if version:
177
+ msg_parts.append(f"since version {version}")
178
+
179
+ if reason:
180
+ msg_parts.append(f"- {reason}")
181
+
182
+ if alternative:
183
+ msg_parts.append(f"Use parameter '{alternative}' instead")
184
+
185
+ message = ". ".join(msg_parts) + "."
186
+
187
+ warnings.warn(message, category=category, stacklevel=stacklevel)
188
+
189
+ return func(*args, **kwargs)
190
+
191
+ return wrapper
192
+
193
+ return decorator
194
+
195
+
196
+ def warn_deprecated(
197
+ item_name: str,
198
+ item_type: str = "feature",
199
+ reason: str | None = None,
200
+ version: str | None = None,
201
+ alternative: str | None = None,
202
+ category: type[Warning] = DeprecationWarning,
203
+ stacklevel: int = 2,
204
+ ) -> None:
205
+ """
206
+ Issue a deprecation warning for any item.
207
+
208
+ Args:
209
+ item_name: Name of the deprecated item
210
+ item_type: Type of item (e.g., "function", "class", "parameter", "feature")
211
+ reason: Optional reason for deprecation
212
+ version: Version when the item was deprecated
213
+ alternative: Suggested alternative
214
+ category: Warning category to use
215
+ stacklevel: Stack level for the warning
216
+
217
+ Example:
218
+ warn_deprecated("old_method", "method", version="1.2.0", alternative="new_method")
219
+ """
220
+ # Build deprecation message
221
+ msg_parts = [f"{item_type.capitalize()} '{item_name}' is deprecated"]
222
+
223
+ if version:
224
+ msg_parts.append(f"since version {version}")
225
+
226
+ if reason:
227
+ msg_parts.append(f"- {reason}")
228
+
229
+ if alternative:
230
+ msg_parts.append(f"Use '{alternative}' instead")
231
+
232
+ message = ". ".join(msg_parts) + "."
233
+
234
+ warnings.warn(message, category=category, stacklevel=stacklevel)
235
+
236
+
237
+ def is_deprecated(obj: Any) -> bool:
238
+ """
239
+ Check if an object is marked as deprecated.
240
+
241
+ Args:
242
+ obj: Object to check
243
+
244
+ Returns:
245
+ True if the object is deprecated, False otherwise
246
+ """
247
+ return getattr(obj, "__deprecated__", False)
248
+
249
+
250
+ def get_deprecation_info(obj: Any) -> dict | None:
251
+ """
252
+ Get deprecation information for an object.
253
+
254
+ Args:
255
+ obj: Object to get deprecation info for
256
+
257
+ Returns:
258
+ Dictionary with deprecation info or None if not deprecated
259
+ """
260
+ if is_deprecated(obj):
261
+ return getattr(obj, "__deprecation_info__", None)
262
+ return None
File without changes