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,685 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Multi-Agent Scope Controller
|
|
3
|
+
|
|
4
|
+
Manages memory scopes and determines appropriate scope for new memories
|
|
5
|
+
based on content analysis and context. Migrated and refactored from
|
|
6
|
+
the original multi_agent implementation.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import logging
|
|
11
|
+
from typing import Any, Dict, List, Optional
|
|
12
|
+
|
|
13
|
+
from powermem.agent.agent import ConfigObject
|
|
14
|
+
from powermem.agent.types import MemoryScope, MemoryType
|
|
15
|
+
from powermem.agent.abstract.scope import AgentScopeManagerBase
|
|
16
|
+
from powermem.integrations import LLMFactory
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ScopeController(AgentScopeManagerBase):
|
|
22
|
+
"""
|
|
23
|
+
Multi-agent scope controller implementation.
|
|
24
|
+
|
|
25
|
+
Determines memory scope based on content analysis and context,
|
|
26
|
+
manages scope-specific storage and retrieval.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self, config: Dict[str, Any]):
|
|
30
|
+
"""
|
|
31
|
+
Initialize the scope controller.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
config: Memory configuration object
|
|
35
|
+
"""
|
|
36
|
+
super().__init__(config.agent_memory.multi_agent_config.__dict__)
|
|
37
|
+
self.config = config
|
|
38
|
+
self.multi_agent_config = config.agent_memory.multi_agent_config
|
|
39
|
+
|
|
40
|
+
# Extract llm config as dict (handle both ConfigObject and dict)
|
|
41
|
+
try:
|
|
42
|
+
llm_provider = config.llm.provider if hasattr(config, 'llm') else 'mock'
|
|
43
|
+
llm_config_obj = config.llm.config if hasattr(config, 'llm') else {}
|
|
44
|
+
|
|
45
|
+
# Convert to dict if ConfigObject
|
|
46
|
+
if isinstance(llm_config_obj, ConfigObject):
|
|
47
|
+
llm_config = llm_config_obj.to_dict()
|
|
48
|
+
else:
|
|
49
|
+
llm_config = dict(llm_config_obj) if llm_config_obj else {}
|
|
50
|
+
except Exception as e:
|
|
51
|
+
logger.warning(f"Failed to extract LLM config: {e}")
|
|
52
|
+
llm_provider = 'mock'
|
|
53
|
+
llm_config = {}
|
|
54
|
+
|
|
55
|
+
self.llm = LLMFactory.create(llm_provider, llm_config)
|
|
56
|
+
|
|
57
|
+
# Scope-specific storage areas
|
|
58
|
+
self.scope_storage = {
|
|
59
|
+
MemoryScope.PRIVATE: {
|
|
60
|
+
MemoryType.WORKING: {},
|
|
61
|
+
MemoryType.SHORT_TERM: {},
|
|
62
|
+
MemoryType.LONG_TERM: {},
|
|
63
|
+
MemoryType.SEMANTIC: {},
|
|
64
|
+
MemoryType.EPISODIC: {},
|
|
65
|
+
MemoryType.PROCEDURAL: {},
|
|
66
|
+
MemoryType.PUBLIC_SHARED: {},
|
|
67
|
+
MemoryType.PRIVATE_AGENT: {},
|
|
68
|
+
MemoryType.COLLABORATIVE: {},
|
|
69
|
+
MemoryType.GROUP_CONSENSUS: {},
|
|
70
|
+
},
|
|
71
|
+
MemoryScope.PUBLIC: {
|
|
72
|
+
MemoryType.WORKING: {},
|
|
73
|
+
MemoryType.SHORT_TERM: {},
|
|
74
|
+
MemoryType.LONG_TERM: {},
|
|
75
|
+
MemoryType.SEMANTIC: {},
|
|
76
|
+
MemoryType.EPISODIC: {},
|
|
77
|
+
MemoryType.PROCEDURAL: {},
|
|
78
|
+
MemoryType.PUBLIC_SHARED: {},
|
|
79
|
+
MemoryType.PRIVATE_AGENT: {},
|
|
80
|
+
MemoryType.COLLABORATIVE: {},
|
|
81
|
+
MemoryType.GROUP_CONSENSUS: {},
|
|
82
|
+
},
|
|
83
|
+
MemoryScope.AGENT_GROUP: {
|
|
84
|
+
MemoryType.WORKING: {},
|
|
85
|
+
MemoryType.SHORT_TERM: {},
|
|
86
|
+
MemoryType.LONG_TERM: {},
|
|
87
|
+
MemoryType.SEMANTIC: {},
|
|
88
|
+
MemoryType.EPISODIC: {},
|
|
89
|
+
MemoryType.PROCEDURAL: {},
|
|
90
|
+
MemoryType.PUBLIC_SHARED: {},
|
|
91
|
+
MemoryType.PRIVATE_AGENT: {},
|
|
92
|
+
MemoryType.COLLABORATIVE: {},
|
|
93
|
+
MemoryType.GROUP_CONSENSUS: {},
|
|
94
|
+
},
|
|
95
|
+
MemoryScope.USER_GROUP: {
|
|
96
|
+
MemoryType.WORKING: {},
|
|
97
|
+
MemoryType.SHORT_TERM: {},
|
|
98
|
+
MemoryType.LONG_TERM: {},
|
|
99
|
+
MemoryType.SEMANTIC: {},
|
|
100
|
+
MemoryType.EPISODIC: {},
|
|
101
|
+
MemoryType.PROCEDURAL: {},
|
|
102
|
+
MemoryType.PUBLIC_SHARED: {},
|
|
103
|
+
MemoryType.PRIVATE_AGENT: {},
|
|
104
|
+
MemoryType.COLLABORATIVE: {},
|
|
105
|
+
MemoryType.GROUP_CONSENSUS: {},
|
|
106
|
+
},
|
|
107
|
+
MemoryScope.RESTRICTED: {
|
|
108
|
+
MemoryType.WORKING: {},
|
|
109
|
+
MemoryType.SHORT_TERM: {},
|
|
110
|
+
MemoryType.LONG_TERM: {},
|
|
111
|
+
MemoryType.SEMANTIC: {},
|
|
112
|
+
MemoryType.EPISODIC: {},
|
|
113
|
+
MemoryType.PROCEDURAL: {},
|
|
114
|
+
MemoryType.PUBLIC_SHARED: {},
|
|
115
|
+
MemoryType.PRIVATE_AGENT: {},
|
|
116
|
+
MemoryType.COLLABORATIVE: {},
|
|
117
|
+
MemoryType.GROUP_CONSENSUS: {},
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
def initialize(self) -> None:
|
|
122
|
+
"""
|
|
123
|
+
Initialize the scope controller.
|
|
124
|
+
"""
|
|
125
|
+
try:
|
|
126
|
+
# Initialize scope-specific configurations
|
|
127
|
+
self._initialize_scope_configs()
|
|
128
|
+
self.initialized = True
|
|
129
|
+
logger.info("Scope controller initialized successfully")
|
|
130
|
+
|
|
131
|
+
except Exception as e:
|
|
132
|
+
logger.error(f"Failed to initialize scope controller: {e}")
|
|
133
|
+
raise
|
|
134
|
+
|
|
135
|
+
def _initialize_scope_configs(self) -> None:
|
|
136
|
+
"""Initialize scope-specific configurations."""
|
|
137
|
+
# Load scope-specific configurations from config
|
|
138
|
+
if hasattr(self.multi_agent_config, 'scope_specific_configs'):
|
|
139
|
+
self.scope_configs = self.multi_agent_config.scope_specific_configs
|
|
140
|
+
else:
|
|
141
|
+
# Default configurations
|
|
142
|
+
self.scope_configs = {
|
|
143
|
+
"private": {
|
|
144
|
+
"decay_rate_multiplier": 1.0,
|
|
145
|
+
"importance_threshold": 0.5,
|
|
146
|
+
"max_size": 5000
|
|
147
|
+
},
|
|
148
|
+
"public": {
|
|
149
|
+
"decay_rate_multiplier": 0.8,
|
|
150
|
+
"importance_threshold": 0.7,
|
|
151
|
+
"consensus_required": True,
|
|
152
|
+
"max_size": 10000
|
|
153
|
+
},
|
|
154
|
+
"agent_group": {
|
|
155
|
+
"decay_rate_multiplier": 0.9,
|
|
156
|
+
"importance_threshold": 0.6,
|
|
157
|
+
"max_size": 8000
|
|
158
|
+
},
|
|
159
|
+
"restricted": {
|
|
160
|
+
"decay_rate_multiplier": 1.2,
|
|
161
|
+
"importance_threshold": 0.8,
|
|
162
|
+
"max_size": 3000
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
def determine_scope(
|
|
167
|
+
self,
|
|
168
|
+
agent_id: str,
|
|
169
|
+
context: Optional[Dict[str, Any]] = None,
|
|
170
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
171
|
+
) -> MemoryScope:
|
|
172
|
+
"""
|
|
173
|
+
Determine memory scope based on content analysis and context.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
agent_id: ID of the agent creating the memory
|
|
177
|
+
context: Additional context information
|
|
178
|
+
metadata: Additional metadata information
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
Determined memory scope
|
|
182
|
+
"""
|
|
183
|
+
try:
|
|
184
|
+
# Extract content from metadata if available
|
|
185
|
+
content = metadata.get('content', '') if metadata else ''
|
|
186
|
+
|
|
187
|
+
# Use LLM to analyze content and determine scope
|
|
188
|
+
scope_analysis_prompt = self._build_scope_analysis_prompt(content, agent_id, context, metadata)
|
|
189
|
+
|
|
190
|
+
response = self.llm.generate_response(
|
|
191
|
+
messages=[{"role": "user", "content": scope_analysis_prompt}],
|
|
192
|
+
response_format={"type": "json_object"}
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
analysis = json.loads(response)
|
|
196
|
+
suggested_scope = analysis.get("suggested_scope", "PRIVATE")
|
|
197
|
+
|
|
198
|
+
# Validate the suggested scope
|
|
199
|
+
try:
|
|
200
|
+
# Try to convert string to MemoryScope enum
|
|
201
|
+
if isinstance(suggested_scope, str):
|
|
202
|
+
# Handle case-insensitive matching
|
|
203
|
+
suggested_scope = suggested_scope.upper()
|
|
204
|
+
# Map common variations
|
|
205
|
+
scope_mapping = {
|
|
206
|
+
'PRIVATE': MemoryScope.PRIVATE,
|
|
207
|
+
'PUBLIC': MemoryScope.PUBLIC,
|
|
208
|
+
'AGENT_GROUP': MemoryScope.AGENT_GROUP,
|
|
209
|
+
'USER_GROUP': MemoryScope.USER_GROUP,
|
|
210
|
+
'RESTRICTED': MemoryScope.RESTRICTED,
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if suggested_scope in scope_mapping:
|
|
214
|
+
scope = scope_mapping[suggested_scope]
|
|
215
|
+
logger.info(f"Determined scope '{scope.value}' for content from agent '{agent_id}'")
|
|
216
|
+
return scope
|
|
217
|
+
else:
|
|
218
|
+
# Try direct enum creation as fallback
|
|
219
|
+
scope = MemoryScope(suggested_scope.lower())
|
|
220
|
+
logger.info(f"Determined scope '{scope.value}' for content from agent '{agent_id}'")
|
|
221
|
+
return scope
|
|
222
|
+
else:
|
|
223
|
+
# Already a MemoryScope enum
|
|
224
|
+
logger.info(f"Determined scope '{suggested_scope.value}' for content from agent '{agent_id}'")
|
|
225
|
+
return suggested_scope
|
|
226
|
+
|
|
227
|
+
except (ValueError, AttributeError):
|
|
228
|
+
logger.warning(f"Invalid scope '{suggested_scope}' suggested by LLM, using default")
|
|
229
|
+
return self.multi_agent_config.default_scope
|
|
230
|
+
|
|
231
|
+
except Exception as e:
|
|
232
|
+
logger.error(f"Error determining scope: {e}")
|
|
233
|
+
return self.multi_agent_config.default_scope
|
|
234
|
+
|
|
235
|
+
def get_accessible_memories(
|
|
236
|
+
self,
|
|
237
|
+
agent_id: str,
|
|
238
|
+
scope: Optional[MemoryScope] = None
|
|
239
|
+
) -> List[str]:
|
|
240
|
+
"""
|
|
241
|
+
Get list of memory IDs accessible to an agent.
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
agent_id: ID of the agent
|
|
245
|
+
scope: Optional scope filter
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
List of accessible memory IDs
|
|
249
|
+
"""
|
|
250
|
+
try:
|
|
251
|
+
accessible_memories = []
|
|
252
|
+
|
|
253
|
+
# Define scopes to check based on agent's access level
|
|
254
|
+
scopes_to_check = [scope] if scope else [
|
|
255
|
+
MemoryScope.PRIVATE,
|
|
256
|
+
MemoryScope.AGENT_GROUP,
|
|
257
|
+
MemoryScope.PUBLIC,
|
|
258
|
+
MemoryScope.RESTRICTED
|
|
259
|
+
]
|
|
260
|
+
|
|
261
|
+
for scope_to_check in scopes_to_check:
|
|
262
|
+
if scope_to_check in self.scope_storage:
|
|
263
|
+
for memory_type in MemoryType:
|
|
264
|
+
for memory_id in self.scope_storage[scope_to_check][memory_type].keys():
|
|
265
|
+
# Check if agent has access to this memory
|
|
266
|
+
if self._check_agent_scope_access(agent_id, memory_id, scope_to_check):
|
|
267
|
+
accessible_memories.append(memory_id)
|
|
268
|
+
|
|
269
|
+
return accessible_memories
|
|
270
|
+
|
|
271
|
+
except Exception as e:
|
|
272
|
+
logger.error(f"Error getting accessible memories for agent {agent_id}: {e}")
|
|
273
|
+
return []
|
|
274
|
+
|
|
275
|
+
def check_scope_access(
|
|
276
|
+
self,
|
|
277
|
+
agent_id: str,
|
|
278
|
+
memory_id: str
|
|
279
|
+
) -> bool:
|
|
280
|
+
"""
|
|
281
|
+
Check if an agent has access to a memory based on scope.
|
|
282
|
+
|
|
283
|
+
Args:
|
|
284
|
+
agent_id: ID of the agent
|
|
285
|
+
memory_id: ID of the memory
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
True if access is allowed, False otherwise
|
|
289
|
+
"""
|
|
290
|
+
try:
|
|
291
|
+
# Find the memory and its scope
|
|
292
|
+
memory_scope = self._find_memory_scope(memory_id)
|
|
293
|
+
if not memory_scope:
|
|
294
|
+
return False
|
|
295
|
+
|
|
296
|
+
return self._check_agent_scope_access(agent_id, memory_id, memory_scope)
|
|
297
|
+
|
|
298
|
+
except Exception as e:
|
|
299
|
+
logger.error(f"Error checking scope access for agent {agent_id} and memory {memory_id}: {e}")
|
|
300
|
+
return False
|
|
301
|
+
|
|
302
|
+
def update_memory_scope(
|
|
303
|
+
self,
|
|
304
|
+
memory_id: str,
|
|
305
|
+
new_scope: MemoryScope,
|
|
306
|
+
agent_id: str
|
|
307
|
+
) -> Dict[str, Any]:
|
|
308
|
+
"""
|
|
309
|
+
Update the scope of a memory.
|
|
310
|
+
|
|
311
|
+
Args:
|
|
312
|
+
memory_id: ID of the memory
|
|
313
|
+
new_scope: New scope for the memory
|
|
314
|
+
agent_id: ID of the agent making the change
|
|
315
|
+
|
|
316
|
+
Returns:
|
|
317
|
+
Dictionary containing the update result
|
|
318
|
+
"""
|
|
319
|
+
try:
|
|
320
|
+
# Find current scope and memory data
|
|
321
|
+
current_scope = self._find_memory_scope(memory_id)
|
|
322
|
+
if not current_scope:
|
|
323
|
+
raise ValueError(f"Memory {memory_id} not found")
|
|
324
|
+
|
|
325
|
+
# Check if agent has permission to change scope
|
|
326
|
+
if not self._check_scope_change_permission(agent_id, memory_id, current_scope, new_scope):
|
|
327
|
+
raise PermissionError(f"Agent {agent_id} does not have permission to change scope")
|
|
328
|
+
|
|
329
|
+
# Move memory to new scope
|
|
330
|
+
memory_data = self._move_memory_to_scope(memory_id, current_scope, new_scope)
|
|
331
|
+
|
|
332
|
+
logger.info(f"Updated memory {memory_id} scope from {current_scope.value} to {new_scope.value}")
|
|
333
|
+
|
|
334
|
+
return {
|
|
335
|
+
'success': True,
|
|
336
|
+
'memory_id': memory_id,
|
|
337
|
+
'old_scope': current_scope.value,
|
|
338
|
+
'new_scope': new_scope.value,
|
|
339
|
+
'updated_by': agent_id,
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
except Exception as e:
|
|
343
|
+
logger.error(f"Error updating memory scope: {e}")
|
|
344
|
+
return {
|
|
345
|
+
'success': False,
|
|
346
|
+
'error': str(e),
|
|
347
|
+
'memory_id': memory_id,
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
def get_scope_members(
|
|
351
|
+
self,
|
|
352
|
+
scope: MemoryScope,
|
|
353
|
+
memory_id: Optional[str] = None
|
|
354
|
+
) -> List[str]:
|
|
355
|
+
"""
|
|
356
|
+
Get list of agents with access to a scope.
|
|
357
|
+
|
|
358
|
+
Args:
|
|
359
|
+
scope: Memory scope
|
|
360
|
+
memory_id: Optional specific memory ID
|
|
361
|
+
|
|
362
|
+
Returns:
|
|
363
|
+
List of agent IDs with access
|
|
364
|
+
"""
|
|
365
|
+
try:
|
|
366
|
+
members = []
|
|
367
|
+
|
|
368
|
+
if memory_id:
|
|
369
|
+
# Get members for specific memory
|
|
370
|
+
memory_data = self._find_memory_data(memory_id)
|
|
371
|
+
if memory_data:
|
|
372
|
+
# Add memory owner
|
|
373
|
+
if 'agent_id' in memory_data:
|
|
374
|
+
members.append(memory_data['agent_id'])
|
|
375
|
+
|
|
376
|
+
# Add shared members if applicable
|
|
377
|
+
if 'shared_with' in memory_data:
|
|
378
|
+
members.extend(memory_data['shared_with'])
|
|
379
|
+
else:
|
|
380
|
+
# Get all agents with access to this scope type
|
|
381
|
+
if scope == MemoryScope.PUBLIC:
|
|
382
|
+
# All agents have access to public memories
|
|
383
|
+
members = ['*'] # Wildcard for all agents
|
|
384
|
+
elif scope == MemoryScope.PRIVATE:
|
|
385
|
+
# Only memory owners have access
|
|
386
|
+
members = self._get_all_memory_owners(scope)
|
|
387
|
+
elif scope == MemoryScope.AGENT_GROUP:
|
|
388
|
+
# Group members have access
|
|
389
|
+
members = self._get_group_members(scope)
|
|
390
|
+
elif scope == MemoryScope.RESTRICTED:
|
|
391
|
+
# Restricted access - need specific permissions
|
|
392
|
+
members = self._get_restricted_members(scope)
|
|
393
|
+
|
|
394
|
+
return list(set(members)) # Remove duplicates
|
|
395
|
+
|
|
396
|
+
except Exception as e:
|
|
397
|
+
logger.error(f"Error getting scope members for {scope.value}: {e}")
|
|
398
|
+
return []
|
|
399
|
+
|
|
400
|
+
def add_scope_member(
|
|
401
|
+
self,
|
|
402
|
+
scope: MemoryScope,
|
|
403
|
+
agent_id: str,
|
|
404
|
+
memory_id: Optional[str] = None
|
|
405
|
+
) -> Dict[str, Any]:
|
|
406
|
+
"""
|
|
407
|
+
Add an agent to a scope.
|
|
408
|
+
|
|
409
|
+
Args:
|
|
410
|
+
scope: Memory scope
|
|
411
|
+
agent_id: ID of the agent to add
|
|
412
|
+
memory_id: Optional specific memory ID
|
|
413
|
+
|
|
414
|
+
Returns:
|
|
415
|
+
Dictionary containing the add result
|
|
416
|
+
"""
|
|
417
|
+
try:
|
|
418
|
+
if memory_id:
|
|
419
|
+
# Add agent to specific memory
|
|
420
|
+
memory_data = self._find_memory_data(memory_id)
|
|
421
|
+
if memory_data:
|
|
422
|
+
if 'shared_with' not in memory_data:
|
|
423
|
+
memory_data['shared_with'] = []
|
|
424
|
+
|
|
425
|
+
if agent_id not in memory_data['shared_with']:
|
|
426
|
+
memory_data['shared_with'].append(agent_id)
|
|
427
|
+
logger.info(f"Added agent {agent_id} to memory {memory_id} scope")
|
|
428
|
+
return {'success': True, 'agent_id': agent_id, 'memory_id': memory_id}
|
|
429
|
+
else:
|
|
430
|
+
# Add agent to scope in general (for group scopes)
|
|
431
|
+
if scope == MemoryScope.AGENT_GROUP:
|
|
432
|
+
# This would typically involve adding to a group membership
|
|
433
|
+
logger.info(f"Added agent {agent_id} to {scope.value} scope")
|
|
434
|
+
return {'success': True, 'agent_id': agent_id, 'scope': scope.value}
|
|
435
|
+
|
|
436
|
+
return {'success': False, 'error': 'Could not add agent to scope'}
|
|
437
|
+
|
|
438
|
+
except Exception as e:
|
|
439
|
+
logger.error(f"Error adding agent {agent_id} to scope {scope.value}: {e}")
|
|
440
|
+
return {'success': False, 'error': str(e)}
|
|
441
|
+
|
|
442
|
+
def remove_scope_member(
|
|
443
|
+
self,
|
|
444
|
+
scope: MemoryScope,
|
|
445
|
+
agent_id: str,
|
|
446
|
+
memory_id: Optional[str] = None
|
|
447
|
+
) -> Dict[str, Any]:
|
|
448
|
+
"""
|
|
449
|
+
Remove an agent from a scope.
|
|
450
|
+
|
|
451
|
+
Args:
|
|
452
|
+
scope: Memory scope
|
|
453
|
+
agent_id: ID of the agent to remove
|
|
454
|
+
memory_id: Optional specific memory ID
|
|
455
|
+
|
|
456
|
+
Returns:
|
|
457
|
+
Dictionary containing the removal result
|
|
458
|
+
"""
|
|
459
|
+
try:
|
|
460
|
+
if memory_id:
|
|
461
|
+
# Remove agent from specific memory
|
|
462
|
+
memory_data = self._find_memory_data(memory_id)
|
|
463
|
+
if memory_data and 'shared_with' in memory_data:
|
|
464
|
+
if agent_id in memory_data['shared_with']:
|
|
465
|
+
memory_data['shared_with'].remove(agent_id)
|
|
466
|
+
logger.info(f"Removed agent {agent_id} from memory {memory_id} scope")
|
|
467
|
+
return {'success': True, 'agent_id': agent_id, 'memory_id': memory_id}
|
|
468
|
+
|
|
469
|
+
return {'success': False, 'error': 'Could not remove agent from scope'}
|
|
470
|
+
|
|
471
|
+
except Exception as e:
|
|
472
|
+
logger.error(f"Error removing agent {agent_id} from scope {scope.value}: {e}")
|
|
473
|
+
return {'success': False, 'error': str(e)}
|
|
474
|
+
|
|
475
|
+
def get_scope_statistics(self) -> Dict[str, Any]:
|
|
476
|
+
"""
|
|
477
|
+
Get statistics about scope usage.
|
|
478
|
+
|
|
479
|
+
Returns:
|
|
480
|
+
Dictionary containing scope statistics
|
|
481
|
+
"""
|
|
482
|
+
try:
|
|
483
|
+
stats = {
|
|
484
|
+
'total_memories': 0,
|
|
485
|
+
'scope_breakdown': {},
|
|
486
|
+
'type_breakdown': {},
|
|
487
|
+
'access_patterns': {},
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
# Count memories by scope and type
|
|
491
|
+
for scope, scope_types in self.scope_storage.items():
|
|
492
|
+
scope_count = 0
|
|
493
|
+
for memory_type, memories in scope_types.items():
|
|
494
|
+
type_count = len(memories)
|
|
495
|
+
scope_count += type_count
|
|
496
|
+
stats['total_memories'] += type_count
|
|
497
|
+
|
|
498
|
+
# Type breakdown
|
|
499
|
+
type_key = memory_type.value
|
|
500
|
+
if type_key not in stats['type_breakdown']:
|
|
501
|
+
stats['type_breakdown'][type_key] = 0
|
|
502
|
+
stats['type_breakdown'][type_key] += type_count
|
|
503
|
+
|
|
504
|
+
stats['scope_breakdown'][scope.value] = scope_count
|
|
505
|
+
|
|
506
|
+
return stats
|
|
507
|
+
|
|
508
|
+
except Exception as e:
|
|
509
|
+
logger.error(f"Error getting scope statistics: {e}")
|
|
510
|
+
return {}
|
|
511
|
+
|
|
512
|
+
def _build_scope_analysis_prompt(
|
|
513
|
+
self,
|
|
514
|
+
content: str,
|
|
515
|
+
agent_id: str,
|
|
516
|
+
context: Optional[Dict[str, Any]] = None,
|
|
517
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
518
|
+
) -> str:
|
|
519
|
+
"""Build prompt for scope analysis."""
|
|
520
|
+
prompt = f"""
|
|
521
|
+
Analyze the following memory content and determine the appropriate scope for storage.
|
|
522
|
+
|
|
523
|
+
Content: {content}
|
|
524
|
+
Agent ID: {agent_id}
|
|
525
|
+
|
|
526
|
+
Context: {context or {}}
|
|
527
|
+
Metadata: {metadata or {}}
|
|
528
|
+
|
|
529
|
+
Consider the following scope options:
|
|
530
|
+
- PRIVATE: Personal/private information, only accessible to the creating agent
|
|
531
|
+
- AGENT_GROUP: Information relevant to a specific group of agents
|
|
532
|
+
- PUBLIC: Information that should be accessible to all agents
|
|
533
|
+
- RESTRICTED: Information requiring special permissions
|
|
534
|
+
|
|
535
|
+
Return a JSON response with:
|
|
536
|
+
{{
|
|
537
|
+
"suggested_scope": "PRIVATE|AGENT_GROUP|PUBLIC|RESTRICTED",
|
|
538
|
+
"reasoning": "Explanation for the scope choice",
|
|
539
|
+
"confidence": 0.0-1.0
|
|
540
|
+
}}
|
|
541
|
+
"""
|
|
542
|
+
return prompt
|
|
543
|
+
|
|
544
|
+
def _check_agent_scope_access(
|
|
545
|
+
self,
|
|
546
|
+
agent_id: str,
|
|
547
|
+
memory_id: str,
|
|
548
|
+
scope: MemoryScope
|
|
549
|
+
) -> bool:
|
|
550
|
+
"""Check if an agent has access to a memory in a specific scope."""
|
|
551
|
+
try:
|
|
552
|
+
if scope == MemoryScope.PUBLIC:
|
|
553
|
+
return True # All agents have access to public memories
|
|
554
|
+
elif scope == MemoryScope.PRIVATE:
|
|
555
|
+
# Only the creating agent has access
|
|
556
|
+
memory_data = self._find_memory_data(memory_id)
|
|
557
|
+
if memory_data:
|
|
558
|
+
creator_id = memory_data.get('agent_id')
|
|
559
|
+
logger.debug(f"Private memory {memory_id}: creator={creator_id}, requester={agent_id}")
|
|
560
|
+
return creator_id == agent_id
|
|
561
|
+
else:
|
|
562
|
+
logger.debug(f"Private memory {memory_id}: memory data not found")
|
|
563
|
+
return False
|
|
564
|
+
elif scope == MemoryScope.AGENT_GROUP:
|
|
565
|
+
# Group members have access
|
|
566
|
+
memory_data = self._find_memory_data(memory_id)
|
|
567
|
+
if memory_data:
|
|
568
|
+
# Check if agent is in the group or shared with
|
|
569
|
+
if memory_data.get('agent_id') == agent_id:
|
|
570
|
+
return True
|
|
571
|
+
if agent_id in memory_data.get('shared_with', []):
|
|
572
|
+
return True
|
|
573
|
+
return False
|
|
574
|
+
elif scope == MemoryScope.RESTRICTED:
|
|
575
|
+
# Need specific permissions
|
|
576
|
+
memory_data = self._find_memory_data(memory_id)
|
|
577
|
+
if memory_data:
|
|
578
|
+
# Check if agent has restricted access
|
|
579
|
+
return agent_id in memory_data.get('restricted_access', [])
|
|
580
|
+
return False
|
|
581
|
+
|
|
582
|
+
return False
|
|
583
|
+
|
|
584
|
+
except Exception as e:
|
|
585
|
+
logger.error(f"Error checking agent scope access: {e}")
|
|
586
|
+
return False
|
|
587
|
+
|
|
588
|
+
def _find_memory_scope(self, memory_id: str) -> Optional[MemoryScope]:
|
|
589
|
+
"""Find the scope of a memory."""
|
|
590
|
+
for scope, scope_types in self.scope_storage.items():
|
|
591
|
+
for memory_type, memories in scope_types.items():
|
|
592
|
+
if memory_id in memories:
|
|
593
|
+
return scope
|
|
594
|
+
return None
|
|
595
|
+
|
|
596
|
+
def _find_memory_data(self, memory_id: str) -> Optional[Dict[str, Any]]:
|
|
597
|
+
"""Find memory data by ID."""
|
|
598
|
+
for scope, scope_types in self.scope_storage.items():
|
|
599
|
+
for memory_type, memories in scope_types.items():
|
|
600
|
+
if memory_id in memories:
|
|
601
|
+
return memories[memory_id]
|
|
602
|
+
return None
|
|
603
|
+
|
|
604
|
+
def _move_memory_to_scope(
|
|
605
|
+
self,
|
|
606
|
+
memory_id: str,
|
|
607
|
+
from_scope: MemoryScope,
|
|
608
|
+
to_scope: MemoryScope
|
|
609
|
+
) -> Dict[str, Any]:
|
|
610
|
+
"""Move a memory from one scope to another."""
|
|
611
|
+
# Find memory data
|
|
612
|
+
memory_data = None
|
|
613
|
+
memory_type = None
|
|
614
|
+
|
|
615
|
+
for mt in MemoryType:
|
|
616
|
+
if memory_id in self.scope_storage[from_scope][mt]:
|
|
617
|
+
memory_data = self.scope_storage[from_scope][mt][memory_id]
|
|
618
|
+
memory_type = mt
|
|
619
|
+
break
|
|
620
|
+
|
|
621
|
+
if not memory_data or not memory_type:
|
|
622
|
+
raise ValueError(f"Memory {memory_id} not found in scope {from_scope.value}")
|
|
623
|
+
|
|
624
|
+
# Remove from old scope
|
|
625
|
+
del self.scope_storage[from_scope][memory_type][memory_id]
|
|
626
|
+
|
|
627
|
+
# Add to new scope
|
|
628
|
+
self.scope_storage[to_scope][memory_type][memory_id] = memory_data
|
|
629
|
+
|
|
630
|
+
# Update memory metadata
|
|
631
|
+
memory_data['scope'] = to_scope
|
|
632
|
+
memory_data['scope_updated_at'] = memory_data.get('updated_at', '')
|
|
633
|
+
|
|
634
|
+
return memory_data
|
|
635
|
+
|
|
636
|
+
def _check_scope_change_permission(
|
|
637
|
+
self,
|
|
638
|
+
agent_id: str,
|
|
639
|
+
memory_id: str,
|
|
640
|
+
current_scope: MemoryScope,
|
|
641
|
+
new_scope: MemoryScope
|
|
642
|
+
) -> bool:
|
|
643
|
+
"""Check if an agent has permission to change memory scope."""
|
|
644
|
+
# Only memory owner can change scope
|
|
645
|
+
memory_data = self._find_memory_data(memory_id)
|
|
646
|
+
if memory_data and memory_data.get('agent_id') == agent_id:
|
|
647
|
+
return True
|
|
648
|
+
|
|
649
|
+
# Check if it's a scope upgrade (more restrictive to less restrictive)
|
|
650
|
+
scope_hierarchy = {
|
|
651
|
+
MemoryScope.PRIVATE: 0,
|
|
652
|
+
MemoryScope.AGENT_GROUP: 1,
|
|
653
|
+
MemoryScope.PUBLIC: 2,
|
|
654
|
+
MemoryScope.RESTRICTED: 3,
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
current_level = scope_hierarchy.get(current_scope, 0)
|
|
658
|
+
new_level = scope_hierarchy.get(new_scope, 0)
|
|
659
|
+
|
|
660
|
+
# Allow scope changes within the same level or to more restrictive
|
|
661
|
+
return new_level <= current_level
|
|
662
|
+
|
|
663
|
+
def _get_all_memory_owners(self, scope: MemoryScope) -> List[str]:
|
|
664
|
+
"""Get all agents who own memories in a scope."""
|
|
665
|
+
owners = set()
|
|
666
|
+
for memory_type in MemoryType:
|
|
667
|
+
for memory_data in self.scope_storage[scope][memory_type].values():
|
|
668
|
+
if 'agent_id' in memory_data:
|
|
669
|
+
owners.add(memory_data['agent_id'])
|
|
670
|
+
return list(owners)
|
|
671
|
+
|
|
672
|
+
def _get_group_members(self, scope: MemoryScope) -> List[str]:
|
|
673
|
+
"""Get all group members for a scope."""
|
|
674
|
+
# This would typically involve checking group memberships
|
|
675
|
+
# For now, return all agents who have memories in this scope
|
|
676
|
+
return self._get_all_memory_owners(scope)
|
|
677
|
+
|
|
678
|
+
def _get_restricted_members(self, scope: MemoryScope) -> List[str]:
|
|
679
|
+
"""Get all agents with restricted access to a scope."""
|
|
680
|
+
restricted_members = set()
|
|
681
|
+
for memory_type in MemoryType:
|
|
682
|
+
for memory_data in self.scope_storage[scope][memory_type].values():
|
|
683
|
+
if 'restricted_access' in memory_data:
|
|
684
|
+
restricted_members.update(memory_data['restricted_access'])
|
|
685
|
+
return list(restricted_members)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Factory classes for agent memory management system.
|
|
3
|
+
|
|
4
|
+
This module provides factory classes for creating different components
|
|
5
|
+
of the agent memory management system.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .memory_factory import MemoryFactory
|
|
9
|
+
from .agent_factory import AgentFactory
|
|
10
|
+
from .config_factory import ConfigFactory
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"MemoryFactory",
|
|
14
|
+
"AgentFactory",
|
|
15
|
+
"ConfigFactory"
|
|
16
|
+
]
|