MemoryOS 0.2.2__py3-none-any.whl → 1.0.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 (82) hide show
  1. {memoryos-0.2.2.dist-info → memoryos-1.0.1.dist-info}/METADATA +7 -1
  2. {memoryos-0.2.2.dist-info → memoryos-1.0.1.dist-info}/RECORD +81 -66
  3. memos/__init__.py +1 -1
  4. memos/api/config.py +31 -8
  5. memos/api/context/context.py +1 -1
  6. memos/api/context/context_thread.py +96 -0
  7. memos/api/middleware/request_context.py +94 -0
  8. memos/api/product_api.py +5 -1
  9. memos/api/product_models.py +16 -0
  10. memos/api/routers/product_router.py +39 -3
  11. memos/api/start_api.py +3 -0
  12. memos/configs/internet_retriever.py +13 -0
  13. memos/configs/mem_scheduler.py +38 -16
  14. memos/configs/memory.py +13 -0
  15. memos/configs/reranker.py +18 -0
  16. memos/graph_dbs/base.py +33 -4
  17. memos/graph_dbs/nebular.py +631 -236
  18. memos/graph_dbs/neo4j.py +18 -7
  19. memos/graph_dbs/neo4j_community.py +6 -3
  20. memos/llms/vllm.py +2 -0
  21. memos/log.py +125 -8
  22. memos/mem_os/core.py +49 -11
  23. memos/mem_os/main.py +1 -1
  24. memos/mem_os/product.py +392 -215
  25. memos/mem_os/utils/default_config.py +1 -1
  26. memos/mem_os/utils/format_utils.py +11 -47
  27. memos/mem_os/utils/reference_utils.py +153 -0
  28. memos/mem_reader/simple_struct.py +112 -43
  29. memos/mem_scheduler/base_scheduler.py +58 -55
  30. memos/mem_scheduler/{modules → general_modules}/base.py +1 -2
  31. memos/mem_scheduler/{modules → general_modules}/dispatcher.py +54 -15
  32. memos/mem_scheduler/{modules → general_modules}/rabbitmq_service.py +4 -4
  33. memos/mem_scheduler/{modules → general_modules}/redis_service.py +1 -1
  34. memos/mem_scheduler/{modules → general_modules}/retriever.py +19 -5
  35. memos/mem_scheduler/{modules → general_modules}/scheduler_logger.py +10 -4
  36. memos/mem_scheduler/general_scheduler.py +110 -67
  37. memos/mem_scheduler/monitors/__init__.py +0 -0
  38. memos/mem_scheduler/monitors/dispatcher_monitor.py +305 -0
  39. memos/mem_scheduler/{modules/monitor.py → monitors/general_monitor.py} +57 -19
  40. memos/mem_scheduler/mos_for_test_scheduler.py +7 -1
  41. memos/mem_scheduler/schemas/general_schemas.py +3 -2
  42. memos/mem_scheduler/schemas/message_schemas.py +2 -1
  43. memos/mem_scheduler/schemas/monitor_schemas.py +10 -2
  44. memos/mem_scheduler/utils/misc_utils.py +43 -2
  45. memos/mem_user/mysql_user_manager.py +4 -2
  46. memos/memories/activation/item.py +1 -1
  47. memos/memories/activation/kv.py +20 -8
  48. memos/memories/textual/base.py +1 -1
  49. memos/memories/textual/general.py +1 -1
  50. memos/memories/textual/item.py +1 -1
  51. memos/memories/textual/tree.py +31 -1
  52. memos/memories/textual/tree_text_memory/organize/{conflict.py → handler.py} +30 -48
  53. memos/memories/textual/tree_text_memory/organize/manager.py +8 -96
  54. memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py +2 -0
  55. memos/memories/textual/tree_text_memory/organize/reorganizer.py +102 -140
  56. memos/memories/textual/tree_text_memory/retrieve/bochasearch.py +231 -0
  57. memos/memories/textual/tree_text_memory/retrieve/internet_retriever_factory.py +9 -0
  58. memos/memories/textual/tree_text_memory/retrieve/recall.py +67 -10
  59. memos/memories/textual/tree_text_memory/retrieve/reranker.py +1 -1
  60. memos/memories/textual/tree_text_memory/retrieve/searcher.py +246 -134
  61. memos/memories/textual/tree_text_memory/retrieve/task_goal_parser.py +7 -2
  62. memos/memories/textual/tree_text_memory/retrieve/utils.py +7 -5
  63. memos/memos_tools/lockfree_dict.py +120 -0
  64. memos/memos_tools/notification_utils.py +46 -0
  65. memos/memos_tools/thread_safe_dict.py +288 -0
  66. memos/reranker/__init__.py +4 -0
  67. memos/reranker/base.py +24 -0
  68. memos/reranker/cosine_local.py +95 -0
  69. memos/reranker/factory.py +43 -0
  70. memos/reranker/http_bge.py +99 -0
  71. memos/reranker/noop.py +16 -0
  72. memos/templates/mem_reader_prompts.py +290 -39
  73. memos/templates/mem_scheduler_prompts.py +23 -10
  74. memos/templates/mos_prompts.py +133 -31
  75. memos/templates/tree_reorganize_prompts.py +24 -17
  76. memos/utils.py +19 -0
  77. memos/memories/textual/tree_text_memory/organize/redundancy.py +0 -193
  78. {memoryos-0.2.2.dist-info → memoryos-1.0.1.dist-info}/LICENSE +0 -0
  79. {memoryos-0.2.2.dist-info → memoryos-1.0.1.dist-info}/WHEEL +0 -0
  80. {memoryos-0.2.2.dist-info → memoryos-1.0.1.dist-info}/entry_points.txt +0 -0
  81. /memos/mem_scheduler/{modules → general_modules}/__init__.py +0 -0
  82. /memos/mem_scheduler/{modules → general_modules}/misc.py +0 -0
