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.
Files changed (123) hide show
  1. powermem/__init__.py +103 -0
  2. powermem/agent/__init__.py +35 -0
  3. powermem/agent/abstract/__init__.py +22 -0
  4. powermem/agent/abstract/collaboration.py +259 -0
  5. powermem/agent/abstract/context.py +187 -0
  6. powermem/agent/abstract/manager.py +232 -0
  7. powermem/agent/abstract/permission.py +217 -0
  8. powermem/agent/abstract/privacy.py +267 -0
  9. powermem/agent/abstract/scope.py +199 -0
  10. powermem/agent/agent.py +791 -0
  11. powermem/agent/components/__init__.py +18 -0
  12. powermem/agent/components/collaboration_coordinator.py +645 -0
  13. powermem/agent/components/permission_controller.py +586 -0
  14. powermem/agent/components/privacy_protector.py +767 -0
  15. powermem/agent/components/scope_controller.py +685 -0
  16. powermem/agent/factories/__init__.py +16 -0
  17. powermem/agent/factories/agent_factory.py +266 -0
  18. powermem/agent/factories/config_factory.py +308 -0
  19. powermem/agent/factories/memory_factory.py +229 -0
  20. powermem/agent/implementations/__init__.py +16 -0
  21. powermem/agent/implementations/hybrid.py +728 -0
  22. powermem/agent/implementations/multi_agent.py +1040 -0
  23. powermem/agent/implementations/multi_user.py +1020 -0
  24. powermem/agent/types.py +53 -0
  25. powermem/agent/wrappers/__init__.py +14 -0
  26. powermem/agent/wrappers/agent_memory_wrapper.py +427 -0
  27. powermem/agent/wrappers/compatibility_wrapper.py +520 -0
  28. powermem/config_loader.py +318 -0
  29. powermem/configs.py +249 -0
  30. powermem/core/__init__.py +19 -0
  31. powermem/core/async_memory.py +1493 -0
  32. powermem/core/audit.py +258 -0
  33. powermem/core/base.py +165 -0
  34. powermem/core/memory.py +1567 -0
  35. powermem/core/setup.py +162 -0
  36. powermem/core/telemetry.py +215 -0
  37. powermem/integrations/__init__.py +17 -0
  38. powermem/integrations/embeddings/__init__.py +13 -0
  39. powermem/integrations/embeddings/aws_bedrock.py +100 -0
  40. powermem/integrations/embeddings/azure_openai.py +55 -0
  41. powermem/integrations/embeddings/base.py +31 -0
  42. powermem/integrations/embeddings/config/base.py +132 -0
  43. powermem/integrations/embeddings/configs.py +31 -0
  44. powermem/integrations/embeddings/factory.py +48 -0
  45. powermem/integrations/embeddings/gemini.py +39 -0
  46. powermem/integrations/embeddings/huggingface.py +41 -0
  47. powermem/integrations/embeddings/langchain.py +35 -0
  48. powermem/integrations/embeddings/lmstudio.py +29 -0
  49. powermem/integrations/embeddings/mock.py +11 -0
  50. powermem/integrations/embeddings/ollama.py +53 -0
  51. powermem/integrations/embeddings/openai.py +49 -0
  52. powermem/integrations/embeddings/qwen.py +102 -0
  53. powermem/integrations/embeddings/together.py +31 -0
  54. powermem/integrations/embeddings/vertexai.py +54 -0
  55. powermem/integrations/llm/__init__.py +18 -0
  56. powermem/integrations/llm/anthropic.py +87 -0
  57. powermem/integrations/llm/base.py +132 -0
  58. powermem/integrations/llm/config/anthropic.py +56 -0
  59. powermem/integrations/llm/config/azure.py +56 -0
  60. powermem/integrations/llm/config/base.py +62 -0
  61. powermem/integrations/llm/config/deepseek.py +56 -0
  62. powermem/integrations/llm/config/ollama.py +56 -0
  63. powermem/integrations/llm/config/openai.py +79 -0
  64. powermem/integrations/llm/config/qwen.py +68 -0
  65. powermem/integrations/llm/config/qwen_asr.py +46 -0
  66. powermem/integrations/llm/config/vllm.py +56 -0
  67. powermem/integrations/llm/configs.py +26 -0
  68. powermem/integrations/llm/deepseek.py +106 -0
  69. powermem/integrations/llm/factory.py +118 -0
  70. powermem/integrations/llm/gemini.py +201 -0
  71. powermem/integrations/llm/langchain.py +65 -0
  72. powermem/integrations/llm/ollama.py +106 -0
  73. powermem/integrations/llm/openai.py +166 -0
  74. powermem/integrations/llm/openai_structured.py +80 -0
  75. powermem/integrations/llm/qwen.py +207 -0
  76. powermem/integrations/llm/qwen_asr.py +171 -0
  77. powermem/integrations/llm/vllm.py +106 -0
  78. powermem/integrations/rerank/__init__.py +20 -0
  79. powermem/integrations/rerank/base.py +43 -0
  80. powermem/integrations/rerank/config/__init__.py +7 -0
  81. powermem/integrations/rerank/config/base.py +27 -0
  82. powermem/integrations/rerank/configs.py +23 -0
  83. powermem/integrations/rerank/factory.py +68 -0
  84. powermem/integrations/rerank/qwen.py +159 -0
  85. powermem/intelligence/__init__.py +17 -0
  86. powermem/intelligence/ebbinghaus_algorithm.py +354 -0
  87. powermem/intelligence/importance_evaluator.py +361 -0
  88. powermem/intelligence/intelligent_memory_manager.py +284 -0
  89. powermem/intelligence/manager.py +148 -0
  90. powermem/intelligence/plugin.py +229 -0
  91. powermem/prompts/__init__.py +29 -0
  92. powermem/prompts/graph/graph_prompts.py +217 -0
  93. powermem/prompts/graph/graph_tools_prompts.py +469 -0
  94. powermem/prompts/importance_evaluation.py +246 -0
  95. powermem/prompts/intelligent_memory_prompts.py +163 -0
  96. powermem/prompts/templates.py +193 -0
  97. powermem/storage/__init__.py +14 -0
  98. powermem/storage/adapter.py +896 -0
  99. powermem/storage/base.py +109 -0
  100. powermem/storage/config/base.py +13 -0
  101. powermem/storage/config/oceanbase.py +58 -0
  102. powermem/storage/config/pgvector.py +52 -0
  103. powermem/storage/config/sqlite.py +27 -0
  104. powermem/storage/configs.py +159 -0
  105. powermem/storage/factory.py +59 -0
  106. powermem/storage/migration_manager.py +438 -0
  107. powermem/storage/oceanbase/__init__.py +8 -0
  108. powermem/storage/oceanbase/constants.py +162 -0
  109. powermem/storage/oceanbase/oceanbase.py +1384 -0
  110. powermem/storage/oceanbase/oceanbase_graph.py +1441 -0
  111. powermem/storage/pgvector/__init__.py +7 -0
  112. powermem/storage/pgvector/pgvector.py +420 -0
  113. powermem/storage/sqlite/__init__.py +0 -0
  114. powermem/storage/sqlite/sqlite.py +218 -0
  115. powermem/storage/sqlite/sqlite_vector_store.py +311 -0
  116. powermem/utils/__init__.py +35 -0
  117. powermem/utils/utils.py +605 -0
  118. powermem/version.py +23 -0
  119. powermem-0.1.0.dist-info/METADATA +187 -0
  120. powermem-0.1.0.dist-info/RECORD +123 -0
  121. powermem-0.1.0.dist-info/WHEEL +5 -0
  122. powermem-0.1.0.dist-info/licenses/LICENSE +206 -0
  123. 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
+ ]