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.
- {memoryos-0.2.0.dist-info → memoryos-0.2.2.dist-info}/METADATA +67 -26
- memoryos-0.2.2.dist-info/RECORD +169 -0
- memoryos-0.2.2.dist-info/entry_points.txt +3 -0
- memos/__init__.py +1 -1
- memos/api/config.py +562 -0
- memos/api/context/context.py +147 -0
- memos/api/context/dependencies.py +90 -0
- memos/api/exceptions.py +28 -0
- memos/api/mcp_serve.py +502 -0
- memos/api/product_api.py +35 -0
- memos/api/product_models.py +163 -0
- memos/api/routers/__init__.py +1 -0
- memos/api/routers/product_router.py +386 -0
- memos/chunkers/sentence_chunker.py +8 -2
- memos/cli.py +113 -0
- memos/configs/embedder.py +27 -0
- memos/configs/graph_db.py +132 -3
- memos/configs/internet_retriever.py +6 -0
- memos/configs/llm.py +47 -0
- memos/configs/mem_cube.py +1 -1
- memos/configs/mem_os.py +5 -0
- memos/configs/mem_reader.py +9 -0
- memos/configs/mem_scheduler.py +107 -7
- memos/configs/mem_user.py +58 -0
- memos/configs/memory.py +5 -4
- memos/dependency.py +52 -0
- memos/embedders/ark.py +92 -0
- memos/embedders/factory.py +4 -0
- memos/embedders/sentence_transformer.py +8 -2
- memos/embedders/universal_api.py +32 -0
- memos/graph_dbs/base.py +11 -3
- memos/graph_dbs/factory.py +4 -0
- memos/graph_dbs/nebular.py +1364 -0
- memos/graph_dbs/neo4j.py +333 -124
- memos/graph_dbs/neo4j_community.py +300 -0
- memos/llms/base.py +9 -0
- memos/llms/deepseek.py +54 -0
- memos/llms/factory.py +10 -1
- memos/llms/hf.py +170 -13
- memos/llms/hf_singleton.py +114 -0
- memos/llms/ollama.py +4 -0
- memos/llms/openai.py +67 -1
- memos/llms/qwen.py +63 -0
- memos/llms/vllm.py +153 -0
- memos/log.py +1 -1
- memos/mem_cube/general.py +77 -16
- memos/mem_cube/utils.py +109 -0
- memos/mem_os/core.py +251 -51
- memos/mem_os/main.py +94 -12
- memos/mem_os/product.py +1220 -43
- memos/mem_os/utils/default_config.py +352 -0
- memos/mem_os/utils/format_utils.py +1401 -0
- memos/mem_reader/simple_struct.py +18 -10
- memos/mem_scheduler/base_scheduler.py +441 -40
- memos/mem_scheduler/general_scheduler.py +249 -248
- memos/mem_scheduler/modules/base.py +14 -5
- memos/mem_scheduler/modules/dispatcher.py +67 -4
- memos/mem_scheduler/modules/misc.py +104 -0
- memos/mem_scheduler/modules/monitor.py +240 -50
- memos/mem_scheduler/modules/rabbitmq_service.py +319 -0
- memos/mem_scheduler/modules/redis_service.py +32 -22
- memos/mem_scheduler/modules/retriever.py +167 -23
- memos/mem_scheduler/modules/scheduler_logger.py +255 -0
- memos/mem_scheduler/mos_for_test_scheduler.py +140 -0
- memos/mem_scheduler/schemas/__init__.py +0 -0
- memos/mem_scheduler/schemas/general_schemas.py +43 -0
- memos/mem_scheduler/{modules/schemas.py → schemas/message_schemas.py} +63 -61
- 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/persistent_user_manager.py +260 -0
- memos/mem_user/user_manager.py +4 -4
- memos/memories/activation/item.py +29 -0
- memos/memories/activation/kv.py +10 -3
- memos/memories/activation/vllmkv.py +219 -0
- memos/memories/factory.py +2 -0
- memos/memories/textual/base.py +1 -1
- memos/memories/textual/general.py +43 -97
- memos/memories/textual/item.py +5 -33
- memos/memories/textual/tree.py +22 -12
- memos/memories/textual/tree_text_memory/organize/conflict.py +9 -5
- memos/memories/textual/tree_text_memory/organize/manager.py +26 -18
- memos/memories/textual/tree_text_memory/organize/redundancy.py +25 -44
- memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py +50 -48
- memos/memories/textual/tree_text_memory/organize/reorganizer.py +81 -56
- 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/recall.py +0 -1
- memos/memories/textual/tree_text_memory/retrieve/reranker.py +2 -2
- memos/memories/textual/tree_text_memory/retrieve/retrieval_mid_structs.py +2 -0
- memos/memories/textual/tree_text_memory/retrieve/searcher.py +52 -28
- 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/parsers/markitdown.py +8 -2
- memos/settings.py +3 -1
- memos/templates/mem_reader_prompts.py +66 -23
- memos/templates/mem_scheduler_prompts.py +126 -43
- memos/templates/mos_prompts.py +87 -0
- memos/templates/tree_reorganize_prompts.py +85 -30
- memos/vec_dbs/base.py +12 -0
- memos/vec_dbs/qdrant.py +46 -20
- memoryos-0.2.0.dist-info/RECORD +0 -128
- memos/mem_scheduler/utils.py +0 -26
- {memoryos-0.2.0.dist-info → memoryos-0.2.2.dist-info}/LICENSE +0 -0
- {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.
|
|
11
|
-
|
|
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
|
-
|
|
15
|
-
DEFAULT_ACTIVATION_MEM_SIZE,
|
|
16
|
-
NOT_INITIALIZED,
|
|
10
|
+
DEFAULT_MAX_QUERY_KEY_WORDS,
|
|
17
11
|
QUERY_LABEL,
|
|
18
|
-
|
|
19
|
-
|
|
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.
|
|
49
|
-
ANSWER_LABEL: self.
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
self
|
|
56
|
-
|
|
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
|
-
|
|
59
|
-
logger.
|
|
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
|
-
|
|
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
|
|
83
|
+
Process and handle query trigger messages from the queue.
|
|
64
84
|
|
|
65
85
|
Args:
|
|
66
|
-
|
|
86
|
+
messages: List of query messages to process
|
|
67
87
|
"""
|
|
68
|
-
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
|
|
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
|
|
148
|
+
Process and handle answer trigger messages from the queue.
|
|
112
149
|
|
|
113
150
|
Args:
|
|
114
|
-
|
|
151
|
+
messages: List of answer messages to process
|
|
115
152
|
"""
|
|
116
|
-
logger.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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
|
-
|
|
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
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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
|
-
|
|
216
|
-
self
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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
|
-
|
|
250
|
-
|
|
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
|
|
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
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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
|
-
|
|
301
|
-
|
|
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.
|
|
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.
|
|
20
|
-
self.
|
|
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.
|
|
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.
|
|
83
|
+
self.current_mem_cube = value
|