@@ -1,5 +1,4 @@
1
1
  import json
2
- import logging
3
2
  import traceback
4
3
 
5
4
  from datetime import datetime
@@ -12,6 +11,7 @@ from memos.api.config import APIConfig
12
11
  from memos.api.context.dependencies import G, get_g_object
13
12
  from memos.api.product_models import (
14
13
  BaseResponse,
14
+ ChatCompleteRequest,
15
15
  ChatRequest,
16
16
  GetMemoryRequest,
17
17
  MemoryCreateRequest,
@@ -25,11 +25,12 @@ from memos.api.product_models import (
25
25
  UserRegisterResponse,
26
26
  )
27
27
  from memos.configs.mem_os import MOSConfig
28
+ from memos.log import get_logger
28
29
  from memos.mem_os.product import MOSProduct
29
30
  from memos.memos_tools.notification_service import get_error_bot_function, get_online_bot_function
30
31
 
31
32
 
32
- logger = logging.getLogger(__name__)
33
+ logger = get_logger(__name__)
33
34
 
34
35
  router = APIRouter(prefix="/product", tags=["Product API"])
35
36
 
@@ -148,7 +149,9 @@ def get_suggestion_queries_post(suggestion_req: SuggestionRequest):
148
149
  try:
149
150
  mos_product = get_mos_product_instance()
150
151
  suggestions = mos_product.get_suggestion_query(
151
- user_id=suggestion_req.user_id, language=suggestion_req.language
152
+ user_id=suggestion_req.user_id,
153
+ language=suggestion_req.language,
154
+ message=suggestion_req.message,
152
155
  )
153
156
  return SuggestionResponse(
154
157
  message="Suggestions retrieved successfully", data={"query": suggestions}
@@ -246,6 +249,7 @@ def chat(chat_req: ChatRequest):
246
249
  cube_id=chat_req.mem_cube_id,
247
250
  history=chat_req.history,
248
251
  internet_search=chat_req.internet_search,
252
+ moscube=chat_req.moscube,
249
253
  )
250
254
 
251
255
  except Exception as e:
@@ -273,6 +277,38 @@ def chat(chat_req: ChatRequest):
273
277
  raise HTTPException(status_code=500, detail=str(traceback.format_exc())) from err
274
278
 
275
279
 
280
+ @router.post("/chat/complete", summary="Chat with MemOS (Complete Response)")
281
+ def chat_complete(chat_req: ChatCompleteRequest):
282
+ """Chat with MemOS for a specific user. Returns complete response (non-streaming)."""
283
+ try:
284
+ mos_product = get_mos_product_instance()
285
+
286
+ # Collect all responses from the generator
287
+ content, references = mos_product.chat(
288
+ query=chat_req.query,
289
+ user_id=chat_req.user_id,
290
+ cube_id=chat_req.mem_cube_id,
291
+ history=chat_req.history,
292
+ internet_search=chat_req.internet_search,
293
+ moscube=chat_req.moscube,
294
+ base_prompt=chat_req.base_prompt,
295
+ top_k=chat_req.top_k,
296
+ threshold=chat_req.threshold,
297
+ )
298
+
299
+ # Return the complete response
300
+ return {
301
+ "message": "Chat completed successfully",
302
+ "data": {"response": content, "references": references},
303
+ }
304
+
305
+ except ValueError as err:
306
+ raise HTTPException(status_code=404, detail=str(traceback.format_exc())) from err
307
+ except Exception as err:
308
+ logger.error(f"Failed to start chat: {traceback.format_exc()}")
309
+ raise HTTPException(status_code=500, detail=str(traceback.format_exc())) from err
310
+
311
+
276
312
  @router.get("/users", summary="List all users", response_model=BaseResponse[list])
277
313
  def list_users():
278
314
  """List all registered users."""
memos/api/start_api.py CHANGED
@@ -9,6 +9,7 @@ from fastapi.requests import Request
9
9
  from fastapi.responses import JSONResponse, RedirectResponse
10
10
  from pydantic import BaseModel, Field
11
11
 
12
+ from memos.api.middleware.request_context import RequestContextMiddleware
12
13
  from memos.configs.mem_os import MOSConfig
13
14
  from memos.mem_os.main import MOS
14
15
  from memos.mem_user.user_manager import UserManager, UserRole
@@ -78,6 +79,8 @@ app = FastAPI(
78
79
  version="1.0.0",
79
80
  )
80
81
 
82
+ app.add_middleware(RequestContextMiddleware)
83
+
81
84
 
82
85
  class BaseRequest(BaseModel):
83
86
  """Base model for all requests."""
@@ -55,6 +55,18 @@ class XinyuSearchConfig(BaseInternetRetrieverConfig):
55
55
  )
