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,767 @@
1
+ """
2
+ Multi-Agent Privacy Protector
3
+
4
+ Manages privacy and data protection in the agent memory system.
5
+ Handles encryption, anonymization, access logging, and GDPR compliance.
6
+ """
7
+
8
+ import logging
9
+ from datetime import datetime
10
+ from typing import Any, Dict, List, Optional
11
+ import hashlib
12
+ import json
13
+
14
+ from typing import Any, Dict
15
+ from powermem.agent.types import PrivacyLevel
16
+ from powermem.agent.abstract.privacy import AgentPrivacyManagerBase
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ class PrivacyProtector(AgentPrivacyManagerBase):
22
+ """
23
+ Multi-agent privacy protector implementation.
24
+
25
+ Manages privacy and data protection, handles encryption, anonymization,
26
+ access logging, and GDPR compliance.
27
+ """
28
+
29
+ def __init__(self, config: Dict[str, Any]):
30
+ """
31
+ Initialize the privacy protector.
32
+
33
+ Args:
34
+ config: Memory configuration object
35
+ """
36
+ super().__init__(config.agent_memory.multi_agent_config.privacy_config)
37
+ self.config = config
38
+ self.multi_agent_config = config.agent_memory.multi_agent_config
39
+
40
+ # Privacy settings
41
+ self.privacy_config = self.multi_agent_config.privacy_config
42
+
43
+ # Privacy storage
44
+ self.memory_privacy_levels: Dict[str, PrivacyLevel] = {}
45
+ self.encrypted_memories: Dict[str, Dict[str, Any]] = {}
46
+ self.anonymized_memories: Dict[str, Dict[str, Any]] = {}
47
+ self.access_logs: Dict[str, List[Dict[str, Any]]] = {}
48
+ self.retention_policies: Dict[str, Dict[str, Any]] = {}
49
+
50
+ # GDPR compliance
51
+ self.consent_records: Dict[str, Dict[str, Any]] = {}
52
+ self.data_processing_records: Dict[str, List[Dict[str, Any]]] = {}
53
+ self.deletion_requests: Dict[str, Dict[str, Any]] = {}
54
+
55
+ def initialize(self) -> None:
56
+ """
57
+ Initialize the privacy protector.
58
+ """
59
+ try:
60
+ # Initialize privacy settings
61
+ self._initialize_privacy_settings()
62
+ self.initialized = True
63
+ logger.info("Privacy protector initialized successfully")
64
+
65
+ except Exception as e:
66
+ logger.error(f"Failed to initialize privacy protector: {e}")
67
+ raise
68
+
69
+ def _initialize_privacy_settings(self) -> None:
70
+ """Initialize privacy settings from configuration."""
71
+ self.enable_encryption = self.privacy_config.get('enable_encryption', False)
72
+ self.data_anonymization = self.privacy_config.get('data_anonymization', True)
73
+ self.access_logging = self.privacy_config.get('access_logging', True)
74
+ self.retention_policy = self.privacy_config.get('retention_policy', '30_days')
75
+ self.gdpr_compliance = self.privacy_config.get('gdpr_compliance', True)
76
+ self.default_privacy_level = self.privacy_config.get('default_privacy_level', PrivacyLevel.STANDARD)
77
+
78
+ def set_privacy_level(
79
+ self,
80
+ memory_id: str,
81
+ privacy_level: PrivacyLevel,
82
+ set_by: str
83
+ ) -> Dict[str, Any]:
84
+ """
85
+ Set the privacy level for a memory.
86
+
87
+ Args:
88
+ memory_id: ID of the memory
89
+ privacy_level: Privacy level to set
90
+ set_by: ID of the agent setting the privacy level
91
+
92
+ Returns:
93
+ Dictionary containing the set result
94
+ """
95
+ try:
96
+ # Set privacy level
97
+ self.memory_privacy_levels[memory_id] = privacy_level
98
+
99
+ # Apply privacy measures based on level
100
+ if privacy_level == PrivacyLevel.CONFIDENTIAL:
101
+ # Apply maximum privacy measures
102
+ self._apply_maximum_privacy(memory_id)
103
+ elif privacy_level == PrivacyLevel.SENSITIVE:
104
+ # Apply enhanced privacy measures
105
+ self._apply_enhanced_privacy(memory_id)
106
+
107
+ # Log the privacy level change
108
+ self._log_privacy_change(memory_id, privacy_level, set_by)
109
+
110
+ logger.info(f"Set privacy level {privacy_level.value} for memory {memory_id}")
111
+
112
+ return {
113
+ 'success': True,
114
+ 'memory_id': memory_id,
115
+ 'privacy_level': privacy_level.value,
116
+ 'set_by': set_by,
117
+ 'set_at': datetime.now().isoformat(),
118
+ }
119
+
120
+ except Exception as e:
121
+ logger.error(f"Failed to set privacy level: {e}")
122
+ return {
123
+ 'success': False,
124
+ 'error': str(e),
125
+ 'memory_id': memory_id,
126
+ }
127
+
128
+ def get_privacy_level(
129
+ self,
130
+ memory_id: str
131
+ ) -> PrivacyLevel:
132
+ """
133
+ Get the privacy level for a memory.
134
+
135
+ Args:
136
+ memory_id: ID of the memory
137
+
138
+ Returns:
139
+ PrivacyLevel enum value
140
+ """
141
+ try:
142
+ return self.memory_privacy_levels.get(memory_id, self.default_privacy_level)
143
+
144
+ except Exception as e:
145
+ logger.error(f"Failed to get privacy level: {e}")
146
+ return self.default_privacy_level
147
+
148
+ def encrypt_memory(
149
+ self,
150
+ memory_id: str,
151
+ encryption_key: Optional[str] = None
152
+ ) -> Dict[str, Any]:
153
+ """
154
+ Encrypt a memory.
155
+
156
+ Args:
157
+ memory_id: ID of the memory
158
+ encryption_key: Optional encryption key
159
+
160
+ Returns:
161
+ Dictionary containing the encryption result
162
+ """
163
+ try:
164
+ if not self.enable_encryption:
165
+ return {
166
+ 'success': False,
167
+ 'error': 'Encryption is not enabled',
168
+ 'memory_id': memory_id,
169
+ }
170
+
171
+ # Generate encryption key if not provided
172
+ if not encryption_key:
173
+ encryption_key = self._generate_encryption_key(memory_id)
174
+
175
+ # Store encryption information
176
+ self.encrypted_memories[memory_id] = {
177
+ 'encryption_key_hash': hashlib.sha256(encryption_key.encode()).hexdigest(),
178
+ 'encrypted_at': datetime.now().isoformat(),
179
+ 'encryption_method': 'AES-256',
180
+ }
181
+
182
+ logger.info(f"Encrypted memory {memory_id}")
183
+
184
+ return {
185
+ 'success': True,
186
+ 'memory_id': memory_id,
187
+ 'encrypted_at': datetime.now().isoformat(),
188
+ 'encryption_method': 'AES-256',
189
+ }
190
+
191
+ except Exception as e:
192
+ logger.error(f"Failed to encrypt memory: {e}")
193
+ return {
194
+ 'success': False,
195
+ 'error': str(e),
196
+ 'memory_id': memory_id,
197
+ }
198
+
199
+ def decrypt_memory(
200
+ self,
201
+ memory_id: str,
202
+ decryption_key: Optional[str] = None
203
+ ) -> Dict[str, Any]:
204
+ """
205
+ Decrypt a memory.
206
+
207
+ Args:
208
+ memory_id: ID of the memory
209
+ decryption_key: Optional decryption key
210
+
211
+ Returns:
212
+ Dictionary containing the decryption result
213
+ """
214
+ try:
215
+ if memory_id not in self.encrypted_memories:
216
+ return {
217
+ 'success': False,
218
+ 'error': 'Memory is not encrypted',
219
+ 'memory_id': memory_id,
220
+ }
221
+
222
+ # Verify decryption key if provided
223
+ if decryption_key:
224
+ key_hash = hashlib.sha256(decryption_key.encode()).hexdigest()
225
+ stored_hash = self.encrypted_memories[memory_id]['encryption_key_hash']
226
+
227
+ if key_hash != stored_hash:
228
+ return {
229
+ 'success': False,
230
+ 'error': 'Invalid decryption key',
231
+ 'memory_id': memory_id,
232
+ }
233
+
234
+ logger.info(f"Decrypted memory {memory_id}")
235
+
236
+ return {
237
+ 'success': True,
238
+ 'memory_id': memory_id,
239
+ 'decrypted_at': datetime.now().isoformat(),
240
+ }
241
+
242
+ except Exception as e:
243
+ logger.error(f"Failed to decrypt memory: {e}")
244
+ return {
245
+ 'success': False,
246
+ 'error': str(e),
247
+ 'memory_id': memory_id,
248
+ }
249
+
250
+ def anonymize_memory(
251
+ self,
252
+ memory_id: str,
253
+ anonymization_rules: Optional[Dict[str, Any]] = None
254
+ ) -> Dict[str, Any]:
255
+ """
256
+ Anonymize a memory.
257
+
258
+ Args:
259
+ memory_id: ID of the memory
260
+ anonymization_rules: Optional anonymization rules
261
+
262
+ Returns:
263
+ Dictionary containing the anonymization result
264
+ """
265
+ try:
266
+ if not self.data_anonymization:
267
+ return {
268
+ 'success': False,
269
+ 'error': 'Data anonymization is not enabled',
270
+ 'memory_id': memory_id,
271
+ }
272
+
273
+ # Apply anonymization rules
274
+ rules = anonymization_rules or self._get_default_anonymization_rules()
275
+
276
+ # Store anonymization information
277
+ self.anonymized_memories[memory_id] = {
278
+ 'anonymization_rules': rules,
279
+ 'anonymized_at': datetime.now().isoformat(),
280
+ 'anonymization_method': 'pattern_based',
281
+ }
282
+
283
+ logger.info(f"Anonymized memory {memory_id}")
284
+
285
+ return {
286
+ 'success': True,
287
+ 'memory_id': memory_id,
288
+ 'anonymized_at': datetime.now().isoformat(),
289
+ 'anonymization_rules': rules,
290
+ }
291
+
292
+ except Exception as e:
293
+ logger.error(f"Failed to anonymize memory: {e}")
294
+ return {
295
+ 'success': False,
296
+ 'error': str(e),
297
+ 'memory_id': memory_id,
298
+ }
299
+
300
+ def log_access(
301
+ self,
302
+ memory_id: str,
303
+ agent_id: str,
304
+ access_type: str,
305
+ metadata: Optional[Dict[str, Any]] = None
306
+ ) -> Dict[str, Any]:
307
+ """
308
+ Log access to a memory.
309
+
310
+ Args:
311
+ memory_id: ID of the memory
312
+ agent_id: ID of the agent accessing
313
+ access_type: Type of access
314
+ metadata: Optional metadata about the access
315
+
316
+ Returns:
317
+ Dictionary containing the log result
318
+ """
319
+ try:
320
+ if not self.access_logging:
321
+ return {
322
+ 'success': True,
323
+ 'message': 'Access logging is not enabled',
324
+ 'memory_id': memory_id,
325
+ }
326
+
327
+ # Create access log entry
328
+ log_entry = {
329
+ 'timestamp': datetime.now().isoformat(),
330
+ 'agent_id': agent_id,
331
+ 'access_type': access_type,
332
+ 'metadata': metadata or {},
333
+ }
334
+
335
+ # Store access log
336
+ if memory_id not in self.access_logs:
337
+ self.access_logs[memory_id] = []
338
+
339
+ self.access_logs[memory_id].append(log_entry)
340
+
341
+ # Keep only recent logs (last 1000 entries)
342
+ if len(self.access_logs[memory_id]) > 1000:
343
+ self.access_logs[memory_id] = self.access_logs[memory_id][-1000:]
344
+
345
+ logger.debug(f"Logged access to memory {memory_id} by agent {agent_id}")
346
+
347
+ return {
348
+ 'success': True,
349
+ 'memory_id': memory_id,
350
+ 'agent_id': agent_id,
351
+ 'access_type': access_type,
352
+ 'logged_at': log_entry['timestamp'],
353
+ }
354
+
355
+ except Exception as e:
356
+ logger.error(f"Failed to log access: {e}")
357
+ return {
358
+ 'success': False,
359
+ 'error': str(e),
360
+ 'memory_id': memory_id,
361
+ }
362
+
363
+ def get_access_logs(
364
+ self,
365
+ memory_id: str,
366
+ agent_id: Optional[str] = None,
367
+ limit: Optional[int] = None
368
+ ) -> List[Dict[str, Any]]:
369
+ """
370
+ Get access logs for a memory.
371
+
372
+ Args:
373
+ memory_id: ID of the memory
374
+ agent_id: Optional ID of the agent
375
+ limit: Optional limit on number of entries
376
+
377
+ Returns:
378
+ List of access log entries
379
+ """
380
+ try:
381
+ if memory_id not in self.access_logs:
382
+ return []
383
+
384
+ logs = self.access_logs[memory_id]
385
+
386
+ # Filter by agent if specified
387
+ if agent_id:
388
+ logs = [log for log in logs if log['agent_id'] == agent_id]
389
+
390
+ # Sort by timestamp (newest first)
391
+ logs.sort(key=lambda x: x['timestamp'], reverse=True)
392
+
393
+ # Apply limit if specified
394
+ if limit:
395
+ logs = logs[:limit]
396
+
397
+ return logs
398
+
399
+ except Exception as e:
400
+ logger.error(f"Failed to get access logs: {e}")
401
+ return []
402
+
403
+ def apply_retention_policy(
404
+ self,
405
+ memory_id: str,
406
+ retention_policy: str
407
+ ) -> Dict[str, Any]:
408
+ """
409
+ Apply a retention policy to a memory.
410
+
411
+ Args:
412
+ memory_id: ID of the memory
413
+ retention_policy: Retention policy to apply
414
+
415
+ Returns:
416
+ Dictionary containing the policy application result
417
+ """
418
+ try:
419
+ # Parse retention policy
420
+ policy_days = self._parse_retention_policy(retention_policy)
421
+
422
+ # Calculate expiration date
423
+ expiration_date = datetime.now().timestamp() + (policy_days * 24 * 60 * 60)
424
+
425
+ # Store retention policy
426
+ self.retention_policies[memory_id] = {
427
+ 'retention_policy': retention_policy,
428
+ 'policy_days': policy_days,
429
+ 'expiration_date': expiration_date,
430
+ 'applied_at': datetime.now().isoformat(),
431
+ }
432
+
433
+ logger.info(f"Applied retention policy {retention_policy} to memory {memory_id}")
434
+
435
+ return {
436
+ 'success': True,
437
+ 'memory_id': memory_id,
438
+ 'retention_policy': retention_policy,
439
+ 'expiration_date': datetime.fromtimestamp(expiration_date).isoformat(),
440
+ 'applied_at': datetime.now().isoformat(),
441
+ }
442
+
443
+ except Exception as e:
444
+ logger.error(f"Failed to apply retention policy: {e}")
445
+ return {
446
+ 'success': False,
447
+ 'error': str(e),
448
+ 'memory_id': memory_id,
449
+ }
450
+
451
+ def check_gdpr_compliance(
452
+ self,
453
+ memory_id: str
454
+ ) -> Dict[str, Any]:
455
+ """
456
+ Check GDPR compliance for a memory.
457
+
458
+ Args:
459
+ memory_id: ID of the memory
460
+
461
+ Returns:
462
+ Dictionary containing compliance check results
463
+ """
464
+ try:
465
+ if not self.gdpr_compliance:
466
+ return {
467
+ 'compliant': True,
468
+ 'message': 'GDPR compliance is not enabled',
469
+ 'memory_id': memory_id,
470
+ }
471
+
472
+ compliance_checks = {
473
+ 'data_minimization': True,
474
+ 'purpose_limitation': True,
475
+ 'storage_limitation': True,
476
+ 'accuracy': True,
477
+ 'integrity_confidentiality': True,
478
+ 'accountability': True,
479
+ }
480
+
481
+ # Check retention policy
482
+ if memory_id in self.retention_policies:
483
+ policy = self.retention_policies[memory_id]
484
+ if datetime.now().timestamp() > policy['expiration_date']:
485
+ compliance_checks['storage_limitation'] = False
486
+
487
+ # Check access logs
488
+ if memory_id in self.access_logs:
489
+ compliance_checks['accountability'] = True
490
+
491
+ # Check privacy level
492
+ privacy_level = self.get_privacy_level(memory_id)
493
+ if privacy_level in [PrivacyLevel.SENSITIVE, PrivacyLevel.CONFIDENTIAL]:
494
+ compliance_checks['integrity_confidentiality'] = True
495
+
496
+ # Overall compliance
497
+ compliant = all(compliance_checks.values())
498
+
499
+ return {
500
+ 'compliant': compliant,
501
+ 'memory_id': memory_id,
502
+ 'compliance_checks': compliance_checks,
503
+ 'privacy_level': privacy_level.value,
504
+ 'checked_at': datetime.now().isoformat(),
505
+ }
506
+
507
+ except Exception as e:
508
+ logger.error(f"Failed to check GDPR compliance: {e}")
509
+ return {
510
+ 'compliant': False,
511
+ 'error': str(e),
512
+ 'memory_id': memory_id,
513
+ }
514
+
515
+ def export_user_data(
516
+ self,
517
+ agent_id: str,
518
+ export_format: str = "json"
519
+ ) -> Dict[str, Any]:
520
+ """
521
+ Export user data for GDPR compliance.
522
+
523
+ Args:
524
+ agent_id: ID of the agent/user
525
+ export_format: Format for the export
526
+
527
+ Returns:
528
+ Dictionary containing the export result
529
+ """
530
+ try:
531
+ if not self.gdpr_compliance:
532
+ return {
533
+ 'success': False,
534
+ 'error': 'GDPR compliance is not enabled',
535
+ 'agent_id': agent_id,
536
+ }
537
+
538
+ # Collect user data
539
+ user_data = {
540
+ 'agent_id': agent_id,
541
+ 'exported_at': datetime.now().isoformat(),
542
+ 'export_format': export_format,
543
+ 'data': {
544
+ 'access_logs': [],
545
+ 'privacy_settings': {},
546
+ 'consent_records': {},
547
+ },
548
+ }
549
+
550
+ # Collect access logs for this user
551
+ for memory_id, logs in self.access_logs.items():
552
+ user_logs = [log for log in logs if log['agent_id'] == agent_id]
553
+ if user_logs:
554
+ user_data['data']['access_logs'].append({
555
+ 'memory_id': memory_id,
556
+ 'logs': user_logs,
557
+ })
558
+
559
+ # Collect privacy settings
560
+ for memory_id, privacy_level in self.memory_privacy_levels.items():
561
+ user_data['data']['privacy_settings'][memory_id] = privacy_level.value
562
+
563
+ # Collect consent records
564
+ if agent_id in self.consent_records:
565
+ user_data['data']['consent_records'] = self.consent_records[agent_id]
566
+
567
+ # Format export based on requested format
568
+ if export_format == "json":
569
+ export_data = json.dumps(user_data, indent=2)
570
+ else:
571
+ export_data = str(user_data)
572
+
573
+ logger.info(f"Exported user data for agent {agent_id}")
574
+
575
+ return {
576
+ 'success': True,
577
+ 'agent_id': agent_id,
578
+ 'export_format': export_format,
579
+ 'export_data': export_data,
580
+ 'exported_at': user_data['exported_at'],
581
+ }
582
+
583
+ except Exception as e:
584
+ logger.error(f"Failed to export user data: {e}")
585
+ return {
586
+ 'success': False,
587
+ 'error': str(e),
588
+ 'agent_id': agent_id,
589
+ }
590
+
591
+ def delete_user_data(
592
+ self,
593
+ agent_id: str,
594
+ confirmation_token: str
595
+ ) -> Dict[str, Any]:
596
+ """
597
+ Delete user data for GDPR compliance.
598
+
599
+ Args:
600
+ agent_id: ID of the agent/user
601
+ confirmation_token: Confirmation token for deletion
602
+
603
+ Returns:
604
+ Dictionary containing the deletion result
605
+ """
606
+ try:
607
+ if not self.gdpr_compliance:
608
+ return {
609
+ 'success': False,
610
+ 'error': 'GDPR compliance is not enabled',
611
+ 'agent_id': agent_id,
612
+ }
613
+
614
+ # Verify confirmation token (simplified)
615
+ if not confirmation_token or len(confirmation_token) < 10:
616
+ return {
617
+ 'success': False,
618
+ 'error': 'Invalid confirmation token',
619
+ 'agent_id': agent_id,
620
+ }
621
+
622
+ # Record deletion request
623
+ self.deletion_requests[agent_id] = {
624
+ 'requested_at': datetime.now().isoformat(),
625
+ 'confirmation_token': confirmation_token,
626
+ 'status': 'pending',
627
+ }
628
+
629
+ # Delete user data
630
+ deleted_items = {
631
+ 'access_logs': 0,
632
+ 'privacy_settings': 0,
633
+ 'consent_records': 0,
634
+ }
635
+
636
+ # Delete access logs
637
+ for memory_id, logs in self.access_logs.items():
638
+ original_count = len(logs)
639
+ self.access_logs[memory_id] = [log for log in logs if log['agent_id'] != agent_id]
640
+ deleted_items['access_logs'] += original_count - len(self.access_logs[memory_id])
641
+
642
+ # Delete privacy settings (only for memories owned by the user)
643
+ # Note: This would need to be coordinated with the memory manager
644
+
645
+ # Delete consent records
646
+ if agent_id in self.consent_records:
647
+ deleted_items['consent_records'] = len(self.consent_records[agent_id])
648
+ del self.consent_records[agent_id]
649
+
650
+ # Update deletion request status
651
+ self.deletion_requests[agent_id]['status'] = 'completed'
652
+ self.deletion_requests[agent_id]['completed_at'] = datetime.now().isoformat()
653
+ self.deletion_requests[agent_id]['deleted_items'] = deleted_items
654
+
655
+ logger.info(f"Deleted user data for agent {agent_id}")
656
+
657
+ return {
658
+ 'success': True,
659
+ 'agent_id': agent_id,
660
+ 'deleted_items': deleted_items,
661
+ 'deleted_at': datetime.now().isoformat(),
662
+ }
663
+
664
+ except Exception as e:
665
+ logger.error(f"Failed to delete user data: {e}")
666
+ return {
667
+ 'success': False,
668
+ 'error': str(e),
669
+ 'agent_id': agent_id,
670
+ }
671
+
672
+ def get_privacy_statistics(self) -> Dict[str, Any]:
673
+ """
674
+ Get privacy statistics.
675
+
676
+ Returns:
677
+ Dictionary containing privacy statistics
678
+ """
679
+ try:
680
+ stats = {
681
+ 'total_memories': len(self.memory_privacy_levels),
682
+ 'privacy_level_breakdown': {},
683
+ 'encrypted_memories': len(self.encrypted_memories),
684
+ 'anonymized_memories': len(self.anonymized_memories),
685
+ 'access_logs_count': sum(len(logs) for logs in self.access_logs.values()),
686
+ 'retention_policies': len(self.retention_policies),
687
+ 'gdpr_compliance_enabled': self.gdpr_compliance,
688
+ 'deletion_requests': len(self.deletion_requests),
689
+ }
690
+
691
+ # Privacy level breakdown
692
+ for privacy_level in PrivacyLevel:
693
+ count = sum(1 for level in self.memory_privacy_levels.values() if level == privacy_level)
694
+ stats['privacy_level_breakdown'][privacy_level.value] = count
695
+
696
+ return stats
697
+
698
+ except Exception as e:
699
+ logger.error(f"Failed to get privacy statistics: {e}")
700
+ return {}
701
+
702
+ def _apply_maximum_privacy(self, memory_id: str) -> None:
703
+ """Apply maximum privacy measures to a memory."""
704
+ # Encrypt the memory
705
+ self.encrypt_memory(memory_id)
706
+
707
+ # Anonymize the memory
708
+ self.anonymize_memory(memory_id)
709
+
710
+ # Apply strict retention policy
711
+ self.apply_retention_policy(memory_id, '7_days')
712
+
713
+ def _apply_enhanced_privacy(self, memory_id: str) -> None:
714
+ """Apply enhanced privacy measures to a memory."""
715
+ # Anonymize the memory
716
+ self.anonymize_memory(memory_id)
717
+
718
+ # Apply standard retention policy
719
+ self.apply_retention_policy(memory_id, '30_days')
720
+
721
+ def _generate_encryption_key(self, memory_id: str) -> str:
722
+ """Generate an encryption key for a memory."""
723
+ # Simple key generation based on memory ID and timestamp
724
+ key_data = f"{memory_id}_{datetime.now().isoformat()}"
725
+ return hashlib.sha256(key_data.encode()).hexdigest()[:32]
726
+
727
+ def _get_default_anonymization_rules(self) -> Dict[str, Any]:
728
+ """Get default anonymization rules."""
729
+ return {
730
+ 'email_patterns': True,
731
+ 'phone_patterns': True,
732
+ 'credit_card_patterns': True,
733
+ 'ssn_patterns': True,
734
+ 'ip_addresses': True,
735
+ 'names': False, # Keep names for context
736
+ }
737
+
738
+ def _parse_retention_policy(self, policy: str) -> int:
739
+ """Parse retention policy string to days."""
740
+ if policy.endswith('_days'):
741
+ return int(policy.split('_')[0])
742
+ elif policy.endswith('_months'):
743
+ return int(policy.split('_')[0]) * 30
744
+ elif policy.endswith('_years'):
745
+ return int(policy.split('_')[0]) * 365
746
+ else:
747
+ return 30 # Default to 30 days
748
+
749
+ def _log_privacy_change(
750
+ self,
751
+ memory_id: str,
752
+ privacy_level: PrivacyLevel,
753
+ set_by: str
754
+ ) -> None:
755
+ """Log privacy level change."""
756
+ log_entry = {
757
+ 'timestamp': datetime.now().isoformat(),
758
+ 'memory_id': memory_id,
759
+ 'privacy_level': privacy_level.value,
760
+ 'set_by': set_by,
761
+ 'type': 'privacy_change',
762
+ }
763
+
764
+ if memory_id not in self.access_logs:
765
+ self.access_logs[memory_id] = []
766
+
767
+ self.access_logs[memory_id].append(log_entry)