MemoryOS 0.2.1__py3-none-any.whl → 1.0.0__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 (92) hide show
  1. {memoryos-0.2.1.dist-info → memoryos-1.0.0.dist-info}/METADATA +7 -1
  2. {memoryos-0.2.1.dist-info → memoryos-1.0.0.dist-info}/RECORD +87 -64
  3. memos/__init__.py +1 -1
  4. memos/api/config.py +158 -69
  5. memos/api/context/context.py +147 -0
  6. memos/api/context/dependencies.py +101 -0
  7. memos/api/product_models.py +5 -1
  8. memos/api/routers/product_router.py +54 -26
  9. memos/configs/graph_db.py +49 -1
  10. memos/configs/internet_retriever.py +19 -0
  11. memos/configs/mem_os.py +5 -0
  12. memos/configs/mem_reader.py +9 -0
  13. memos/configs/mem_scheduler.py +54 -18
  14. memos/configs/mem_user.py +58 -0
  15. memos/graph_dbs/base.py +38 -3
  16. memos/graph_dbs/factory.py +2 -0
  17. memos/graph_dbs/nebular.py +1612 -0
  18. memos/graph_dbs/neo4j.py +18 -9
  19. memos/log.py +6 -1
  20. memos/mem_cube/utils.py +13 -6
  21. memos/mem_os/core.py +157 -37
  22. memos/mem_os/main.py +2 -2
  23. memos/mem_os/product.py +252 -201
  24. memos/mem_os/utils/default_config.py +1 -1
  25. memos/mem_os/utils/format_utils.py +281 -70
  26. memos/mem_os/utils/reference_utils.py +133 -0
  27. memos/mem_reader/simple_struct.py +13 -5
  28. memos/mem_scheduler/base_scheduler.py +239 -266
  29. memos/mem_scheduler/{modules → general_modules}/base.py +4 -5
  30. memos/mem_scheduler/{modules → general_modules}/dispatcher.py +57 -21
  31. memos/mem_scheduler/general_modules/misc.py +104 -0
  32. memos/mem_scheduler/{modules → general_modules}/rabbitmq_service.py +12 -10
  33. memos/mem_scheduler/{modules → general_modules}/redis_service.py +1 -1
  34. memos/mem_scheduler/general_modules/retriever.py +199 -0
  35. memos/mem_scheduler/general_modules/scheduler_logger.py +261 -0
  36. memos/mem_scheduler/general_scheduler.py +243 -80
  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} +106 -57
  40. memos/mem_scheduler/mos_for_test_scheduler.py +23 -20
  41. memos/mem_scheduler/schemas/__init__.py +0 -0
  42. memos/mem_scheduler/schemas/general_schemas.py +44 -0
  43. memos/mem_scheduler/schemas/message_schemas.py +149 -0
  44. memos/mem_scheduler/schemas/monitor_schemas.py +337 -0
  45. memos/mem_scheduler/utils/__init__.py +0 -0
  46. memos/mem_scheduler/utils/filter_utils.py +176 -0
  47. memos/mem_scheduler/utils/misc_utils.py +102 -0
  48. memos/mem_user/factory.py +94 -0
  49. memos/mem_user/mysql_persistent_user_manager.py +271 -0
  50. memos/mem_user/mysql_user_manager.py +500 -0
  51. memos/mem_user/persistent_factory.py +96 -0
  52. memos/mem_user/user_manager.py +4 -4
  53. memos/memories/activation/item.py +5 -1
  54. memos/memories/activation/kv.py +20 -8
  55. memos/memories/textual/base.py +2 -2
  56. memos/memories/textual/general.py +36 -92
  57. memos/memories/textual/item.py +5 -33
  58. memos/memories/textual/tree.py +13 -7
  59. memos/memories/textual/tree_text_memory/organize/{conflict.py → handler.py} +34 -50
  60. memos/memories/textual/tree_text_memory/organize/manager.py +8 -96
  61. memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py +49 -43
  62. memos/memories/textual/tree_text_memory/organize/reorganizer.py +107 -142
  63. memos/memories/textual/tree_text_memory/retrieve/bochasearch.py +229 -0
  64. memos/memories/textual/tree_text_memory/retrieve/internet_retriever.py +6 -3
  65. memos/memories/textual/tree_text_memory/retrieve/internet_retriever_factory.py +11 -0
  66. memos/memories/textual/tree_text_memory/retrieve/recall.py +15 -8
  67. memos/memories/textual/tree_text_memory/retrieve/reranker.py +1 -1
  68. memos/memories/textual/tree_text_memory/retrieve/retrieval_mid_structs.py +2 -0
  69. memos/memories/textual/tree_text_memory/retrieve/searcher.py +191 -116
  70. memos/memories/textual/tree_text_memory/retrieve/task_goal_parser.py +47 -15
  71. memos/memories/textual/tree_text_memory/retrieve/utils.py +11 -7
  72. memos/memories/textual/tree_text_memory/retrieve/xinyusearch.py +62 -58
  73. memos/memos_tools/dinding_report_bot.py +422 -0
  74. memos/memos_tools/lockfree_dict.py +120 -0
  75. memos/memos_tools/notification_service.py +44 -0
  76. memos/memos_tools/notification_utils.py +96 -0
  77. memos/memos_tools/thread_safe_dict.py +288 -0
  78. memos/settings.py +3 -1
  79. memos/templates/mem_reader_prompts.py +4 -1
  80. memos/templates/mem_scheduler_prompts.py +62 -15
  81. memos/templates/mos_prompts.py +116 -0
  82. memos/templates/tree_reorganize_prompts.py +24 -17
  83. memos/utils.py +19 -0
  84. memos/mem_scheduler/modules/misc.py +0 -39
  85. memos/mem_scheduler/modules/retriever.py +0 -268
  86. memos/mem_scheduler/modules/schemas.py +0 -328
  87. memos/mem_scheduler/utils.py +0 -75
  88. memos/memories/textual/tree_text_memory/organize/redundancy.py +0 -193
  89. {memoryos-0.2.1.dist-info → memoryos-1.0.0.dist-info}/LICENSE +0 -0
  90. {memoryos-0.2.1.dist-info → memoryos-1.0.0.dist-info}/WHEEL +0 -0
  91. {memoryos-0.2.1.dist-info → memoryos-1.0.0.dist-info}/entry_points.txt +0 -0
  92. /memos/mem_scheduler/{modules → general_modules}/__init__.py +0 -0
