MemoryOS 1.0.0__py3-none-any.whl → 1.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of MemoryOS might be problematic. Click here for more details.

Files changed (94) hide show
  1. {memoryos-1.0.0.dist-info → memoryos-1.1.1.dist-info}/METADATA +8 -2
  2. {memoryos-1.0.0.dist-info → memoryos-1.1.1.dist-info}/RECORD +92 -69
  3. {memoryos-1.0.0.dist-info → memoryos-1.1.1.dist-info}/WHEEL +1 -1
  4. memos/__init__.py +1 -1
  5. memos/api/client.py +109 -0
  6. memos/api/config.py +35 -8
  7. memos/api/context/dependencies.py +15 -66
  8. memos/api/middleware/request_context.py +63 -0
  9. memos/api/product_api.py +5 -2
  10. memos/api/product_models.py +107 -16
  11. memos/api/routers/product_router.py +62 -19
  12. memos/api/start_api.py +13 -0
  13. memos/configs/graph_db.py +4 -0
  14. memos/configs/mem_scheduler.py +38 -3
  15. memos/configs/memory.py +13 -0
  16. memos/configs/reranker.py +18 -0
  17. memos/context/context.py +255 -0
  18. memos/embedders/factory.py +2 -0
  19. memos/graph_dbs/base.py +4 -2
  20. memos/graph_dbs/nebular.py +368 -223
  21. memos/graph_dbs/neo4j.py +49 -13
  22. memos/graph_dbs/neo4j_community.py +13 -3
  23. memos/llms/factory.py +2 -0
  24. memos/llms/openai.py +74 -2
  25. memos/llms/vllm.py +2 -0
  26. memos/log.py +128 -4
  27. memos/mem_cube/general.py +3 -1
  28. memos/mem_os/core.py +89 -23
  29. memos/mem_os/main.py +3 -6
  30. memos/mem_os/product.py +418 -154
  31. memos/mem_os/utils/reference_utils.py +20 -0
  32. memos/mem_reader/factory.py +2 -0
  33. memos/mem_reader/simple_struct.py +204 -82
  34. memos/mem_scheduler/analyzer/__init__.py +0 -0
  35. memos/mem_scheduler/analyzer/mos_for_test_scheduler.py +569 -0
  36. memos/mem_scheduler/analyzer/scheduler_for_eval.py +280 -0
  37. memos/mem_scheduler/base_scheduler.py +126 -56
  38. memos/mem_scheduler/general_modules/dispatcher.py +2 -2
  39. memos/mem_scheduler/general_modules/misc.py +99 -1
  40. memos/mem_scheduler/general_modules/scheduler_logger.py +17 -11
  41. memos/mem_scheduler/general_scheduler.py +40 -88
  42. memos/mem_scheduler/memory_manage_modules/__init__.py +5 -0
  43. memos/mem_scheduler/memory_manage_modules/memory_filter.py +308 -0
  44. memos/mem_scheduler/{general_modules → memory_manage_modules}/retriever.py +34 -7
  45. memos/mem_scheduler/monitors/dispatcher_monitor.py +9 -8
  46. memos/mem_scheduler/monitors/general_monitor.py +119 -39
  47. memos/mem_scheduler/optimized_scheduler.py +124 -0
  48. memos/mem_scheduler/orm_modules/__init__.py +0 -0
  49. memos/mem_scheduler/orm_modules/base_model.py +635 -0
  50. memos/mem_scheduler/orm_modules/monitor_models.py +261 -0
  51. memos/mem_scheduler/scheduler_factory.py +2 -0
  52. memos/mem_scheduler/schemas/monitor_schemas.py +96 -29
  53. memos/mem_scheduler/utils/config_utils.py +100 -0
  54. memos/mem_scheduler/utils/db_utils.py +33 -0
  55. memos/mem_scheduler/utils/filter_utils.py +1 -1
  56. memos/mem_scheduler/webservice_modules/__init__.py +0 -0
  57. memos/mem_user/mysql_user_manager.py +4 -2
  58. memos/memories/activation/kv.py +2 -1
  59. memos/memories/textual/item.py +96 -17
  60. memos/memories/textual/naive.py +1 -1
  61. memos/memories/textual/tree.py +57 -3
  62. memos/memories/textual/tree_text_memory/organize/handler.py +4 -2
  63. memos/memories/textual/tree_text_memory/organize/manager.py +28 -14
  64. memos/memories/textual/tree_text_memory/organize/relation_reason_detector.py +1 -2
  65. memos/memories/textual/tree_text_memory/organize/reorganizer.py +75 -23
  66. memos/memories/textual/tree_text_memory/retrieve/bochasearch.py +10 -6
  67. memos/memories/textual/tree_text_memory/retrieve/internet_retriever.py +6 -2
  68. memos/memories/textual/tree_text_memory/retrieve/internet_retriever_factory.py +2 -0
  69. memos/memories/textual/tree_text_memory/retrieve/recall.py +119 -21
  70. memos/memories/textual/tree_text_memory/retrieve/searcher.py +172 -44
  71. memos/memories/textual/tree_text_memory/retrieve/utils.py +6 -4
  72. memos/memories/textual/tree_text_memory/retrieve/xinyusearch.py +5 -4
  73. memos/memos_tools/notification_utils.py +46 -0
  74. memos/memos_tools/singleton.py +174 -0
  75. memos/memos_tools/thread_safe_dict.py +22 -0
  76. memos/memos_tools/thread_safe_dict_segment.py +382 -0
  77. memos/parsers/factory.py +2 -0
  78. memos/reranker/__init__.py +4 -0
  79. memos/reranker/base.py +24 -0
  80. memos/reranker/concat.py +59 -0
  81. memos/reranker/cosine_local.py +96 -0
  82. memos/reranker/factory.py +48 -0
  83. memos/reranker/http_bge.py +312 -0
  84. memos/reranker/noop.py +16 -0
  85. memos/templates/mem_reader_prompts.py +289 -40
  86. memos/templates/mem_scheduler_prompts.py +242 -0
  87. memos/templates/mos_prompts.py +133 -60
  88. memos/types.py +4 -1
  89. memos/api/context/context.py +0 -147
  90. memos/mem_scheduler/mos_for_test_scheduler.py +0 -146
  91. {memoryos-1.0.0.dist-info → memoryos-1.1.1.dist-info}/entry_points.txt +0 -0
  92. {memoryos-1.0.0.dist-info → memoryos-1.1.1.dist-info/licenses}/LICENSE +0 -0
  93. /memos/mem_scheduler/{general_modules → webservice_modules}/rabbitmq_service.py +0 -0
  94. /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 = filter_similar_memories(
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", 60)
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: ThreadPoolExecutor,
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.warning(
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.warning(f"Attempting to restart thread pool '{name}'")
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 = ThreadPoolExecutor(
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
  )