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,1040 @@
1
+ """
2
+ Multi-Agent Memory Manager Implementation
3
+
4
+ This module provides the concrete implementation of the multi-agent memory manager,
5
+ refactored to conform to the new AgentMemoryManagerBase interface.
6
+ """
7
+
8
+ import logging
9
+ from datetime import datetime
10
+ from typing import Any, Dict, List, Optional, Union
11
+ from powermem.agent.types import (
12
+ AccessPermission,
13
+ MemoryScope,
14
+ MemoryType,
15
+ PrivacyLevel,
16
+ CollaborationLevel,
17
+ )
18
+ from powermem.intelligence.intelligent_memory_manager import IntelligentMemoryManager
19
+ from powermem.agent.abstract.manager import AgentMemoryManagerBase
20
+ from powermem.agent.components.scope_controller import ScopeController
21
+ from powermem.agent.components.permission_controller import PermissionController
22
+ from powermem.agent.components.collaboration_coordinator import CollaborationCoordinator
23
+ from powermem.agent.components.privacy_protector import PrivacyProtector
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ class MultiAgentMemoryManager(AgentMemoryManagerBase):
29
+ """
30
+ Multi-agent memory manager implementation.
31
+
32
+ Coordinates intelligent memory management with multi-agent capabilities,
33
+ handles scope-based memory storage, permissions, and collaboration.
34
+ """
35
+
36
+ def __init__(self, config: Dict[str, Any]):
37
+ """
38
+ Initialize the multi-agent memory manager.
39
+
40
+ Args:
41
+ config: Memory configuration object
42
+ """
43
+ super().__init__(config)
44
+ self.multi_agent_config = config.agent_memory.multi_agent_config
45
+
46
+ # Initialize components
47
+ self.intelligent_manager = None
48
+ self.scope_controller = None
49
+ self.permission_controller = None
50
+ self.collaboration_coordinator = None
51
+ self.privacy_protector = None
52
+
53
+ # Scope-based memory storage
54
+ self.scope_memories = {
55
+ MemoryScope.PRIVATE: {
56
+ MemoryType.WORKING: {},
57
+ MemoryType.SHORT_TERM: {},
58
+ MemoryType.LONG_TERM: {},
59
+ MemoryType.SEMANTIC: {},
60
+ MemoryType.EPISODIC: {},
61
+ MemoryType.PROCEDURAL: {},
62
+ MemoryType.PUBLIC_SHARED: {},
63
+ MemoryType.PRIVATE_AGENT: {},
64
+ MemoryType.COLLABORATIVE: {},
65
+ MemoryType.GROUP_CONSENSUS: {},
66
+ },
67
+ MemoryScope.AGENT_GROUP: {
68
+ MemoryType.WORKING: {},
69
+ MemoryType.SHORT_TERM: {},
70
+ MemoryType.LONG_TERM: {},
71
+ MemoryType.SEMANTIC: {},
72
+ MemoryType.EPISODIC: {},
73
+ MemoryType.PROCEDURAL: {},
74
+ MemoryType.PUBLIC_SHARED: {},
75
+ MemoryType.PRIVATE_AGENT: {},
76
+ MemoryType.COLLABORATIVE: {},
77
+ MemoryType.GROUP_CONSENSUS: {},
78
+ },
79
+ MemoryScope.USER_GROUP: {
80
+ MemoryType.WORKING: {},
81
+ MemoryType.SHORT_TERM: {},
82
+ MemoryType.LONG_TERM: {},
83
+ MemoryType.SEMANTIC: {},
84
+ MemoryType.EPISODIC: {},
85
+ MemoryType.PROCEDURAL: {},
86
+ MemoryType.PUBLIC_SHARED: {},
87
+ MemoryType.PRIVATE_AGENT: {},
88
+ MemoryType.COLLABORATIVE: {},
89
+ MemoryType.GROUP_CONSENSUS: {},
90
+ },
91
+ MemoryScope.PUBLIC: {
92
+ MemoryType.WORKING: {},
93
+ MemoryType.SHORT_TERM: {},
94
+ MemoryType.LONG_TERM: {},
95
+ MemoryType.SEMANTIC: {},
96
+ MemoryType.EPISODIC: {},
97
+ MemoryType.PROCEDURAL: {},
98
+ MemoryType.PUBLIC_SHARED: {},
99
+ MemoryType.PRIVATE_AGENT: {},
100
+ MemoryType.COLLABORATIVE: {},
101
+ MemoryType.GROUP_CONSENSUS: {},
102
+ },
103
+ MemoryScope.RESTRICTED: {
104
+ MemoryType.WORKING: {},
105
+ MemoryType.SHORT_TERM: {},
106
+ MemoryType.LONG_TERM: {},
107
+ MemoryType.SEMANTIC: {},
108
+ MemoryType.EPISODIC: {},
109
+ MemoryType.PROCEDURAL: {},
110
+ MemoryType.PUBLIC_SHARED: {},
111
+ MemoryType.PRIVATE_AGENT: {},
112
+ MemoryType.COLLABORATIVE: {},
113
+ MemoryType.GROUP_CONSENSUS: {},
114
+ },
115
+ }
116
+
117
+ # Agent group management
118
+ self.agent_groups = {}
119
+ self.agent_memberships = {}
120
+
121
+ # Collaboration tracking
122
+ self.active_collaborations = {}
123
+ self.collaboration_memories = {}
124
+
125
+ def initialize(self) -> None:
126
+ """
127
+ Initialize the memory manager and all components.
128
+ """
129
+ try:
130
+ # Initialize intelligent memory manager
131
+ self.intelligent_manager = IntelligentMemoryManager(self.config)
132
+
133
+ # Initialize components
134
+ self.scope_controller = ScopeController(self.config)
135
+ self.permission_controller = PermissionController(self.config)
136
+ self.collaboration_coordinator = CollaborationCoordinator(self.config)
137
+ self.privacy_protector = PrivacyProtector(self.config)
138
+
139
+ # Initialize agent groups from config
140
+ self._initialize_agent_groups()
141
+
142
+ self.initialized = True
143
+ logger.info("Multi-agent memory manager initialized successfully")
144
+
145
+ except Exception as e:
146
+ logger.error(f"Failed to initialize multi-agent memory manager: {e}")
147
+ raise
148
+
149
+ def _initialize_agent_groups(self) -> None:
150
+ """Initialize agent groups from configuration."""
151
+ if hasattr(self.multi_agent_config, 'agent_groups'):
152
+ for group_name, group_config in self.multi_agent_config.agent_groups.items():
153
+ self.agent_groups[group_name] = {
154
+ 'members': group_config.get('members', []),
155
+ 'permissions': group_config.get('permissions', {}),
156
+ 'created_at': datetime.now().isoformat(),
157
+ }
158
+
159
+ # Update agent memberships
160
+ for agent_id in group_config.get('members', []):
161
+ if agent_id not in self.agent_memberships:
162
+ self.agent_memberships[agent_id] = []
163
+ self.agent_memberships[agent_id].append(group_name)
164
+
165
+ def process_memory(
166
+ self,
167
+ content: str,
168
+ agent_id: str,
169
+ context: Optional[Dict[str, Any]] = None,
170
+ metadata: Optional[Dict[str, Any]] = None
171
+ ) -> Dict[str, Any]:
172
+ """
173
+ Process and store a new memory.
174
+
175
+ Args:
176
+ content: The memory content to store
177
+ agent_id: ID of the agent creating the memory
178
+ context: Additional context information
179
+ metadata: Additional metadata for the memory
180
+
181
+ Returns:
182
+ Dictionary containing the processed memory information
183
+ """
184
+ try:
185
+ # Determine memory scope
186
+ scope = self.scope_controller.determine_scope(agent_id, context, metadata)
187
+
188
+ # Add agent collaboration context information to metadata
189
+ agent_context_metadata = metadata or {}
190
+
191
+ # Get collaboration information
192
+ collaboration_info = self._get_collaboration_context(agent_id, context)
193
+
194
+ # Get permission information
195
+ permission_info = self._get_permission_context(agent_id, scope)
196
+
197
+ # Get sharing information
198
+ sharing_info = self._get_sharing_context(agent_id, context)
199
+
200
+ # Organize all agent-related information under 'agent' key
201
+ agent_info = {
202
+ 'agent_id': agent_id,
203
+ 'mode': 'multi_agent',
204
+ 'scope': scope.value if hasattr(scope, 'value') else str(scope),
205
+ 'collaboration': collaboration_info,
206
+ 'permissions': permission_info,
207
+ 'sharing': sharing_info,
208
+ }
209
+
210
+ agent_context_metadata['agent'] = agent_info
211
+
212
+ # Process with intelligent memory manager
213
+ enhanced_metadata = self.intelligent_manager.process_metadata(
214
+ content=content,
215
+ metadata=agent_context_metadata,
216
+ context=context or {}
217
+ )
218
+
219
+ # Determine memory type from enhanced metadata
220
+ memory_type = self._determine_memory_type_from_metadata(enhanced_metadata)
221
+
222
+ # Persist to database first to get Snowflake ID
223
+ # Use temporary memory data for database insertion
224
+ temp_memory_data = {
225
+ 'content': content,
226
+ 'agent_id': agent_id,
227
+ 'scope': scope,
228
+ 'memory_type': memory_type,
229
+ 'metadata': enhanced_metadata,
230
+ }
231
+
232
+ # Get Snowflake ID from database
233
+ memory_id = self._persist_memory_to_storage(temp_memory_data)
234
+ if not memory_id:
235
+ raise ValueError("Failed to get memory ID from database")
236
+
237
+ # Create complete memory data with Snowflake ID from database
238
+ memory_data = {
239
+ 'id': memory_id,
240
+ 'content': content, # Keep original content unchanged
241
+ 'agent_id': agent_id,
242
+ 'scope': scope,
243
+ 'memory_type': memory_type,
244
+ 'metadata': enhanced_metadata, # Use enhanced metadata
245
+ 'context': context or {},
246
+ 'created_at': datetime.now().isoformat(),
247
+ 'updated_at': datetime.now().isoformat(),
248
+ 'access_count': 0,
249
+ 'last_accessed': None,
250
+ 'retention_score': enhanced_metadata.get('intelligence', {}).get('current_retention', 1.0),
251
+ 'importance_level': enhanced_metadata.get('intelligence', {}).get('importance_score'),
252
+ }
253
+
254
+ # Store in appropriate scope and type
255
+ self.scope_memories[scope][memory_type][memory_id] = memory_data
256
+
257
+ # Also store in scope controller's storage for access control
258
+ if self.scope_controller:
259
+ self.scope_controller.scope_storage[scope][memory_type][memory_id] = memory_data
260
+
261
+ # Set up permissions - grant owner permissions to the memory creator
262
+ owner_permissions = self.multi_agent_config.default_permissions.get("owner", [])
263
+ # Convert string permissions to AccessPermission enum
264
+ owner_permissions_enum = []
265
+ for perm in owner_permissions:
266
+ try:
267
+ # Handle both string and AccessPermission enum inputs
268
+ if isinstance(perm, AccessPermission):
269
+ owner_permissions_enum.append(perm)
270
+ else:
271
+ # Convert to AccessPermission enum (case insensitive)
272
+ owner_permissions_enum.append(AccessPermission(perm.lower()))
273
+ except ValueError:
274
+ logger.warning(f"Invalid permission: {perm}")
275
+
276
+ # Grant owner permissions to the memory creator
277
+ for permission in owner_permissions_enum:
278
+ self.permission_controller.grant_permission(
279
+ memory_id=memory_id,
280
+ agent_id=agent_id,
281
+ permission=permission,
282
+ granted_by=agent_id
283
+ )
284
+
285
+ # Handle collaboration if applicable
286
+ if context and context.get('collaboration_level') == 'high':
287
+ self._handle_collaborative_memory(memory_id, agent_id, context)
288
+
289
+ # Apply privacy protection
290
+ self.privacy_protector.set_privacy_level(
291
+ memory_id=memory_id,
292
+ privacy_level=PrivacyLevel.STANDARD,
293
+ set_by=agent_id
294
+ )
295
+
296
+ logger.info(f"Processed memory {memory_id} for agent {agent_id} with scope {scope}")
297
+
298
+ return {
299
+ 'id': memory_id,
300
+ 'memory': content,
301
+ 'scope': scope.value,
302
+ 'memory_type': memory_type.value,
303
+ 'agent_id': agent_id,
304
+ 'created_at': memory_data['created_at'],
305
+ 'metadata': memory_data['metadata'],
306
+ }
307
+
308
+ except Exception as e:
309
+ logger.error(f"Failed to process memory for agent {agent_id}: {e}")
310
+ raise
311
+
312
+ def _persist_memory_to_storage(self, memory_data: Dict[str, Any]) -> int:
313
+ """
314
+ Persist memory data to database and vector store.
315
+
316
+ Args:
317
+ memory_data: Memory data dictionary (minimal data for database insertion)
318
+
319
+ Returns:
320
+ Snowflake ID (int) from database
321
+ """
322
+ try:
323
+ # Use existing Memory infrastructure
324
+ if not hasattr(self, '_memory_instance'):
325
+ from powermem.core.memory import Memory
326
+ # Convert ConfigObject back to dict for Memory class
327
+ if hasattr(self.config, '_data'):
328
+ config_dict = self.config._data
329
+ elif hasattr(self.config, 'to_dict'):
330
+ config_dict = self.config.to_dict()
331
+ else:
332
+ config_dict = self.config
333
+
334
+ self._memory_instance = Memory(config_dict)
335
+
336
+ # Use the existing Memory.add() method
337
+ # Get the Snowflake ID returned from database to ensure consistency
338
+ add_result = self._memory_instance.add(
339
+ messages=memory_data['content'],
340
+ user_id=memory_data.get('user_id'),
341
+ agent_id=memory_data.get('agent_id'),
342
+ metadata={
343
+ 'scope': memory_data.get('scope').value if memory_data.get('scope') else None,
344
+ 'memory_type': memory_data.get('memory_type').value if memory_data.get('memory_type') else None,
345
+ 'retention_score': memory_data.get('retention_score'),
346
+ 'importance_level': memory_data.get('importance_level'),
347
+ **memory_data.get('metadata', {})
348
+ }
349
+ )
350
+
351
+ # Get the Snowflake ID from database
352
+ if add_result and 'results' in add_result and len(add_result['results']) > 0:
353
+ db_memory_id = add_result['results'][0].get('id')
354
+ if db_memory_id:
355
+ logger.info(f"Persisted memory {db_memory_id} to storage")
356
+ return db_memory_id
357
+ else:
358
+ raise ValueError("Failed to get memory ID from database")
359
+ else:
360
+ raise ValueError("Failed to persist memory to database")
361
+
362
+ except Exception as e:
363
+ logger.error(f"Failed to persist memory to storage: {e}")
364
+ # Re-raise exception to allow caller to handle it
365
+ raise
366
+
367
+ def _get_collaboration_context(self, agent_id: str, context: Optional[Dict[str, Any]]) -> Dict[str, Any]:
368
+ """Get collaboration context information."""
369
+ collaboration_info = {
370
+ 'is_collaborating': False,
371
+ 'collaboration_type': None,
372
+ 'collaboration_status': None,
373
+ 'participants': [],
374
+ 'collaboration_level': context.get('collaboration_level', 'low') if context else 'low'
375
+ }
376
+
377
+ # Check if agent is in active collaboration
378
+ for collaboration_id, collaboration_data in self.active_collaborations.items():
379
+ if agent_id in collaboration_data.get('participants', []):
380
+ collaboration_info.update({
381
+ 'is_collaborating': True,
382
+ 'collaboration_type': collaboration_data.get('type', 'asynchronous'),
383
+ 'collaboration_status': collaboration_data.get('status', 'active'),
384
+ 'participants': collaboration_data.get('participants', []),
385
+ })
386
+ break
387
+
388
+ return collaboration_info
389
+
390
+ def _get_permission_context(self, agent_id: str, scope: MemoryScope) -> Dict[str, Any]:
391
+ """Get permission context information."""
392
+ return {
393
+ 'scope_permissions': {
394
+ 'read': True, # Agent can always read their own memories
395
+ 'write': True, # Agent can always write to their scope
396
+ 'delete': True, # Agent can delete their own memories
397
+ 'admin': scope in [MemoryScope.PUBLIC, MemoryScope.AGENT_GROUP] # Admin for public/group scopes
398
+ },
399
+ 'scope_type': scope.value if hasattr(scope, 'value') else str(scope),
400
+ 'access_level': 'owner' if scope == MemoryScope.PRIVATE else 'member'
401
+ }
402
+
403
+ def _get_sharing_context(self, agent_id: str, context: Optional[Dict[str, Any]]) -> Dict[str, Any]:
404
+ """Get sharing context information."""
405
+ sharing_info = {
406
+ 'is_shared': False,
407
+ 'shared_with': [],
408
+ 'sharing_level': 'private',
409
+ 'can_share': True
410
+ }
411
+
412
+ # Check if memory is being shared
413
+ if context and context.get('share_with'):
414
+ sharing_info.update({
415
+ 'is_shared': True,
416
+ 'shared_with': context.get('share_with', []),
417
+ 'sharing_level': 'collaborative'
418
+ })
419
+
420
+ # Check if agent is in any groups
421
+ agent_groups = [group for group, members in self.agent_groups.items() if agent_id in members]
422
+ if agent_groups:
423
+ sharing_info.update({
424
+ 'is_shared': True,
425
+ 'shared_with': agent_groups,
426
+ 'sharing_level': 'group'
427
+ })
428
+
429
+ return sharing_info
430
+
431
+ def _determine_memory_type_from_metadata(self, enhanced_metadata: Dict[str, Any]) -> MemoryType:
432
+ """Determine the memory type based on enhanced metadata."""
433
+ intelligence = enhanced_metadata.get('intelligence', {})
434
+ memory_type_str = intelligence.get('memory_type', 'working')
435
+
436
+ # Map string to enum
437
+ type_mapping = {
438
+ 'working': MemoryType.WORKING,
439
+ 'short_term': MemoryType.SHORT_TERM,
440
+ 'long_term': MemoryType.LONG_TERM,
441
+ 'semantic_memory': MemoryType.SEMANTIC,
442
+ 'episodic_memory': MemoryType.EPISODIC,
443
+ 'procedural_memory': MemoryType.PROCEDURAL,
444
+ }
445
+
446
+ return type_mapping.get(memory_type_str, MemoryType.WORKING)
447
+
448
+ def _handle_collaborative_memory(
449
+ self,
450
+ memory_id: str,
451
+ agent_id: str,
452
+ context: Dict[str, Any]
453
+ ) -> None:
454
+ """Handle collaborative memory creation."""
455
+ participants = context.get('participants', [])
456
+ if participants:
457
+ collaboration_id = self.collaboration_coordinator.initiate_collaboration(
458
+ initiator_id=agent_id,
459
+ participant_ids=participants,
460
+ collaboration_type=CollaborationLevel.COLLABORATIVE,
461
+ context=context
462
+ )
463
+
464
+ if collaboration_id:
465
+ self.collaboration_memories[memory_id] = collaboration_id
466
+
467
+ def get_memories(
468
+ self,
469
+ agent_id: str,
470
+ query: Optional[str] = None,
471
+ filters: Optional[Dict[str, Any]] = None
472
+ ) -> List[Dict[str, Any]]:
473
+ """
474
+ Retrieve memories for a specific agent.
475
+
476
+ Args:
477
+ agent_id: ID of the agent
478
+ query: Optional query string for filtering
479
+ filters: Optional additional filters
480
+
481
+ Returns:
482
+ List of memory dictionaries
483
+ """
484
+ try:
485
+ accessible_memories = []
486
+ total_memories = 0
487
+ scope_access_passed = 0
488
+ permission_passed = 0
489
+
490
+ # Get accessible memory IDs based on scope and permissions
491
+ for scope in MemoryScope:
492
+ for memory_type in MemoryType:
493
+ for memory_id, memory_data in self.scope_memories[scope][memory_type].items():
494
+ total_memories += 1
495
+
496
+ # Check if agent has access to this memory
497
+ scope_access = self.scope_controller.check_scope_access(agent_id, memory_id)
498
+ if scope_access:
499
+ scope_access_passed += 1
500
+
501
+ permission_check = self.permission_controller.check_permission(
502
+ agent_id, memory_id, AccessPermission.READ
503
+ )
504
+ if permission_check:
505
+ permission_passed += 1
506
+ accessible_memories.append(memory_data)
507
+ else:
508
+ logger.debug(f"Permission denied for agent {agent_id} on memory {memory_id}")
509
+ else:
510
+ logger.debug(f"Scope access denied for agent {agent_id} on memory {memory_id}")
511
+
512
+ # Apply query filtering if provided
513
+ if query:
514
+ accessible_memories = [
515
+ memory for memory in accessible_memories
516
+ if query.lower() in memory['content'].lower()
517
+ ]
518
+
519
+ # Apply additional filters if provided
520
+ if filters:
521
+ for key, value in filters.items():
522
+ accessible_memories = [
523
+ memory for memory in accessible_memories
524
+ if memory.get(key) == value
525
+ ]
526
+
527
+ # Update access statistics
528
+ for memory in accessible_memories:
529
+ memory['access_count'] += 1
530
+ memory['last_accessed'] = datetime.now().isoformat()
531
+
532
+ logger.info(f"Retrieved {len(accessible_memories)} memories for agent {agent_id} "
533
+ f"(total: {total_memories}, scope_passed: {scope_access_passed}, permission_passed: {permission_passed})")
534
+ return accessible_memories
535
+
536
+ except Exception as e:
537
+ logger.error(f"Failed to get memories for agent {agent_id}: {e}")
538
+ raise
539
+
540
+ def update_memory(
541
+ self,
542
+ memory_id: str,
543
+ agent_id: str,
544
+ updates: Dict[str, Any]
545
+ ) -> Dict[str, Any]:
546
+ """
547
+ Update an existing memory.
548
+
549
+ Args:
550
+ memory_id: ID of the memory to update
551
+ agent_id: ID of the agent making the update
552
+ updates: Dictionary of updates to apply
553
+
554
+ Returns:
555
+ Dictionary containing the updated memory information
556
+ """
557
+ try:
558
+ # Find the memory
559
+ memory_data = self._find_memory(memory_id)
560
+ if not memory_data:
561
+ raise ValueError(f"Memory {memory_id} not found")
562
+
563
+ # Check permissions
564
+ if not self.permission_controller.check_permission(
565
+ agent_id, memory_id, AccessPermission.WRITE
566
+ ):
567
+ raise PermissionError(f"Agent {agent_id} does not have write permission for memory {memory_id}")
568
+
569
+ # Apply updates
570
+ for key, value in updates.items():
571
+ if key in memory_data:
572
+ memory_data[key] = value
573
+
574
+ memory_data['updated_at'] = datetime.now().isoformat()
575
+
576
+ # Update intelligent memory manager if content changed
577
+ if 'content' in updates:
578
+ self.intelligent_manager.update_memory(
579
+ memory_id=memory_id,
580
+ content=updates['content'],
581
+ metadata=memory_data.get('metadata', {})
582
+ )
583
+
584
+ logger.info(f"Updated memory {memory_id} by agent {agent_id}")
585
+
586
+ return {
587
+ 'id': memory_id,
588
+ 'memory': memory_data['content'],
589
+ 'updated_at': memory_data['updated_at'],
590
+ 'agent_id': agent_id,
591
+ }
592
+
593
+ except Exception as e:
594
+ logger.error(f"Failed to update memory {memory_id}: {e}")
595
+ raise
596
+
597
+ def delete_memory(
598
+ self,
599
+ memory_id: str,
600
+ agent_id: str
601
+ ) -> Dict[str, Any]:
602
+ """
603
+ Delete a memory.
604
+
605
+ Args:
606
+ memory_id: ID of the memory to delete
607
+ agent_id: ID of the agent making the deletion
608
+
609
+ Returns:
610
+ Dictionary containing the deletion result
611
+ """
612
+ try:
613
+ # Find the memory
614
+ memory_data = self._find_memory(memory_id)
615
+ if not memory_data:
616
+ raise ValueError(f"Memory {memory_id} not found")
617
+
618
+ # Check permissions
619
+ if not self.permission_controller.check_permission(
620
+ agent_id, memory_id, AccessPermission.DELETE
621
+ ):
622
+ raise PermissionError(f"Agent {agent_id} does not have delete permission for memory {memory_id}")
623
+
624
+ # Remove from scope-based storage
625
+ scope = memory_data['scope']
626
+ memory_type = memory_data['memory_type']
627
+ del self.scope_memories[scope][memory_type][memory_id]
628
+
629
+ # Clean up permissions
630
+ self.permission_controller.revoke_permission(
631
+ memory_id=memory_id,
632
+ agent_id=agent_id,
633
+ permission=AccessPermission.DELETE,
634
+ revoked_by=agent_id
635
+ )
636
+
637
+ # Clean up collaboration if applicable
638
+ if memory_id in self.collaboration_memories:
639
+ del self.collaboration_memories[memory_id]
640
+
641
+ logger.info(f"Deleted memory {memory_id} by agent {agent_id}")
642
+
643
+ return {
644
+ 'success': True,
645
+ 'deleted_id': memory_id,
646
+ 'deleted_by': agent_id,
647
+ }
648
+
649
+ except Exception as e:
650
+ logger.error(f"Failed to delete memory {memory_id}: {e}")
651
+ raise
652
+
653
+ def share_memory(
654
+ self,
655
+ memory_id: str,
656
+ from_agent: str,
657
+ to_agents: List[str],
658
+ permissions: Optional[List[str]] = None
659
+ ) -> Dict[str, Any]:
660
+ """
661
+ Share a memory with other agents.
662
+
663
+ Args:
664
+ memory_id: ID of the memory to share
665
+ from_agent: ID of the agent sharing the memory
666
+ to_agents: List of agent IDs to share with
667
+ permissions: Optional list of permissions to grant
668
+
669
+ Returns:
670
+ Dictionary containing the sharing result
671
+ """
672
+ try:
673
+ # Find the memory
674
+ memory_data = self._find_memory(memory_id)
675
+ if not memory_data:
676
+ raise ValueError(f"Memory {memory_id} not found")
677
+
678
+ # Check if sharing agent has share permission
679
+ if not self.permission_controller.check_permission(
680
+ from_agent, memory_id, AccessPermission.SHARE
681
+ ):
682
+ raise PermissionError(f"Agent {from_agent} does not have share permission for memory {memory_id}")
683
+
684
+ # Grant permissions to target agents
685
+ shared_with = []
686
+ default_permissions = permissions or ['read']
687
+
688
+ for agent_id in to_agents:
689
+ for perm in default_permissions:
690
+ try:
691
+ # Handle both string and AccessPermission enum inputs
692
+ if isinstance(perm, AccessPermission):
693
+ permission = perm
694
+ else:
695
+ # Convert to AccessPermission enum (case insensitive)
696
+ permission = AccessPermission(perm.lower())
697
+ self.permission_controller.grant_permission(
698
+ memory_id=memory_id,
699
+ agent_id=agent_id,
700
+ permission=permission,
701
+ granted_by=from_agent
702
+ )
703
+ shared_with.append(agent_id)
704
+ except ValueError:
705
+ logger.warning(f"Invalid permission: {perm}")
706
+
707
+ # Update memory data to include shared_with list for scope access
708
+ memory_data = self._find_memory(memory_id)
709
+ if memory_data:
710
+ if 'shared_with' not in memory_data:
711
+ memory_data['shared_with'] = []
712
+ memory_data['shared_with'].extend(shared_with)
713
+
714
+ # Also update in scope controller's storage
715
+ if self.scope_controller:
716
+ for scope in MemoryScope:
717
+ for memory_type in MemoryType:
718
+ if memory_id in self.scope_controller.scope_storage[scope][memory_type]:
719
+ self.scope_controller.scope_storage[scope][memory_type][memory_id] = memory_data
720
+ break
721
+
722
+ logger.info(f"Shared memory {memory_id} from {from_agent} to {len(shared_with)} agents")
723
+
724
+ return {
725
+ 'success': True,
726
+ 'memory_id': memory_id,
727
+ 'shared_from': from_agent,
728
+ 'shared_with': shared_with,
729
+ 'permissions': default_permissions,
730
+ }
731
+
732
+ except Exception as e:
733
+ logger.error(f"Failed to share memory {memory_id}: {e}")
734
+ raise
735
+
736
+ def get_context_info(self, agent_id: str) -> Dict[str, Any]:
737
+ """
738
+ Get context information for an agent.
739
+
740
+ Args:
741
+ agent_id: ID of the agent
742
+
743
+ Returns:
744
+ Dictionary containing context information
745
+ """
746
+ try:
747
+ context_info = {
748
+ 'agent_id': agent_id,
749
+ 'groups': self.agent_memberships.get(agent_id, []),
750
+ 'active_collaborations': [],
751
+ 'memory_count': 0,
752
+ 'scope_breakdown': {},
753
+ }
754
+
755
+ # Count memories by scope
756
+ for scope in MemoryScope:
757
+ count = 0
758
+ for memory_type in MemoryType:
759
+ for memory_data in self.scope_memories[scope][memory_type].values():
760
+ if memory_data['agent_id'] == agent_id:
761
+ count += 1
762
+ context_info['scope_breakdown'][scope.value] = count
763
+ context_info['memory_count'] += count
764
+
765
+ # Get active collaborations
766
+ for collaboration_id, collaboration_data in self.active_collaborations.items():
767
+ if agent_id in collaboration_data.get('participants', []):
768
+ context_info['active_collaborations'].append(collaboration_id)
769
+
770
+ return context_info
771
+
772
+ except Exception as e:
773
+ logger.error(f"Failed to get context info for agent {agent_id}: {e}")
774
+ raise
775
+
776
+ def update_memory_decay(self) -> Dict[str, Any]:
777
+ """
778
+ Update memory decay based on Ebbinghaus forgetting curve.
779
+
780
+ Returns:
781
+ Dictionary containing the decay update results
782
+ """
783
+ try:
784
+ decay_results = {
785
+ 'updated_memories': 0,
786
+ 'forgotten_memories': 0,
787
+ 'reinforced_memories': 0,
788
+ }
789
+
790
+ # Update decay for all memories
791
+ for scope in MemoryScope:
792
+ for memory_type in MemoryType:
793
+ for memory_id, memory_data in self.scope_memories[scope][memory_type].items():
794
+ # Update decay using intelligent memory manager
795
+ # Note: IntelligentMemoryManager.update_memory_decay() updates all memories
796
+ # We'll call it once and then process individual results
797
+ if not hasattr(self, '_decay_updated'):
798
+ self.intelligent_manager.update_memory_decay()
799
+ self._decay_updated = True
800
+
801
+ # For individual memory processing, we'll use a simplified approach
802
+ current_score = memory_data.get('retention_score', 1.0)
803
+ access_count = memory_data.get('access_count', 0)
804
+ last_accessed = memory_data.get('last_accessed')
805
+
806
+ # Simple decay calculation (this should be replaced with proper Ebbinghaus algorithm)
807
+ decay_rate = 0.1
808
+ if last_accessed:
809
+ # Parse ISO format string to datetime
810
+ if isinstance(last_accessed, str):
811
+ last_accessed_dt = datetime.fromisoformat(last_accessed.replace('Z', '+00:00'))
812
+ else:
813
+ last_accessed_dt = last_accessed
814
+ time_since_access = (datetime.now() - last_accessed_dt).total_seconds() / 3600
815
+ else:
816
+ time_since_access = 24
817
+ new_score = current_score * (1 - decay_rate * time_since_access / 24)
818
+ new_score = max(0.0, min(1.0, new_score))
819
+
820
+ decay_result = {
821
+ 'new_score': new_score,
822
+ 'decay_rate': decay_rate,
823
+ 'forgotten': new_score < 0.1
824
+ }
825
+
826
+ # Update memory data
827
+ memory_data['retention_score'] = decay_result.get('new_score', memory_data.get('retention_score', 1.0))
828
+ memory_data['decay_rate'] = decay_result.get('decay_rate', 0.1)
829
+
830
+ decay_results['updated_memories'] += 1
831
+
832
+ # Check if memory should be forgotten
833
+ if decay_result.get('forgotten', False):
834
+ decay_results['forgotten_memories'] += 1
835
+
836
+ # Check if memory should be reinforced
837
+ if decay_result.get('reinforced', False):
838
+ decay_results['reinforced_memories'] += 1
839
+
840
+ logger.info(f"Updated memory decay: {decay_results}")
841
+ return decay_results
842
+
843
+ except Exception as e:
844
+ logger.error(f"Failed to update memory decay: {e}")
845
+ raise
846
+
847
+ def cleanup_forgotten_memories(self) -> Dict[str, Any]:
848
+ """
849
+ Clean up memories that have been forgotten.
850
+
851
+ Returns:
852
+ Dictionary containing the cleanup results
853
+ """
854
+ try:
855
+ cleanup_results = {
856
+ 'cleaned_memories': 0,
857
+ 'archived_memories': 0,
858
+ 'deleted_memories': 0,
859
+ }
860
+
861
+ # Clean up forgotten memories
862
+ for scope in MemoryScope:
863
+ for memory_type in MemoryType:
864
+ memories_to_remove = []
865
+
866
+ for memory_id, memory_data in self.scope_memories[scope][memory_type].items():
867
+ retention_score = memory_data.get('retention_score', 1.0)
868
+
869
+ # Check if memory should be cleaned up
870
+ if retention_score < 0.1: # Forgotten threshold
871
+ memories_to_remove.append(memory_id)
872
+ cleanup_results['deleted_memories'] += 1
873
+ elif retention_score < 0.3: # Archive threshold
874
+ # Archive memory instead of deleting
875
+ memory_data['archived'] = True
876
+ cleanup_results['archived_memories'] += 1
877
+
878
+ # Remove forgotten memories
879
+ for memory_id in memories_to_remove:
880
+ del self.scope_memories[scope][memory_type][memory_id]
881
+ cleanup_results['cleaned_memories'] += 1
882
+
883
+ logger.info(f"Cleaned up forgotten memories: {cleanup_results}")
884
+ return cleanup_results
885
+
886
+ except Exception as e:
887
+ logger.error(f"Failed to cleanup forgotten memories: {e}")
888
+ raise
889
+
890
+ def get_memory_statistics(self) -> Dict[str, Any]:
891
+ """
892
+ Get statistics about the memory system.
893
+
894
+ Returns:
895
+ Dictionary containing memory statistics
896
+ """
897
+ try:
898
+ stats = {
899
+ 'total_memories': 0,
900
+ 'scope_breakdown': {},
901
+ 'type_breakdown': {},
902
+ 'agent_breakdown': {},
903
+ 'collaboration_stats': {
904
+ 'active_collaborations': len(self.active_collaborations),
905
+ 'collaborative_memories': len(self.collaboration_memories),
906
+ },
907
+ 'group_stats': {
908
+ 'total_groups': len(self.agent_groups),
909
+ 'total_memberships': len(self.agent_memberships),
910
+ },
911
+ }
912
+
913
+ # Count memories by scope and type
914
+ for scope in MemoryScope:
915
+ scope_count = 0
916
+ for memory_type in MemoryType:
917
+ type_count = len(self.scope_memories[scope][memory_type])
918
+ scope_count += type_count
919
+ stats['total_memories'] += type_count
920
+
921
+ # Type breakdown
922
+ type_key = memory_type.value
923
+ if type_key not in stats['type_breakdown']:
924
+ stats['type_breakdown'][type_key] = 0
925
+ stats['type_breakdown'][type_key] += type_count
926
+
927
+ stats['scope_breakdown'][scope.value] = scope_count
928
+
929
+ # Count memories by agent
930
+ for scope in MemoryScope:
931
+ for memory_type in MemoryType:
932
+ for memory_data in self.scope_memories[scope][memory_type].values():
933
+ agent_id = memory_data['agent_id']
934
+ if agent_id not in stats['agent_breakdown']:
935
+ stats['agent_breakdown'][agent_id] = 0
936
+ stats['agent_breakdown'][agent_id] += 1
937
+
938
+ return stats
939
+
940
+ except Exception as e:
941
+ logger.error(f"Failed to get memory statistics: {e}")
942
+ raise
943
+
944
+ def check_permission(
945
+ self,
946
+ agent_id: str,
947
+ memory_id: str,
948
+ permission: str
949
+ ) -> bool:
950
+ """
951
+ Check if an agent has a specific permission for a memory.
952
+
953
+ Args:
954
+ agent_id: ID of the agent
955
+ memory_id: ID of the memory
956
+ permission: Permission to check
957
+
958
+ Returns:
959
+ True if the agent has the permission, False otherwise
960
+ """
961
+ try:
962
+ # Handle both string and AccessPermission enum inputs
963
+ if isinstance(permission, AccessPermission):
964
+ permission_enum = permission
965
+ else:
966
+ permission_enum = AccessPermission(permission.upper())
967
+ return self.permission_controller.check_permission(agent_id, memory_id, permission_enum)
968
+ except (ValueError, Exception):
969
+ return False
970
+
971
+ def _find_memory(self, memory_id: str) -> Optional[Dict[str, Any]]:
972
+ """Find a memory by ID across all scopes and types."""
973
+ for scope in MemoryScope:
974
+ for memory_type in MemoryType:
975
+ if memory_id in self.scope_memories[scope][memory_type]:
976
+ return self.scope_memories[scope][memory_type][memory_id]
977
+ return None
978
+
979
+ def create_group(self, group_name: str, agent_ids: List[str], permissions: Optional[Dict[str, List[str]]] = None) -> Dict[str, Any]:
980
+ """
981
+ Create an agent group.
982
+
983
+ Args:
984
+ group_name: Name of the group
985
+ agent_ids: List of agent IDs to include in the group
986
+ permissions: Optional permissions configuration
987
+
988
+ Returns:
989
+ Dictionary containing the group creation result
990
+ """
991
+ try:
992
+ # Check if group already exists
993
+ if group_name in self.agent_groups:
994
+ raise ValueError(f"Group '{group_name}' already exists")
995
+
996
+ # Validate agent IDs
997
+ for agent_id in agent_ids:
998
+ if not agent_id or not isinstance(agent_id, str):
999
+ raise ValueError(f"Invalid agent ID: {agent_id}")
1000
+
1001
+ # Set default permissions if not provided
1002
+ if permissions is None:
1003
+ permissions = {
1004
+ 'owner': ['read', 'write', 'delete', 'admin'],
1005
+ 'collaborator': ['read', 'write'],
1006
+ 'viewer': ['read']
1007
+ }
1008
+
1009
+ # Create the group
1010
+ self.agent_groups[group_name] = {
1011
+ 'members': agent_ids.copy(),
1012
+ 'permissions': permissions.copy(),
1013
+ 'created_at': datetime.now().isoformat(),
1014
+ 'created_by': 'system' # Could be passed as parameter
1015
+ }
1016
+
1017
+ # Update agent memberships
1018
+ for agent_id in agent_ids:
1019
+ if agent_id not in self.agent_memberships:
1020
+ self.agent_memberships[agent_id] = []
1021
+ if group_name not in self.agent_memberships[agent_id]:
1022
+ self.agent_memberships[agent_id].append(group_name)
1023
+
1024
+ logger.info(f"Created group '{group_name}' with {len(agent_ids)} members")
1025
+
1026
+ return {
1027
+ 'success': True,
1028
+ 'group_name': group_name,
1029
+ 'members': agent_ids,
1030
+ 'permissions': permissions,
1031
+ 'created_at': self.agent_groups[group_name]['created_at']
1032
+ }
1033
+
1034
+ except Exception as e:
1035
+ logger.error(f"Failed to create group '{group_name}': {e}")
1036
+ return {
1037
+ 'success': False,
1038
+ 'error': str(e),
1039
+ 'group_name': group_name
1040
+ }