MemoryOS 1.0.0__py3-none-any.whl → 1.1.1__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.

Potentially problematic release.


This version of MemoryOS might be problematic. Click here for more details.

Files changed (94) hide show
  1. {memoryos-1.0.0.dist-info → memoryos-1.1.1.dist-info}/METADATA +8 -2
  2. {memoryos-1.0.0.dist-info → memoryos-1.1.1.dist-info}/RECORD +92 -69
  3. {memoryos-1.0.0.dist-info → memoryos-1.1.1.dist-info}/WHEEL +1 -1
  4. memos/__init__.py +1 -1
  5. memos/api/client.py +109 -0
  6. memos/api/config.py +35 -8
  7. memos/api/context/dependencies.py +15 -66
  8. memos/api/middleware/request_context.py +63 -0
  9. memos/api/product_api.py +5 -2
  10. memos/api/product_models.py +107 -16
  11. memos/api/routers/product_router.py +62 -19
  12. memos/api/start_api.py +13 -0
  13. memos/configs/graph_db.py +4 -0
  14. memos/configs/mem_scheduler.py +38 -3
  15. memos/configs/memory.py +13 -0
  16. memos/configs/reranker.py +18 -0
  17. memos/context/context.py +255 -0
  18. memos/embedders/factory.py +2 -0
  19. memos/graph_dbs/base.py +4 -2
  20. memos/graph_dbs/nebular.py +368 -223
  21. memos/graph_dbs/neo4j.py +49 -13
  22. memos/graph_dbs/neo4j_community.py +13 -3
  23. memos/llms/factory.py +2 -0
  24. memos/llms/openai.py +74 -2
  25. memos/llms/vllm.py +2 -0
  26. memos/log.py +128 -4
  27. memos/mem_cube/general.py +3 -1
  28. memos/mem_os/core.py +89 -23
  29. memos/mem_os/main.py +3 -6
  30. memos/mem_os/product.py +418 -154
  31. memos/mem_os/utils/reference_utils.py +20 -0
  32. memos/mem_reader/factory.py +2 -0
  33. memos/mem_reader/simple_struct.py +204 -82
  34. memos/mem_scheduler/analyzer/__init__.py +0 -0
  35. memos/mem_scheduler/analyzer/mos_for_test_scheduler.py +569 -0
  36. memos/mem_scheduler/analyzer/scheduler_for_eval.py +280 -0
  37. memos/mem_scheduler/base_scheduler.py +126 -56
  38. memos/mem_scheduler/general_modules/dispatcher.py +2 -2
  39. memos/mem_scheduler/general_modules/misc.py +99 -1
  40. memos/mem_scheduler/general_modules/scheduler_logger.py +17 -11
  41. memos/mem_scheduler/general_scheduler.py +40 -88
  42. memos/mem_scheduler/memory_manage_modules/__init__.py +5 -0
  43. memos/mem_scheduler/memory_manage_modules/memory_filter.py +308 -0
  44. memos/mem_scheduler/{general_modules → memory_manage_modules}/retriever.py +34 -7
  45. memos/mem_scheduler/monitors/dispatcher_monitor.py +9 -8
  46. memos/mem_scheduler/monitors/general_monitor.py +119 -39
  47. memos/mem_scheduler/optimized_scheduler.py +124 -0
  48. memos/mem_scheduler/orm_modules/__init__.py +0 -0
  49. memos/mem_scheduler/orm_modules/base_model.py +635 -0
  50. memos/mem_scheduler/orm_modules/monitor_models.py +261 -0
  51. memos/mem_scheduler/scheduler_factory.py +2 -0
  52. memos/mem_scheduler/schemas/monitor_schemas.py +96 -29
  53. memos/mem_scheduler/utils/config_utils.py +100 -0
  54. memos/mem_scheduler/utils/db_utils.py +33 -0
  55. memos/mem_scheduler/utils/filter_utils.py +1 -1
  56. memos/mem_scheduler/webservice_modules/__init__.py +0 -0
  57. memos/mem_user/mysql_user_manager.py +4 -2
  58. memos/memories/activation/kv.py +2 -1
  59. memos/memories/textual/item.py +96 -17
  60. memos/memories/textual/naive.py +1 -1
  61. memos/memories/textual/tree.py +57 -3
  62. memos/memories/textual/tree_text_memory/organize/handler.py +4 -2
  63. memos/memories/textual/tree_text_memory/organize/manager.py +28 -14
  64. memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py +1 -2
  65. memos/memories/textual/tree_text_memory/organize/reorganizer.py +75 -23
  66. memos/memories/textual/tree_text_memory/retrieve/bochasearch.py +10 -6
  67. memos/memories/textual/tree_text_memory/retrieve/internet_retriever.py +6 -2
  68. memos/memories/textual/tree_text_memory/retrieve/internet_retriever_factory.py +2 -0
  69. memos/memories/textual/tree_text_memory/retrieve/recall.py +119 -21
  70. memos/memories/textual/tree_text_memory/retrieve/searcher.py +172 -44
  71. memos/memories/textual/tree_text_memory/retrieve/utils.py +6 -4
  72. memos/memories/textual/tree_text_memory/retrieve/xinyusearch.py +5 -4
  73. memos/memos_tools/notification_utils.py +46 -0
  74. memos/memos_tools/singleton.py +174 -0
  75. memos/memos_tools/thread_safe_dict.py +22 -0
  76. memos/memos_tools/thread_safe_dict_segment.py +382 -0
  77. memos/parsers/factory.py +2 -0
  78. memos/reranker/__init__.py +4 -0
  79. memos/reranker/base.py +24 -0
  80. memos/reranker/concat.py +59 -0
  81. memos/reranker/cosine_local.py +96 -0
  82. memos/reranker/factory.py +48 -0
  83. memos/reranker/http_bge.py +312 -0
  84. memos/reranker/noop.py +16 -0
  85. memos/templates/mem_reader_prompts.py +289 -40
  86. memos/templates/mem_scheduler_prompts.py +242 -0
  87. memos/templates/mos_prompts.py +133 -60
  88. memos/types.py +4 -1
  89. memos/api/context/context.py +0 -147
  90. memos/mem_scheduler/mos_for_test_scheduler.py +0 -146
  91. {memoryos-1.0.0.dist-info → memoryos-1.1.1.dist-info}/entry_points.txt +0 -0
  92. {memoryos-1.0.0.dist-info → memoryos-1.1.1.dist-info/licenses}/LICENSE +0 -0
  93. /memos/mem_scheduler/{general_modules → webservice_modules}/rabbitmq_service.py +0 -0
  94. /memos/mem_scheduler/{general_modules → webservice_modules}/redis_service.py +0 -0
