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.
- {memoryos-0.2.1.dist-info → memoryos-0.2.2.dist-info}/METADATA +2 -1
- {memoryos-0.2.1.dist-info → memoryos-0.2.2.dist-info}/RECORD +72 -55
- memos/__init__.py +1 -1
- memos/api/config.py +156 -65
- memos/api/context/context.py +147 -0
- memos/api/context/dependencies.py +90 -0
- memos/api/product_models.py +5 -1
- memos/api/routers/product_router.py +54 -26
- memos/configs/graph_db.py +49 -1
- memos/configs/internet_retriever.py +6 -0
- memos/configs/mem_os.py +5 -0
- memos/configs/mem_reader.py +9 -0
- memos/configs/mem_scheduler.py +18 -4
- memos/configs/mem_user.py +58 -0
- memos/graph_dbs/base.py +9 -1
- memos/graph_dbs/factory.py +2 -0
- memos/graph_dbs/nebular.py +1364 -0
- memos/graph_dbs/neo4j.py +4 -4
- memos/log.py +1 -1
- memos/mem_cube/utils.py +13 -6
- memos/mem_os/core.py +140 -30
- memos/mem_os/main.py +1 -1
- memos/mem_os/product.py +266 -152
- memos/mem_os/utils/format_utils.py +314 -67
- memos/mem_reader/simple_struct.py +13 -5
- memos/mem_scheduler/base_scheduler.py +220 -250
- memos/mem_scheduler/general_scheduler.py +193 -73
- memos/mem_scheduler/modules/base.py +5 -5
- memos/mem_scheduler/modules/dispatcher.py +6 -9
- memos/mem_scheduler/modules/misc.py +81 -16
- memos/mem_scheduler/modules/monitor.py +52 -41
- memos/mem_scheduler/modules/rabbitmq_service.py +9 -7
- memos/mem_scheduler/modules/retriever.py +108 -191
- memos/mem_scheduler/modules/scheduler_logger.py +255 -0
- memos/mem_scheduler/mos_for_test_scheduler.py +16 -19
- memos/mem_scheduler/schemas/__init__.py +0 -0
- memos/mem_scheduler/schemas/general_schemas.py +43 -0
- memos/mem_scheduler/schemas/message_schemas.py +148 -0
- memos/mem_scheduler/schemas/monitor_schemas.py +329 -0
- memos/mem_scheduler/utils/__init__.py +0 -0
- memos/mem_scheduler/utils/filter_utils.py +176 -0
- memos/mem_scheduler/utils/misc_utils.py +61 -0
- memos/mem_user/factory.py +94 -0
- memos/mem_user/mysql_persistent_user_manager.py +271 -0
- memos/mem_user/mysql_user_manager.py +500 -0
- memos/mem_user/persistent_factory.py +96 -0
- memos/mem_user/user_manager.py +4 -4
- memos/memories/activation/item.py +4 -0
- memos/memories/textual/base.py +1 -1
- memos/memories/textual/general.py +35 -91
- memos/memories/textual/item.py +5 -33
- memos/memories/textual/tree.py +13 -7
- memos/memories/textual/tree_text_memory/organize/conflict.py +4 -2
- memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py +47 -43
- memos/memories/textual/tree_text_memory/organize/reorganizer.py +8 -5
- memos/memories/textual/tree_text_memory/retrieve/internet_retriever.py +6 -3
- memos/memories/textual/tree_text_memory/retrieve/internet_retriever_factory.py +2 -0
- memos/memories/textual/tree_text_memory/retrieve/retrieval_mid_structs.py +2 -0
- memos/memories/textual/tree_text_memory/retrieve/searcher.py +46 -23
- memos/memories/textual/tree_text_memory/retrieve/task_goal_parser.py +42 -15
- memos/memories/textual/tree_text_memory/retrieve/utils.py +11 -7
- memos/memories/textual/tree_text_memory/retrieve/xinyusearch.py +62 -58
- memos/memos_tools/dinding_report_bot.py +422 -0
- memos/memos_tools/notification_service.py +44 -0
- memos/memos_tools/notification_utils.py +96 -0
- memos/settings.py +3 -1
- memos/templates/mem_reader_prompts.py +2 -1
- memos/templates/mem_scheduler_prompts.py +41 -7
- memos/templates/mos_prompts.py +87 -0
- memos/mem_scheduler/modules/schemas.py +0 -328
- memos/mem_scheduler/utils.py +0 -75
- {memoryos-0.2.1.dist-info → memoryos-0.2.2.dist-info}/LICENSE +0 -0
- {memoryos-0.2.1.dist-info → memoryos-0.2.2.dist-info}/WHEEL +0 -0
- {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.
|
|
19
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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.
|
|
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",
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
133
|
-
self.
|
|
134
|
-
self.
|
|
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
|
|
137
|
-
|
|
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
|
-
|
|
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
|
-
|
|
144
|
+
List of MemoryMonitorItem objects with computed importance scores.
|
|
145
145
|
"""
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
153
|
-
|
|
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
|
-
|
|
156
|
-
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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
|
-
|
|
251
|
-
|
|
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
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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
|
-
|
|
260
|
-
|
|
261
|
-
|
|
387
|
+
logger.info(
|
|
388
|
+
f"Collected {len(new_activation_memories)} new memory entries for processing"
|
|
389
|
+
)
|
|
262
390
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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
|
-
|
|
399
|
+
self.monitor.last_activation_mem_update_time = datetime.now()
|
|
272
400
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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(
|
|
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
|
-
|
|
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.
|