memos/graph_dbs/neo4j.py CHANGED
@@ -114,14 +114,14 @@ class Neo4jGraphDB(BaseGraphDB):
114
114
  )
115
115
  return result.single()["count"]
116
116
 
117
- def count_nodes(self, scope: str) -> int:
117
+ def node_not_exist(self, scope: str) -> int:
118
118
  query = """
119
119
  MATCH (n:Memory)
120
120
  WHERE n.memory_type = $scope
121
121
  """
122
122
  if not self.config.use_multi_db and self.config.user_name:
123
123
  query += "\nAND n.user_name = $user_name"
124
- query += "\nRETURN count(n) AS count"
124
+ query += "\nRETURN n LIMIT 1"
125
125
 
126
126
  with self.driver.session(database=self.db_name) as session:
127
127
  result = session.run(
@@ -131,7 +131,7 @@ class Neo4jGraphDB(BaseGraphDB):
131
131
  "user_name": self.config.user_name if self.config.user_name else None,
132
132
  },
133
133
  )
134
- return result.single()["count"]
134
+ return result.single() is None
135
135
 
136
136
  def remove_oldest_memory(self, memory_type: str, keep_latest: int) -> None:
137
137
  """
@@ -323,14 +323,16 @@ class Neo4jGraphDB(BaseGraphDB):
323
323
  return result.single() is not None
324
324
 
325
325
  # Graph Query & Reasoning
326
- def get_node(self, id: str) -> dict[str, Any] | None:
326
+ def get_node(self, id: str, include_embedding: bool = True) -> dict[str, Any] | None:
327
327
  """
328
328
  Retrieve the metadata and memory of a node.
329
329
  Args:
330
330
  id: Node identifier.
331
+ include_embedding (bool): Whether to include the large embedding field.
331
332
  Returns:
332
333
  Dictionary of node fields, or None if not found.