@@ -0,0 +1,255 @@
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__(self, trace_id: str | None = None, api_path: str | None = None):
33
+ self.trace_id = trace_id or "trace-id"
34
+ self.api_path = api_path
35
+ self._data: dict[str, Any] = {}
36
+
37
+ def set(self, key: str, value: Any) -> None:
38
+ """Set a value in the context."""
39
+ self._data[key] = value
40
+
41
+ def get(self, key: str, default: Any | None = None) -> Any:
42
+ """Get a value from the context."""
43
+ return self._data.get(key, default)
44
+
45
+ def __setattr__(self, name: str, value: Any) -> None:
46
+ if name.startswith("_") or name in ("trace_id", "api_path"):
47
+ super().__setattr__(name, value)
48
+ else:
49
+ if not hasattr(self, "_data"):
50
+ super().__setattr__(name, value)
51
+ else:
52
+ self._data[name] = value
53
+
54
+ def __getattr__(self, name: str) -> Any:
55
+ if hasattr(self, "_data") and name in self._data:
56
+ return self._data[name]
57
+ raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")
58
+
59
+ def to_dict(self) -> dict[str, Any]:
60
+ """Convert context to dictionary."""
61
+ return {"trace_id": self.trace_id, "api_path": self.api_path, "data": self._data.copy()}
62
+
63
+
64
+ def set_request_context(context: RequestContext) -> None:
65
+ """
66
+ Set the current request context.
67
+
68
+ This is typically called by the API dependency injection system.
69
+ """
70
+ _request_context.set(context.to_dict())
71
+
72
+
73
+ def get_current_trace_id() -> str | None:
74
+ """
75
+ Get the current request's trace_id.
76
+
77
+ Returns:
78
+ The trace_id if available, None otherwise.
79
+ """
80
+ context = _request_context.get()
81
+ if context:
82
+ return context.get("trace_id")
83
+ return None
84
+
85
+
86
+ def get_current_api_path() -> str | None:
87
+ """
88
+ Get the current request's api path.
89
+ """
90
+ context = _request_context.get()
91
+ if context:
92
+ return context.get("api_path")
93
+ return None
94
+
95
+
96
+ def get_current_context() -> RequestContext | None:
97
+ """
98
+ Get the current request context.
99
+
100
+ Returns:
101
+ The current RequestContext if available, None otherwise.
102
+ """
103
+ context_dict = _request_context.get()
104
+ if context_dict:
105
+ ctx = RequestContext(
106
+ trace_id=context_dict.get("trace_id"), api_path=context_dict.get("api_path")
107
+ )
108
+ ctx._data = context_dict.get("data", {}).copy()
109
+ return ctx
110
+ return None
111
+
112
+
113
+ def require_context() -> RequestContext:
114
+ """
115
+ Get the current request context, raising an error if not available.
116
+
117
+ Returns:
118
+ The current RequestContext.
119
+
120
+ Raises:
121
+ RuntimeError: If called outside of a request context.
122
+ """
123
+ context = get_current_context()
124
+ if context is None:
125
+ raise RuntimeError(
126
+ "No request context available. This function must be called within a request handler."
127
+ )
128
+ return context
129
+
130
+
131
+ class ContextThread(threading.Thread):
132
+ """
133
+ Thread class that automatically propagates the main thread's trace_id to child threads.
134
+ """
135
+
136
+ def __init__(self, target, args=(), kwargs=None, **thread_kwargs):
137
+ super().__init__(**thread_kwargs)
138
+ self.target = target
139
+ self.args = args
140
+ self.kwargs = kwargs or {}
141
+
142
+ self.main_trace_id = get_current_trace_id()
143
+ self.main_api_path = get_current_api_path()
144
+ self.main_context = get_current_context()
145
+
146
+ def run(self):
147
+ # Create a new RequestContext with the main thread's trace_id
148
+ if self.main_context:
149
+ # Copy the context data
150
+ child_context = RequestContext(
151
+ trace_id=self.main_trace_id, api_path=self.main_context.api_path
152
+ )
153
+ child_context._data = self.main_context._data.copy()
154
+
155
+ # Set the context in the child thread
156
+ set_request_context(child_context)
157
+
158
+ # Run the target function
159
+ self.target(*self.args, **self.kwargs)
160
+
161
+
162
+ class ContextThreadPoolExecutor(ThreadPoolExecutor):
163
+ """
164
+ ThreadPoolExecutor that automatically propagates the main thread's trace_id to worker threads.
165
+ """
166
+
167
+ def submit(self, fn: Callable[..., T], *args: Any, **kwargs: Any) -> Any:
168
+ """
169
+ Submit a callable to be executed with the given arguments.
170
+ Automatically propagates the current thread's context to the worker thread.
171
+ """
172
+ main_trace_id = get_current_trace_id()
173
+ main_api_path = get_current_api_path()
174
+ main_context = get_current_context()
175
+
176
+ @functools.wraps(fn)
177
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
178
+ if main_context:
179
+ # Create and set new context in worker thread
180
+ child_context = RequestContext(trace_id=main_trace_id, api_path=main_api_path)
181
+ child_context._data = main_context._data.copy()
182
+ set_request_context(child_context)
183
+
184
+ return fn(*args, **kwargs)
185
+
186
+ return super().submit(wrapper, *args, **kwargs)
187
+
188
+ def map(
189
+ self,
190
+ fn: Callable[..., T],
191
+ *iterables: Any,
192
+ timeout: float | None = None,
193
+ chunksize: int = 1,
194
+ ) -> Any:
195
+ """
196
+ Returns an iterator equivalent to map(fn, iter).
197
+ Automatically propagates the current thread's context to worker threads.
198
+ """
199
+ main_trace_id = get_current_trace_id()
200
+ main_api_path = get_current_api_path()
201
+ main_context = get_current_context()
202
+
203
+ @functools.wraps(fn)
204
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
205
+ if main_context:
206
+ # Create and set new context in worker thread
207
+ child_context = RequestContext(trace_id=main_trace_id, api_path=main_api_path)
208
+ child_context._data = main_context._data.copy()
209
+ set_request_context(child_context)
210
+
211
+ return fn(*args, **kwargs)
212
+
213
+ return super().map(wrapper, *iterables, timeout=timeout, chunksize=chunksize)
214
+
215
+
216
+ # Type for trace_id getter function
217
+ TraceIdGetter = Callable[[], str | None]
218
+
219
+ # Global variable to hold the trace_id getter function
220
+ _trace_id_getter: TraceIdGetter | None = None
221
+
222
+
223
+ def generate_trace_id() -> str:
224
+ """Generate a random trace_id."""
225
+ return os.urandom(16).hex()
226
+
227
+
228
+ def set_trace_id_getter(getter: TraceIdGetter) -> None:
229
+ """
230
+ Set a custom trace_id getter function.
231
+
232
+ This allows the logging system to retrieve trace_id without importing
233
+ API-specific general_modules.
234
+ """
235
+ global _trace_id_getter
236
+ _trace_id_getter = getter
237
+
238
+
239
+ def get_trace_id_for_logging() -> str | None:
240
+ """
241
+ Get trace_id for logging purposes.
242
+
243
+ This function is used by the logging system and will use either
244
+ the custom getter function or fall back to the default context.
245
+ """
246
+ if _trace_id_getter:
247
+ try:
248
+ return _trace_id_getter()
249
+ except Exception:
250
+ pass
251
+ return get_current_trace_id()
252
+
253
+
254
+ # Initialize the default trace_id getter
255
+ set_trace_id_getter(get_current_trace_id)
@@ -6,6 +6,7 @@ from memos.embedders.base import BaseEmbedder
6
6
  from memos.embedders.ollama import OllamaEmbedder
