MemoryOS 1.0.1__py3-none-any.whl → 1.1.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-1.0.1.dist-info → memoryos-1.1.2.dist-info}/METADATA +7 -2
- {memoryos-1.0.1.dist-info → memoryos-1.1.2.dist-info}/RECORD +79 -65
- {memoryos-1.0.1.dist-info → memoryos-1.1.2.dist-info}/WHEEL +1 -1
- memos/__init__.py +1 -1
- memos/api/client.py +109 -0
- memos/api/config.py +11 -9
- memos/api/context/dependencies.py +15 -55
- memos/api/middleware/request_context.py +9 -40
- memos/api/product_api.py +2 -3
- memos/api/product_models.py +91 -16
- memos/api/routers/product_router.py +23 -16
- memos/api/start_api.py +10 -0
- memos/configs/graph_db.py +4 -0
- memos/configs/mem_scheduler.py +38 -3
- memos/context/context.py +255 -0
- memos/embedders/factory.py +2 -0
- memos/graph_dbs/nebular.py +230 -232
- memos/graph_dbs/neo4j.py +35 -1
- memos/graph_dbs/neo4j_community.py +7 -0
- memos/llms/factory.py +2 -0
- memos/llms/openai.py +74 -2
- memos/log.py +27 -15
- memos/mem_cube/general.py +3 -1
- memos/mem_os/core.py +60 -22
- memos/mem_os/main.py +3 -6
- memos/mem_os/product.py +35 -11
- memos/mem_reader/factory.py +2 -0
- memos/mem_reader/simple_struct.py +127 -74
- memos/mem_scheduler/analyzer/__init__.py +0 -0
- memos/mem_scheduler/analyzer/mos_for_test_scheduler.py +569 -0
- memos/mem_scheduler/analyzer/scheduler_for_eval.py +280 -0
- memos/mem_scheduler/base_scheduler.py +126 -56
- memos/mem_scheduler/general_modules/dispatcher.py +2 -2
- memos/mem_scheduler/general_modules/misc.py +99 -1
- memos/mem_scheduler/general_modules/scheduler_logger.py +17 -11
- memos/mem_scheduler/general_scheduler.py +40 -88
- memos/mem_scheduler/memory_manage_modules/__init__.py +5 -0
- memos/mem_scheduler/memory_manage_modules/memory_filter.py +308 -0
- memos/mem_scheduler/{general_modules → memory_manage_modules}/retriever.py +34 -7
- memos/mem_scheduler/monitors/dispatcher_monitor.py +9 -8
- memos/mem_scheduler/monitors/general_monitor.py +119 -39
- memos/mem_scheduler/optimized_scheduler.py +124 -0
- memos/mem_scheduler/orm_modules/__init__.py +0 -0
- memos/mem_scheduler/orm_modules/base_model.py +635 -0
- memos/mem_scheduler/orm_modules/monitor_models.py +261 -0
- memos/mem_scheduler/scheduler_factory.py +2 -0
- memos/mem_scheduler/schemas/monitor_schemas.py +96 -29
- memos/mem_scheduler/utils/config_utils.py +100 -0
- memos/mem_scheduler/utils/db_utils.py +33 -0
- memos/mem_scheduler/utils/filter_utils.py +1 -1
- memos/mem_scheduler/webservice_modules/__init__.py +0 -0
- memos/memories/activation/kv.py +2 -1
- memos/memories/textual/item.py +95 -16
- memos/memories/textual/naive.py +1 -1
- memos/memories/textual/tree.py +27 -3
- memos/memories/textual/tree_text_memory/organize/handler.py +4 -2
- memos/memories/textual/tree_text_memory/organize/manager.py +28 -14
- memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py +1 -2
- memos/memories/textual/tree_text_memory/organize/reorganizer.py +75 -23
- memos/memories/textual/tree_text_memory/retrieve/bochasearch.py +7 -5
- memos/memories/textual/tree_text_memory/retrieve/internet_retriever.py +6 -2
- memos/memories/textual/tree_text_memory/retrieve/internet_retriever_factory.py +2 -0
- memos/memories/textual/tree_text_memory/retrieve/recall.py +70 -22
- memos/memories/textual/tree_text_memory/retrieve/searcher.py +101 -33
- memos/memories/textual/tree_text_memory/retrieve/xinyusearch.py +5 -4
- memos/memos_tools/singleton.py +174 -0
- memos/memos_tools/thread_safe_dict.py +22 -0
- memos/memos_tools/thread_safe_dict_segment.py +382 -0
- memos/parsers/factory.py +2 -0
- memos/reranker/concat.py +59 -0
- memos/reranker/cosine_local.py +1 -0
- memos/reranker/factory.py +5 -0
- memos/reranker/http_bge.py +225 -12
- memos/templates/mem_scheduler_prompts.py +242 -0
- memos/types.py +4 -1
- memos/api/context/context.py +0 -147
- memos/api/context/context_thread.py +0 -96
- memos/mem_scheduler/mos_for_test_scheduler.py +0 -146
- {memoryos-1.0.1.dist-info → memoryos-1.1.2.dist-info}/entry_points.txt +0 -0
- {memoryos-1.0.1.dist-info → memoryos-1.1.2.dist-info/licenses}/LICENSE +0 -0
- /memos/mem_scheduler/{general_modules → webservice_modules}/rabbitmq_service.py +0 -0
- /memos/mem_scheduler/{general_modules → webservice_modules}/redis_service.py +0 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
from memos.configs.mem_scheduler import BaseSchedulerConfig
|
|
2
|
+
from memos.llms.base import BaseLLM
|
|
3
|
+
from memos.log import get_logger
|
|
4
|
+
from memos.mem_scheduler.general_modules.base import BaseSchedulerModule
|
|
5
|
+
from memos.mem_scheduler.utils.misc_utils import extract_json_dict
|
|
6
|
+
from memos.memories.textual.tree import TextualMemoryItem
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
logger = get_logger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MemoryFilter(BaseSchedulerModule):
|
|
13
|
+
def __init__(self, process_llm: BaseLLM, config: BaseSchedulerConfig):
|
|
14
|
+
super().__init__()
|
|
15
|
+
self.config: BaseSchedulerConfig = config
|
|
16
|
+
self.process_llm = process_llm
|
|
17
|
+
|
|
18
|
+
def filter_unrelated_memories(
|
|
19
|
+
self,
|
|
20
|
+
query_history: list[str],
|
|
21
|
+
memories: list[TextualMemoryItem],
|
|
22
|
+
) -> (list[TextualMemoryItem], bool):
|
|
23
|
+
"""
|
|
24
|
+
Filter out memories that are completely unrelated to the query history using LLM.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
query_history: List of query strings to determine relevance
|
|
28
|
+
memories: List of TextualMemoryItem objects to be filtered
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Tuple of (filtered_memories, success_flag)
|
|
32
|
+
- filtered_memories: List of TextualMemoryItem objects that are relevant to queries
|
|
33
|
+
- success_flag: Boolean indicating if LLM filtering was successful
|
|
34
|
+
|
|
35
|
+
Note:
|
|
36
|
+
If LLM filtering fails, returns all memories (conservative approach)
|
|
37
|
+
"""
|
|
38
|
+
success_flag = False
|
|
39
|
+
|
|
40
|
+
if not memories:
|
|
41
|
+
logger.info("No memories to filter - returning empty list")
|
|
42
|
+
return [], True
|
|
43
|
+
|
|
44
|
+
if not query_history:
|
|
45
|
+
logger.info("No query history provided - keeping all memories")
|
|
46
|
+
return memories, True
|
|
47
|
+
|
|
48
|
+
logger.info(
|
|
49
|
+
f"Starting memory filtering for {len(memories)} memories against {len(query_history)} queries"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Extract memory texts for LLM processing
|
|
53
|
+
memory_texts = [mem.memory for mem in memories]
|
|
54
|
+
|
|
55
|
+
# Build LLM prompt for memory filtering
|
|
56
|
+
prompt = self.build_prompt(
|
|
57
|
+
"memory_filtering",
|
|
58
|
+
query_history=[f"[{i}] {query}" for i, query in enumerate(query_history)],
|
|
59
|
+
memories=[f"[{i}] {mem}" for i, mem in enumerate(memory_texts)],
|
|
60
|
+
)
|
|
61
|
+
logger.debug(f"Generated filtering prompt: {prompt[:200]}...") # Log first 200 chars
|
|
62
|
+
|
|
63
|
+
# Get LLM response
|
|
64
|
+
response = self.process_llm.generate([{"role": "user", "content": prompt}])
|
|
65
|
+
logger.debug(f"Received LLM filtering response: {response[:200]}...") # Log first 200 chars
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
# Parse JSON response
|
|
69
|
+
response = extract_json_dict(response)
|
|
70
|
+
logger.debug(f"Parsed JSON response: {response}")
|
|
71
|
+
relevant_indices = response["relevant_memories"]
|
|
72
|
+
filtered_count = response["filtered_count"]
|
|
73
|
+
reasoning = response["reasoning"]
|
|
74
|
+
|
|
75
|
+
# Validate indices
|
|
76
|
+
if not isinstance(relevant_indices, list):
|
|
77
|
+
raise ValueError("relevant_memories must be a list")
|
|
78
|
+
|
|
79
|
+
# Filter memories based on relevant indices
|
|
80
|
+
filtered_memories = []
|
|
81
|
+
for idx in relevant_indices:
|
|
82
|
+
if isinstance(idx, int) and 0 <= idx < len(memories):
|
|
83
|
+
filtered_memories.append(memories[idx])
|
|
84
|
+
else:
|
|
85
|
+
logger.warning(f"Invalid memory index {idx} - skipping")
|
|
86
|
+
|
|
87
|
+
logger.info(
|
|
88
|
+
f"Successfully filtered memories. Kept {len(filtered_memories)} out of {len(memories)} memories. "
|
|
89
|
+
f"Filtered out {filtered_count} unrelated memories. "
|
|
90
|
+
f"Filtering reasoning: {reasoning}"
|
|
91
|
+
)
|
|
92
|
+
success_flag = True
|
|
93
|
+
|
|
94
|
+
except Exception as e:
|
|
95
|
+
logger.error(
|
|
96
|
+
f"Failed to filter memories with LLM. Exception: {e}. Raw response: {response}",
|
|
97
|
+
exc_info=True,
|
|
98
|
+
)
|
|
99
|
+
# Conservative approach: keep all memories if filtering fails
|
|
100
|
+
filtered_memories = memories
|
|
101
|
+
success_flag = False
|
|
102
|
+
|
|
103
|
+
return filtered_memories, success_flag
|
|
104
|
+
|
|
105
|
+
def filter_redundant_memories(
|
|
106
|
+
self,
|
|
107
|
+
query_history: list[str],
|
|
108
|
+
memories: list[TextualMemoryItem],
|
|
109
|
+
) -> (list[TextualMemoryItem], bool):
|
|
110
|
+
"""
|
|
111
|
+
Filter out redundant memories using LLM analysis.
|
|
112
|
+
|
|
113
|
+
This function removes redundant memories by keeping the most informative
|
|
114
|
+
version when multiple memories contain similar information relevant to queries.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
query_history: List of query strings to determine relevance and value
|
|
118
|
+
memories: List of TextualMemoryItem objects to be filtered
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
Tuple of (filtered_memories, success_flag)
|
|
122
|
+
- filtered_memories: List of TextualMemoryItem objects after redundancy filtering
|
|
123
|
+
- success_flag: Boolean indicating if LLM filtering was successful
|
|
124
|
+
|
|
125
|
+
Note:
|
|
126
|
+
If LLM filtering fails, returns all memories (conservative approach)
|
|
127
|
+
"""
|
|
128
|
+
success_flag = False
|
|
129
|
+
|
|
130
|
+
if not memories:
|
|
131
|
+
logger.info("No memories to filter for redundancy - returning empty list")
|
|
132
|
+
return [], True
|
|
133
|
+
|
|
134
|
+
if not query_history:
|
|
135
|
+
logger.info("No query history provided - keeping all memories")
|
|
136
|
+
return memories, True
|
|
137
|
+
|
|
138
|
+
if len(memories) <= 1:
|
|
139
|
+
logger.info("Only one memory - no redundancy to filter")
|
|
140
|
+
return memories, True
|
|
141
|
+
|
|
142
|
+
logger.info(
|
|
143
|
+
f"Starting redundancy filtering for {len(memories)} memories against {len(query_history)} queries"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
# Extract memory texts for LLM processing
|
|
147
|
+
memory_texts = [mem.memory for mem in memories]
|
|
148
|
+
|
|
149
|
+
# Build LLM prompt for redundancy filtering
|
|
150
|
+
prompt = self.build_prompt(
|
|
151
|
+
"memory_redundancy_filtering",
|
|
152
|
+
query_history=[f"[{i}] {query}" for i, query in enumerate(query_history)],
|
|
153
|
+
memories=[f"[{i}] {mem}" for i, mem in enumerate(memory_texts)],
|
|
154
|
+
)
|
|
155
|
+
logger.debug(
|
|
156
|
+
f"Generated redundancy filtering prompt: {prompt[:200]}..."
|
|
157
|
+
) # Log first 200 chars
|
|
158
|
+
|
|
159
|
+
# Get LLM response
|
|
160
|
+
response = self.process_llm.generate([{"role": "user", "content": prompt}])
|
|
161
|
+
logger.debug(
|
|
162
|
+
f"Received LLM redundancy filtering response: {response[:200]}..."
|
|
163
|
+
) # Log first 200 chars
|
|
164
|
+
|
|
165
|
+
try:
|
|
166
|
+
# Parse JSON response
|
|
167
|
+
response = extract_json_dict(response)
|
|
168
|
+
logger.debug(f"Parsed JSON response: {response}")
|
|
169
|
+
kept_indices = response["kept_memories"]
|
|
170
|
+
redundant_groups = response.get("redundant_groups", [])
|
|
171
|
+
reasoning = response["reasoning"]
|
|
172
|
+
|
|
173
|
+
# Validate indices
|
|
174
|
+
if not isinstance(kept_indices, list):
|
|
175
|
+
raise ValueError("kept_memories must be a list")
|
|
176
|
+
|
|
177
|
+
# Filter memories based on kept indices
|
|
178
|
+
filtered_memories = []
|
|
179
|
+
for idx in kept_indices:
|
|
180
|
+
if isinstance(idx, int) and 0 <= idx < len(memories):
|
|
181
|
+
filtered_memories.append(memories[idx])
|
|
182
|
+
else:
|
|
183
|
+
logger.warning(f"Invalid memory index {idx} - skipping")
|
|
184
|
+
|
|
185
|
+
logger.info(
|
|
186
|
+
f"Successfully filtered redundant memories. "
|
|
187
|
+
f"Kept {len(filtered_memories)} out of {len(memories)} memories. "
|
|
188
|
+
f"Removed {len(memories) - len(filtered_memories)} redundant memories. "
|
|
189
|
+
f"Redundant groups identified: {len(redundant_groups)}. "
|
|
190
|
+
f"Filtering reasoning: {reasoning}"
|
|
191
|
+
)
|
|
192
|
+
success_flag = True
|
|
193
|
+
|
|
194
|
+
except Exception as e:
|
|
195
|
+
logger.error(
|
|
196
|
+
f"Failed to filter redundant memories with LLM. Exception: {e}. Raw response: {response}",
|
|
197
|
+
exc_info=True,
|
|
198
|
+
)
|
|
199
|
+
# Conservative approach: keep all memories if filtering fails
|
|
200
|
+
filtered_memories = memories
|
|
201
|
+
success_flag = False
|
|
202
|
+
|
|
203
|
+
return filtered_memories, success_flag
|
|
204
|
+
|
|
205
|
+
def filter_unrelated_and_redundant_memories(
|
|
206
|
+
self,
|
|
207
|
+
query_history: list[str],
|
|
208
|
+
memories: list[TextualMemoryItem],
|
|
209
|
+
) -> (list[TextualMemoryItem], bool):
|
|
210
|
+
"""
|
|
211
|
+
Filter out both unrelated and redundant memories using LLM analysis.
|
|
212
|
+
|
|
213
|
+
This function performs two types of filtering in sequence:
|
|
214
|
+
1. Remove memories that are completely unrelated to the query history
|
|
215
|
+
2. Remove redundant memories by keeping the most informative version
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
query_history: List of query strings to determine relevance and value
|
|
219
|
+
memories: List of TextualMemoryItem objects to be filtered
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
Tuple of (filtered_memories, success_flag)
|
|
223
|
+
- filtered_memories: List of TextualMemoryItem objects after both filtering steps
|
|
224
|
+
- success_flag: Boolean indicating if LLM filtering was successful
|
|
225
|
+
|
|
226
|
+
Note:
|
|
227
|
+
If LLM filtering fails, returns all memories (conservative approach)
|
|
228
|
+
"""
|
|
229
|
+
success_flag = False
|
|
230
|
+
|
|
231
|
+
if not memories:
|
|
232
|
+
logger.info("No memories to filter for unrelated and redundant - returning empty list")
|
|
233
|
+
return [], True
|
|
234
|
+
|
|
235
|
+
if not query_history:
|
|
236
|
+
logger.info("No query history provided - keeping all memories")
|
|
237
|
+
return memories, True
|
|
238
|
+
|
|
239
|
+
if len(memories) <= 1:
|
|
240
|
+
logger.info("Only one memory - no filtering needed")
|
|
241
|
+
return memories, True
|
|
242
|
+
|
|
243
|
+
logger.info(
|
|
244
|
+
f"Starting combined unrelated and redundant filtering for {len(memories)} memories against {len(query_history)} queries"
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
# Extract memory texts for LLM processing
|
|
248
|
+
memory_texts = [mem.memory for mem in memories]
|
|
249
|
+
|
|
250
|
+
# Build LLM prompt for combined filtering
|
|
251
|
+
prompt = self.build_prompt(
|
|
252
|
+
"memory_combined_filtering",
|
|
253
|
+
query_history=[f"[{i}] {query}" for i, query in enumerate(query_history)],
|
|
254
|
+
memories=[f"[{i}] {mem}" for i, mem in enumerate(memory_texts)],
|
|
255
|
+
)
|
|
256
|
+
logger.debug(
|
|
257
|
+
f"Generated combined filtering prompt: {prompt[:200]}..."
|
|
258
|
+
) # Log first 200 chars
|
|
259
|
+
|
|
260
|
+
# Get LLM response
|
|
261
|
+
response = self.process_llm.generate([{"role": "user", "content": prompt}])
|
|
262
|
+
logger.debug(
|
|
263
|
+
f"Received LLM combined filtering response: {response[:200]}..."
|
|
264
|
+
) # Log first 200 chars
|
|
265
|
+
|
|
266
|
+
try:
|
|
267
|
+
# Parse JSON response
|
|
268
|
+
response = extract_json_dict(response)
|
|
269
|
+
logger.debug(f"Parsed JSON response: {response}")
|
|
270
|
+
kept_indices = response["kept_memories"]
|
|
271
|
+
unrelated_removed_count = response.get("unrelated_removed_count", 0)
|
|
272
|
+
redundant_removed_count = response.get("redundant_removed_count", 0)
|
|
273
|
+
redundant_groups = response.get("redundant_groups", [])
|
|
274
|
+
reasoning = response["reasoning"]
|
|
275
|
+
|
|
276
|
+
# Validate indices
|
|
277
|
+
if not isinstance(kept_indices, list):
|
|
278
|
+
raise ValueError("kept_memories must be a list")
|
|
279
|
+
|
|
280
|
+
# Filter memories based on kept indices
|
|
281
|
+
filtered_memories = []
|
|
282
|
+
for idx in kept_indices:
|
|
283
|
+
if isinstance(idx, int) and 0 <= idx < len(memories):
|
|
284
|
+
filtered_memories.append(memories[idx])
|
|
285
|
+
else:
|
|
286
|
+
logger.warning(f"Invalid memory index {idx} - skipping")
|
|
287
|
+
|
|
288
|
+
logger.info(
|
|
289
|
+
f"Successfully filtered unrelated and redundant memories. "
|
|
290
|
+
f"Kept {len(filtered_memories)} out of {len(memories)} memories. "
|
|
291
|
+
f"Removed {len(memories) - len(filtered_memories)} memories total. "
|
|
292
|
+
f"Unrelated removed: {unrelated_removed_count}. "
|
|
293
|
+
f"Redundant removed: {redundant_removed_count}. "
|
|
294
|
+
f"Redundant groups identified: {len(redundant_groups)}. "
|
|
295
|
+
f"Filtering reasoning: {reasoning}"
|
|
296
|
+
)
|
|
297
|
+
success_flag = True
|
|
298
|
+
|
|
299
|
+
except Exception as e:
|
|
300
|
+
logger.error(
|
|
301
|
+
f"Failed to filter unrelated and redundant memories with LLM. Exception: {e}. Raw response: {response}",
|
|
302
|
+
exc_info=True,
|
|
303
|
+
)
|
|
304
|
+
# Conservative approach: keep all memories if filtering fails
|
|
305
|
+
filtered_memories = memories
|
|
306
|
+
success_flag = False
|
|
307
|
+
|
|
308
|
+
return filtered_memories, success_flag
|
|
@@ -8,8 +8,8 @@ from memos.mem_scheduler.schemas.general_schemas import (
|
|
|
8
8
|
TreeTextMemory_SEARCH_METHOD,
|
|
9
9
|
)
|
|
10
10
|
from memos.mem_scheduler.utils.filter_utils import (
|
|
11
|
-
filter_similar_memories,
|
|
12
11
|
filter_too_short_memories,
|
|
12
|
+
filter_vector_based_similar_memories,
|
|
13
13
|
transform_name_to_key,
|
|
14
14
|
)
|
|
15
15
|
from memos.mem_scheduler.utils.misc_utils import (
|
|
@@ -17,6 +17,8 @@ from memos.mem_scheduler.utils.misc_utils import (
|
|
|
17
17
|
)
|
|
18
18
|
from memos.memories.textual.tree import TextualMemoryItem, TreeTextMemory
|
|
19
19
|
|
|
20
|
+
from .memory_filter import MemoryFilter
|
|
21
|
+
|
|
20
22
|
|
|
21
23
|
logger = get_logger(__name__)
|
|
22
24
|
|
|
@@ -32,6 +34,9 @@ class SchedulerRetriever(BaseSchedulerModule):
|
|
|
32
34
|
self.config: BaseSchedulerConfig = config
|
|
33
35
|
self.process_llm = process_llm
|
|
34
36
|
|
|
37
|
+
# Initialize memory filter
|
|
38
|
+
self.memory_filter = MemoryFilter(process_llm=process_llm, config=config)
|
|
39
|
+
|
|
35
40
|
def search(
|
|
36
41
|
self,
|
|
37
42
|
query: str,
|
|
@@ -77,10 +82,7 @@ class SchedulerRetriever(BaseSchedulerModule):
|
|
|
77
82
|
return results
|
|
78
83
|
|
|
79
84
|
def rerank_memories(
|
|
80
|
-
self,
|
|
81
|
-
queries: list[str],
|
|
82
|
-
original_memories: list[str],
|
|
83
|
-
top_k: int,
|
|
85
|
+
self, queries: list[str], original_memories: list[str], top_k: int
|
|
84
86
|
) -> (list[str], bool):
|
|
85
87
|
"""
|
|
86
88
|
Rerank memories based on relevance to given queries using LLM.
|
|
@@ -96,7 +98,6 @@ class SchedulerRetriever(BaseSchedulerModule):
|
|
|
96
98
|
Note:
|
|
97
99
|
If LLM reranking fails, falls back to original order (truncated to top_k)
|
|
98
100
|
"""
|
|
99
|
-
success_flag = False
|
|
100
101
|
|
|
101
102
|
logger.info(f"Starting memory reranking for {len(original_memories)} memories")
|
|
102
103
|
|
|
@@ -163,7 +164,7 @@ class SchedulerRetriever(BaseSchedulerModule):
|
|
|
163
164
|
combined_text_memory = [m.memory for m in combined_memory]
|
|
164
165
|
|
|
165
166
|
# Apply similarity filter to remove overly similar memories
|
|
166
|
-
filtered_combined_text_memory =
|
|
167
|
+
filtered_combined_text_memory = filter_vector_based_similar_memories(
|
|
167
168
|
text_memories=combined_text_memory,
|
|
168
169
|
similarity_threshold=self.filter_similarity_threshold,
|
|
169
170
|
)
|
|
@@ -197,3 +198,29 @@ class SchedulerRetriever(BaseSchedulerModule):
|
|
|
197
198
|
)
|
|
198
199
|
|
|
199
200
|
return memories_with_new_order, success_flag
|
|
201
|
+
|
|
202
|
+
def filter_unrelated_memories(
|
|
203
|
+
self,
|
|
204
|
+
query_history: list[str],
|
|
205
|
+
memories: list[TextualMemoryItem],
|
|
206
|
+
) -> (list[TextualMemoryItem], bool):
|
|
207
|
+
return self.memory_filter.filter_unrelated_memories(query_history, memories)
|
|
208
|
+
|
|
209
|
+
def filter_redundant_memories(
|
|
210
|
+
self,
|
|
211
|
+
query_history: list[str],
|
|
212
|
+
memories: list[TextualMemoryItem],
|
|
213
|
+
) -> (list[TextualMemoryItem], bool):
|
|
214
|
+
return self.memory_filter.filter_redundant_memories(query_history, memories)
|
|
215
|
+
|
|
216
|
+
def filter_unrelated_and_redundant_memories(
|
|
217
|
+
self,
|
|
218
|
+
query_history: list[str],
|
|
219
|
+
memories: list[TextualMemoryItem],
|
|
220
|
+
) -> (list[TextualMemoryItem], bool):
|
|
221
|
+
"""
|
|
222
|
+
Filter out both unrelated and redundant memories using LLM analysis.
|
|
223
|
+
|
|
224
|
+
This method delegates to the MemoryFilter class.
|
|
225
|
+
"""
|
|
226
|
+
return self.memory_filter.filter_unrelated_and_redundant_memories(query_history, memories)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import threading
|
|
2
2
|
import time
|
|
3
3
|
|
|
4
|
-
from concurrent.futures import ThreadPoolExecutor
|
|
5
4
|
from datetime import datetime
|
|
6
5
|
from time import perf_counter
|
|
7
6
|
|
|
8
7
|
from memos.configs.mem_scheduler import BaseSchedulerConfig
|
|
8
|
+
from memos.context.context import ContextThreadPoolExecutor
|
|
9
9
|
from memos.log import get_logger
|
|
10
10
|
from memos.mem_scheduler.general_modules.base import BaseSchedulerModule
|
|
11
11
|
from memos.mem_scheduler.general_modules.dispatcher import SchedulerDispatcher
|
|
@@ -21,7 +21,7 @@ class SchedulerDispatcherMonitor(BaseSchedulerModule):
|
|
|
21
21
|
super().__init__()
|
|
22
22
|
self.config: BaseSchedulerConfig = config
|
|
23
23
|
|
|
24
|
-
self.check_interval = self.config.get("dispatcher_monitor_check_interval",
|
|
24
|
+
self.check_interval = self.config.get("dispatcher_monitor_check_interval", 300)
|
|
25
25
|
self.max_failures = self.config.get("dispatcher_monitor_max_failures", 2)
|
|
26
26
|
|
|
27
27
|
# Registry of monitored thread pools
|
|
@@ -49,7 +49,7 @@ class SchedulerDispatcherMonitor(BaseSchedulerModule):
|
|
|
49
49
|
def register_pool(
|
|
50
50
|
self,
|
|
51
51
|
name: str,
|
|
52
|
-
executor:
|
|
52
|
+
executor: ContextThreadPoolExecutor,
|
|
53
53
|
max_workers: int,
|
|
54
54
|
restart_on_failure: bool = True,
|
|
55
55
|
) -> bool:
|
|
@@ -177,10 +177,11 @@ class SchedulerDispatcherMonitor(BaseSchedulerModule):
|
|
|
177
177
|
else:
|
|
178
178
|
pool_info["failure_count"] += 1
|
|
179
179
|
pool_info["healthy"] = False
|
|
180
|
-
logger.
|
|
181
|
-
f"Pool '{name}' unhealthy ({pool_info['failure_count']}/{self.max_failures}): {reason}"
|
|
180
|
+
logger.info(
|
|
181
|
+
f"Pool '{name}' unhealthy ({pool_info['failure_count']}/{self.max_failures}): {reason}."
|
|
182
|
+
f" Note: This status does not necessarily indicate a problem with the pool itself - "
|
|
183
|
+
f"it may also be considered unhealthy if no tasks have been scheduled for an extended period"
|
|
182
184
|
)
|
|
183
|
-
|
|
184
185
|
if (
|
|
185
186
|
pool_info["failure_count"] >= self.max_failures
|
|
186
187
|
and pool_info["restart"]
|
|
@@ -236,14 +237,14 @@ class SchedulerDispatcherMonitor(BaseSchedulerModule):
|
|
|
236
237
|
return
|
|
237
238
|
|
|
238
239
|
self._restart_in_progress = True
|
|
239
|
-
logger.
|
|
240
|
+
logger.info(f"Attempting to restart thread pool '{name}'")
|
|
240
241
|
|
|
241
242
|
try:
|
|
242
243
|
old_executor = pool_info["executor"]
|
|
243
244
|
self.dispatcher.shutdown()
|
|
244
245
|
|
|
245
246
|
# Create new executor with same parameters
|
|
246
|
-
new_executor =
|
|
247
|
+
new_executor = ContextThreadPoolExecutor(
|
|
247
248
|
max_workers=pool_info["max_workers"],
|
|
248
249
|
thread_name_prefix=self.dispatcher.thread_name_prefix, # pylint: disable=protected-access
|
|
249
250
|
)
|