MemoryOS 0.2.1__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 (74) hide show
  1. {memoryos-0.2.1.dist-info → memoryos-0.2.2.dist-info}/METADATA +2 -1
  2. {memoryos-0.2.1.dist-info → memoryos-0.2.2.dist-info}/RECORD +72 -55
  3. memos/__init__.py +1 -1
  4. memos/api/config.py +156 -65
  5. memos/api/context/context.py +147 -0
  6. memos/api/context/dependencies.py +90 -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 +6 -0
  11. memos/configs/mem_os.py +5 -0
  12. memos/configs/mem_reader.py +9 -0
  13. memos/configs/mem_scheduler.py +18 -4
  14. memos/configs/mem_user.py +58 -0
  15. memos/graph_dbs/base.py +9 -1
  16. memos/graph_dbs/factory.py +2 -0
  17. memos/graph_dbs/nebular.py +1364 -0
  18. memos/graph_dbs/neo4j.py +4 -4
  19. memos/log.py +1 -1
  20. memos/mem_cube/utils.py +13 -6
  21. memos/mem_os/core.py +140 -30
  22. memos/mem_os/main.py +1 -1
  23. memos/mem_os/product.py +266 -152
  24. memos/mem_os/utils/format_utils.py +314 -67
  25. memos/mem_reader/simple_struct.py +13 -5
  26. memos/mem_scheduler/base_scheduler.py +220 -250
  27. memos/mem_scheduler/general_scheduler.py +193 -73
  28. memos/mem_scheduler/modules/base.py +5 -5
  29. memos/mem_scheduler/modules/dispatcher.py +6 -9
  30. memos/mem_scheduler/modules/misc.py +81 -16
  31. memos/mem_scheduler/modules/monitor.py +52 -41
  32. memos/mem_scheduler/modules/rabbitmq_service.py +9 -7
  33. memos/mem_scheduler/modules/retriever.py +108 -191
  34. memos/mem_scheduler/modules/scheduler_logger.py +255 -0
  35. memos/mem_scheduler/mos_for_test_scheduler.py +16 -19
  36. memos/mem_scheduler/schemas/__init__.py +0 -0
  37. memos/mem_scheduler/schemas/general_schemas.py +43 -0
  38. memos/mem_scheduler/schemas/message_schemas.py +148 -0
  39. memos/mem_scheduler/schemas/monitor_schemas.py +329 -0
  40. memos/mem_scheduler/utils/__init__.py +0 -0
  41. memos/mem_scheduler/utils/filter_utils.py +176 -0
  42. memos/mem_scheduler/utils/misc_utils.py +61 -0
  43. memos/mem_user/factory.py +94 -0
  44. memos/mem_user/mysql_persistent_user_manager.py +271 -0
  45. memos/mem_user/mysql_user_manager.py +500 -0
  46. memos/mem_user/persistent_factory.py +96 -0
  47. memos/mem_user/user_manager.py +4 -4
  48. memos/memories/activation/item.py +4 -0
  49. memos/memories/textual/base.py +1 -1
  50. memos/memories/textual/general.py +35 -91
  51. memos/memories/textual/item.py +5 -33
  52. memos/memories/textual/tree.py +13 -7
  53. memos/memories/textual/tree_text_memory/organize/conflict.py +4 -2
  54. memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py +47 -43
  55. memos/memories/textual/tree_text_memory/organize/reorganizer.py +8 -5
  56. memos/memories/textual/tree_text_memory/retrieve/internet_retriever.py +6 -3
  57. memos/memories/textual/tree_text_memory/retrieve/internet_retriever_factory.py +2 -0
  58. memos/memories/textual/tree_text_memory/retrieve/retrieval_mid_structs.py +2 -0
  59. memos/memories/textual/tree_text_memory/retrieve/searcher.py +46 -23
  60. memos/memories/textual/tree_text_memory/retrieve/task_goal_parser.py +42 -15
  61. memos/memories/textual/tree_text_memory/retrieve/utils.py +11 -7
  62. memos/memories/textual/tree_text_memory/retrieve/xinyusearch.py +62 -58
  63. memos/memos_tools/dinding_report_bot.py +422 -0
  64. memos/memos_tools/notification_service.py +44 -0
  65. memos/memos_tools/notification_utils.py +96 -0
  66. memos/settings.py +3 -1
  67. memos/templates/mem_reader_prompts.py +2 -1
  68. memos/templates/mem_scheduler_prompts.py +41 -7
  69. memos/templates/mos_prompts.py +87 -0
  70. memos/mem_scheduler/modules/schemas.py +0 -328
  71. memos/mem_scheduler/utils.py +0 -75
  72. {memoryos-0.2.1.dist-info → memoryos-0.2.2.dist-info}/LICENSE +0 -0
  73. {memoryos-0.2.1.dist-info → memoryos-0.2.2.dist-info}/WHEEL +0 -0
  74. {memoryos-0.2.1.dist-info → memoryos-0.2.2.dist-info}/entry_points.txt +0 -0