7
7
  from memos.embedders.sentence_transformer import SenTranEmbedder
8
8
  from memos.embedders.universal_api import UniversalAPIEmbedder
9
+ from memos.memos_tools.singleton import singleton_factory
9
10
 
10
11
 
11
12
  class EmbedderFactory(BaseEmbedder):
@@ -19,6 +20,7 @@ class EmbedderFactory(BaseEmbedder):
19
20
  }
20
21
 
21
22
  @classmethod
23
+ @singleton_factory()
22
24
  def from_config(cls, config_factory: EmbedderConfigFactory) -> BaseEmbedder:
23
25
  backend = config_factory.backend
24
26
  if backend not in cls.backend_to_class:
memos/graph_dbs/base.py CHANGED
@@ -81,7 +81,9 @@ class BaseGraphDB(ABC):
81
81
  """
82
82
 
83
83
  @abstractmethod
84
- def get_nodes(self, id: str, include_embedding: bool = False) -> dict[str, Any] | None:
84
+ def get_nodes(
85
+ self, id: str, include_embedding: bool = False, **kwargs
86
+ ) -> dict[str, Any] | None:
85
87
  """
86
88
  Retrieve the metadata and memory of a list of nodes.
87
89
  Args:
@@ -141,7 +143,7 @@ class BaseGraphDB(ABC):
141
143
 
142
144
  # Search / recall operations
143
145
  @abstractmethod
144
- def search_by_embedding(self, vector: list[float], top_k: int = 5) -> list[dict]:
146
+ def search_by_embedding(self, vector: list[float], top_k: int = 5, **kwargs) -> list[dict]:
145
147
  """
146
148
  Retrieve node IDs based on vector similarity.
147
149