333
334
  """
335
+
334
336
  where_user = ""
335
337
  params = {"id": id}
336
338
  if not self.config.use_multi_db and self.config.user_name:
@@ -343,11 +345,12 @@ class Neo4jGraphDB(BaseGraphDB):
343
345
  record = session.run(query, params).single()
344
346
  return self._parse_node(dict(record["n"])) if record else None
345
347
 
346
- def get_nodes(self, ids: list[str]) -> list[dict[str, Any]]:
348
+ def get_nodes(self, ids: list[str], include_embedding: bool = True) -> list[dict[str, Any]]:
347
349
  """
348
350
  Retrieve the metadata and memory of a list of nodes.
349
351
  Args:
350
352
  ids: List of Node identifier.
353
+ include_embedding (bool): Whether to include the large embedding field.
351
354
  Returns:
352
355
  list[dict]: Parsed node records containing 'id', 'memory', and 'metadata'.
353
356
 
@@ -355,6 +358,7 @@ class Neo4jGraphDB(BaseGraphDB):
355
358
  - Assumes all provided IDs are valid and exist.
356
359
  - Returns empty list if input is empty.
357
360
  """
361
+
358
362
  if not ids:
359
363
  return []
360
364
 
@@ -829,7 +833,7 @@ class Neo4jGraphDB(BaseGraphDB):
829
833
  logger.error(f"[ERROR] Failed to clear database '{self.db_name}': {e}")
830
834
  raise
831
835
 
832
- def export_graph(self) -> dict[str, Any]:
836
+ def export_graph(self, include_embedding: bool = True) -> dict[str, Any]:
833
837
  """
834
838
  Export all graph nodes and edges in a structured form.
835
839
 
@@ -910,17 +914,19 @@ class Neo4jGraphDB(BaseGraphDB):
910
914
  target_id=edge["target"],
911
915
  )
912
916
 
913
- def get_all_memory_items(self, scope: str) -> list[dict]:
917
+ def get_all_memory_items(self, scope: str, include_embedding: bool = True) -> list[dict]:
914
918
  """
915
919
  Retrieve all memory items of a specific memory_type.
916
920
 
917
921
  Args:
918
922
  scope (str): Must be one of 'WorkingMemory', 'LongTermMemory', or 'UserMemory'.
923
+ include_embedding (bool): Whether to include the large embedding field.
924
+ Returns:
919
925
 
920
926
  Returns:
921
927
  list[dict]: Full list of memory items under this scope.
922
928
  """
923
- if scope not in {"WorkingMemory", "LongTermMemory", "UserMemory"}:
929
+ if scope not in {"WorkingMemory", "LongTermMemory", "UserMemory", "OuterMemory"}:
924
930
  raise ValueError(f"Unsupported memory type scope: {scope}")
925
931
 
926
932
  where_clause = "WHERE n.memory_type = $scope"
@@ -940,12 +946,15 @@ class Neo4jGraphDB(BaseGraphDB):
940
946
  results = session.run(query, params)
941
947
  return [self._parse_node(dict(record["n"])) for record in results]
942
948
 
943
- def get_structure_optimization_candidates(self, scope: str) -> list[dict]:
949
+ def get_structure_optimization_candidates(
950
+ self, scope: str, include_embedding: bool = True
951
+ ) -> list[dict]:
944
952
  """
945
953
  Find nodes that are likely candidates for structure optimization:
946
954
  - Isolated nodes, nodes with empty background, or nodes with exactly one child.
947
955
  - Plus: the child of any parent node that has exactly one child.
948
956
  """