56
56
 
57
57
 
58
+ class BochaSearchConfig(BaseInternetRetrieverConfig):
59
+ """Configuration class for Bocha Search API."""
60
+
61
+ max_results: int = Field(default=20, description="Maximum number of results to retrieve")
62
+ num_per_request: int = Field(default=10, description="Number of results per API request")
63
+ reader: MemReaderConfigFactory = Field(
64
+ ...,
65
+ default_factory=MemReaderConfigFactory,
66
+ description="Reader configuration",
67
+ )
68
+
69
+
58
70
  class InternetRetrieverConfigFactory(BaseConfig):
59
71
  """Factory class for creating internet retriever configurations."""
60
72
 
@@ -69,6 +81,7 @@ class InternetRetrieverConfigFactory(BaseConfig):
69
81
  "google": GoogleCustomSearchConfig,
70
82
  "bing": BingSearchConfig,
71
83
  "xinyu": XinyuSearchConfig,
84
+ "bocha": BochaSearchConfig,
72
85
  }
73
86
 
74
87
  @field_validator("backend")
@@ -6,7 +6,7 @@ from typing import Any, ClassVar
6
6
  from pydantic import ConfigDict, Field, field_validator, model_validator
7
7
 
8
8
  from memos.configs.base import BaseConfig
9
- from memos.mem_scheduler.modules.misc import DictConversionMixin
9
+ from memos.mem_scheduler.general_modules.misc import DictConversionMixin
10
10
  from memos.mem_scheduler.schemas.general_schemas import (
11
11
  BASE_DIR,
12
12
  DEFAULT_ACT_MEM_DUMP_PATH,
@@ -21,8 +21,6 @@ class BaseSchedulerConfig(BaseConfig):
21
21
  top_k: int = Field(
22
22
  default=10, description="Number of top candidates to consider in initial retrieval"
23
23
  )
24
- # TODO: The 'top_n' field is deprecated and will be removed in future versions.
25
- top_n: int = Field(default=5, description="Number of final results to return after processing")
26
24
  enable_parallel_dispatch: bool = Field(
27
25
  default=True, description="Whether to enable parallel message processing using thread pool"
28
26
  )
@@ -45,6 +43,7 @@ class BaseSchedulerConfig(BaseConfig):
45
43
 
46
44
 
47
45
  class GeneralSchedulerConfig(BaseSchedulerConfig):
46
+ model_config = ConfigDict(extra="ignore", strict=True)
48
47
  act_mem_update_interval: int | None = Field(
49
48
  default=300, description="Interval in seconds for updating activation memory"
50
49
  )
@@ -55,9 +54,15 @@ class GeneralSchedulerConfig(BaseSchedulerConfig):
55
54
  default=DEFAULT_ACT_MEM_DUMP_PATH, # Replace with DEFAULT_ACT_MEM_DUMP_PATH
56
55
  description="File path for dumping activation memory",
57
56
  )
