MemoryOS 0.2.0__py3-none-any.whl → 0.2.2__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 (114) hide show
  1. {memoryos-0.2.0.dist-info → memoryos-0.2.2.dist-info}/METADATA +67 -26
  2. memoryos-0.2.2.dist-info/RECORD +169 -0
  3. memoryos-0.2.2.dist-info/entry_points.txt +3 -0
  4. memos/__init__.py +1 -1
  5. memos/api/config.py +562 -0
  6. memos/api/context/context.py +147 -0
  7. memos/api/context/dependencies.py +90 -0
  8. memos/api/exceptions.py +28 -0
  9. memos/api/mcp_serve.py +502 -0
  10. memos/api/product_api.py +35 -0
  11. memos/api/product_models.py +163 -0
  12. memos/api/routers/__init__.py +1 -0
  13. memos/api/routers/product_router.py +386 -0
  14. memos/chunkers/sentence_chunker.py +8 -2
  15. memos/cli.py +113 -0
  16. memos/configs/embedder.py +27 -0
  17. memos/configs/graph_db.py +132 -3
  18. memos/configs/internet_retriever.py +6 -0
  19. memos/configs/llm.py +47 -0
  20. memos/configs/mem_cube.py +1 -1
  21. memos/configs/mem_os.py +5 -0
  22. memos/configs/mem_reader.py +9 -0
  23. memos/configs/mem_scheduler.py +107 -7
  24. memos/configs/mem_user.py +58 -0
  25. memos/configs/memory.py +5 -4
  26. memos/dependency.py +52 -0
  27. memos/embedders/ark.py +92 -0
  28. memos/embedders/factory.py +4 -0
  29. memos/embedders/sentence_transformer.py +8 -2
  30. memos/embedders/universal_api.py +32 -0
  31. memos/graph_dbs/base.py +11 -3
  32. memos/graph_dbs/factory.py +4 -0
  33. memos/graph_dbs/nebular.py +1364 -0
  34. memos/graph_dbs/neo4j.py +333 -124
  35. memos/graph_dbs/neo4j_community.py +300 -0
  36. memos/llms/base.py +9 -0
  37. memos/llms/deepseek.py +54 -0
  38. memos/llms/factory.py +10 -1
  39. memos/llms/hf.py +170 -13
  40. memos/llms/hf_singleton.py +114 -0
  41. memos/llms/ollama.py +4 -0
  42. memos/llms/openai.py +67 -1
  43. memos/llms/qwen.py +63 -0
  44. memos/llms/vllm.py +153 -0
  45. memos/log.py +1 -1
  46. memos/mem_cube/general.py +77 -16
  47. memos/mem_cube/utils.py +109 -0
  48. memos/mem_os/core.py +251 -51
  49. memos/mem_os/main.py +94 -12
  50. memos/mem_os/product.py +1220 -43
  51. memos/mem_os/utils/default_config.py +352 -0
  52. memos/mem_os/utils/format_utils.py +1401 -0
  53. memos/mem_reader/simple_struct.py +18 -10
  54. memos/mem_scheduler/base_scheduler.py +441 -40
  55. memos/mem_scheduler/general_scheduler.py +249 -248
  56. memos/mem_scheduler/modules/base.py +14 -5
  57. memos/mem_scheduler/modules/dispatcher.py +67 -4
  58. memos/mem_scheduler/modules/misc.py +104 -0
  59. memos/mem_scheduler/modules/monitor.py +240 -50
  60. memos/mem_scheduler/modules/rabbitmq_service.py +319 -0
  61. memos/mem_scheduler/modules/redis_service.py +32 -22
  62. memos/mem_scheduler/modules/retriever.py +167 -23
  63. memos/mem_scheduler/modules/scheduler_logger.py +255 -0
  64. memos/mem_scheduler/mos_for_test_scheduler.py +140 -0
  65. memos/mem_scheduler/schemas/__init__.py +0 -0
  66. memos/mem_scheduler/schemas/general_schemas.py +43 -0
  67. memos/mem_scheduler/{modules/schemas.py → schemas/message_schemas.py} +63 -61
  68. memos/mem_scheduler/schemas/monitor_schemas.py +329 -0
  69. memos/mem_scheduler/utils/__init__.py +0 -0
  70. memos/mem_scheduler/utils/filter_utils.py +176 -0
  71. memos/mem_scheduler/utils/misc_utils.py +61 -0
  72. memos/mem_user/factory.py +94 -0
  73. memos/mem_user/mysql_persistent_user_manager.py +271 -0
  74. memos/mem_user/mysql_user_manager.py +500 -0
  75. memos/mem_user/persistent_factory.py +96 -0
  76. memos/mem_user/persistent_user_manager.py +260 -0
  77. memos/mem_user/user_manager.py +4 -4
  78. memos/memories/activation/item.py +29 -0
  79. memos/memories/activation/kv.py +10 -3
  80. memos/memories/activation/vllmkv.py +219 -0
  81. memos/memories/factory.py +2 -0
  82. memos/memories/textual/base.py +1 -1
  83. memos/memories/textual/general.py +43 -97
  84. memos/memories/textual/item.py +5 -33
  85. memos/memories/textual/tree.py +22 -12
  86. memos/memories/textual/tree_text_memory/organize/conflict.py +9 -5
  87. memos/memories/textual/tree_text_memory/organize/manager.py +26 -18
  88. memos/memories/textual/tree_text_memory/organize/redundancy.py +25 -44
  89. memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py +50 -48
  90. memos/memories/textual/tree_text_memory/organize/reorganizer.py +81 -56
  91. memos/memories/textual/tree_text_memory/retrieve/internet_retriever.py +6 -3
  92. memos/memories/textual/tree_text_memory/retrieve/internet_retriever_factory.py +2 -0
  93. memos/memories/textual/tree_text_memory/retrieve/recall.py +0 -1
  94. memos/memories/textual/tree_text_memory/retrieve/reranker.py +2 -2
  95. memos/memories/textual/tree_text_memory/retrieve/retrieval_mid_structs.py +2 -0
  96. memos/memories/textual/tree_text_memory/retrieve/searcher.py +52 -28
  97. memos/memories/textual/tree_text_memory/retrieve/task_goal_parser.py +42 -15
  98. memos/memories/textual/tree_text_memory/retrieve/utils.py +11 -7
  99. memos/memories/textual/tree_text_memory/retrieve/xinyusearch.py +62 -58
  100. memos/memos_tools/dinding_report_bot.py +422 -0
  101. memos/memos_tools/notification_service.py +44 -0
  102. memos/memos_tools/notification_utils.py +96 -0
  103. memos/parsers/markitdown.py +8 -2
  104. memos/settings.py +3 -1
  105. memos/templates/mem_reader_prompts.py +66 -23
  106. memos/templates/mem_scheduler_prompts.py +126 -43
  107. memos/templates/mos_prompts.py +87 -0
  108. memos/templates/tree_reorganize_prompts.py +85 -30
  109. memos/vec_dbs/base.py +12 -0
  110. memos/vec_dbs/qdrant.py +46 -20
  111. memoryos-0.2.0.dist-info/RECORD +0 -128
  112. memos/mem_scheduler/utils.py +0 -26
  113. {memoryos-0.2.0.dist-info → memoryos-0.2.2.dist-info}/LICENSE +0 -0
  114. {memoryos-0.2.0.dist-info → memoryos-0.2.2.dist-info}/WHEEL +0 -0