957
+
949
958
  where_clause = """
950
959
  WHERE n.memory_type = $scope
951
960
  AND n.status = 'activated'
memos/log.py CHANGED
@@ -4,9 +4,14 @@ from logging.config import dictConfig
4
4
  from pathlib import Path
5
5
  from sys import stdout
6
6
 
7
+ from dotenv import load_dotenv
8
+
7
9
  from memos import settings
8
10
 
9
11
 
12
+ # Load environment variables
13
+ load_dotenv()
14
+
10
15
  selected_log_level = logging.DEBUG if settings.DEBUG else logging.WARNING
11
16
 
12
17
 
@@ -48,7 +53,7 @@ LOGGING_CONFIG = {
48
53
  "class": "logging.handlers.RotatingFileHandler",
49
54
  "filename": _setup_logfile(),
50
55
  "maxBytes": 1024**2 * 10,
51
- "backupCount": 3,
56
+ "backupCount": 10,
52
57
  "formatter": "standard",
53
58
  },
54
59
  },
memos/mem_cube/utils.py CHANGED
@@ -71,10 +71,6 @@ def merge_config_with_default(
71
71
 
72
72
  # Define graph_db fields to preserve (user-specific)
73
73
  preserve_graph_fields = {
74
- "uri",
75
- "user",
76
- "password",
77
- "db_name",
78
74
  "auto_create",
79
75
  "user_name",
80
76
  "use_multi_db",
@@ -96,9 +92,20 @@ def merge_config_with_default(
96
92
  merged_graph_config["db_name"] = default_graph_config.get("db_name")
97
93
  else:
98
94
  logger.info("use_multi_db is already False, no need to change")
99
-
95
+ if "neo4j" not in default_text_config["graph_db"]["backend"]:
96
+ if "db_name" in merged_graph_config:
97
+ merged_graph_config.pop("db_name")
98
+ logger.info("neo4j is not supported, remove db_name")
99
+ else:
100
+ logger.info("db_name is not in merged_graph_config, no need to remove")
101
+ else:
102
+ if "space" in merged_graph_config:
103
+ merged_graph_config.pop("space")
104
+ logger.info("neo4j is not supported, remove db_name")
105
+ else:
106
+ logger.info("space is not in merged_graph_config, no need to remove")
100
107
  preserved_graph_db = {
101
- "backend": existing_text_config["graph_db"]["backend"],
108
+ "backend": default_text_config["graph_db"]["backend"],
102
109
  "config": merged_graph_config,
103
110
  }
104
111
 
memos/mem_os/core.py CHANGED
@@ -1,6 +1,6 @@
1
1
  import json
2
2
  import os
3
- import uuid
3
+ import time
4
4
 
5
5
  from datetime import datetime
6
6
  from pathlib import Path
@@ -13,16 +13,19 @@ from memos.log import get_logger
13
13
  from memos.mem_cube.general import GeneralMemCube
14
14
  from memos.mem_reader.factory import MemReaderFactory
15
15
  from memos.mem_scheduler.general_scheduler import GeneralScheduler
16
- from memos.mem_scheduler.modules.schemas import (
16
+ from memos.mem_scheduler.scheduler_factory import SchedulerFactory
17
+ from memos.mem_scheduler.schemas.general_schemas import (
17
18
  ADD_LABEL,
18
19
  ANSWER_LABEL,
19
- ScheduleMessageItem,
20
+ QUERY_LABEL,
20
21
  )
21
- from memos.mem_scheduler.scheduler_factory import SchedulerFactory
22
+ from memos.mem_scheduler.schemas.message_schemas import ScheduleMessageItem
22
23
  from memos.mem_user.user_manager import UserManager, UserRole
23
24
  from memos.memories.activation.item import ActivationMemoryItem
24
25
  from memos.memories.parametric.item import ParametricMemoryItem
25
26
  from memos.memories.textual.item import TextualMemoryItem, TextualMemoryMetadata
27
+ from memos.memos_tools.thread_safe_dict import ThreadSafeDict
28
+ from memos.templates.mos_prompts import QUERY_REWRITING_PROMPT
26
29
  from memos.types import ChatHistory, MessageList, MOSSearchResult
27
30
 
28
31
 
@@ -40,10 +43,13 @@ class MOSCore:
40
43
  self.config = config
41
44
  self.user_id = config.user_id
42
45
  self.session_id = config.session_id
43
- self.mem_cubes: dict[str, GeneralMemCube] = {}
44
46
  self.chat_llm = LLMFactory.from_config(config.chat_model)
45
47
  self.mem_reader = MemReaderFactory.from_config(config.mem_reader)
46
48
  self.chat_history_manager: dict[str, ChatHistory] = {}
49
+ # use thread safe dict for multi-user product-server scenario
50
+ self.mem_cubes: ThreadSafeDict[str, GeneralMemCube] = (
51
+ ThreadSafeDict() if user_manager is not None else {}
52
+ )
47
53
  self._register_chat_history()
48
54
 
49
55
  # Use provided user_manager or create a new one
@@ -58,10 +64,15 @@ class MOSCore:
58
64
  f"User '{self.user_id}' does not exist or is inactive. Please create user first."
59
65
  )
60
66
 
61
- # Lazy initialization marker
67
+ # Initialize mem_scheduler
62
68
  self._mem_scheduler_lock = Lock()
63
69
  self.enable_mem_scheduler = self.config.get("enable_mem_scheduler", False)
64
- self._mem_scheduler: GeneralScheduler = None
70
+ if self.enable_mem_scheduler:
71
+ self._mem_scheduler = self._initialize_mem_scheduler()
72
+ self._mem_scheduler.mem_cubes = self.mem_cubes
73
+ else:
74
+ self._mem_scheduler: GeneralScheduler = None
75
+
65
76
  logger.info(f"MOS initialized for user: {self.user_id}")
66
77
 
67
78
  @property
@@ -93,14 +104,16 @@ class MOSCore:
93
104
  else:
94
105
  logger.debug("Memory scheduler cleared")
95
106
 
96
- def _initialize_mem_scheduler(self):
107
+ def _initialize_mem_scheduler(self) -> GeneralScheduler:
97
108
  """Initialize the memory scheduler on first access."""
98
109
  if not self.config.enable_mem_scheduler:
99
110
  logger.debug("Memory scheduler is disabled in config")
100
111
  self._mem_scheduler = None
112
+ return self._mem_scheduler
101
113
  elif not hasattr(self.config, "mem_scheduler"):
102
114
  logger.error("Config of Memory scheduler is not available")
103
115
  self._mem_scheduler = None
116
+ return self._mem_scheduler
104
117
  else:
105
118
  logger.info("Initializing memory scheduler...")
106
119
  scheduler_config = self.config.mem_scheduler
@@ -111,13 +124,16 @@ class MOSCore:
111
124
  f"Memory reader of type {type(self.mem_reader).__name__} "
112
125
  "missing required 'llm' attribute"
113
126
  )
114
- self._mem_scheduler.initialize_modules(chat_llm=self.chat_llm)
127
+ self._mem_scheduler.initialize_modules(
128
+ chat_llm=self.chat_llm, process_llm=self.chat_llm
129
+ )
115
130
  else:
116
- # Configure scheduler modules
131
+ # Configure scheduler general_modules
117
132
  self._mem_scheduler.initialize_modules(
118
133
  chat_llm=self.chat_llm, process_llm=self.mem_reader.llm
119
134
  )
120
135
  self._mem_scheduler.start()
136
+ return self._mem_scheduler
121
137
 
122
138
  def mem_scheduler_on(self) -> bool:
123
139
  if not self.config.enable_mem_scheduler or self._mem_scheduler is None:
@@ -157,6 +173,14 @@ class MOSCore:
157
173
  if mem_cube.text_mem and mem_cube.text_mem.is_reorganize:
158
174
  logger.info(f"close reorganizer for {mem_cube.text_mem.config.cube_id}")
159
175
  mem_cube.text_mem.memory_manager.close()
176
+ mem_cube.text_mem.memory_manager.wait_reorganizer()
177
+
178
+ def mem_reorganizer_wait(self) -> bool:
179
+ for mem_cube in self.mem_cubes.values():
180
+ logger.info(f"try to close reorganizer for {mem_cube.text_mem.config.cube_id}")
181
+ if mem_cube.text_mem and mem_cube.text_mem.is_reorganize:
182
+ logger.info(f"close reorganizer for {mem_cube.text_mem.config.cube_id}")
183
+ mem_cube.text_mem.memory_manager.wait_reorganizer()
160
184
 
161
185
  def _register_chat_history(self, user_id: str | None = None) -> None:
162
186
  """Initialize chat history with user ID."""
@@ -165,7 +189,7 @@ class MOSCore:
165
189
  self.chat_history_manager[user_id] = ChatHistory(
166
190
  user_id=user_id,
167
191
  session_id=self.session_id,
168
- created_at=datetime.now(),
192
+ created_at=datetime.utcnow(),
169
193
  total_messages=0,
170
194
  chat_history=[],
171
195
  )
@@ -257,13 +281,21 @@ class MOSCore:
257
281
  user_id=target_user_id,
258
282
  mem_cube_id=mem_cube_id,
259
283
  mem_cube=mem_cube,
260
- label=ADD_LABEL,
284
+ label=QUERY_LABEL,
261
285
  content=query,
262
- timestamp=datetime.now(),
286
+ timestamp=datetime.utcnow(),
263
287
  )
264
288
  self.mem_scheduler.submit_messages(messages=[message_item])
265
289
 
266
- memories = mem_cube.text_mem.search(query, top_k=self.config.top_k)
290
+ memories = mem_cube.text_mem.search(
291
+ query,
292
+ top_k=self.config.top_k,
293
+ info={
294
+ "user_id": target_user_id,
295
+ "session_id": self.session_id,
296
+ "chat_history": chat_history.chat_history,
297
+ },
298
+ )
267
299
  memories_all.extend(memories)
268
300
  logger.info(f"🧠 [Memory] Searched memories:\n{self._str_memories(memories_all)}\n")
269
301
  system_prompt = self._build_system_prompt(memories_all, base_prompt=base_prompt)
@@ -310,7 +342,7 @@ class MOSCore:
310
342
  mem_cube=mem_cube,
311
343
  label=ANSWER_LABEL,
312
344
  content=response,
313
- timestamp=datetime.now(),
345
+ timestamp=datetime.utcnow(),
314
346
  )
315
347
  self.mem_scheduler.submit_messages(messages=[message_item])
316
348
 
@@ -511,6 +543,8 @@ class MOSCore:
511
543
  user_id: str | None = None,
512
544
  install_cube_ids: list[str] | None = None,
513
545
  top_k: int | None = None,
546
+ mode: Literal["fast", "fine"] = "fast",
547
+ internet_search: bool = False,
514
548
  ) -> MOSSearchResult:
515
549
  """
