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.
- {memoryos-0.2.1.dist-info → memoryos-1.0.0.dist-info}/METADATA +7 -1
- {memoryos-0.2.1.dist-info → memoryos-1.0.0.dist-info}/RECORD +87 -64
- memos/__init__.py +1 -1
- memos/api/config.py +158 -69
- memos/api/context/context.py +147 -0
- memos/api/context/dependencies.py +101 -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 +19 -0
- memos/configs/mem_os.py +5 -0
- memos/configs/mem_reader.py +9 -0
- memos/configs/mem_scheduler.py +54 -18
- memos/configs/mem_user.py +58 -0
- memos/graph_dbs/base.py +38 -3
- memos/graph_dbs/factory.py +2 -0
- memos/graph_dbs/nebular.py +1612 -0
- memos/graph_dbs/neo4j.py +18 -9
- memos/log.py +6 -1
- memos/mem_cube/utils.py +13 -6
- memos/mem_os/core.py +157 -37
- memos/mem_os/main.py +2 -2
- memos/mem_os/product.py +252 -201
- memos/mem_os/utils/default_config.py +1 -1
- memos/mem_os/utils/format_utils.py +281 -70
- memos/mem_os/utils/reference_utils.py +133 -0
- memos/mem_reader/simple_struct.py +13 -5
- memos/mem_scheduler/base_scheduler.py +239 -266
- memos/mem_scheduler/{modules → general_modules}/base.py +4 -5
- memos/mem_scheduler/{modules → general_modules}/dispatcher.py +57 -21
- memos/mem_scheduler/general_modules/misc.py +104 -0
- memos/mem_scheduler/{modules → general_modules}/rabbitmq_service.py +12 -10
- memos/mem_scheduler/{modules → general_modules}/redis_service.py +1 -1
- memos/mem_scheduler/general_modules/retriever.py +199 -0
- memos/mem_scheduler/general_modules/scheduler_logger.py +261 -0
- memos/mem_scheduler/general_scheduler.py +243 -80
- memos/mem_scheduler/monitors/__init__.py +0 -0
- memos/mem_scheduler/monitors/dispatcher_monitor.py +305 -0
- memos/mem_scheduler/{modules/monitor.py → monitors/general_monitor.py} +106 -57
- memos/mem_scheduler/mos_for_test_scheduler.py +23 -20
- memos/mem_scheduler/schemas/__init__.py +0 -0
- memos/mem_scheduler/schemas/general_schemas.py +44 -0
- memos/mem_scheduler/schemas/message_schemas.py +149 -0
- memos/mem_scheduler/schemas/monitor_schemas.py +337 -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 +102 -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 +5 -1
- memos/memories/activation/kv.py +20 -8
- memos/memories/textual/base.py +2 -2
- memos/memories/textual/general.py +36 -92
- memos/memories/textual/item.py +5 -33
- memos/memories/textual/tree.py +13 -7
- memos/memories/textual/tree_text_memory/organize/{conflict.py → handler.py} +34 -50
- memos/memories/textual/tree_text_memory/organize/manager.py +8 -96
- memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py +49 -43
- memos/memories/textual/tree_text_memory/organize/reorganizer.py +107 -142
- memos/memories/textual/tree_text_memory/retrieve/bochasearch.py +229 -0
- memos/memories/textual/tree_text_memory/retrieve/internet_retriever.py +6 -3
- memos/memories/textual/tree_text_memory/retrieve/internet_retriever_factory.py +11 -0
- memos/memories/textual/tree_text_memory/retrieve/recall.py +15 -8
- memos/memories/textual/tree_text_memory/retrieve/reranker.py +1 -1
- memos/memories/textual/tree_text_memory/retrieve/retrieval_mid_structs.py +2 -0
- memos/memories/textual/tree_text_memory/retrieve/searcher.py +191 -116
- memos/memories/textual/tree_text_memory/retrieve/task_goal_parser.py +47 -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/lockfree_dict.py +120 -0
- memos/memos_tools/notification_service.py +44 -0
- memos/memos_tools/notification_utils.py +96 -0
- memos/memos_tools/thread_safe_dict.py +288 -0
- memos/settings.py +3 -1
- memos/templates/mem_reader_prompts.py +4 -1
- memos/templates/mem_scheduler_prompts.py +62 -15
- memos/templates/mos_prompts.py +116 -0
- memos/templates/tree_reorganize_prompts.py +24 -17
- memos/utils.py +19 -0
- memos/mem_scheduler/modules/misc.py +0 -39
- memos/mem_scheduler/modules/retriever.py +0 -268
- memos/mem_scheduler/modules/schemas.py +0 -328
- memos/mem_scheduler/utils.py +0 -75
- memos/memories/textual/tree_text_memory/organize/redundancy.py +0 -193
- {memoryos-0.2.1.dist-info → memoryos-1.0.0.dist-info}/LICENSE +0 -0
- {memoryos-0.2.1.dist-info → memoryos-1.0.0.dist-info}/WHEEL +0 -0
- {memoryos-0.2.1.dist-info → memoryos-1.0.0.dist-info}/entry_points.txt +0 -0
- /memos/mem_scheduler/{modules → general_modules}/__init__.py +0 -0
|
@@ -9,30 +9,30 @@ from memos.configs.mem_scheduler import AuthConfig, BaseSchedulerConfig
|
|
|
9
9
|
from memos.llms.base import BaseLLM
|
|
10
10
|
from memos.log import get_logger
|
|
11
11
|
from memos.mem_cube.general import GeneralMemCube
|
|
12
|
-
from memos.mem_scheduler.
|
|
13
|
-
from memos.mem_scheduler.
|
|
14
|
-
from memos.mem_scheduler.
|
|
15
|
-
from memos.mem_scheduler.
|
|
16
|
-
from memos.mem_scheduler.
|
|
17
|
-
from memos.mem_scheduler.
|
|
18
|
-
from memos.mem_scheduler.
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
from memos.mem_scheduler.general_modules.dispatcher import SchedulerDispatcher
|
|
13
|
+
from memos.mem_scheduler.general_modules.misc import AutoDroppingQueue as Queue
|
|
14
|
+
from memos.mem_scheduler.general_modules.rabbitmq_service import RabbitMQSchedulerModule
|
|
15
|
+
from memos.mem_scheduler.general_modules.redis_service import RedisSchedulerModule
|
|
16
|
+
from memos.mem_scheduler.general_modules.retriever import SchedulerRetriever
|
|
17
|
+
from memos.mem_scheduler.general_modules.scheduler_logger import SchedulerLoggerModule
|
|
18
|
+
from memos.mem_scheduler.monitors.dispatcher_monitor import SchedulerDispatcherMonitor
|
|
19
|
+
from memos.mem_scheduler.monitors.general_monitor import SchedulerGeneralMonitor
|
|
20
|
+
from memos.mem_scheduler.schemas.general_schemas import (
|
|
21
21
|
DEFAULT_ACT_MEM_DUMP_PATH,
|
|
22
22
|
DEFAULT_CONSUME_INTERVAL_SECONDS,
|
|
23
23
|
DEFAULT_THREAD__POOL_MAX_WORKERS,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
USER_INPUT_TYPE,
|
|
30
|
-
WORKING_MEMORY_TYPE,
|
|
24
|
+
MemCubeID,
|
|
25
|
+
TreeTextMemory_SEARCH_METHOD,
|
|
26
|
+
UserID,
|
|
27
|
+
)
|
|
28
|
+
from memos.mem_scheduler.schemas.message_schemas import (
|
|
31
29
|
ScheduleLogForWebItem,
|
|
32
30
|
ScheduleMessageItem,
|
|
33
|
-
TreeTextMemory_SEARCH_METHOD,
|
|
34
31
|
)
|
|
35
|
-
from memos.mem_scheduler.
|
|
32
|
+
from memos.mem_scheduler.schemas.monitor_schemas import MemoryMonitorItem
|
|
33
|
+
from memos.mem_scheduler.utils.filter_utils import (
|
|
34
|
+
transform_name_to_key,
|
|
35
|
+
)
|
|
36
36
|
from memos.memories.activation.kv import KVCacheMemory
|
|
37
37
|
from memos.memories.activation.vllmkv import VLLMKVCacheItem, VLLMKVCacheMemory
|
|
38
38
|
from memos.memories.textual.tree import TextualMemoryItem, TreeTextMemory
|
|
@@ -42,7 +42,7 @@ from memos.templates.mem_scheduler_prompts import MEMORY_ASSEMBLY_TEMPLATE
|
|
|
42
42
|
logger = get_logger(__name__)
|
|
43
43
|
|
|
44
44
|
|
|
45
|
-
class BaseScheduler(RabbitMQSchedulerModule, RedisSchedulerModule):
|
|
45
|
+
class BaseScheduler(RabbitMQSchedulerModule, RedisSchedulerModule, SchedulerLoggerModule):
|
|
46
46
|
"""Base class for all mem_scheduler."""
|
|
47
47
|
|
|
48
48
|
def __init__(self, config: BaseSchedulerConfig):
|
|
@@ -51,22 +51,22 @@ class BaseScheduler(RabbitMQSchedulerModule, RedisSchedulerModule):
|
|
|
51
51
|
self.config = config
|
|
52
52
|
|
|
53
53
|
# hyper-parameters
|
|
54
|
-
self.top_k = self.config.get("top_k",
|
|
54
|
+
self.top_k = self.config.get("top_k", 10)
|
|
55
55
|
self.context_window_size = self.config.get("context_window_size", 5)
|
|
56
|
-
self.
|
|
56
|
+
self.enable_activation_memory = self.config.get("enable_activation_memory", False)
|
|
57
57
|
self.act_mem_dump_path = self.config.get("act_mem_dump_path", DEFAULT_ACT_MEM_DUMP_PATH)
|
|
58
58
|
self.search_method = TreeTextMemory_SEARCH_METHOD
|
|
59
|
-
|
|
60
59
|
self.enable_parallel_dispatch = self.config.get("enable_parallel_dispatch", False)
|
|
61
|
-
self.
|
|
60
|
+
self.thread_pool_max_workers = self.config.get(
|
|
62
61
|
"thread_pool_max_workers", DEFAULT_THREAD__POOL_MAX_WORKERS
|
|
63
62
|
)
|
|
64
63
|
|
|
65
64
|
self.retriever: SchedulerRetriever | None = None
|
|
66
|
-
self.monitor:
|
|
67
|
-
|
|
65
|
+
self.monitor: SchedulerGeneralMonitor | None = None
|
|
66
|
+
self.dispatcher_monitor: SchedulerDispatcherMonitor | None = None
|
|
68
67
|
self.dispatcher = SchedulerDispatcher(
|
|
69
|
-
max_workers=self.
|
|
68
|
+
max_workers=self.thread_pool_max_workers,
|
|
69
|
+
enable_parallel_dispatch=self.enable_parallel_dispatch,
|
|
70
70
|
)
|
|
71
71
|
|
|
72
72
|
# internal message queue
|
|
@@ -85,7 +85,9 @@ class BaseScheduler(RabbitMQSchedulerModule, RedisSchedulerModule):
|
|
|
85
85
|
|
|
86
86
|
# other attributes
|
|
87
87
|
self._context_lock = threading.Lock()
|
|
88
|
-
self.
|
|
88
|
+
self.current_user_id: UserID | str | None = None
|
|
89
|
+
self.current_mem_cube_id: MemCubeID | str | None = None
|
|
90
|
+
self.current_mem_cube: GeneralMemCube | None = None
|
|
89
91
|
self.auth_config_path: str | Path | None = self.config.get("auth_config_path", None)
|
|
90
92
|
self.auth_config = None
|
|
91
93
|
self.rabbitmq_config = None
|
|
@@ -97,15 +99,19 @@ class BaseScheduler(RabbitMQSchedulerModule, RedisSchedulerModule):
|
|
|
97
99
|
# initialize submodules
|
|
98
100
|
self.chat_llm = chat_llm
|
|
99
101
|
self.process_llm = process_llm
|
|
100
|
-
self.monitor =
|
|
102
|
+
self.monitor = SchedulerGeneralMonitor(process_llm=self.process_llm, config=self.config)
|
|
103
|
+
self.dispatcher_monitor = SchedulerDispatcherMonitor(config=self.config)
|
|
101
104
|
self.retriever = SchedulerRetriever(process_llm=self.process_llm, config=self.config)
|
|
102
|
-
|
|
105
|
+
|
|
106
|
+
if self.enable_parallel_dispatch:
|
|
107
|
+
self.dispatcher_monitor.initialize(dispatcher=self.dispatcher)
|
|
108
|
+
self.dispatcher_monitor.start()
|
|
103
109
|
|
|
104
110
|
# initialize with auth_cofig
|
|
105
111
|
if self.auth_config_path is not None and Path(self.auth_config_path).exists():
|
|
106
|
-
self.auth_config = AuthConfig.
|
|
112
|
+
self.auth_config = AuthConfig.from_local_config(config_path=self.auth_config_path)
|
|
107
113
|
elif AuthConfig.default_config_exists():
|
|
108
|
-
self.auth_config = AuthConfig.
|
|
114
|
+
self.auth_config = AuthConfig.from_local_config()
|
|
109
115
|
else:
|
|
110
116
|
self.auth_config = None
|
|
111
117
|
|
|
@@ -118,58 +124,145 @@ class BaseScheduler(RabbitMQSchedulerModule, RedisSchedulerModule):
|
|
|
118
124
|
@property
|
|
119
125
|
def mem_cube(self) -> GeneralMemCube:
|
|
120
126
|
"""The memory cube associated with this MemChat."""
|
|
121
|
-
return self.
|
|
127
|
+
return self.current_mem_cube
|
|
122
128
|
|
|
123
129
|
@mem_cube.setter
|
|
124
130
|
def mem_cube(self, value: GeneralMemCube) -> None:
|
|
125
131
|
"""The memory cube associated with this MemChat."""
|
|
126
|
-
self.
|
|
132
|
+
self.current_mem_cube = value
|
|
127
133
|
self.retriever.mem_cube = value
|
|
128
134
|
|
|
129
135
|
def _set_current_context_from_message(self, msg: ScheduleMessageItem) -> None:
|
|
130
136
|
"""Update current user/cube context from the incoming message (thread-safe)."""
|
|
131
137
|
with self._context_lock:
|
|
132
|
-
self.
|
|
133
|
-
self.
|
|
134
|
-
self.
|
|
138
|
+
self.current_user_id = msg.user_id
|
|
139
|
+
self.current_mem_cube_id = msg.mem_cube_id
|
|
140
|
+
self.current_mem_cube = msg.mem_cube
|
|
135
141
|
|
|
136
|
-
def
|
|
137
|
-
|
|
142
|
+
def transform_working_memories_to_monitors(
|
|
143
|
+
self, query_keywords, memories: list[TextualMemoryItem]
|
|
144
|
+
) -> list[MemoryMonitorItem]:
|
|
145
|
+
"""
|
|
146
|
+
Convert a list of TextualMemoryItem objects into MemoryMonitorItem objects
|
|
147
|
+
with importance scores based on keyword matching.
|
|
138
148
|
|
|
139
149
|
Args:
|
|
140
|
-
|
|
141
|
-
label: Expected message label (e.g., QUERY_LABEL/ANSWER_LABEL).
|
|
150
|
+
memories: List of TextualMemoryItem objects to be transformed.
|
|
142
151
|
|
|
143
152
|
Returns:
|
|
144
|
-
|
|
153
|
+
List of MemoryMonitorItem objects with computed importance scores.
|
|
145
154
|
"""
|
|
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
|
|
151
155
|
|
|
152
|
-
|
|
153
|
-
|
|
156
|
+
result = []
|
|
157
|
+
mem_length = len(memories)
|
|
158
|
+
for idx, mem in enumerate(memories):
|
|
159
|
+
text_mem = mem.memory
|
|
160
|
+
mem_key = transform_name_to_key(name=text_mem)
|
|
161
|
+
|
|
162
|
+
# Calculate importance score based on keyword matches
|
|
163
|
+
keywords_score = 0
|
|
164
|
+
if query_keywords and text_mem:
|
|
165
|
+
for keyword, count in query_keywords.items():
|
|
166
|
+
keyword_count = text_mem.count(keyword)
|
|
167
|
+
if keyword_count > 0:
|
|
168
|
+
keywords_score += keyword_count * count
|
|
169
|
+
logger.debug(
|
|
170
|
+
f"Matched keyword '{keyword}' {keyword_count} times, added {keywords_score} to keywords_score"
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# rank score
|
|
174
|
+
sorting_score = mem_length - idx
|
|
175
|
+
|
|
176
|
+
mem_monitor = MemoryMonitorItem(
|
|
177
|
+
memory_text=text_mem,
|
|
178
|
+
tree_memory_item=mem,
|
|
179
|
+
tree_memory_item_mapping_key=mem_key,
|
|
180
|
+
sorting_score=sorting_score,
|
|
181
|
+
keywords_score=keywords_score,
|
|
182
|
+
recording_count=1,
|
|
183
|
+
)
|
|
184
|
+
result.append(mem_monitor)
|
|
154
185
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
label: Expected message label (e.g., QUERY_LABEL/ANSWER_LABEL).
|
|
186
|
+
logger.info(f"Transformed {len(result)} memories to monitors")
|
|
187
|
+
return result
|
|
158
188
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
189
|
+
def replace_working_memory(
|
|
190
|
+
self,
|
|
191
|
+
user_id: UserID | str,
|
|
192
|
+
mem_cube_id: MemCubeID | str,
|
|
193
|
+
mem_cube: GeneralMemCube,
|
|
194
|
+
original_memory: list[TextualMemoryItem],
|
|
195
|
+
new_memory: list[TextualMemoryItem],
|
|
196
|
+
) -> None | list[TextualMemoryItem]:
|
|
197
|
+
"""Replace working memory with new memories after reranking."""
|
|
198
|
+
text_mem_base = mem_cube.text_mem
|
|
199
|
+
if isinstance(text_mem_base, TreeTextMemory):
|
|
200
|
+
text_mem_base: TreeTextMemory = text_mem_base
|
|
201
|
+
|
|
202
|
+
# process rerank memories with llm
|
|
203
|
+
query_monitor = self.monitor.query_monitors[user_id][mem_cube_id]
|
|
204
|
+
query_history = query_monitor.get_queries_with_timesort()
|
|
205
|
+
memories_with_new_order, rerank_success_flag = (
|
|
206
|
+
self.retriever.process_and_rerank_memories(
|
|
207
|
+
queries=query_history,
|
|
208
|
+
original_memory=original_memory,
|
|
209
|
+
new_memory=new_memory,
|
|
210
|
+
top_k=self.top_k,
|
|
211
|
+
)
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
# update working memory monitors
|
|
215
|
+
query_keywords = query_monitor.get_keywords_collections()
|
|
216
|
+
logger.info(
|
|
217
|
+
f"Processing {len(memories_with_new_order)} memories with {len(query_keywords)} query keywords"
|
|
218
|
+
)
|
|
219
|
+
new_working_memory_monitors = self.transform_working_memories_to_monitors(
|
|
220
|
+
query_keywords=query_keywords,
|
|
221
|
+
memories=memories_with_new_order,
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
if not rerank_success_flag:
|
|
225
|
+
for one in new_working_memory_monitors:
|
|
226
|
+
one.sorting_score = 0
|
|
227
|
+
|
|
228
|
+
logger.info(f"update {len(new_working_memory_monitors)} working_memory_monitors")
|
|
229
|
+
self.monitor.update_working_memory_monitors(
|
|
230
|
+
new_working_memory_monitors=new_working_memory_monitors,
|
|
231
|
+
user_id=user_id,
|
|
232
|
+
mem_cube_id=mem_cube_id,
|
|
233
|
+
mem_cube=mem_cube,
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
mem_monitors: list[MemoryMonitorItem] = self.monitor.working_memory_monitors[user_id][
|
|
237
|
+
mem_cube_id
|
|
238
|
+
].get_sorted_mem_monitors(reverse=True)
|
|
239
|
+
new_working_memories = [mem_monitor.tree_memory_item for mem_monitor in mem_monitors]
|
|
240
|
+
|
|
241
|
+
text_mem_base.replace_working_memory(memories=new_working_memories)
|
|
242
|
+
|
|
243
|
+
logger.info(
|
|
244
|
+
f"The working memory has been replaced with {len(memories_with_new_order)} new memories."
|
|
245
|
+
)
|
|
246
|
+
self.log_working_memory_replacement(
|
|
247
|
+
original_memory=original_memory,
|
|
248
|
+
new_memory=new_working_memories,
|
|
249
|
+
user_id=user_id,
|
|
250
|
+
mem_cube_id=mem_cube_id,
|
|
251
|
+
mem_cube=mem_cube,
|
|
252
|
+
log_func_callback=self._submit_web_logs,
|
|
253
|
+
)
|
|
254
|
+
else:
|
|
255
|
+
logger.error("memory_base is not supported")
|
|
256
|
+
memories_with_new_order = new_memory
|
|
257
|
+
|
|
258
|
+
return memories_with_new_order
|
|
166
259
|
|
|
167
260
|
def update_activation_memory(
|
|
168
261
|
self,
|
|
169
262
|
new_memories: list[str | TextualMemoryItem],
|
|
170
263
|
label: str,
|
|
171
|
-
user_id: str,
|
|
172
|
-
mem_cube_id: str,
|
|
264
|
+
user_id: UserID | str,
|
|
265
|
+
mem_cube_id: MemCubeID | str,
|
|
173
266
|
mem_cube: GeneralMemCube,
|
|
174
267
|
) -> None:
|
|
175
268
|
"""
|
|
@@ -195,7 +288,7 @@ class BaseScheduler(RabbitMQSchedulerModule, RedisSchedulerModule):
|
|
|
195
288
|
logger.error("Not Implemented.")
|
|
196
289
|
return
|
|
197
290
|
|
|
198
|
-
|
|
291
|
+
new_text_memory = MEMORY_ASSEMBLY_TEMPLATE.format(
|
|
199
292
|
memory_text="".join(
|
|
200
293
|
[
|
|
201
294
|
f"{i + 1}. {sentence.strip()}\n"
|
|
@@ -211,10 +304,20 @@ class BaseScheduler(RabbitMQSchedulerModule, RedisSchedulerModule):
|
|
|
211
304
|
if len(original_cache_items) > 0:
|
|
212
305
|
pre_cache_item: VLLMKVCacheItem = original_cache_items[-1]
|
|
213
306
|
original_text_memories = pre_cache_item.records.text_memories
|
|
307
|
+
original_composed_text_memory = pre_cache_item.records.composed_text_memory
|
|
308
|
+
if original_composed_text_memory == new_text_memory:
|
|
309
|
+
logger.warning(
|
|
310
|
+
"Skipping memory update - new composition matches existing cache: %s",
|
|
311
|
+
new_text_memory[:50] + "..."
|
|
312
|
+
if len(new_text_memory) > 50
|
|
313
|
+
else new_text_memory,
|
|
314
|
+
)
|
|
315
|
+
return
|
|
214
316
|
act_mem.delete_all()
|
|
215
317
|
|
|
216
|
-
cache_item = act_mem.extract(
|
|
318
|
+
cache_item = act_mem.extract(new_text_memory)
|
|
217
319
|
cache_item.records.text_memories = new_text_memories
|
|
320
|
+
cache_item.records.timestamp = datetime.utcnow()
|
|
218
321
|
|
|
219
322
|
act_mem.add([cache_item])
|
|
220
323
|
act_mem.dump(self.act_mem_dump_path)
|
|
@@ -226,6 +329,7 @@ class BaseScheduler(RabbitMQSchedulerModule, RedisSchedulerModule):
|
|
|
226
329
|
user_id=user_id,
|
|
227
330
|
mem_cube_id=mem_cube_id,
|
|
228
331
|
mem_cube=mem_cube,
|
|
332
|
+
log_func_callback=self._submit_web_logs,
|
|
229
333
|
)
|
|
230
334
|
|
|
231
335
|
except Exception as e:
|
|
@@ -235,50 +339,72 @@ class BaseScheduler(RabbitMQSchedulerModule, RedisSchedulerModule):
|
|
|
235
339
|
self,
|
|
236
340
|
interval_seconds: int,
|
|
237
341
|
label: str,
|
|
238
|
-
user_id: str,
|
|
239
|
-
mem_cube_id: str,
|
|
342
|
+
user_id: UserID | str,
|
|
343
|
+
mem_cube_id: MemCubeID | str,
|
|
240
344
|
mem_cube: GeneralMemCube,
|
|
241
345
|
):
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
346
|
+
try:
|
|
347
|
+
if (
|
|
348
|
+
self.monitor.last_activation_mem_update_time == datetime.min
|
|
349
|
+
or self.monitor.timed_trigger(
|
|
350
|
+
last_time=self.monitor.last_activation_mem_update_time,
|
|
351
|
+
interval_seconds=interval_seconds,
|
|
352
|
+
)
|
|
353
|
+
):
|
|
354
|
+
logger.info(
|
|
355
|
+
f"Updating activation memory for user {user_id} and mem_cube {mem_cube_id}"
|
|
356
|
+
)
|
|
249
357
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
358
|
+
if (
|
|
359
|
+
user_id not in self.monitor.working_memory_monitors
|
|
360
|
+
or mem_cube_id not in self.monitor.working_memory_monitors[user_id]
|
|
361
|
+
or len(self.monitor.working_memory_monitors[user_id][mem_cube_id].memories) == 0
|
|
362
|
+
):
|
|
363
|
+
logger.warning(
|
|
364
|
+
"No memories found in working_memory_monitors, activation memory update is skipped"
|
|
365
|
+
)
|
|
366
|
+
return
|
|
367
|
+
|
|
368
|
+
self.monitor.update_activation_memory_monitors(
|
|
369
|
+
user_id=user_id, mem_cube_id=mem_cube_id, mem_cube=mem_cube
|
|
370
|
+
)
|
|
253
371
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
372
|
+
new_activation_memories = [
|
|
373
|
+
m.memory_text
|
|
374
|
+
for m in self.monitor.activation_memory_monitors[user_id][mem_cube_id].memories
|
|
375
|
+
]
|
|
258
376
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
377
|
+
logger.info(
|
|
378
|
+
f"Collected {len(new_activation_memories)} new memory entries for processing"
|
|
379
|
+
)
|
|
380
|
+
# Print the content of each new activation memory
|
|
381
|
+
for i, memory in enumerate(new_activation_memories[:5], 1):
|
|
382
|
+
logger.info(
|
|
383
|
+
f"Part of New Activation Memorires | {i}/{len(new_activation_memories)}: {memory[:20]}"
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
self.update_activation_memory(
|
|
387
|
+
new_memories=new_activation_memories,
|
|
388
|
+
label=label,
|
|
389
|
+
user_id=user_id,
|
|
390
|
+
mem_cube_id=mem_cube_id,
|
|
391
|
+
mem_cube=mem_cube,
|
|
392
|
+
)
|
|
262
393
|
|
|
263
|
-
|
|
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
|
-
)
|
|
394
|
+
self.monitor.last_activation_mem_update_time = datetime.utcnow()
|
|
270
395
|
|
|
271
|
-
|
|
396
|
+
logger.debug(
|
|
397
|
+
f"Activation memory update completed at {self.monitor.last_activation_mem_update_time}"
|
|
398
|
+
)
|
|
272
399
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
)
|
|
400
|
+
else:
|
|
401
|
+
logger.info(
|
|
402
|
+
f"Skipping update - {interval_seconds} second interval not yet reached. "
|
|
403
|
+
f"Last update time is {self.monitor.last_activation_mem_update_time} and now is"
|
|
404
|
+
f"{datetime.utcnow()}"
|
|
405
|
+
)
|
|
406
|
+
except Exception as e:
|
|
407
|
+
logger.error(f"Error in update_activation_memory_periodically: {e}", exc_info=True)
|
|
282
408
|
|
|
283
409
|
def submit_messages(self, messages: ScheduleMessageItem | list[ScheduleMessageItem]):
|
|
284
410
|
"""Submit multiple messages to the message queue."""
|
|
@@ -289,7 +415,9 @@ class BaseScheduler(RabbitMQSchedulerModule, RedisSchedulerModule):
|
|
|
289
415
|
self.memos_message_queue.put(message)
|
|
290
416
|
logger.info(f"Submitted message: {message.label} - {message.content}")
|
|
291
417
|
|
|
292
|
-
def _submit_web_logs(
|
|
418
|
+
def _submit_web_logs(
|
|
419
|
+
self, messages: ScheduleLogForWebItem | list[ScheduleLogForWebItem]
|
|
420
|
+
) -> None:
|
|
293
421
|
"""Submit log messages to the web log queue and optionally to RabbitMQ.
|
|
294
422
|
|
|
295
423
|
Args:
|
|
@@ -300,176 +428,14 @@ class BaseScheduler(RabbitMQSchedulerModule, RedisSchedulerModule):
|
|
|
300
428
|
|
|
301
429
|
for message in messages:
|
|
302
430
|
self._web_log_message_queue.put(message)
|
|
303
|
-
|
|
431
|
+
message_info = message.debug_info()
|
|
432
|
+
logger.debug(f"Submitted Scheduling log for web: {message_info}")
|
|
304
433
|
|
|
305
434
|
if self.is_rabbitmq_connected():
|
|
306
|
-
logger.info("Submitted Scheduling log to rabbitmq")
|
|
435
|
+
logger.info(f"Submitted Scheduling log to rabbitmq: {message_info}")
|
|
307
436
|
self.rabbitmq_publish_message(message=message.to_dict())
|
|
308
437
|
logger.debug(f"{len(messages)} submitted. {self._web_log_message_queue.qsize()} in queue.")
|
|
309
438
|
|
|
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
439
|
def get_web_log_messages(self) -> list[dict]:
|
|
474
440
|
"""
|
|
475
441
|
Retrieves all web log messages from the queue and returns them as a list of JSON-serializable dictionaries.
|
|
@@ -536,7 +502,9 @@ class BaseScheduler(RabbitMQSchedulerModule, RedisSchedulerModule):
|
|
|
536
502
|
|
|
537
503
|
# Initialize dispatcher resources
|
|
538
504
|
if self.enable_parallel_dispatch:
|
|
539
|
-
logger.info(
|
|
505
|
+
logger.info(
|
|
506
|
+
f"Initializing dispatcher thread pool with {self.thread_pool_max_workers} workers"
|
|
507
|
+
)
|
|
540
508
|
|
|
541
509
|
# Start consumer thread
|
|
542
510
|
self._running = True
|
|
@@ -571,10 +539,15 @@ class BaseScheduler(RabbitMQSchedulerModule, RedisSchedulerModule):
|
|
|
571
539
|
logger.info("Consumer thread stopped")
|
|
572
540
|
|
|
573
541
|
# Shutdown dispatcher
|
|
574
|
-
if
|
|
542
|
+
if self.dispatcher:
|
|
575
543
|
logger.info("Shutting down dispatcher...")
|
|
576
544
|
self.dispatcher.shutdown()
|
|
577
545
|
|
|
546
|
+
# Shutdown dispatcher_monitor
|
|
547
|
+
if self.dispatcher_monitor:
|
|
548
|
+
logger.info("Shutting down monitor...")
|
|
549
|
+
self.dispatcher_monitor.stop()
|
|
550
|
+
|
|
578
551
|
# Clean up queues
|
|
579
552
|
self._cleanup_queues()
|
|
580
553
|
logger.info("Memory Scheduler stopped completely")
|