@@ -15,24 +15,23 @@ from memos.mem_scheduler.modules.monitor import SchedulerMonitor
15
15
  from memos.mem_scheduler.modules.rabbitmq_service import RabbitMQSchedulerModule
16
16
  from memos.mem_scheduler.modules.redis_service import RedisSchedulerModule
17
17
  from memos.mem_scheduler.modules.retriever import SchedulerRetriever
18
- from memos.mem_scheduler.modules.schemas import (
19
- ACTIVATION_MEMORY_TYPE,
20
- ADD_LABEL,
18
+ from memos.mem_scheduler.modules.scheduler_logger import SchedulerLoggerModule
19
+ from memos.mem_scheduler.schemas.general_schemas import (
21
20
  DEFAULT_ACT_MEM_DUMP_PATH,
22
21
  DEFAULT_CONSUME_INTERVAL_SECONDS,
23
22
  DEFAULT_THREAD__POOL_MAX_WORKERS,
24
- LONG_TERM_MEMORY_TYPE,
25
- NOT_INITIALIZED,
26
- PARAMETER_MEMORY_TYPE,
27
- QUERY_LABEL,
28
- TEXT_MEMORY_TYPE,
29
- USER_INPUT_TYPE,
30
- WORKING_MEMORY_TYPE,
23
+ MemCubeID,
24
+ TreeTextMemory_SEARCH_METHOD,
25
+ UserID,
26
+ )
27
+ from memos.mem_scheduler.schemas.message_schemas import (
31
28
  ScheduleLogForWebItem,
32
29
  ScheduleMessageItem,
33
- TreeTextMemory_SEARCH_METHOD,
34
30
  )
35
- from memos.mem_scheduler.utils import transform_name_to_key
31
+ from memos.mem_scheduler.schemas.monitor_schemas import MemoryMonitorItem
32
+ from memos.mem_scheduler.utils.filter_utils import (
33
+ transform_name_to_key,
34
+ )
36
35
  from memos.memories.activation.kv import KVCacheMemory
37
36
  from memos.memories.activation.vllmkv import VLLMKVCacheItem, VLLMKVCacheMemory
38
37
  from memos.memories.textual.tree import TextualMemoryItem, TreeTextMemory
@@ -42,7 +41,7 @@ from memos.templates.mem_scheduler_prompts import MEMORY_ASSEMBLY_TEMPLATE
42
41
  logger = get_logger(__name__)
43
42
 
44
43
 
45
- class BaseScheduler(RabbitMQSchedulerModule, RedisSchedulerModule):
44
+ class BaseScheduler(RabbitMQSchedulerModule, RedisSchedulerModule, SchedulerLoggerModule):
46
45
  """Base class for all mem_scheduler."""
47
46
 
48
47
  def __init__(self, config: BaseSchedulerConfig):
@@ -51,12 +50,11 @@ class BaseScheduler(RabbitMQSchedulerModule, RedisSchedulerModule):
51
50
  self.config = config
52
51
 
53
52
  # hyper-parameters
54
- self.top_k = self.config.get("top_k", 5)
53
+ self.top_k = self.config.get("top_k", 10)
55
54
  self.context_window_size = self.config.get("context_window_size", 5)
56
55
  self.enable_act_memory_update = self.config.get("enable_act_memory_update", False)
57
56
  self.act_mem_dump_path = self.config.get("act_mem_dump_path", DEFAULT_ACT_MEM_DUMP_PATH)
58
57
  self.search_method = TreeTextMemory_SEARCH_METHOD
59
-
60
58
  self.enable_parallel_dispatch = self.config.get("enable_parallel_dispatch", False)
61
59
  self.max_workers = self.config.get(
62
60
  "thread_pool_max_workers", DEFAULT_THREAD__POOL_MAX_WORKERS
@@ -85,7 +83,7 @@ class BaseScheduler(RabbitMQSchedulerModule, RedisSchedulerModule):
85
83
 
86
84
  # other attributes
87
85
  self._context_lock = threading.Lock()
88
- self._current_user_id: str | None = None
86
+ self.current_user_id: UserID | str | None = None
89
87
  self.auth_config_path: str | Path | None = self.config.get("auth_config_path", None)
90
88
  self.auth_config = None
91
89
  self.rabbitmq_config = None
@@ -99,7 +97,6 @@ class BaseScheduler(RabbitMQSchedulerModule, RedisSchedulerModule):
99
97
  self.process_llm = process_llm
100
98
  self.monitor = SchedulerMonitor(process_llm=self.process_llm, config=self.config)
101
99
  self.retriever = SchedulerRetriever(process_llm=self.process_llm, config=self.config)
102
- self.retriever.log_working_memory_replacement = self.log_working_memory_replacement
103
100
 
104
101
  # initialize with auth_cofig
105
102
  if self.auth_config_path is not None and Path(self.auth_config_path).exists():
@@ -118,58 +115,161 @@ class BaseScheduler(RabbitMQSchedulerModule, RedisSchedulerModule):
118
115
  @property
119
116
  def mem_cube(self) -> GeneralMemCube:
120
117
  """The memory cube associated with this MemChat."""
121
- return self._current_mem_cube
118
+ return self.current_mem_cube
122
119
 
123
120
  @mem_cube.setter
124
121
  def mem_cube(self, value: GeneralMemCube) -> None:
125
122
  """The memory cube associated with this MemChat."""
126
- self._current_mem_cube = value
123
+ self.current_mem_cube = value
127
124
  self.retriever.mem_cube = value
128
125
 
129
126
  def _set_current_context_from_message(self, msg: ScheduleMessageItem) -> None:
130
127
  """Update current user/cube context from the incoming message (thread-safe)."""
131
128
  with self._context_lock:
132
- self._current_user_id = msg.user_id
133
- self._current_mem_cube_id = msg.mem_cube_id
134
- self._current_mem_cube = msg.mem_cube
129
+ self.current_user_id = msg.user_id
130
+ self.current_mem_cube_id = msg.mem_cube_id
131
+ self.current_mem_cube = msg.mem_cube
135
132
 
136
- def _validate_messages(self, messages: list[ScheduleMessageItem], label: str):
137
- """Validate if all messages match the expected label.
133
+ def transform_memories_to_monitors(
134
+ self, memories: list[TextualMemoryItem]
135
+ ) -> list[MemoryMonitorItem]:
136
+ """
137
+ Convert a list of TextualMemoryItem objects into MemoryMonitorItem objects
138
+ with importance scores based on keyword matching.
138
139
 
139
140
  Args:
140
- messages: List of message items to validate.
141
- label: Expected message label (e.g., QUERY_LABEL/ANSWER_LABEL).
141
+ memories: List of TextualMemoryItem objects to be transformed.
142
142
 
143
143
  Returns:
144
- bool: True if all messages passed validation, False if any failed.
144
+ List of MemoryMonitorItem objects with computed importance scores.
145
145
  """
146
- for message in messages:
147
- if not self._validate_message(message, label):
148
- return False
149
- logger.error("Message batch contains invalid labels, aborting processing")
150
- return True
146
+ query_keywords = self.monitor.query_monitors.get_keywords_collections()
147
+ logger.debug(
148
+ f"Processing {len(memories)} memories with {len(query_keywords)} query keywords"
149
+ )
151
150
 
152
- def _validate_message(self, message: ScheduleMessageItem, label: str):
153
- """Validate if the message matches the expected label.
151
+ result = []
152
+ mem_length = len(memories)
153
+ for idx, mem in enumerate(memories):
154
+ text_mem = mem.memory
155
+ mem_key = transform_name_to_key(name=text_mem)
156
+
157
+ # Calculate importance score based on keyword matches
158
+ keywords_score = 0
159
+ if query_keywords and text_mem:
160
+ for keyword, count in query_keywords.items():
161
+ keyword_count = text_mem.count(keyword)
162
+ if keyword_count > 0:
163
+ keywords_score += keyword_count * count
164
+ logger.debug(
165
+ f"Matched keyword '{keyword}' {keyword_count} times, added {keywords_score} to keywords_score"
166
+ )
167
+
168
+ # rank score
169
+ sorting_score = mem_length - idx
170
+
171
+ mem_monitor = MemoryMonitorItem(
172
+ memory_text=text_mem,
173
+ tree_memory_item=mem,
174
+ tree_memory_item_mapping_key=mem_key,
175
+ sorting_score=sorting_score,
176
+ keywords_score=keywords_score,
177
+ recording_count=1,
178
+ )
179
+ result.append(mem_monitor)
154
180
 
155
- Args:
156
- message: Incoming message item to validate.
157
- label: Expected message label (e.g., QUERY_LABEL/ANSWER_LABEL).
181
+ logger.debug(f"Transformed {len(result)} memories to monitors")
182
+ return result
158
183
 
159
- Returns:
160
- bool: True if validation passed, False otherwise.
161
- """
162
- if message.label != label:
163
- logger.error(f"Handler validation failed: expected={label}, actual={message.label}")
164
- return False
165
- return True
184
+ def replace_working_memory(
185
+ self,
186
+ user_id: UserID | str,
187
+ mem_cube_id: MemCubeID | str,
188
+ mem_cube: GeneralMemCube,
189
+ original_memory: list[TextualMemoryItem],
190
+ new_memory: list[TextualMemoryItem],
191
+ ) -> None | list[TextualMemoryItem]:
192
+ """Replace working memory with new memories after reranking."""
193
+ text_mem_base = mem_cube.text_mem
194
+ if isinstance(text_mem_base, TreeTextMemory):
195
+ text_mem_base: TreeTextMemory = text_mem_base
196
+
197
+ # process rerank memories with llm
198
+ query_history = self.monitor.query_monitors.get_queries_with_timesort()
199
+ memories_with_new_order, rerank_success_flag = (
200
+ self.retriever.process_and_rerank_memories(
201
+ queries=query_history,
202
+ original_memory=original_memory,
203
+ new_memory=new_memory,
204
+ top_k=self.top_k,
205
+ )
206
+ )
207
+
208
+ # update working memory monitors
209
+ new_working_memory_monitors = self.transform_memories_to_monitors(
210
+ memories=memories_with_new_order
211
+ )
212
+
213
+ if not rerank_success_flag:
214
+ for one in new_working_memory_monitors:
215
+ one.sorting_score = 0
216
+
217
+ self.monitor.update_working_memory_monitors(
218
+ new_working_memory_monitors=new_working_memory_monitors,
219
+ user_id=user_id,
220
+ mem_cube_id=mem_cube_id,
221
+ mem_cube=mem_cube,
222
+ )
223
+
224
+ mem_monitors: list[MemoryMonitorItem] = self.monitor.working_memory_monitors[user_id][
225
+ mem_cube_id
226
+ ].get_sorted_mem_monitors(reverse=True)
227
+ new_working_memories = [mem_monitor.tree_memory_item for mem_monitor in mem_monitors]
228
+
229
+ text_mem_base.replace_working_memory(memories=new_working_memories)
230
+
231
+ logger.info(
232
+ f"The working memory has been replaced with {len(memories_with_new_order)} new memories."
233
+ )
234
+ self.log_working_memory_replacement(
235
+ original_memory=original_memory,
236
+ new_memory=new_working_memories,
237
+ user_id=user_id,
238
+ mem_cube_id=mem_cube_id,
239
+ mem_cube=mem_cube,
240
+ log_func_callback=self._submit_web_logs,
241
+ )
242
+ else:
243
+ logger.error("memory_base is not supported")
244
+ memories_with_new_order = new_memory
245
+
246
+ return memories_with_new_order
247
+
248
+ def initialize_working_memory_monitors(
249
+ self,
250
+ user_id: UserID | str,
251
+ mem_cube_id: MemCubeID | str,
252
+ mem_cube: GeneralMemCube,
253
+ ):
254
+ text_mem_base: TreeTextMemory = mem_cube.text_mem
255
+ working_memories = text_mem_base.get_working_memory()
256
+
257
+ working_memory_monitors = self.transform_memories_to_monitors(
258
+ memories=working_memories,
259
+ )
260
+ self.monitor.update_working_memory_monitors(
261
+ new_working_memory_monitors=working_memory_monitors,
262
+ user_id=user_id,
263
+ mem_cube_id=mem_cube_id,
264
+ mem_cube=mem_cube,
265
+ )
166
266
 
167
267
  def update_activation_memory(
168
268
  self,
169
269
  new_memories: list[str | TextualMemoryItem],
170
270
  label: str,
171
- user_id: str,
172
- mem_cube_id: str,
271
+ user_id: UserID | str,
272
+ mem_cube_id: MemCubeID | str,
173
273
  mem_cube: GeneralMemCube,
174
274
  ) -> None:
175
275
  """
@@ -195,7 +295,7 @@ class BaseScheduler(RabbitMQSchedulerModule, RedisSchedulerModule):
195
295
  logger.error("Not Implemented.")
196
296
  return
197
297
 
198
- text_memory = MEMORY_ASSEMBLY_TEMPLATE.format(
298
+ new_text_memory = MEMORY_ASSEMBLY_TEMPLATE.format(
199
299
  memory_text="".join(
200
300
  [
201
301
  f"{i + 1}. {sentence.strip()}\n"
@@ -211,9 +311,18 @@ class BaseScheduler(RabbitMQSchedulerModule, RedisSchedulerModule):
211
311
  if len(original_cache_items) > 0:
212
312
  pre_cache_item: VLLMKVCacheItem = original_cache_items[-1]
213
313
  original_text_memories = pre_cache_item.records.text_memories
314
+ original_composed_text_memory = pre_cache_item.records.composed_text_memory
315
+ if original_composed_text_memory == new_text_memory:
316
+ logger.warning(
317
+ "Skipping memory update - new composition matches existing cache: %s",
318
+ new_text_memory[:50] + "..."
319
+ if len(new_text_memory) > 50
320
+ else new_text_memory,
321
+ )
322
+ return
214
323
  act_mem.delete_all()
215
324
 
216
- cache_item = act_mem.extract(text_memory)
325
+ cache_item = act_mem.extract(new_text_memory)
217
326
  cache_item.records.text_memories = new_text_memories
218
327
 
219
328
  act_mem.add([cache_item])
@@ -226,6 +335,7 @@ class BaseScheduler(RabbitMQSchedulerModule, RedisSchedulerModule):
226
335
  user_id=user_id,
227
336
  mem_cube_id=mem_cube_id,
228
337
  mem_cube=mem_cube,
338
+ log_func_callback=self._submit_web_logs,
229
339
  )
230
340
 
231
341
  except Exception as e:
@@ -235,50 +345,70 @@ class BaseScheduler(RabbitMQSchedulerModule, RedisSchedulerModule):
235
345
  self,
236
346
  interval_seconds: int,
237
347
  label: str,
238
- user_id: str,
239
- mem_cube_id: str,
348
+ user_id: UserID | str,
349
+ mem_cube_id: MemCubeID | str,
240
350
  mem_cube: GeneralMemCube,
241
351
  ):
242
- new_activation_memories = []
243
-
244
- if self.monitor.timed_trigger(
245
- last_time=self.monitor._last_activation_mem_update_time,
246
- interval_seconds=interval_seconds,
247
- ):
248
- logger.info(f"Updating activation memory for user {user_id} and mem_cube {mem_cube_id}")
352
+ try:
353
+ if (
354
+ self.monitor.last_activation_mem_update_time == datetime.min
355
+ or self.monitor.timed_trigger(
356
+ last_time=self.monitor.last_activation_mem_update_time,
357
+ interval_seconds=interval_seconds,
358
+ )
359
+ ):
360
+ logger.info(
361
+ f"Updating activation memory for user {user_id} and mem_cube {mem_cube_id}"
362
+ )
249
363
 
250
- self.monitor.update_memory_monitors(
251
- user_id=user_id, mem_cube_id=mem_cube_id, mem_cube=mem_cube
252
- )
364
+ if (
365
+ user_id not in self.monitor.working_memory_monitors
366
+ or mem_cube_id not in self.monitor.working_memory_monitors[user_id]
367
+ or len(self.monitor.working_memory_monitors[user_id][mem_cube_id].memories) == 0
368
+ ):
369
+ logger.warning(
370
+ "No memories found in working_memory_monitors, initializing from current working_memories"
371
+ )
372
+ self.initialize_working_memory_monitors(
373
+ user_id=user_id,
374
+ mem_cube_id=mem_cube_id,
375
+ mem_cube=mem_cube,
376
+ )
377
+
378
+ self.monitor.update_activation_memory_monitors(
379
+ user_id=user_id, mem_cube_id=mem_cube_id, mem_cube=mem_cube
380
+ )
253
381
 
254
- new_activation_memories = [
255
- m.memory_text
256
- for m in self.monitor.activation_memory_monitors[user_id][mem_cube_id].memories
257
- ]
382
+ new_activation_memories = [
383
+ m.memory_text
384
+ for m in self.monitor.activation_memory_monitors[user_id][mem_cube_id].memories
385
+ ]
258
386
 
259
- logger.info(
260
- f"Collected {len(new_activation_memories)} new memory entries for processing"
261
- )
387
+ logger.info(
388
+ f"Collected {len(new_activation_memories)} new memory entries for processing"
389
+ )
262
390
 
263
- self.update_activation_memory(
264
- new_memories=new_activation_memories,
265
- label=label,
266
- user_id=user_id,
267
- mem_cube_id=mem_cube_id,
268
- mem_cube=mem_cube,
269
- )
391
+ self.update_activation_memory(
392
+ new_memories=new_activation_memories,
393
+ label=label,
394
+ user_id=user_id,
395
+ mem_cube_id=mem_cube_id,
396
+ mem_cube=mem_cube,
397
+ )
270
398
 
271
- self.monitor._last_activation_mem_update_time = datetime.now()
399
+ self.monitor.last_activation_mem_update_time = datetime.now()
272
400
 
273
- logger.debug(
274
- f"Activation memory update completed at {self.monitor._last_activation_mem_update_time}"
275
- )
276
- else:
277
- logger.info(
278
- f"Skipping update - {interval_seconds} second interval not yet reached. "
279
- f"Last update time is {self.monitor._last_activation_mem_update_time} and now is"
280
- f"{datetime.now()}"
281
- )
401
+ logger.debug(
402
+ f"Activation memory update completed at {self.monitor.last_activation_mem_update_time}"
403
+ )
404
+ else:
405
+ logger.info(
406
+ f"Skipping update - {interval_seconds} second interval not yet reached. "
407
+ f"Last update time is {self.monitor.last_activation_mem_update_time} and now is"
408
+ f"{datetime.now()}"
409
+ )
410
+ except Exception as e:
411
+ logger.error(f"Error: {e}", exc_info=True)
282
412
 
283
413
  def submit_messages(self, messages: ScheduleMessageItem | list[ScheduleMessageItem]):
284
414
  """Submit multiple messages to the message queue."""
@@ -289,7 +419,9 @@ class BaseScheduler(RabbitMQSchedulerModule, RedisSchedulerModule):
289
419
  self.memos_message_queue.put(message)
290
420
  logger.info(f"Submitted message: {message.label} - {message.content}")
291
421
 
292
- def _submit_web_logs(self, messages: ScheduleLogForWebItem | list[ScheduleLogForWebItem]):
422
+ def _submit_web_logs(
423
+ self, messages: ScheduleLogForWebItem | list[ScheduleLogForWebItem]
424
+ ) -> None:
293
425
  """Submit log messages to the web log queue and optionally to RabbitMQ.
294
426
 
295
427
  Args:
@@ -300,176 +432,14 @@ class BaseScheduler(RabbitMQSchedulerModule, RedisSchedulerModule):
300
432
 
301
433
  for message in messages:
302
434
  self._web_log_message_queue.put(message)
303
- logger.info(f"Submitted Scheduling log for web: {message.log_content}")
435
+ message_info = message.debug_info()
436
+ logger.debug(f"Submitted Scheduling log for web: {message_info}")
304
437
 
305
438
  if self.is_rabbitmq_connected():
306
- logger.info("Submitted Scheduling log to rabbitmq")
439
+ logger.info(f"Submitted Scheduling log to rabbitmq: {message_info}")
307
440
  self.rabbitmq_publish_message(message=message.to_dict())
308
441
  logger.debug(f"{len(messages)} submitted. {self._web_log_message_queue.qsize()} in queue.")
309
442
 
310
- def log_activation_memory_update(
311
- self,
312
- original_text_memories: list[str],
313
- new_text_memories: list[str],
314
- label: str,
315
- user_id: str,
316
- mem_cube_id: str,
317
- mem_cube: GeneralMemCube,
318
- ):
319
- """Log changes when activation memory is updated.
320
-
321
- Args:
322
- original_text_memories: List of original memory texts
323
- new_text_memories: List of new memory texts
324
- """
325
- original_set = set(original_text_memories)
326
- new_set = set(new_text_memories)
327
-
328
- # Identify changes
329
- added_memories = list(new_set - original_set) # Present in new but not original
330
-
331
- # recording messages
332
- for mem in added_memories:
333
- log_message_a = self.create_autofilled_log_item(
334
- log_content=mem,
335
- label=label,
336
- from_memory_type=TEXT_MEMORY_TYPE,
337
- to_memory_type=ACTIVATION_MEMORY_TYPE,
338
- user_id=user_id,
339
- mem_cube_id=mem_cube_id,
340
- mem_cube=mem_cube,
341
- )
342
- log_message_b = self.create_autofilled_log_item(
343
- log_content=mem,
344
- label=label,
345
- from_memory_type=ACTIVATION_MEMORY_TYPE,
346
- to_memory_type=PARAMETER_MEMORY_TYPE,
347
- user_id=user_id,
348
- mem_cube_id=mem_cube_id,
349
- mem_cube=mem_cube,
350
- )
351
- self._submit_web_logs(messages=[log_message_a, log_message_b])
352
- logger.info(
353
- f"{len(added_memories)} {LONG_TERM_MEMORY_TYPE} memorie(s) "
354
- f"transformed to {WORKING_MEMORY_TYPE} memories."
355
- )
356
-
357
- def log_working_memory_replacement(
358
- self,
359
- original_memory: list[TextualMemoryItem],
360
- new_memory: list[TextualMemoryItem],
361
- user_id: str,
362
- mem_cube_id: str,
363
- mem_cube: GeneralMemCube,
364
- ):
365
- """Log changes when working memory is replaced."""
366
- memory_type_map = {
367
- transform_name_to_key(name=m.memory): m.metadata.memory_type
368
- for m in original_memory + new_memory
369
- }
370
-
371
- original_text_memories = [m.memory for m in original_memory]
372
- new_text_memories = [m.memory for m in new_memory]
373
-
374
- # Convert to sets for efficient difference operations
375
- original_set = set(original_text_memories)
376
- new_set = set(new_text_memories)
377
-
378
- # Identify changes
379
- added_memories = list(new_set - original_set) # Present in new but not original
380
-
381
- # recording messages
382
- for mem in added_memories:
383
- normalized_mem = transform_name_to_key(name=mem)
384
- if normalized_mem not in memory_type_map:
385
- logger.error(f"Memory text not found in type mapping: {mem[:50]}...")
386
- # Get the memory type from the map, default to LONG_TERM_MEMORY_TYPE if not found
387
- mem_type = memory_type_map.get(normalized_mem, LONG_TERM_MEMORY_TYPE)
388
-
389
- if mem_type == WORKING_MEMORY_TYPE:
390
- logger.warning(f"Memory already in working memory: {mem[:50]}...")
391
- continue
392
-
393
- log_message = self.create_autofilled_log_item(
394
- log_content=mem,
395
- label=QUERY_LABEL,
396
- from_memory_type=mem_type,
397
- to_memory_type=WORKING_MEMORY_TYPE,
398
- user_id=user_id,
399
- mem_cube_id=mem_cube_id,
400
- mem_cube=mem_cube,
401
- )
402
- self._submit_web_logs(messages=log_message)
403
- logger.info(
404
- f"{len(added_memories)} {LONG_TERM_MEMORY_TYPE} memorie(s) "
405
- f"transformed to {WORKING_MEMORY_TYPE} memories."
406
- )
407
-
408
- def log_adding_user_inputs(
409
- self,
410
- user_inputs: list[str],
411
- user_id: str,
412
- mem_cube_id: str,
413
- mem_cube: GeneralMemCube,
414
- ):
415
- """Log changes when working memory is replaced."""
416
-
417
- # recording messages
418
- for input_str in user_inputs:
419
- log_message = self.create_autofilled_log_item(
420
- log_content=input_str,
421
- label=ADD_LABEL,
422
- from_memory_type=USER_INPUT_TYPE,
423
- to_memory_type=TEXT_MEMORY_TYPE,
424
- user_id=user_id,
425
- mem_cube_id=mem_cube_id,
426
- mem_cube=mem_cube,
427
- )
428
- self._submit_web_logs(messages=log_message)
429
- logger.info(
430
- f"{len(user_inputs)} {USER_INPUT_TYPE} memorie(s) "
431
- f"transformed to {TEXT_MEMORY_TYPE} memories."
432
- )
433
-
434
- def create_autofilled_log_item(
435
- self,
436
- log_content: str,
437
- label: str,
438
- from_memory_type: str,
439
- to_memory_type: str,
440
- user_id: str,
441
- mem_cube_id: str,
442
- mem_cube: GeneralMemCube,
443
- ) -> ScheduleLogForWebItem:
444
- text_mem_base: TreeTextMemory = mem_cube.text_mem
445
- current_memory_sizes = text_mem_base.get_current_memory_size()
446
- current_memory_sizes = {
447
- "long_term_memory_size": current_memory_sizes["LongTermMemory"],
448
- "user_memory_size": current_memory_sizes["UserMemory"],
449
- "working_memory_size": current_memory_sizes["WorkingMemory"],
450
- "transformed_act_memory_size": NOT_INITIALIZED,
451
- "parameter_memory_size": NOT_INITIALIZED,
452
- }
453
- memory_capacities = {
454
- "long_term_memory_capacity": text_mem_base.memory_manager.memory_size["LongTermMemory"],
455
- "user_memory_capacity": text_mem_base.memory_manager.memory_size["UserMemory"],
456
- "working_memory_capacity": text_mem_base.memory_manager.memory_size["WorkingMemory"],
457
- "transformed_act_memory_capacity": NOT_INITIALIZED,
458
- "parameter_memory_capacity": NOT_INITIALIZED,
459
- }
460
-
461
- log_message = ScheduleLogForWebItem(
462
- user_id=user_id,
463
- mem_cube_id=mem_cube_id,
464
- label=label,
465
- from_memory_type=from_memory_type,
466
- to_memory_type=to_memory_type,
467
- log_content=log_content,
468
- current_memory_sizes=current_memory_sizes,
469
- memory_capacities=memory_capacities,
470
- )
471
- return log_message
472
-
473
443
  def get_web_log_messages(self) -> list[dict]:
474
444
  """
475
445
  Retrieves all web log messages from the queue and returns them as a list of JSON-serializable dictionaries.