516
550
  Search for textual memories across all registered MemCubes.
@@ -534,6 +568,10 @@ class MOSCore:
534
568
  logger.info(
535
569
  f"User {target_user_id} has access to {len(user_cube_ids)} cubes: {user_cube_ids}"
536
570
  )
571
+ if target_user_id not in self.chat_history_manager:
572
+ self._register_chat_history(target_user_id)
573
+ chat_history = self.chat_history_manager[target_user_id]
574
+
537
575
  result: MOSSearchResult = {
538
576
  "text_mem": [],
539
577
  "act_mem": [],
@@ -541,19 +579,38 @@ class MOSCore:
541
579
  }
542
580
  if install_cube_ids is None:
543
581
  install_cube_ids = user_cube_ids
544
- for mem_cube_id, mem_cube in self.mem_cubes.items():
582
+ # create exist dict in mem_cubes and avoid one search slow
583
+ tmp_mem_cubes = {}
584
+ for mem_cube_id in install_cube_ids:
585
+ if mem_cube_id in self.mem_cubes:
586
+ tmp_mem_cubes[mem_cube_id] = self.mem_cubes.get(mem_cube_id)
587
+
588
+ for mem_cube_id, mem_cube in tmp_mem_cubes.items():
545
589
  if (
546
590
  (mem_cube_id in install_cube_ids)
547
591
  and (mem_cube.text_mem is not None)
548
592
  and self.config.enable_textual_memory
549
593
  ):