58
- enable_act_memory_update: bool = Field(
57
+ enable_activation_memory: bool = Field(
59
58
  default=False, description="Whether to enable automatic activation memory updates"
60
59
  )
60
+ working_mem_monitor_capacity: int = Field(
61
+ default=30, description="Capacity of the working memory monitor"
62
+ )
63
+ activation_mem_monitor_capacity: int = Field(
64
+ default=20, description="Capacity of the activation memory monitor"
65
+ )
61
66
 
62
67
 
63
68
  class SchedulerConfigFactory(BaseConfig):
@@ -137,29 +142,46 @@ class AuthConfig(BaseConfig, DictConversionMixin):
137
142
  )
138
143
 
139
144
  @classmethod
140
- def from_local_yaml(cls, config_path: str | None = None) -> "AuthConfig":
145
+ def from_local_config(cls, config_path: str | Path | None = None) -> "AuthConfig":
141
146
  """
142
- Load configuration from YAML file
147
+ Load configuration from either a YAML or JSON file based on file extension.
148
+
149
+ Automatically detects file type (YAML or JSON) from the file extension
150
+ and uses the appropriate parser. If no path is provided, uses the default
151
+ configuration path (YAML) or its JSON counterpart.
143
152
 
144
153
  Args:
145
- config_path: Path to YAML configuration file
154
+ config_path: Optional path to configuration file.
155
+ If not provided, uses default configuration path.
146
156
 
147
157
  Returns:
148
- AuthConfig instance
158
+ AuthConfig instance populated with data from the configuration file.
149
159
 
150
160
  Raises:
151
- FileNotFoundError: If config file doesn't exist
152
- ValueError: If YAML parsing or validation fails
161
+ FileNotFoundError: If the specified or default configuration file does not exist.
162
+ ValueError: If file extension is not .yaml/.yml or .json, or if parsing fails.
153
163
  """
154
-
164
+ # Determine config path
155
165
  if config_path is None:
156
166
  config_path = cls.default_config_path
157
167
 
158
- # Check file exists
159
- if not Path(config_path).exists():
160
- raise FileNotFoundError(f"Config file not found: {config_path}")
161
-
162
- return cls.from_yaml_file(yaml_path=config_path)
168
+ # Validate file existence
169
+ config_path_obj = Path(config_path)
170
+ if not config_path_obj.exists():
171
+ raise FileNotFoundError(f"Configuration file not found: {config_path}")
172
+
173
+ # Get file extension and determine parser
174
+ file_ext = config_path_obj.suffix.lower()
175
+
176
+ if file_ext in (".yaml", ".yml"):
177
+ return cls.from_yaml_file(yaml_path=str(config_path_obj))
178
+ elif file_ext == ".json":
179
+ return cls.from_json_file(json_path=str(config_path_obj))
180
+ else:
181
+ raise ValueError(
182
+ f"Unsupported file format: {file_ext}. "
183
+ "Please use YAML (.yaml, .yml) or JSON (.json) files."
184
+ )
163
185
 
164
186
  def set_openai_config_to_environment(self):
165
187
  # Set environment variables
memos/configs/memory.py CHANGED
@@ -7,6 +7,7 @@ from memos.configs.embedder import EmbedderConfigFactory
7
7
  from memos.configs.graph_db import GraphDBConfigFactory
8
8
  from memos.configs.internet_retriever import InternetRetrieverConfigFactory
9
9
  from memos.configs.llm import LLMConfigFactory
10
+ from memos.configs.reranker import RerankerConfigFactory
10
11
  from memos.configs.vec_db import VectorDBConfigFactory
11
12
  from memos.exceptions import ConfigurationError
12
13
 
@@ -151,6 +152,10 @@ class TreeTextMemoryConfig(BaseTextMemoryConfig):
151
152
  default_factory=EmbedderConfigFactory,
152
153
  description="Embedder configuration for the memory embedding",
153
154
  )
155
+ reranker: RerankerConfigFactory | None = Field(
156
+ None,
157
+ description="Reranker configuration (optional, defaults to cosine_local).",
158
+ )
154
159
  graph_db: GraphDBConfigFactory = Field(
155
160
  ...,
156
161
  default_factory=GraphDBConfigFactory,
@@ -166,6 +171,14 @@ class TreeTextMemoryConfig(BaseTextMemoryConfig):
166
171
  description="Optional description for this memory configuration.",
167
172
  )
