powermem 0.1.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.
- powermem/__init__.py +103 -0
- powermem/agent/__init__.py +35 -0
- powermem/agent/abstract/__init__.py +22 -0
- powermem/agent/abstract/collaboration.py +259 -0
- powermem/agent/abstract/context.py +187 -0
- powermem/agent/abstract/manager.py +232 -0
- powermem/agent/abstract/permission.py +217 -0
- powermem/agent/abstract/privacy.py +267 -0
- powermem/agent/abstract/scope.py +199 -0
- powermem/agent/agent.py +791 -0
- powermem/agent/components/__init__.py +18 -0
- powermem/agent/components/collaboration_coordinator.py +645 -0
- powermem/agent/components/permission_controller.py +586 -0
- powermem/agent/components/privacy_protector.py +767 -0
- powermem/agent/components/scope_controller.py +685 -0
- powermem/agent/factories/__init__.py +16 -0
- powermem/agent/factories/agent_factory.py +266 -0
- powermem/agent/factories/config_factory.py +308 -0
- powermem/agent/factories/memory_factory.py +229 -0
- powermem/agent/implementations/__init__.py +16 -0
- powermem/agent/implementations/hybrid.py +728 -0
- powermem/agent/implementations/multi_agent.py +1040 -0
- powermem/agent/implementations/multi_user.py +1020 -0
- powermem/agent/types.py +53 -0
- powermem/agent/wrappers/__init__.py +14 -0
- powermem/agent/wrappers/agent_memory_wrapper.py +427 -0
- powermem/agent/wrappers/compatibility_wrapper.py +520 -0
- powermem/config_loader.py +318 -0
- powermem/configs.py +249 -0
- powermem/core/__init__.py +19 -0
- powermem/core/async_memory.py +1493 -0
- powermem/core/audit.py +258 -0
- powermem/core/base.py +165 -0
- powermem/core/memory.py +1567 -0
- powermem/core/setup.py +162 -0
- powermem/core/telemetry.py +215 -0
- powermem/integrations/__init__.py +17 -0
- powermem/integrations/embeddings/__init__.py +13 -0
- powermem/integrations/embeddings/aws_bedrock.py +100 -0
- powermem/integrations/embeddings/azure_openai.py +55 -0
- powermem/integrations/embeddings/base.py +31 -0
- powermem/integrations/embeddings/config/base.py +132 -0
- powermem/integrations/embeddings/configs.py +31 -0
- powermem/integrations/embeddings/factory.py +48 -0
- powermem/integrations/embeddings/gemini.py +39 -0
- powermem/integrations/embeddings/huggingface.py +41 -0
- powermem/integrations/embeddings/langchain.py +35 -0
- powermem/integrations/embeddings/lmstudio.py +29 -0
- powermem/integrations/embeddings/mock.py +11 -0
- powermem/integrations/embeddings/ollama.py +53 -0
- powermem/integrations/embeddings/openai.py +49 -0
- powermem/integrations/embeddings/qwen.py +102 -0
- powermem/integrations/embeddings/together.py +31 -0
- powermem/integrations/embeddings/vertexai.py +54 -0
- powermem/integrations/llm/__init__.py +18 -0
- powermem/integrations/llm/anthropic.py +87 -0
- powermem/integrations/llm/base.py +132 -0
- powermem/integrations/llm/config/anthropic.py +56 -0
- powermem/integrations/llm/config/azure.py +56 -0
- powermem/integrations/llm/config/base.py +62 -0
- powermem/integrations/llm/config/deepseek.py +56 -0
- powermem/integrations/llm/config/ollama.py +56 -0
- powermem/integrations/llm/config/openai.py +79 -0
- powermem/integrations/llm/config/qwen.py +68 -0
- powermem/integrations/llm/config/qwen_asr.py +46 -0
- powermem/integrations/llm/config/vllm.py +56 -0
- powermem/integrations/llm/configs.py +26 -0
- powermem/integrations/llm/deepseek.py +106 -0
- powermem/integrations/llm/factory.py +118 -0
- powermem/integrations/llm/gemini.py +201 -0
- powermem/integrations/llm/langchain.py +65 -0
- powermem/integrations/llm/ollama.py +106 -0
- powermem/integrations/llm/openai.py +166 -0
- powermem/integrations/llm/openai_structured.py +80 -0
- powermem/integrations/llm/qwen.py +207 -0
- powermem/integrations/llm/qwen_asr.py +171 -0
- powermem/integrations/llm/vllm.py +106 -0
- powermem/integrations/rerank/__init__.py +20 -0
- powermem/integrations/rerank/base.py +43 -0
- powermem/integrations/rerank/config/__init__.py +7 -0
- powermem/integrations/rerank/config/base.py +27 -0
- powermem/integrations/rerank/configs.py +23 -0
- powermem/integrations/rerank/factory.py +68 -0
- powermem/integrations/rerank/qwen.py +159 -0
- powermem/intelligence/__init__.py +17 -0
- powermem/intelligence/ebbinghaus_algorithm.py +354 -0
- powermem/intelligence/importance_evaluator.py +361 -0
- powermem/intelligence/intelligent_memory_manager.py +284 -0
- powermem/intelligence/manager.py +148 -0
- powermem/intelligence/plugin.py +229 -0
- powermem/prompts/__init__.py +29 -0
- powermem/prompts/graph/graph_prompts.py +217 -0
- powermem/prompts/graph/graph_tools_prompts.py +469 -0
- powermem/prompts/importance_evaluation.py +246 -0
- powermem/prompts/intelligent_memory_prompts.py +163 -0
- powermem/prompts/templates.py +193 -0
- powermem/storage/__init__.py +14 -0
- powermem/storage/adapter.py +896 -0
- powermem/storage/base.py +109 -0
- powermem/storage/config/base.py +13 -0
- powermem/storage/config/oceanbase.py +58 -0
- powermem/storage/config/pgvector.py +52 -0
- powermem/storage/config/sqlite.py +27 -0
- powermem/storage/configs.py +159 -0
- powermem/storage/factory.py +59 -0
- powermem/storage/migration_manager.py +438 -0
- powermem/storage/oceanbase/__init__.py +8 -0
- powermem/storage/oceanbase/constants.py +162 -0
- powermem/storage/oceanbase/oceanbase.py +1384 -0
- powermem/storage/oceanbase/oceanbase_graph.py +1441 -0
- powermem/storage/pgvector/__init__.py +7 -0
- powermem/storage/pgvector/pgvector.py +420 -0
- powermem/storage/sqlite/__init__.py +0 -0
- powermem/storage/sqlite/sqlite.py +218 -0
- powermem/storage/sqlite/sqlite_vector_store.py +311 -0
- powermem/utils/__init__.py +35 -0
- powermem/utils/utils.py +605 -0
- powermem/version.py +23 -0
- powermem-0.1.0.dist-info/METADATA +187 -0
- powermem-0.1.0.dist-info/RECORD +123 -0
- powermem-0.1.0.dist-info/WHEEL +5 -0
- powermem-0.1.0.dist-info/licenses/LICENSE +206 -0
- powermem-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,728 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Hybrid Memory Manager Implementation
|
|
3
|
+
|
|
4
|
+
This module provides the concrete implementation of the hybrid memory manager,
|
|
5
|
+
which dynamically switches between multi-agent and multi-user modes based on context.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from typing import Any, Dict, List, Optional
|
|
11
|
+
|
|
12
|
+
from typing import Any, Dict
|
|
13
|
+
from powermem.intelligence.intelligent_memory_manager import IntelligentMemoryManager
|
|
14
|
+
from powermem.agent.abstract.manager import AgentMemoryManagerBase
|
|
15
|
+
from powermem.agent.implementations.multi_agent import MultiAgentMemoryManager
|
|
16
|
+
from powermem.agent.implementations.multi_user import MultiUserMemoryManager
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class HybridMemoryManager(AgentMemoryManagerBase):
|
|
22
|
+
"""
|
|
23
|
+
Hybrid memory manager implementation.
|
|
24
|
+
|
|
25
|
+
Dynamically switches between multi-agent and multi-user modes based on context,
|
|
26
|
+
providing intelligent mode selection and seamless transitions.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self, config: Dict[str, Any]):
|
|
30
|
+
"""
|
|
31
|
+
Initialize the hybrid memory manager.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
config: Memory configuration object
|
|
35
|
+
"""
|
|
36
|
+
super().__init__(config)
|
|
37
|
+
self.hybrid_config = config.agent_memory.hybrid_config
|
|
38
|
+
|
|
39
|
+
# Initialize components
|
|
40
|
+
self.intelligent_manager = None
|
|
41
|
+
self.multi_agent_manager = None
|
|
42
|
+
self.multi_user_manager = None
|
|
43
|
+
|
|
44
|
+
# Mode management
|
|
45
|
+
self.current_mode = self.hybrid_config.primary_mode
|
|
46
|
+
self.mode_switch_history = []
|
|
47
|
+
self.mode_switch_cooldown = {}
|
|
48
|
+
|
|
49
|
+
# Context analysis
|
|
50
|
+
self.context_analyzer = None
|
|
51
|
+
self.mode_confidence_scores = {}
|
|
52
|
+
|
|
53
|
+
# Unified memory storage for cross-mode access
|
|
54
|
+
self.unified_memory_index = {}
|
|
55
|
+
self.mode_specific_memories = {
|
|
56
|
+
'multi_agent': {},
|
|
57
|
+
'multi_user': {},
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
def initialize(self) -> None:
|
|
61
|
+
"""
|
|
62
|
+
Initialize the memory manager and all components.
|
|
63
|
+
"""
|
|
64
|
+
try:
|
|
65
|
+
# Initialize intelligent memory manager
|
|
66
|
+
self.intelligent_manager = IntelligentMemoryManager(self.config)
|
|
67
|
+
|
|
68
|
+
# Initialize mode-specific managers
|
|
69
|
+
self._initialize_mode_managers()
|
|
70
|
+
|
|
71
|
+
# Initialize context analyzer
|
|
72
|
+
self._initialize_context_analyzer()
|
|
73
|
+
|
|
74
|
+
# Set initial mode
|
|
75
|
+
self._switch_mode(self.hybrid_config.primary_mode, "initialization")
|
|
76
|
+
|
|
77
|
+
self.initialized = True
|
|
78
|
+
logger.info(f"Hybrid memory manager initialized with primary mode: {self.current_mode}")
|
|
79
|
+
|
|
80
|
+
except Exception as e:
|
|
81
|
+
logger.error(f"Failed to initialize hybrid memory manager: {e}")
|
|
82
|
+
raise
|
|
83
|
+
|
|
84
|
+
def _initialize_mode_managers(self) -> None:
|
|
85
|
+
"""Initialize mode-specific managers."""
|
|
86
|
+
# Create multi-agent manager
|
|
87
|
+
multi_agent_config = self.config.copy()
|
|
88
|
+
# Ensure multi_agent_config exists
|
|
89
|
+
if 'multi_agent_config' not in multi_agent_config.agent_memory._data:
|
|
90
|
+
multi_agent_config.agent_memory._data['multi_agent_config'] = {
|
|
91
|
+
'enabled': True,
|
|
92
|
+
'default_permissions': {
|
|
93
|
+
'owner': ['read', 'write', 'delete', 'admin'],
|
|
94
|
+
'collaborator': ['read', 'write'],
|
|
95
|
+
'viewer': ['read']
|
|
96
|
+
},
|
|
97
|
+
'collaborative_memory_config': {
|
|
98
|
+
'enabled': True,
|
|
99
|
+
'auto_collaboration': True,
|
|
100
|
+
'collaboration_threshold': 0.7,
|
|
101
|
+
'max_collaborators': 5,
|
|
102
|
+
'collaboration_timeout': 3600
|
|
103
|
+
},
|
|
104
|
+
'privacy_config': {
|
|
105
|
+
'enabled': True,
|
|
106
|
+
'default_privacy_level': 'standard',
|
|
107
|
+
'encryption_enabled': False,
|
|
108
|
+
'anonymization_enabled': True
|
|
109
|
+
},
|
|
110
|
+
'default_scope': 'private'
|
|
111
|
+
}
|
|
112
|
+
multi_agent_config.agent_memory.mode = "multi_agent"
|
|
113
|
+
multi_agent_config.agent_memory.enabled = True
|
|
114
|
+
self.multi_agent_manager = MultiAgentMemoryManager(multi_agent_config)
|
|
115
|
+
self.multi_agent_manager.initialize()
|
|
116
|
+
|
|
117
|
+
# Create multi-user manager
|
|
118
|
+
multi_user_config = self.config.copy()
|
|
119
|
+
# Ensure multi_user_config exists
|
|
120
|
+
if 'multi_user_config' not in multi_user_config.agent_memory._data:
|
|
121
|
+
multi_user_config.agent_memory._data['multi_user_config'] = {
|
|
122
|
+
'enabled': True,
|
|
123
|
+
'user_isolation': True,
|
|
124
|
+
'cross_user_sharing': True,
|
|
125
|
+
'privacy_protection': True,
|
|
126
|
+
'default_permissions': {
|
|
127
|
+
'owner': ['read', 'write', 'delete', 'admin'],
|
|
128
|
+
'collaborator': ['read', 'write'],
|
|
129
|
+
'viewer': ['read']
|
|
130
|
+
},
|
|
131
|
+
'user_context_config': {
|
|
132
|
+
'max_user_memories': 10000,
|
|
133
|
+
'context_retention_days': 30,
|
|
134
|
+
'auto_cleanup': True,
|
|
135
|
+
'context_sharing_enabled': True
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
multi_user_config.agent_memory.mode = "multi_user"
|
|
139
|
+
multi_user_config.agent_memory.enabled = True
|
|
140
|
+
self.multi_user_manager = MultiUserMemoryManager(multi_user_config)
|
|
141
|
+
self.multi_user_manager.initialize()
|
|
142
|
+
|
|
143
|
+
def _initialize_context_analyzer(self) -> None:
|
|
144
|
+
"""Initialize context analyzer for mode switching."""
|
|
145
|
+
self.context_analyzer = {
|
|
146
|
+
'collaboration_keywords': [
|
|
147
|
+
'team', 'collaborate', 'meeting', 'discuss', 'share', 'group',
|
|
148
|
+
'project', 'work together', 'joint', 'collective'
|
|
149
|
+
],
|
|
150
|
+
'personal_keywords': [
|
|
151
|
+
'personal', 'private', 'individual', 'my', 'own', 'self',
|
|
152
|
+
'personal preference', 'private note', 'individual task'
|
|
153
|
+
],
|
|
154
|
+
'context_weights': {
|
|
155
|
+
'collaboration_level': 0.4,
|
|
156
|
+
'participants_count': 0.3,
|
|
157
|
+
'content_keywords': 0.2,
|
|
158
|
+
'metadata_hints': 0.1,
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
def process_memory(
|
|
163
|
+
self,
|
|
164
|
+
content: str,
|
|
165
|
+
agent_id: str,
|
|
166
|
+
context: Optional[Dict[str, Any]] = None,
|
|
167
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
168
|
+
) -> Dict[str, Any]:
|
|
169
|
+
"""
|
|
170
|
+
Process and store a new memory with intelligent mode selection.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
content: The memory content to store
|
|
174
|
+
agent_id: ID of the agent/user creating the memory
|
|
175
|
+
context: Additional context information
|
|
176
|
+
metadata: Additional metadata for the memory
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
Dictionary containing the processed memory information
|
|
180
|
+
"""
|
|
181
|
+
try:
|
|
182
|
+
# Analyze context to determine optimal mode
|
|
183
|
+
optimal_mode = self._analyze_context_for_mode(content, context, metadata)
|
|
184
|
+
|
|
185
|
+
# Switch mode if necessary
|
|
186
|
+
if optimal_mode != self.current_mode:
|
|
187
|
+
self._switch_mode(optimal_mode, "context_analysis")
|
|
188
|
+
|
|
189
|
+
# Process memory with current mode manager
|
|
190
|
+
if self.current_mode == "multi_agent":
|
|
191
|
+
result = self.multi_agent_manager.process_memory(content, agent_id, context, metadata)
|
|
192
|
+
else:
|
|
193
|
+
result = self.multi_user_manager.process_memory(content, agent_id, context, metadata)
|
|
194
|
+
|
|
195
|
+
# Store in unified index
|
|
196
|
+
memory_id = result['id']
|
|
197
|
+
self.unified_memory_index[memory_id] = {
|
|
198
|
+
'id': memory_id,
|
|
199
|
+
'content': content,
|
|
200
|
+
'agent_id': agent_id,
|
|
201
|
+
'mode': self.current_mode,
|
|
202
|
+
'processed_at': datetime.now().isoformat(),
|
|
203
|
+
'context': context or {},
|
|
204
|
+
'metadata': metadata or {},
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
# Store in mode-specific storage
|
|
208
|
+
if self.current_mode not in self.mode_specific_memories:
|
|
209
|
+
self.mode_specific_memories[self.current_mode] = {}
|
|
210
|
+
self.mode_specific_memories[self.current_mode][memory_id] = result
|
|
211
|
+
|
|
212
|
+
# Add mode information to result
|
|
213
|
+
result['processed_in_mode'] = self.current_mode
|
|
214
|
+
result['mode_switch_reason'] = "context_analysis" if optimal_mode != self.hybrid_config.primary_mode else "primary_mode"
|
|
215
|
+
|
|
216
|
+
logger.info(f"Processed memory {memory_id} in {self.current_mode} mode")
|
|
217
|
+
return result
|
|
218
|
+
|
|
219
|
+
except Exception as e:
|
|
220
|
+
logger.error(f"Failed to process memory for {agent_id}: {e}")
|
|
221
|
+
raise
|
|
222
|
+
|
|
223
|
+
def _analyze_context_for_mode(
|
|
224
|
+
self,
|
|
225
|
+
content: str,
|
|
226
|
+
context: Optional[Dict[str, Any]],
|
|
227
|
+
metadata: Optional[Dict[str, Any]]
|
|
228
|
+
) -> str:
|
|
229
|
+
"""
|
|
230
|
+
Analyze context to determine the optimal mode for processing.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
content: Memory content
|
|
234
|
+
context: Context information
|
|
235
|
+
metadata: Metadata information
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
Optimal mode ('multi_agent' or 'multi_user')
|
|
239
|
+
"""
|
|
240
|
+
try:
|
|
241
|
+
scores = {
|
|
242
|
+
'multi_agent': 0.0,
|
|
243
|
+
'multi_user': 0.0,
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
# Analyze collaboration level
|
|
247
|
+
collaboration_level = context.get('collaboration_level', 'none') if context else 'none'
|
|
248
|
+
if collaboration_level in ['high', 'medium']:
|
|
249
|
+
scores['multi_agent'] += 0.4
|
|
250
|
+
else:
|
|
251
|
+
scores['multi_user'] += 0.3
|
|
252
|
+
|
|
253
|
+
# Analyze participants
|
|
254
|
+
participants = context.get('participants', []) if context else []
|
|
255
|
+
if len(participants) > 1:
|
|
256
|
+
scores['multi_agent'] += 0.3
|
|
257
|
+
else:
|
|
258
|
+
scores['multi_user'] += 0.2
|
|
259
|
+
|
|
260
|
+
# Analyze content keywords
|
|
261
|
+
content_lower = content.lower()
|
|
262
|
+
collaboration_keywords = sum(1 for keyword in self.context_analyzer['collaboration_keywords']
|
|
263
|
+
if keyword in content_lower)
|
|
264
|
+
personal_keywords = sum(1 for keyword in self.context_analyzer['personal_keywords']
|
|
265
|
+
if keyword in content_lower)
|
|
266
|
+
|
|
267
|
+
if collaboration_keywords > personal_keywords:
|
|
268
|
+
scores['multi_agent'] += 0.2
|
|
269
|
+
else:
|
|
270
|
+
scores['multi_user'] += 0.2
|
|
271
|
+
|
|
272
|
+
# Analyze metadata hints
|
|
273
|
+
if metadata:
|
|
274
|
+
if metadata.get('collaboration') or metadata.get('meeting_type'):
|
|
275
|
+
scores['multi_agent'] += 0.1
|
|
276
|
+
elif metadata.get('personal') or metadata.get('privacy_level') == 'private':
|
|
277
|
+
scores['multi_user'] += 0.1
|
|
278
|
+
|
|
279
|
+
# Apply confidence threshold
|
|
280
|
+
confidence_threshold = self.hybrid_config.mode_switching_config.get('confidence_threshold', 0.7)
|
|
281
|
+
|
|
282
|
+
if scores['multi_agent'] > scores['multi_user'] and scores['multi_agent'] > confidence_threshold:
|
|
283
|
+
return 'multi_agent'
|
|
284
|
+
elif scores['multi_user'] > scores['multi_agent'] and scores['multi_user'] > confidence_threshold:
|
|
285
|
+
return 'multi_user'
|
|
286
|
+
else:
|
|
287
|
+
# Default to primary mode if confidence is low
|
|
288
|
+
return self.hybrid_config.primary_mode
|
|
289
|
+
|
|
290
|
+
except Exception as e:
|
|
291
|
+
logger.error(f"Failed to analyze context for mode: {e}")
|
|
292
|
+
return self.hybrid_config.primary_mode
|
|
293
|
+
|
|
294
|
+
def _switch_mode(self, new_mode: str, reason: str) -> None:
|
|
295
|
+
"""
|
|
296
|
+
Switch to a new mode.
|
|
297
|
+
|
|
298
|
+
Args:
|
|
299
|
+
new_mode: New mode to switch to
|
|
300
|
+
reason: Reason for the switch
|
|
301
|
+
"""
|
|
302
|
+
try:
|
|
303
|
+
# Check cooldown
|
|
304
|
+
if self._is_in_cooldown(new_mode):
|
|
305
|
+
logger.info(f"Mode switch to {new_mode} blocked by cooldown")
|
|
306
|
+
return
|
|
307
|
+
|
|
308
|
+
old_mode = self.current_mode
|
|
309
|
+
self.current_mode = new_mode
|
|
310
|
+
|
|
311
|
+
# Record switch
|
|
312
|
+
switch_record = {
|
|
313
|
+
'from_mode': old_mode,
|
|
314
|
+
'to_mode': new_mode,
|
|
315
|
+
'reason': reason,
|
|
316
|
+
'timestamp': datetime.now().isoformat(),
|
|
317
|
+
}
|
|
318
|
+
self.mode_switch_history.append(switch_record)
|
|
319
|
+
|
|
320
|
+
# Set cooldown
|
|
321
|
+
cooldown_seconds = self.hybrid_config.mode_switching_config.get('switch_cooldown_seconds', 300)
|
|
322
|
+
self.mode_switch_cooldown[new_mode] = datetime.now().timestamp() + cooldown_seconds
|
|
323
|
+
|
|
324
|
+
logger.info(f"Switched from {old_mode} to {new_mode} mode (reason: {reason})")
|
|
325
|
+
|
|
326
|
+
except Exception as e:
|
|
327
|
+
logger.error(f"Failed to switch mode to {new_mode}: {e}")
|
|
328
|
+
|
|
329
|
+
def _is_in_cooldown(self, mode: str) -> bool:
|
|
330
|
+
"""Check if a mode is in cooldown."""
|
|
331
|
+
if mode not in self.mode_switch_cooldown:
|
|
332
|
+
return False
|
|
333
|
+
|
|
334
|
+
return datetime.now().timestamp() < self.mode_switch_cooldown[mode]
|
|
335
|
+
|
|
336
|
+
def get_memories(
|
|
337
|
+
self,
|
|
338
|
+
agent_id: str,
|
|
339
|
+
query: Optional[str] = None,
|
|
340
|
+
filters: Optional[Dict[str, Any]] = None
|
|
341
|
+
) -> List[Dict[str, Any]]:
|
|
342
|
+
"""
|
|
343
|
+
Retrieve memories for a specific agent/user from both modes.
|
|
344
|
+
|
|
345
|
+
Args:
|
|
346
|
+
agent_id: ID of the agent/user
|
|
347
|
+
query: Optional query string for filtering
|
|
348
|
+
filters: Optional additional filters
|
|
349
|
+
|
|
350
|
+
Returns:
|
|
351
|
+
List of memory dictionaries from both modes
|
|
352
|
+
"""
|
|
353
|
+
try:
|
|
354
|
+
all_memories = []
|
|
355
|
+
|
|
356
|
+
# Get memories from current mode
|
|
357
|
+
if self.current_mode == "multi_agent":
|
|
358
|
+
current_memories = self.multi_agent_manager.get_memories(agent_id, query, filters)
|
|
359
|
+
else:
|
|
360
|
+
current_memories = self.multi_user_manager.get_memories(agent_id, query, filters)
|
|
361
|
+
|
|
362
|
+
all_memories.extend(current_memories)
|
|
363
|
+
|
|
364
|
+
# Get memories from other mode if cross-mode access is enabled
|
|
365
|
+
if self.hybrid_config.mode_switching_config.get('enable_cross_mode_access', True):
|
|
366
|
+
other_mode = "multi_user" if self.current_mode == "multi_agent" else "multi_agent"
|
|
367
|
+
|
|
368
|
+
if other_mode == "multi_agent":
|
|
369
|
+
other_memories = self.multi_agent_manager.get_memories(agent_id, query, filters)
|
|
370
|
+
else:
|
|
371
|
+
other_memories = self.multi_user_manager.get_memories(agent_id, query, filters)
|
|
372
|
+
|
|
373
|
+
# Mark memories from other mode
|
|
374
|
+
for memory in other_memories:
|
|
375
|
+
memory['source_mode'] = other_mode
|
|
376
|
+
memory['cross_mode_access'] = True
|
|
377
|
+
|
|
378
|
+
all_memories.extend(other_memories)
|
|
379
|
+
|
|
380
|
+
# Remove duplicates based on content
|
|
381
|
+
unique_memories = []
|
|
382
|
+
seen_content = set()
|
|
383
|
+
|
|
384
|
+
for memory in all_memories:
|
|
385
|
+
content = memory.get('content', '')
|
|
386
|
+
if content not in seen_content:
|
|
387
|
+
seen_content.add(content)
|
|
388
|
+
unique_memories.append(memory)
|
|
389
|
+
|
|
390
|
+
logger.info(f"Retrieved {len(unique_memories)} memories for {agent_id} (current mode: {self.current_mode})")
|
|
391
|
+
return unique_memories
|
|
392
|
+
|
|
393
|
+
except Exception as e:
|
|
394
|
+
logger.error(f"Failed to get memories for {agent_id}: {e}")
|
|
395
|
+
raise
|
|
396
|
+
|
|
397
|
+
def update_memory(
|
|
398
|
+
self,
|
|
399
|
+
memory_id: str,
|
|
400
|
+
agent_id: str,
|
|
401
|
+
updates: Dict[str, Any]
|
|
402
|
+
) -> Dict[str, Any]:
|
|
403
|
+
"""
|
|
404
|
+
Update an existing memory.
|
|
405
|
+
|
|
406
|
+
Args:
|
|
407
|
+
memory_id: ID of the memory to update
|
|
408
|
+
agent_id: ID of the agent/user making the update
|
|
409
|
+
updates: Dictionary of updates to apply
|
|
410
|
+
|
|
411
|
+
Returns:
|
|
412
|
+
Dictionary containing the updated memory information
|
|
413
|
+
"""
|
|
414
|
+
try:
|
|
415
|
+
# Find which mode the memory belongs to
|
|
416
|
+
memory_mode = self._get_memory_mode(memory_id)
|
|
417
|
+
|
|
418
|
+
if not memory_mode:
|
|
419
|
+
raise ValueError(f"Memory {memory_id} not found in any mode")
|
|
420
|
+
|
|
421
|
+
# Update with appropriate manager
|
|
422
|
+
if memory_mode == "multi_agent":
|
|
423
|
+
result = self.multi_agent_manager.update_memory(memory_id, agent_id, updates)
|
|
424
|
+
else:
|
|
425
|
+
result = self.multi_user_manager.update_memory(memory_id, agent_id, updates)
|
|
426
|
+
|
|
427
|
+
# Update unified index
|
|
428
|
+
if memory_id in self.unified_memory_index:
|
|
429
|
+
self.unified_memory_index[memory_id]['updated_at'] = datetime.now().isoformat()
|
|
430
|
+
self.unified_memory_index[memory_id]['updated_by'] = agent_id
|
|
431
|
+
|
|
432
|
+
result['updated_in_mode'] = memory_mode
|
|
433
|
+
logger.info(f"Updated memory {memory_id} in {memory_mode} mode")
|
|
434
|
+
return result
|
|
435
|
+
|
|
436
|
+
except Exception as e:
|
|
437
|
+
logger.error(f"Failed to update memory {memory_id}: {e}")
|
|
438
|
+
raise
|
|
439
|
+
|
|
440
|
+
def delete_memory(
|
|
441
|
+
self,
|
|
442
|
+
memory_id: str,
|
|
443
|
+
agent_id: str
|
|
444
|
+
) -> Dict[str, Any]:
|
|
445
|
+
"""
|
|
446
|
+
Delete a memory.
|
|
447
|
+
|
|
448
|
+
Args:
|
|
449
|
+
memory_id: ID of the memory to delete
|
|
450
|
+
agent_id: ID of the agent/user making the deletion
|
|
451
|
+
|
|
452
|
+
Returns:
|
|
453
|
+
Dictionary containing the deletion result
|
|
454
|
+
"""
|
|
455
|
+
try:
|
|
456
|
+
# Find which mode the memory belongs to
|
|
457
|
+
memory_mode = self._get_memory_mode(memory_id)
|
|
458
|
+
|
|
459
|
+
if not memory_mode:
|
|
460
|
+
raise ValueError(f"Memory {memory_id} not found in any mode")
|
|
461
|
+
|
|
462
|
+
# Delete with appropriate manager
|
|
463
|
+
if memory_mode == "multi_agent":
|
|
464
|
+
result = self.multi_agent_manager.delete_memory(memory_id, agent_id)
|
|
465
|
+
else:
|
|
466
|
+
result = self.multi_user_manager.delete_memory(memory_id, agent_id)
|
|
467
|
+
|
|
468
|
+
# Clean up unified index
|
|
469
|
+
if memory_id in self.unified_memory_index:
|
|
470
|
+
del self.unified_memory_index[memory_id]
|
|
471
|
+
|
|
472
|
+
# Clean up mode-specific storage
|
|
473
|
+
if memory_mode in self.mode_specific_memories:
|
|
474
|
+
if memory_id in self.mode_specific_memories[memory_mode]:
|
|
475
|
+
del self.mode_specific_memories[memory_mode][memory_id]
|
|
476
|
+
|
|
477
|
+
result['deleted_from_mode'] = memory_mode
|
|
478
|
+
logger.info(f"Deleted memory {memory_id} from {memory_mode} mode")
|
|
479
|
+
return result
|
|
480
|
+
|
|
481
|
+
except Exception as e:
|
|
482
|
+
logger.error(f"Failed to delete memory {memory_id}: {e}")
|
|
483
|
+
raise
|
|
484
|
+
|
|
485
|
+
def share_memory(
|
|
486
|
+
self,
|
|
487
|
+
memory_id: str,
|
|
488
|
+
from_agent: str,
|
|
489
|
+
to_agents: List[str],
|
|
490
|
+
permissions: Optional[List[str]] = None
|
|
491
|
+
) -> Dict[str, Any]:
|
|
492
|
+
"""
|
|
493
|
+
Share a memory with other agents/users.
|
|
494
|
+
|
|
495
|
+
Args:
|
|
496
|
+
memory_id: ID of the memory to share
|
|
497
|
+
from_agent: ID of the agent/user sharing the memory
|
|
498
|
+
to_agents: List of agent/user IDs to share with
|
|
499
|
+
permissions: Optional list of permissions to grant
|
|
500
|
+
|
|
501
|
+
Returns:
|
|
502
|
+
Dictionary containing the sharing result
|
|
503
|
+
"""
|
|
504
|
+
try:
|
|
505
|
+
# Find which mode the memory belongs to
|
|
506
|
+
memory_mode = self._get_memory_mode(memory_id)
|
|
507
|
+
|
|
508
|
+
if not memory_mode:
|
|
509
|
+
raise ValueError(f"Memory {memory_id} not found in any mode")
|
|
510
|
+
|
|
511
|
+
# Share with appropriate manager
|
|
512
|
+
if memory_mode == "multi_agent":
|
|
513
|
+
result = self.multi_agent_manager.share_memory(memory_id, from_agent, to_agents, permissions)
|
|
514
|
+
else:
|
|
515
|
+
result = self.multi_user_manager.share_memory(memory_id, from_agent, to_agents, permissions)
|
|
516
|
+
|
|
517
|
+
result['shared_from_mode'] = memory_mode
|
|
518
|
+
logger.info(f"Shared memory {memory_id} from {memory_mode} mode")
|
|
519
|
+
return result
|
|
520
|
+
|
|
521
|
+
except Exception as e:
|
|
522
|
+
logger.error(f"Failed to share memory {memory_id}: {e}")
|
|
523
|
+
raise
|
|
524
|
+
|
|
525
|
+
def get_context_info(self, agent_id: str) -> Dict[str, Any]:
|
|
526
|
+
"""
|
|
527
|
+
Get context information for an agent/user from both modes.
|
|
528
|
+
|
|
529
|
+
Args:
|
|
530
|
+
agent_id: ID of the agent/user
|
|
531
|
+
|
|
532
|
+
Returns:
|
|
533
|
+
Dictionary containing context information from both modes
|
|
534
|
+
"""
|
|
535
|
+
try:
|
|
536
|
+
# Get context from both modes
|
|
537
|
+
multi_agent_context = self.multi_agent_manager.get_context_info(agent_id)
|
|
538
|
+
multi_user_context = self.multi_user_manager.get_context_info(agent_id)
|
|
539
|
+
|
|
540
|
+
# Combine context information
|
|
541
|
+
combined_context = {
|
|
542
|
+
'agent_id': agent_id,
|
|
543
|
+
'current_mode': self.current_mode,
|
|
544
|
+
'mode_switch_history': self.mode_switch_history[-5:], # Last 5 switches
|
|
545
|
+
'multi_agent_context': multi_agent_context,
|
|
546
|
+
'multi_user_context': multi_user_context,
|
|
547
|
+
'hybrid_stats': {
|
|
548
|
+
'total_memories': len(self.unified_memory_index),
|
|
549
|
+
'mode_breakdown': {
|
|
550
|
+
'multi_agent': len(self.mode_specific_memories.get('multi_agent', {})),
|
|
551
|
+
'multi_user': len(self.mode_specific_memories.get('multi_user', {})),
|
|
552
|
+
},
|
|
553
|
+
'switch_count': len(self.mode_switch_history),
|
|
554
|
+
},
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
return combined_context
|
|
558
|
+
|
|
559
|
+
except Exception as e:
|
|
560
|
+
logger.error(f"Failed to get context info for {agent_id}: {e}")
|
|
561
|
+
raise
|
|
562
|
+
|
|
563
|
+
def update_memory_decay(self) -> Dict[str, Any]:
|
|
564
|
+
"""
|
|
565
|
+
Update memory decay for both modes.
|
|
566
|
+
|
|
567
|
+
Returns:
|
|
568
|
+
Dictionary containing the decay update results
|
|
569
|
+
"""
|
|
570
|
+
try:
|
|
571
|
+
# Update decay for both modes
|
|
572
|
+
multi_agent_decay = self.multi_agent_manager.update_memory_decay()
|
|
573
|
+
multi_user_decay = self.multi_user_manager.update_memory_decay()
|
|
574
|
+
|
|
575
|
+
# Combine results
|
|
576
|
+
combined_decay = {
|
|
577
|
+
'multi_agent_decay': multi_agent_decay,
|
|
578
|
+
'multi_user_decay': multi_user_decay,
|
|
579
|
+
'total_updated': multi_agent_decay.get('updated_memories', 0) + multi_user_decay.get('updated_memories', 0),
|
|
580
|
+
'total_forgotten': multi_agent_decay.get('forgotten_memories', 0) + multi_user_decay.get('forgotten_memories', 0),
|
|
581
|
+
'total_reinforced': multi_agent_decay.get('reinforced_memories', 0) + multi_user_decay.get('reinforced_memories', 0),
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
logger.info(f"Updated memory decay for both modes: {combined_decay}")
|
|
585
|
+
return combined_decay
|
|
586
|
+
|
|
587
|
+
except Exception as e:
|
|
588
|
+
logger.error(f"Failed to update memory decay: {e}")
|
|
589
|
+
raise
|
|
590
|
+
|
|
591
|
+
def cleanup_forgotten_memories(self) -> Dict[str, Any]:
|
|
592
|
+
"""
|
|
593
|
+
Clean up forgotten memories from both modes.
|
|
594
|
+
|
|
595
|
+
Returns:
|
|
596
|
+
Dictionary containing the cleanup results
|
|
597
|
+
"""
|
|
598
|
+
try:
|
|
599
|
+
# Clean up both modes
|
|
600
|
+
multi_agent_cleanup = self.multi_agent_manager.cleanup_forgotten_memories()
|
|
601
|
+
multi_user_cleanup = self.multi_user_manager.cleanup_forgotten_memories()
|
|
602
|
+
|
|
603
|
+
# Combine results
|
|
604
|
+
combined_cleanup = {
|
|
605
|
+
'multi_agent_cleanup': multi_agent_cleanup,
|
|
606
|
+
'multi_user_cleanup': multi_user_cleanup,
|
|
607
|
+
'total_cleaned': multi_agent_cleanup.get('cleaned_memories', 0) + multi_user_cleanup.get('cleaned_memories', 0),
|
|
608
|
+
'total_archived': multi_agent_cleanup.get('archived_memories', 0) + multi_user_cleanup.get('archived_memories', 0),
|
|
609
|
+
'total_deleted': multi_agent_cleanup.get('deleted_memories', 0) + multi_user_cleanup.get('deleted_memories', 0),
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
# Clean up unified index
|
|
613
|
+
cleaned_memory_ids = set()
|
|
614
|
+
for cleanup_result in [multi_agent_cleanup, multi_user_cleanup]:
|
|
615
|
+
if 'cleaned_memory_ids' in cleanup_result:
|
|
616
|
+
cleaned_memory_ids.update(cleanup_result['cleaned_memory_ids'])
|
|
617
|
+
|
|
618
|
+
for memory_id in cleaned_memory_ids:
|
|
619
|
+
if memory_id in self.unified_memory_index:
|
|
620
|
+
del self.unified_memory_index[memory_id]
|
|
621
|
+
|
|
622
|
+
logger.info(f"Cleaned up forgotten memories from both modes: {combined_cleanup}")
|
|
623
|
+
return combined_cleanup
|
|
624
|
+
|
|
625
|
+
except Exception as e:
|
|
626
|
+
logger.error(f"Failed to cleanup forgotten memories: {e}")
|
|
627
|
+
raise
|
|
628
|
+
|
|
629
|
+
def get_memory_statistics(self) -> Dict[str, Any]:
|
|
630
|
+
"""
|
|
631
|
+
Get statistics about the memory system from both modes.
|
|
632
|
+
|
|
633
|
+
Returns:
|
|
634
|
+
Dictionary containing memory statistics
|
|
635
|
+
"""
|
|
636
|
+
try:
|
|
637
|
+
# Get statistics from both modes
|
|
638
|
+
multi_agent_stats = self.multi_agent_manager.get_memory_statistics()
|
|
639
|
+
multi_user_stats = self.multi_user_manager.get_memory_statistics()
|
|
640
|
+
|
|
641
|
+
# Combine statistics
|
|
642
|
+
combined_stats = {
|
|
643
|
+
'current_mode': self.current_mode,
|
|
644
|
+
'mode_switch_history': len(self.mode_switch_history),
|
|
645
|
+
'unified_memory_count': len(self.unified_memory_index),
|
|
646
|
+
'multi_agent_stats': multi_agent_stats,
|
|
647
|
+
'multi_user_stats': multi_user_stats,
|
|
648
|
+
'hybrid_stats': {
|
|
649
|
+
'total_memories': multi_agent_stats.get('total_memories', 0) + multi_user_stats.get('total_memories', 0),
|
|
650
|
+
'mode_breakdown': {
|
|
651
|
+
'multi_agent': multi_agent_stats.get('total_memories', 0),
|
|
652
|
+
'multi_user': multi_user_stats.get('total_memories', 0),
|
|
653
|
+
},
|
|
654
|
+
'switch_frequency': len(self.mode_switch_history) / max(1, (datetime.now() - datetime.fromisoformat(self.mode_switch_history[0]['timestamp'])).days) if self.mode_switch_history else 0,
|
|
655
|
+
},
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
return combined_stats
|
|
659
|
+
|
|
660
|
+
except Exception as e:
|
|
661
|
+
logger.error(f"Failed to get memory statistics: {e}")
|
|
662
|
+
raise
|
|
663
|
+
|
|
664
|
+
def check_permission(
|
|
665
|
+
self,
|
|
666
|
+
agent_id: str,
|
|
667
|
+
memory_id: str,
|
|
668
|
+
permission: str
|
|
669
|
+
) -> bool:
|
|
670
|
+
"""
|
|
671
|
+
Check if an agent/user has a specific permission for a memory.
|
|
672
|
+
|
|
673
|
+
Args:
|
|
674
|
+
agent_id: ID of the agent/user
|
|
675
|
+
memory_id: ID of the memory
|
|
676
|
+
permission: Permission to check
|
|
677
|
+
|
|
678
|
+
Returns:
|
|
679
|
+
True if the agent/user has the permission, False otherwise
|
|
680
|
+
"""
|
|
681
|
+
try:
|
|
682
|
+
# Find which mode the memory belongs to
|
|
683
|
+
memory_mode = self._get_memory_mode(memory_id)
|
|
684
|
+
|
|
685
|
+
if not memory_mode:
|
|
686
|
+
return False
|
|
687
|
+
|
|
688
|
+
# Check permission with appropriate manager
|
|
689
|
+
if memory_mode == "multi_agent":
|
|
690
|
+
return self.multi_agent_manager.check_permission(agent_id, memory_id, permission)
|
|
691
|
+
else:
|
|
692
|
+
return self.multi_user_manager.check_permission(agent_id, memory_id, permission)
|
|
693
|
+
|
|
694
|
+
except Exception:
|
|
695
|
+
return False
|
|
696
|
+
|
|
697
|
+
def _get_memory_mode(self, memory_id: str) -> Optional[str]:
|
|
698
|
+
"""Get the mode that a memory belongs to."""
|
|
699
|
+
# Check unified index first
|
|
700
|
+
if memory_id in self.unified_memory_index:
|
|
701
|
+
return self.unified_memory_index[memory_id]['mode']
|
|
702
|
+
|
|
703
|
+
# Check mode-specific storage
|
|
704
|
+
for mode, memories in self.mode_specific_memories.items():
|
|
705
|
+
if memory_id in memories:
|
|
706
|
+
return mode
|
|
707
|
+
|
|
708
|
+
return None
|
|
709
|
+
|
|
710
|
+
def get_mode_info(self) -> Dict[str, Any]:
|
|
711
|
+
"""
|
|
712
|
+
Get information about the current mode and switching history.
|
|
713
|
+
|
|
714
|
+
Returns:
|
|
715
|
+
Dictionary containing mode information
|
|
716
|
+
"""
|
|
717
|
+
return {
|
|
718
|
+
'current_mode': self.current_mode,
|
|
719
|
+
'primary_mode': self.hybrid_config.primary_mode,
|
|
720
|
+
'fallback_mode': self.hybrid_config.fallback_mode,
|
|
721
|
+
'auto_switch_enabled': self.hybrid_config.mode_switching_config.get('enable_auto_switch', True),
|
|
722
|
+
'switch_history': self.mode_switch_history[-10:], # Last 10 switches
|
|
723
|
+
'cooldown_status': {
|
|
724
|
+
mode: self._is_in_cooldown(mode)
|
|
725
|
+
for mode in ['multi_agent', 'multi_user']
|
|
726
|
+
},
|
|
727
|
+
'confidence_threshold': self.hybrid_config.mode_switching_config.get('confidence_threshold', 0.7),
|
|
728
|
+
}
|