594
+ time_start = time.time()
550
595
  memories = mem_cube.text_mem.search(
551
- query, top_k=top_k if top_k else self.config.top_k
596
+ query,
597
+ top_k=top_k if top_k else self.config.top_k,
598
+ mode=mode,
599
+ manual_close_internet=not internet_search,
600
+ info={
601
+ "user_id": target_user_id,
602
+ "session_id": self.session_id,
603
+ "chat_history": chat_history.chat_history,
604
+ },
552
605
  )
553
606
  result["text_mem"].append({"cube_id": mem_cube_id, "memories": memories})
554
607
  logger.info(
555
608
  f"🧠 [Memory] Searched memories from {mem_cube_id}:\n{self._str_memories(memories)}\n"
556
609
  )
610
+ search_time_end = time.time()
611
+ logger.info(
612
+ f"time search graph: search graph time user_id: {target_user_id} time is: {search_time_end - time_start}"
613
+ )
557
614
  return result
558
615
 
559
616
  def add(
@@ -576,6 +633,7 @@ class MOSCore:
576
633
  user_id (str, optional): The identifier of the user to add the memories to.
577
634
  If None, the default user is used.
578
635
  """
636
+ # user input messages
579
637
  assert (messages is not None) or (memory_content is not None) or (doc_path is not None), (
580
638
  "messages_or_doc_path or memory_content or doc_path must be provided."
581
639
  )
@@ -613,25 +671,31 @@ class MOSCore:
613
671
  memories = self.mem_reader.get_memory(
614
672
  messages_list,
615
673
  type="chat",
616
- info={"user_id": target_user_id, "session_id": str(uuid.uuid4())},
674
+ info={"user_id": target_user_id, "session_id": self.session_id},
617
675
  )
676
+
677
+ mem_ids = []
618
678
  for mem in memories:
619
- self.mem_cubes[mem_cube_id].text_mem.add(mem)
679
+ mem_id_list: list[str] = self.mem_cubes[mem_cube_id].text_mem.add(mem)
680
+ mem_ids.extend(mem_id_list)
681
+ logger.info(
682
+ f"Added memory user {target_user_id} to memcube {mem_cube_id}: {mem_id_list}"
683
+ )
620
684
 
621
685
  # submit messages for scheduler
622
- mem_cube = self.mem_cubes[mem_cube_id]
623
686
  if self.enable_mem_scheduler and self.mem_scheduler is not None:
624
- text_messages = [message["content"] for message in messages]
687
+ mem_cube = self.mem_cubes[mem_cube_id]
625
688
  message_item = ScheduleMessageItem(
626
689
  user_id=target_user_id,
627
690
  mem_cube_id=mem_cube_id,
628
691
  mem_cube=mem_cube,
629
692
  label=ADD_LABEL,
630
- content=json.dumps(text_messages),
631
- timestamp=datetime.now(),
693
+ content=json.dumps(mem_ids),
694
+ timestamp=datetime.utcnow(),
632
695
  )
633
696
  self.mem_scheduler.submit_messages(messages=[message_item])
634
697
 
698
+ # user profile
635
699
  if (
636
700
  (memory_content is not None)
637
701
  and self.config.enable_textual_memory
@@ -646,34 +710,66 @@ class MOSCore:
646
710
  )
647
711
  else:
648
712
  messages_list = [
649
- [
650
- {"role": "user", "content": memory_content},
651
- {
652
- "role": "assistant",
653
- "content": "",
654
- }, # add by str to keep the format,assistant role is empty
655
- ]
656
- ]
713
+ [{"role": "user", "content": memory_content}]
714
+ ] # for only user-str input and convert message
657
715
  memories = self.mem_reader.get_memory(
658
716
  messages_list,
659
717
  type="chat",
660
- info={"user_id": target_user_id, "session_id": str(uuid.uuid4())},
718
+ info={"user_id": target_user_id, "session_id": self.session_id},
661
719
  )
720
+
721
+ mem_ids = []
662
722
  for mem in memories:
663
- self.mem_cubes[mem_cube_id].text_mem.add(mem)
723
+ mem_id_list: list[str] = self.mem_cubes[mem_cube_id].text_mem.add(mem)
724
+ logger.info(
725
+ f"Added memory user {target_user_id} to memcube {mem_cube_id}: {mem_id_list}"
726
+ )
727
+ mem_ids.extend(mem_id_list)
728
+
729
+ # submit messages for scheduler
730
+ if self.enable_mem_scheduler and self.mem_scheduler is not None:
731
+ mem_cube = self.mem_cubes[mem_cube_id]
732
+ message_item = ScheduleMessageItem(
733
+ user_id=target_user_id,
734
+ mem_cube_id=mem_cube_id,
735
+ mem_cube=mem_cube,
736
+ label=ADD_LABEL,
737
+ content=json.dumps(mem_ids),
738
+ timestamp=datetime.utcnow(),
739
+ )
740
+ self.mem_scheduler.submit_messages(messages=[message_item])
741
+
742
+ # user doc input
664
743
  if (
665
744
  (doc_path is not None)
666
745
  and self.config.enable_textual_memory
667
746
  and self.mem_cubes[mem_cube_id].text_mem
668
747
  ):
669
748
  documents = self._get_all_documents(doc_path)
670
- doc_memory = self.mem_reader.get_memory(
749
+ doc_memories = self.mem_reader.get_memory(
671
750
  documents,
672
751
  type="doc",
673
- info={"user_id": target_user_id, "session_id": str(uuid.uuid4())},
752
+ info={"user_id": target_user_id, "session_id": self.session_id},
674
753
  )
675
- for mem in doc_memory:
676
- self.mem_cubes[mem_cube_id].text_mem.add(mem)
754
+
755
+ mem_ids = []
756
+ for mem in doc_memories:
757
+ mem_id_list: list[str] = self.mem_cubes[mem_cube_id].text_mem.add(mem)
758
+ mem_ids.extend(mem_id_list)
759
+
760
+ # submit messages for scheduler
761
+ if self.enable_mem_scheduler and self.mem_scheduler is not None:
762
+ mem_cube = self.mem_cubes[mem_cube_id]
763
+ message_item = ScheduleMessageItem(
764
+ user_id=target_user_id,
765
+ mem_cube_id=mem_cube_id,
766
+ mem_cube=mem_cube,
767
+ label=ADD_LABEL,
768
+ content=json.dumps(mem_ids),
769
+ timestamp=datetime.utcnow(),
770
+ )
771
+ self.mem_scheduler.submit_messages(messages=[message_item])
772
+
677
773
  logger.info(f"Add memory to {mem_cube_id} successfully")
678
774
 
679
775
  def get(
@@ -907,3 +1003,27 @@ class MOSCore:
907
1003
  raise ValueError(f"Target user '{target_user_id}' does not exist or is inactive.")
908
1004
 
909
1005
  return self.user_manager.add_user_to_cube(target_user_id, cube_id)
1006
+
1007
+ def get_query_rewrite(self, query: str, user_id: str | None = None):
1008
+ """
1009
+ Rewrite user's query according the context.
1010
+ Args:
1011
+ query (str): The search query that needs rewriting.
1012
+ user_id(str, optional): The identifier of the user that the query belongs to.
1013
+ If None, the default user is used.
1014
+
1015
+ Returns:
1016
+ str: query after rewriting process.
1017
+ """
1018
+ target_user_id = user_id if user_id is not None else self.user_id
1019
+ chat_history = self.chat_history_manager[target_user_id]
1020
+
1021
+ dialogue = "————{}".format("\n————".join(chat_history.chat_history))
1022
+ user_prompt = QUERY_REWRITING_PROMPT.format(dialogue=dialogue, query=query)
1023
+ messages = {"role": "user", "content": user_prompt}
1024
+ rewritten_result = self.chat_llm.generate(messages=messages)
1025
+ rewritten_result = json.loads(rewritten_result)
1026
+ if rewritten_result.get("former_dialogue_related", False):
1027
+ rewritten_query = rewritten_result.get("rewritten_question")
1028
+ return rewritten_query if len(rewritten_query) > 0 else query
1029
+ return query
memos/mem_os/main.py CHANGED
@@ -208,7 +208,7 @@ class MOS(MOSCore):
208
208
  if self.enable_mem_scheduler and self.mem_scheduler is not None:
209
209
  from datetime import datetime
210
210
 
211
- from memos.mem_scheduler.modules.schemas import (
211
+ from memos.mem_scheduler.schemas import (
212
212
  ANSWER_LABEL,
213
213
  ScheduleMessageItem,
214
214
  )
@@ -219,7 +219,7 @@ class MOS(MOSCore):
219
219
  mem_cube=mem_cube,
220
220
  label=ANSWER_LABEL,
221
221
  content=enhanced_response,
222
- timestamp=datetime.now(),
222
+ timestamp=datetime.now().isoformat(),
223
223
  )
224
224
  self.mem_scheduler.submit_messages(messages=[message_item])
225
225