168
173
 
174
+ memory_size: dict[str, Any] | None = Field(
175
+ default=None,
176
+ description=(
177
+ "Maximum item counts per memory bucket, e.g.: "
178
+ '{"WorkingMemory": 20, "LongTermMemory": 10000, "UserMemory": 10000}'
179
+ ),
180
+ )
181
+
169
182
 
170
183
  # ─── 3. Global Memory Config Factory ──────────────────────────────────────────
171
184
 
@@ -0,0 +1,18 @@
1
+ # memos/configs/reranker.py
2
+ from __future__ import annotations
3
+
4
+ from typing import Any
5
+
6
+ from pydantic import BaseModel, Field
7
+
8
+
9
+ class RerankerConfigFactory(BaseModel):
10
+ """
11
+ {
12
+ "backend": "http_bge" | "cosine_local" | "noop",
13
+ "config": { ... backend-specific ... }
14
+ }
15
+ """
16
+
17
+ backend: str = Field(..., description="Reranker backend id")
18
+ config: dict[str, Any] = Field(default_factory=dict, description="Backend-specific options")
memos/graph_dbs/base.py CHANGED
@@ -70,15 +70,29 @@ class BaseGraphDB(ABC):
70
70
 
71
71
  # Graph Query & Reasoning
72
72
  @abstractmethod
73
- def get_node(self, id: str) -> dict[str, Any] | None:
73
+ def get_node(self, id: str, include_embedding: bool = False) -> dict[str, Any] | None:
74
74
  """
75
75
  Retrieve the metadata and content of a node.
76
76
  Args:
77
77
  id: Node identifier.
78
+ include_embedding: with/without embedding
78
79
  Returns:
79
80
  Dictionary of node fields, or None if not found.
80
81
  """
81
82
 
83
+ @abstractmethod
84
+ def get_nodes(
85
+ self, id: str, include_embedding: bool = False, **kwargs
86
+ ) -> dict[str, Any] | None:
87
+ """
88
+ Retrieve the metadata and memory of a list of nodes.
89
+ Args:
90
+ ids: List of Node identifier.
91
+ include_embedding: with/without embedding
92
+ Returns:
93
+ list[dict]: Parsed node records containing 'id', 'memory', and 'metadata'.
94
+ """
95
+
82
96
  @abstractmethod
83
97
  def get_neighbors(
84
98
  self, id: str, type: str, direction: Literal["in", "out", "both"] = "out"
@@ -129,7 +143,7 @@ class BaseGraphDB(ABC):
129
143
 
130
144
  # Search / recall operations
131
145
  @abstractmethod
132
- 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]:
133
147
  """
134
148
  Retrieve node IDs based on vector similarity.
135
149
 
@@ -163,7 +177,9 @@ class BaseGraphDB(ABC):
163
177
  """
164
178
 
165
179
  @abstractmethod
166
- def get_structure_optimization_candidates(self, scope: str) -> list[dict]:
180
+ def get_structure_optimization_candidates(
181
+ self, scope: str, include_embedding: bool = False
182
+ ) -> list[dict]:
167
183
  """
168
184
  Find nodes that are likely candidates for structure optimization:
169
185
  - Isolated nodes, nodes with empty background, or nodes with exactly one child.
@@ -205,7 +221,7 @@ class BaseGraphDB(ABC):
205
221
  """
206
222
 
207
223
  @abstractmethod
208
- def export_graph(self) -> dict[str, Any]:
224
+ def export_graph(self, include_embedding: bool = False) -> dict[str, Any]:
209
225
  """
210
226
  Export the entire graph as a serializable dictionary.
211
227
 
@@ -221,3 +237,16 @@ class BaseGraphDB(ABC):
221
237
  Args:
222
238
  data: A dictionary containing all nodes and edges to be loaded.
223
239
  """
240
+
241
+ @abstractmethod
242
+ def get_all_memory_items(self, scope: str, include_embedding: bool = False) -> list[dict]:
243
+ """
244
+ Retrieve all memory items of a specific memory_type.
245
+
246
+ Args:
247
+ scope (str): Must be one of 'WorkingMemory', 'LongTermMemory', or 'UserMemory'.
248
+ include_embedding: with/without embedding
249
+
250
+ Returns:
251
+ list[dict]: Full list of memory items under this scope.
252
+ """