@@ -1,27 +1,20 @@
1
1
  import json
2
2
 
3
- from datetime import datetime, timedelta
4
-
5
3
  from memos.configs.mem_scheduler import GeneralSchedulerConfig
6
- from memos.llms.base import BaseLLM
7
4
  from memos.log import get_logger
8
5
  from memos.mem_cube.general import GeneralMemCube
9
6
  from memos.mem_scheduler.base_scheduler import BaseScheduler
10
- from memos.mem_scheduler.modules.monitor import SchedulerMonitor
11
- from memos.mem_scheduler.modules.retriever import SchedulerRetriever
12
- from memos.mem_scheduler.modules.schemas import (
7
+ from memos.mem_scheduler.schemas.general_schemas import (
8
+ ADD_LABEL,
13
9
  ANSWER_LABEL,
14
- DEFAULT_ACT_MEM_DUMP_PATH,
15
- DEFAULT_ACTIVATION_MEM_SIZE,
16
- NOT_INITIALIZED,
10
+ DEFAULT_MAX_QUERY_KEY_WORDS,
17
11
  QUERY_LABEL,
18
- ScheduleLogForWebItem,
19
- ScheduleMessageItem,
20
- TextMemory_SEARCH_METHOD,
21
- TreeTextMemory_SEARCH_METHOD,
12
+ MemCubeID,
13
+ UserID,
22
14
  )
15
+ from memos.mem_scheduler.schemas.message_schemas import ScheduleMessageItem
16
+ from memos.mem_scheduler.schemas.monitor_schemas import QueryMonitorItem
23
17
  from memos.memories.textual.tree import TextualMemoryItem, TreeTextMemory
24
- from memos.templates.mem_scheduler_prompts import MEMORY_ASSEMBLY_TEMPLATE
25
18
 
26
19
 
27
20
  logger = get_logger(__name__)
@@ -31,275 +24,283 @@ class GeneralScheduler(BaseScheduler):
31
24
  def __init__(self, config: GeneralSchedulerConfig):
32
25
  """Initialize the scheduler with the given configuration."""
33
26
  super().__init__(config)
34
- self.top_k = self.config.get("top_k", 10)
35
- self.top_n = self.config.get("top_n", 5)
36
- self.act_mem_update_interval = self.config.get("act_mem_update_interval", 300)
37
- self.context_window_size = self.config.get("context_window_size", 5)
38
- self.activation_mem_size = self.config.get(
39
- "activation_mem_size", DEFAULT_ACTIVATION_MEM_SIZE
40
- )
41
- self.act_mem_dump_path = self.config.get("act_mem_dump_path", DEFAULT_ACT_MEM_DUMP_PATH)
42
- self.search_method = TextMemory_SEARCH_METHOD
43
- self._last_activation_mem_update_time = 0.0
44
- self.query_list = []
45
27
 
46
28
  # register handlers
47
29
  handlers = {
48
- QUERY_LABEL: self._query_message_consume,
49
- ANSWER_LABEL: self._answer_message_consume,
30
+ QUERY_LABEL: self._query_message_consumer,
31
+ ANSWER_LABEL: self._answer_message_consumer,
32
+ ADD_LABEL: self._add_message_consumer,
50
33
  }
51
34
  self.dispatcher.register_handlers(handlers)
52
35
 
53
- def initialize_modules(self, chat_llm: BaseLLM):
54
- self.chat_llm = chat_llm
55
- self.monitor = SchedulerMonitor(
56
- chat_llm=self.chat_llm, activation_mem_size=self.activation_mem_size
36
+ # for evaluation
37
+ def search_for_eval(
38
+ self,
39
+ query: str,
40
+ user_id: UserID | str,
41
+ top_k: int,
42
+ ) -> list[str]:
43
+ query_keywords = self.monitor.extract_query_keywords(query=query)
44
+ logger.info(f'Extract keywords "{query_keywords}" from query "{query}"')
45
+
46
+ item = QueryMonitorItem(
47
+ query_text=query,
48
+ keywords=query_keywords,
49
+ max_keywords=DEFAULT_MAX_QUERY_KEY_WORDS,
50
+ )
51
+ self.monitor.query_monitors.put(item=item)
52
+ logger.debug(
53
+ f"Queries in monitor are {self.monitor.query_monitors.get_queries_with_timesort()}."
54
+ )
55
+
56
+ queries = [query]
57
+
58
+ # recall
59
+ cur_working_memory, new_candidates = self.process_session_turn(
60
+ queries=queries,
61
+ user_id=user_id,
62
+ mem_cube_id=self.current_mem_cube_id,
63
+ mem_cube=self.current_mem_cube,
64
+ top_k=self.top_k,
65
+ )
66
+ logger.info(f"Processed {queries} and get {len(new_candidates)} new candidate memories.")
67
+
68
+ # rerank
69
+ new_order_working_memory = self.replace_working_memory(
70
+ user_id=user_id,
71
+ mem_cube_id=self.current_mem_cube_id,
72
+ mem_cube=self.current_mem_cube,
73
+ original_memory=cur_working_memory,
74
+ new_memory=new_candidates,
57
75
  )
58
- self.retriever = SchedulerRetriever(chat_llm=self.chat_llm)
59
- logger.debug("GeneralScheduler has been initialized")
76
+ new_order_working_memory = new_order_working_memory[:top_k]
77
+ logger.info(f"size of new_order_working_memory: {len(new_order_working_memory)}")
60
78
 
61
- def _answer_message_consume(self, messages: list[ScheduleMessageItem]) -> None:
79
+ return [m.memory for m in new_order_working_memory]
80
+
81
+ def _query_message_consumer(self, messages: list[ScheduleMessageItem]) -> None:
62
82
  """
63
- Process and handle answer trigger messages from the queue.
83
+ Process and handle query trigger messages from the queue.
64
84
 
65
85
  Args:
66
- messages: List of answer messages to process
86
+ messages: List of query messages to process
67
87
  """
68
- # TODO: This handler is not ready yet
69
- logger.debug(f"Messages {messages} assigned to {ANSWER_LABEL} handler.")
70
- for msg in messages:
71
- if msg.label is not ANSWER_LABEL:
72
- logger.error(f"_answer_message_consume is not designed for {msg.label}")
73
- continue
74
- answer = msg.content
75
- self._current_user_id = msg.user_id
76
- self._current_mem_cube_id = msg.mem_cube_id
77
- self._current_mem_cube = msg.mem_cube
78
-
79
- # Get current activation memory items
80
- current_activation_mem = [
81
- item["memory"]
82
- for item in self.monitor.activation_memory_freq_list
83
- if item["memory"] is not None
84
- ]
85
-
86
- # Update memory frequencies based on the answer
87
- # TODO: not implemented
88
- self.monitor.activation_memory_freq_list = self.monitor.update_freq(
89
- answer=answer, activation_memory_freq_list=self.monitor.activation_memory_freq_list
90
- )
88
+ logger.info(f"Messages {messages} assigned to {QUERY_LABEL} handler.")
91
89
 
92
- # Check if it's time to update activation memory
93
- now = datetime.now()
94
- if (now - self._last_activation_mem_update_time) >= timedelta(
95
- seconds=self.act_mem_update_interval
96
- ):
97
- # TODO: not implemented
98
- self.update_activation_memory(current_activation_mem)
99
- self._last_activation_mem_update_time = now
100
-
101
- # recording messages
102
- log_message = self.create_autofilled_log_item(
103
- log_title="memos answer triggers scheduling...",
104
- label=ANSWER_LABEL,
105
- log_content="activation_memory has been updated",
106
- )
107
- self._submit_web_logs(messages=log_message)
90
+ # Process the query in a session turn
91
+ grouped_messages = self.dispatcher.group_messages_by_user_and_cube(messages=messages)
92
+
93
+ self.validate_schedule_messages(messages=messages, label=QUERY_LABEL)
94
+
95
+ for user_id in grouped_messages:
96
+ for mem_cube_id in grouped_messages[user_id]:
97
+ messages = grouped_messages[user_id][mem_cube_id]
98
+ if len(messages) == 0:
99
+ return
100
+
101
+ mem_cube = messages[0].mem_cube
108
102
 
109
- def _query_message_consume(self, messages: list[ScheduleMessageItem]) -> None:
103
+ # for status update
104
+ self._set_current_context_from_message(msg=messages[0])
105
+
106
+ # update query monitors
107
+ for msg in messages:
108
+ query = msg.content
109
+ query_keywords = self.monitor.extract_query_keywords(query=query)
110
+ logger.info(f'Extract keywords "{query_keywords}" from query "{query}"')
111
+
112
+ item = QueryMonitorItem(
113
+ query_text=query,
114
+ keywords=query_keywords,
115
+ max_keywords=DEFAULT_MAX_QUERY_KEY_WORDS,
116
+ )
117
+ self.monitor.query_monitors.put(item=item)
118
+ logger.debug(
119
+ f"Queries in monitor are {self.monitor.query_monitors.get_queries_with_timesort()}."
120
+ )
121
+
122
+ queries = [msg.content for msg in messages]
123
+
124
+ # recall
125
+ cur_working_memory, new_candidates = self.process_session_turn(
126
+ queries=queries,
127
+ user_id=user_id,
128
+ mem_cube_id=mem_cube_id,
129
+ mem_cube=mem_cube,
130
+ top_k=self.top_k,
131
+ )
132
+ logger.info(
133
+ f"Processed {queries} and get {len(new_candidates)} new candidate memories."
134
+ )
135
+
136
+ # rerank
137
+ new_order_working_memory = self.replace_working_memory(
138
+ user_id=user_id,
139
+ mem_cube_id=mem_cube_id,
140
+ mem_cube=mem_cube,
141
+ original_memory=cur_working_memory,
142
+ new_memory=new_candidates,
143
+ )
144
+ logger.info(f"size of new_order_working_memory: {len(new_order_working_memory)}")
145
+
146
+ def _answer_message_consumer(self, messages: list[ScheduleMessageItem]) -> None:
110
147
  """
111
- Process and handle query trigger messages from the queue.
148
+ Process and handle answer trigger messages from the queue.
112
149
 
113
150
  Args:
114
- messages: List of query messages to process
151
+ messages: List of answer messages to process
115
152
  """
116
- logger.debug(f"Messages {messages} assigned to {QUERY_LABEL} handler.")
117
- for msg in messages:
118
- if msg.label is not QUERY_LABEL:
119
- logger.error(f"_query_message_consume is not designed for {msg.label}")
120
- continue
121
- # Process the query in a session turn
122
- self._current_user_id = msg.user_id
123
- self._current_mem_cube_id = msg.mem_cube_id
124
- self._current_mem_cube = msg.mem_cube
125
- self.process_session_turn(query=msg.content, top_k=self.top_k, top_n=self.top_n)
153
+ logger.info(f"Messages {messages} assigned to {ANSWER_LABEL} handler.")
154
+ # Process the query in a session turn
155
+ grouped_messages = self.dispatcher.group_messages_by_user_and_cube(messages=messages)
156
+
157
+ self.validate_schedule_messages(messages=messages, label=ANSWER_LABEL)
158
+
159
+ for user_id in grouped_messages:
160
+ for mem_cube_id in grouped_messages[user_id]:
161
+ messages = grouped_messages[user_id][mem_cube_id]
162
+ if len(messages) == 0:
163
+ return
164
+
165
+ # for status update
166
+ self._set_current_context_from_message(msg=messages[0])
167
+
168
+ # update activation memories
169
+ if self.enable_act_memory_update:
170
+ if (
171
+ len(self.monitor.working_memory_monitors[user_id][mem_cube_id].memories)
172
+ == 0
173
+ ):
174
+ self.initialize_working_memory_monitors(
175
+ user_id=user_id,
176
+ mem_cube_id=mem_cube_id,
177
+ mem_cube=messages[0].mem_cube,
178
+ )
179
+
180
+ self.update_activation_memory_periodically(
181
+ interval_seconds=self.monitor.act_mem_update_interval,
182
+ label=ANSWER_LABEL,
183
+ user_id=user_id,
184
+ mem_cube_id=mem_cube_id,
185
+ mem_cube=messages[0].mem_cube,
186
+ )
187
+
188
+ def _add_message_consumer(self, messages: list[ScheduleMessageItem]) -> None:
189
+ logger.info(f"Messages {messages} assigned to {ADD_LABEL} handler.")
190
+ # Process the query in a session turn
191
+ grouped_messages = self.dispatcher.group_messages_by_user_and_cube(messages=messages)
192
+
193
+ self.validate_schedule_messages(messages=messages, label=ADD_LABEL)
194
+ try:
195
+ for user_id in grouped_messages:
196
+ for mem_cube_id in grouped_messages[user_id]:
197
+ messages = grouped_messages[user_id][mem_cube_id]
198
+ if len(messages) == 0:
199
+ return
200
+
201
+ # for status update
202
+ self._set_current_context_from_message(msg=messages[0])
203
+
204
+ # submit logs
205
+ for msg in messages:
206
+ try:
207
+ userinput_memory_ids = json.loads(msg.content)
208
+ except Exception as e:
209
+ logger.error(f"Error: {e}. Content: {msg.content}", exc_info=True)
210
+ userinput_memory_ids = []
211
+
212
+ mem_cube = msg.mem_cube
213
+ for memory_id in userinput_memory_ids:
214
+ mem_item: TextualMemoryItem = mem_cube.text_mem.get(memory_id=memory_id)
215
+ mem_type = mem_item.metadata.memory_type
216
+ mem_content = mem_item.memory
217
+
218
+ self.log_adding_memory(
219
+ memory=mem_content,
220
+ memory_type=mem_type,
221
+ user_id=msg.user_id,
222
+ mem_cube_id=msg.mem_cube_id,
223
+ mem_cube=msg.mem_cube,
224
+ log_func_callback=self._submit_web_logs,
225
+ )
226
+
227
+ # update activation memories
228
+ if self.enable_act_memory_update:
229
+ self.update_activation_memory_periodically(
230
+ interval_seconds=self.monitor.act_mem_update_interval,
231
+ label=ADD_LABEL,
232
+ user_id=user_id,
233
+ mem_cube_id=mem_cube_id,
234
+ mem_cube=messages[0].mem_cube,
235
+ )
236
+ except Exception as e:
237
+ logger.error(f"Error: {e}", exc_info=True)
126
238
 
127
239
  def process_session_turn(
128
240
  self,
129
- query: str,
241
+ queries: str | list[str],
242
+ user_id: UserID | str,
243
+ mem_cube_id: MemCubeID | str,
244
+ mem_cube: GeneralMemCube,
130
245
  top_k: int = 10,
131
- top_n: int = 5,
132
- ) -> None:
246
+ ) -> tuple[list[TextualMemoryItem], list[TextualMemoryItem]] | None:
133
247
  """
134
248
  Process a dialog turn:
135
249
  - If q_list reaches window size, trigger retrieval;
136
250
  - Immediately switch to the new memory if retrieval is triggered.
137
251
  """
138
- q_list = [query]
139
- self.query_list.append(query)
140
- text_mem_base = self.mem_cube.text_mem
141
- if isinstance(text_mem_base, TreeTextMemory):
142
- working_memory: list[TextualMemoryItem] = text_mem_base.get_working_memory()
143
- else:
144
- logger.error("Not implemented!")
145
- return
146
- text_working_memory: list[str] = [w_m.memory for w_m in working_memory]
147
- intent_result = self.monitor.detect_intent(
148
- q_list=q_list, text_working_memory=text_working_memory
149
- )
150
- if intent_result["trigger_retrieval"]:
151
- missing_evidence = intent_result["missing_evidence"]
152
- num_evidence = len(missing_evidence)
153
- k_per_evidence = max(1, top_k // max(1, num_evidence))
154
- new_candidates = []
155
- for item in missing_evidence:
156
- logger.debug(f"missing_evidence: {item}")
157
- results = self.search(query=item, top_k=k_per_evidence, method=self.search_method)
158
- logger.debug(f"search results for {missing_evidence}: {results}")
159
- new_candidates.extend(results)
160
-
161
- # recording messages
162
- log_message = self.create_autofilled_log_item(
163
- log_title="user query triggers scheduling...",
164
- label=QUERY_LABEL,
165
- log_content=f"search new candidates for working memory: {len(new_candidates)}",
166
- )
167
- self._submit_web_logs(messages=log_message)
168
- new_order_working_memory = self.replace_working_memory(
169
- original_memory=working_memory, new_memory=new_candidates, top_k=top_k, top_n=top_n
170
- )
171
- self.update_activation_memory(new_order_working_memory)
172
-
173
- def create_autofilled_log_item(
174
- self, log_title: str, log_content: str, label: str
175
- ) -> ScheduleLogForWebItem:
176
- # TODO: create the log iterm with real stats
177
- text_mem_base: TreeTextMemory = self.mem_cube.text_mem
178
- current_memory_sizes = {
179
- "long_term_memory_size": NOT_INITIALIZED,
180
- "user_memory_size": NOT_INITIALIZED,
181
- "working_memory_size": NOT_INITIALIZED,
182
- "transformed_act_memory_size": NOT_INITIALIZED,
183
- "parameter_memory_size": NOT_INITIALIZED,
184
- }
185
- memory_capacities = {
186
- "long_term_memory_capacity": text_mem_base.memory_manager.memory_size["LongTermMemory"],
187
- "user_memory_capacity": text_mem_base.memory_manager.memory_size["UserMemory"],
188
- "working_memory_capacity": text_mem_base.memory_manager.memory_size["WorkingMemory"],
189
- "transformed_act_memory_capacity": NOT_INITIALIZED,
190
- "parameter_memory_capacity": NOT_INITIALIZED,
191
- }
192
252
 
193
- log_message = ScheduleLogForWebItem(
194
- user_id=self._current_user_id,
195
- mem_cube_id=self._current_mem_cube_id,
196
- label=label,
197
- log_title=log_title,
198
- log_content=log_content,
199
- current_memory_sizes=current_memory_sizes,
200
- memory_capacities=memory_capacities,
201
- )
202
- return log_message
253
+ text_mem_base = mem_cube.text_mem
254
+ if not isinstance(text_mem_base, TreeTextMemory):
255
+ logger.error("Not implemented!", exc_info=True)
256
+ return
203
257
 
204
- @property
205
- def mem_cube(self) -> GeneralMemCube:
206
- """The memory cube associated with this MemChat."""
207
- return self._current_mem_cube
258
+ logger.info(f"Processing {len(queries)} queries.")
208
259
 
209
- @mem_cube.setter
210
- def mem_cube(self, value: GeneralMemCube) -> None:
211
- """The memory cube associated with this MemChat."""
212
- self._current_mem_cube = value
213
- self.retriever.mem_cube = value
260
+ cur_working_memory: list[TextualMemoryItem] = text_mem_base.get_working_memory()
261
+ text_working_memory: list[str] = [w_m.memory for w_m in cur_working_memory]
262
+ intent_result = self.monitor.detect_intent(
263
+ q_list=queries, text_working_memory=text_working_memory
264
+ )
214
265
 
215
- def replace_working_memory(
216
- self,
217
- original_memory: list[TextualMemoryItem],
218
- new_memory: list[TextualMemoryItem],
219
- top_k: int = 10,
220
- top_n: int = 5,
221
- ) -> None | list[TextualMemoryItem]:
222
- new_order_memory = None
223
- text_mem_base = self.mem_cube.text_mem
224
- if isinstance(text_mem_base, TreeTextMemory):
225
- text_mem_base: TreeTextMemory = text_mem_base
226
- combined_text_memory = [new_m.memory for new_m in original_memory] + [
227
- new_m.memory for new_m in new_memory
228
- ]
229
- combined_memory = original_memory + new_memory
230
- memory_map = {mem_obj.memory: mem_obj for mem_obj in combined_memory}
231
-
232
- unique_memory = list(dict.fromkeys(combined_text_memory))
233
- prompt = self.build_prompt(
234
- "memory_reranking", query="", current_order=unique_memory, staging_buffer=[]
235
- )
236
- response = self.chat_llm.generate([{"role": "user", "content": prompt}])
237
- response = json.loads(response)
238
- new_order_text_memory = response.get("new_order", [])[: top_n + top_k]
239
-
240
- new_order_memory = []
241
- for text in new_order_text_memory:
242
- if text in memory_map:
243
- new_order_memory.append(memory_map[text])
244
- else:
245
- logger.warning(
246
- f"Memory text not found in memory map. text: {text}; memory_map: {memory_map}"
247
- )
266
+ time_trigger_flag = False
267
+ if self.monitor.timed_trigger(
268
+ last_time=self.monitor.last_query_consume_time,
269
+ interval_seconds=self.monitor.query_trigger_interval,
270
+ ):
271
+ time_trigger_flag = True
248
272
 
249
- text_mem_base.replace_working_memory(new_order_memory[top_n:])
250
- new_order_memory = new_order_memory[:top_n]
273
+ if (not intent_result["trigger_retrieval"]) and (not time_trigger_flag):
274
+ logger.info(f"Query schedule not triggered. Intent_result: {intent_result}")
275
+ return
276
+ elif (not intent_result["trigger_retrieval"]) and time_trigger_flag:
277
+ logger.info("Query schedule is forced to trigger due to time ticker")
278
+ intent_result["trigger_retrieval"] = True
279
+ intent_result["missing_evidences"] = queries
280
+ else:
251
281
  logger.info(
252
- f"The working memory has been replaced with {len(new_order_memory)} new memories."
282
+ f'Query schedule triggered for user "{user_id}" and mem_cube "{mem_cube_id}".'
283
+ f" Missing evidences: {intent_result['missing_evidences']}"
253
284
  )
254
- else:
255
- logger.error("memory_base is not supported")
256
285
 
257
- return new_order_memory
258
-
259
- def search(self, query: str, top_k: int, method=TreeTextMemory_SEARCH_METHOD):
260
- text_mem_base = self.mem_cube.text_mem
261
- if isinstance(text_mem_base, TreeTextMemory) and method == TextMemory_SEARCH_METHOD:
262
- results_long_term = text_mem_base.search(
263
- query=query, top_k=top_k, memory_type="LongTermMemory"
286
+ missing_evidences = intent_result["missing_evidences"]
287
+ num_evidence = len(missing_evidences)
288
+ k_per_evidence = max(1, top_k // max(1, num_evidence))
289
+ new_candidates = []
290
+ for item in missing_evidences:
291
+ logger.info(f"missing_evidences: {item}")
292
+ results: list[TextualMemoryItem] = self.retriever.search(
293
+ query=item, mem_cube=mem_cube, top_k=k_per_evidence, method=self.search_method
264
294
  )
265
- results_user = text_mem_base.search(query=query, top_k=top_k, memory_type="UserMemory")
266
- results = results_long_term + results_user
267
- else:
268
- logger.error("Not implemented.")
269
- results = None
270
- return results
271
-
272
- def update_activation_memory(self, new_memory: list[str | TextualMemoryItem]) -> None:
273
- """
274
- Update activation memory by extracting KVCacheItems from new_memory (list of str),
275
- add them to a KVCacheMemory instance, and dump to disk.
276
- """
277
- # TODO: The function of update activation memory is waiting to test
278
- if len(new_memory) == 0:
279
- logger.error("update_activation_memory: new_memory is empty.")
280
- return
281
- if isinstance(new_memory[0], TextualMemoryItem):
282
- new_text_memory = [mem.memory for mem in new_memory]
283
- elif isinstance(new_memory[0], str):
284
- new_text_memory = new_memory
285
- else:
286
- logger.error("Not Implemented.")
295
+ logger.info(
296
+ f"search results for {missing_evidences}: {[one.memory for one in results]}"
297
+ )
298
+ new_candidates.extend(results)
287
299
 
288
- try:
289
- act_mem = self.mem_cube.act_mem
290
-
291
- text_memory = MEMORY_ASSEMBLY_TEMPLATE.format(
292
- memory_text="".join(
293
- [
294
- f"{i + 1}. {sentence.strip()}\n"
295
- for i, sentence in enumerate(new_text_memory)
296
- if sentence.strip() # Skip empty strings
297
- ]
298
- )
300
+ if len(new_candidates) == 0:
301
+ logger.warning(
302
+ f"As new_candidates is empty, new_candidates is set same to working_memory.\n"
303
+ f"time_trigger_flag: {time_trigger_flag}; intent_result: {intent_result}"
299
304
  )
300
- act_mem.delete_all()
301
- cache_item = act_mem.extract(text_memory)
302
- act_mem.add(cache_item)
303
- act_mem.dump(self.act_mem_dump_path)
304
- except Exception as e:
305
- logger.warning(f"MOS-based activation memory update failed: {e}")
305
+ new_candidates = cur_working_memory
306
+ return cur_working_memory, new_candidates
@@ -3,7 +3,7 @@ from pathlib import Path
3
3
  from memos.llms.base import BaseLLM
4
4
  from memos.log import get_logger
5
5
  from memos.mem_cube.general import GeneralMemCube
6
- from memos.mem_scheduler.modules.schemas import BASE_DIR
6
+ from memos.mem_scheduler.schemas.general_schemas import BASE_DIR
7
7
  from memos.templates.mem_scheduler_prompts import PROMPT_MAPPING
8
8
 
9
9
 
@@ -16,8 +16,9 @@ class BaseSchedulerModule:
16
16
  self.base_dir = Path(BASE_DIR)
17
17
 
18
18
  self._chat_llm = None
19
- self._current_mem_cube_id: str | None = None
20
- self._current_mem_cube: GeneralMemCube | None = None
19
+ self._process_llm = None
20
+ self.current_mem_cube_id: str | None = None
21
+ self.current_mem_cube: GeneralMemCube | None = None
21
22
  self.mem_cubes: dict[str, GeneralMemCube] = {}
22
23
 
23
24
  def load_template(self, template_name: str) -> str:
@@ -63,12 +64,20 @@ class BaseSchedulerModule:
63
64
  """The memory cube associated with this MemChat."""
64
65
  self._chat_llm = value
65
66
 
67
+ @property
68
+ def process_llm(self) -> BaseLLM:
69
+ return self._process_llm
70
+
71
+ @process_llm.setter
72
+ def process_llm(self, value: BaseLLM) -> None:
73
+ self._process_llm = value
74
+
66
75
  @property
67
76
  def mem_cube(self) -> GeneralMemCube:
68
77
  """The memory cube associated with this MemChat."""
69
- return self._current_mem_cube
78
+ return self.current_mem_cube
70
79
 
71
80
  @mem_cube.setter
72
81
  def mem_cube(self, value: GeneralMemCube) -> None:
73
82
  """The memory cube associated with this MemChat."""
74
- self._current_mem_cube = value
83
+ self.current_mem_cube = value