aiecs 1.1.0__py3-none-any.whl → 1.2.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.

Potentially problematic release.


This version of aiecs might be problematic. Click here for more details.

Files changed (58) hide show
  1. aiecs/__init__.py +1 -1
  2. aiecs/config/config.py +2 -0
  3. aiecs/domain/__init__.py +95 -0
  4. aiecs/domain/community/__init__.py +159 -0
  5. aiecs/domain/community/agent_adapter.py +516 -0
  6. aiecs/domain/community/analytics.py +465 -0
  7. aiecs/domain/community/collaborative_workflow.py +99 -7
  8. aiecs/domain/community/communication_hub.py +649 -0
  9. aiecs/domain/community/community_builder.py +322 -0
  10. aiecs/domain/community/community_integration.py +365 -12
  11. aiecs/domain/community/community_manager.py +481 -5
  12. aiecs/domain/community/decision_engine.py +459 -13
  13. aiecs/domain/community/exceptions.py +238 -0
  14. aiecs/domain/community/models/__init__.py +36 -0
  15. aiecs/domain/community/resource_manager.py +1 -1
  16. aiecs/domain/community/shared_context_manager.py +621 -0
  17. aiecs/domain/context/context_engine.py +37 -33
  18. aiecs/main.py +2 -2
  19. aiecs/scripts/aid/VERSION_MANAGEMENT.md +97 -0
  20. aiecs/scripts/aid/__init__.py +15 -0
  21. aiecs/scripts/aid/version_manager.py +224 -0
  22. aiecs/scripts/dependance_check/download_nlp_data.py +1 -0
  23. aiecs/tools/__init__.py +23 -23
  24. aiecs/tools/docs/__init__.py +5 -2
  25. aiecs/tools/docs/ai_document_orchestrator.py +39 -26
  26. aiecs/tools/docs/ai_document_writer_orchestrator.py +61 -38
  27. aiecs/tools/docs/content_insertion_tool.py +48 -28
  28. aiecs/tools/docs/document_creator_tool.py +47 -29
  29. aiecs/tools/docs/document_layout_tool.py +35 -20
  30. aiecs/tools/docs/document_parser_tool.py +56 -36
  31. aiecs/tools/docs/document_writer_tool.py +115 -62
  32. aiecs/tools/schema_generator.py +56 -56
  33. aiecs/tools/statistics/__init__.py +82 -0
  34. aiecs/tools/statistics/ai_data_analysis_orchestrator.py +581 -0
  35. aiecs/tools/statistics/ai_insight_generator_tool.py +473 -0
  36. aiecs/tools/statistics/ai_report_orchestrator_tool.py +629 -0
  37. aiecs/tools/statistics/data_loader_tool.py +518 -0
  38. aiecs/tools/statistics/data_profiler_tool.py +599 -0
  39. aiecs/tools/statistics/data_transformer_tool.py +531 -0
  40. aiecs/tools/statistics/data_visualizer_tool.py +460 -0
  41. aiecs/tools/statistics/model_trainer_tool.py +470 -0
  42. aiecs/tools/statistics/statistical_analyzer_tool.py +426 -0
  43. aiecs/tools/task_tools/chart_tool.py +2 -1
  44. aiecs/tools/task_tools/image_tool.py +43 -43
  45. aiecs/tools/task_tools/office_tool.py +39 -36
  46. aiecs/tools/task_tools/pandas_tool.py +37 -33
  47. aiecs/tools/task_tools/report_tool.py +67 -56
  48. aiecs/tools/task_tools/research_tool.py +32 -31
  49. aiecs/tools/task_tools/scraper_tool.py +53 -46
  50. aiecs/tools/task_tools/search_tool.py +1123 -0
  51. aiecs/tools/task_tools/stats_tool.py +20 -15
  52. {aiecs-1.1.0.dist-info → aiecs-1.2.0.dist-info}/METADATA +5 -1
  53. {aiecs-1.1.0.dist-info → aiecs-1.2.0.dist-info}/RECORD +57 -36
  54. {aiecs-1.1.0.dist-info → aiecs-1.2.0.dist-info}/entry_points.txt +1 -0
  55. aiecs/tools/task_tools/search_api.py +0 -7
  56. {aiecs-1.1.0.dist-info → aiecs-1.2.0.dist-info}/WHEEL +0 -0
  57. {aiecs-1.1.0.dist-info → aiecs-1.2.0.dist-info}/licenses/LICENSE +0 -0
  58. {aiecs-1.1.0.dist-info → aiecs-1.2.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,621 @@
1
+ """
2
+ Shared Context Manager
3
+
4
+ Manages shared memory and context for agents in a community,
5
+ with support for versioning, conflict resolution, and real-time streaming.
6
+ """
7
+
8
+ import logging
9
+ import asyncio
10
+ from datetime import datetime
11
+ from typing import Dict, List, Any, Optional, Set, Callable
12
+ from enum import Enum
13
+ from collections import defaultdict
14
+ import uuid
15
+ import copy
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class ContextScope(str, Enum):
21
+ """Scope levels for shared context."""
22
+ COMMUNITY = "community"
23
+ SESSION = "session"
24
+ TASK = "task"
25
+ AGENT = "agent"
26
+
27
+
28
+ class ConflictResolutionStrategy(str, Enum):
29
+ """Strategies for resolving context conflicts."""
30
+ LAST_WRITE_WINS = "last_write_wins"
31
+ FIRST_WRITE_WINS = "first_write_wins"
32
+ MERGE = "merge"
33
+ MANUAL = "manual"
34
+ TIMESTAMP_BASED = "timestamp_based"
35
+
36
+
37
+ class ContextVersion:
38
+ """Represents a version of context data."""
39
+
40
+ def __init__(
41
+ self,
42
+ context_id: str,
43
+ data: Dict[str, Any],
44
+ version_number: int,
45
+ author_id: str,
46
+ parent_version: Optional[int] = None
47
+ ):
48
+ """
49
+ Initialize a context version.
50
+
51
+ Args:
52
+ context_id: ID of the context
53
+ data: Context data
54
+ version_number: Version number
55
+ author_id: ID of the author who created this version
56
+ parent_version: Optional parent version number
57
+ """
58
+ self.context_id = context_id
59
+ self.data = copy.deepcopy(data)
60
+ self.version_number = version_number
61
+ self.author_id = author_id
62
+ self.parent_version = parent_version
63
+ self.timestamp = datetime.utcnow()
64
+ self.metadata: Dict[str, Any] = {}
65
+
66
+
67
+ class SharedContext:
68
+ """Represents a shared context in the community."""
69
+
70
+ def __init__(
71
+ self,
72
+ context_id: str,
73
+ scope: ContextScope,
74
+ owner_id: str,
75
+ initial_data: Optional[Dict[str, Any]] = None
76
+ ):
77
+ """
78
+ Initialize a shared context.
79
+
80
+ Args:
81
+ context_id: Unique context identifier
82
+ scope: Scope level of the context
83
+ owner_id: ID of the context owner
84
+ initial_data: Optional initial data
85
+ """
86
+ self.context_id = context_id
87
+ self.scope = scope
88
+ self.owner_id = owner_id
89
+ self.current_version = 0
90
+ self.versions: List[ContextVersion] = []
91
+ self.data = initial_data or {}
92
+ self.access_control: Set[str] = {owner_id} # IDs with access
93
+ self.subscribers: Set[str] = set() # IDs subscribed to updates
94
+ self.created_at = datetime.utcnow()
95
+ self.updated_at = datetime.utcnow()
96
+ self.metadata: Dict[str, Any] = {}
97
+
98
+ # Create initial version
99
+ if initial_data:
100
+ self._create_version(initial_data, owner_id)
101
+
102
+ def _create_version(self, data: Dict[str, Any], author_id: str) -> ContextVersion:
103
+ """Create a new version of the context."""
104
+ version = ContextVersion(
105
+ self.context_id,
106
+ data,
107
+ self.current_version + 1,
108
+ author_id,
109
+ self.current_version if self.current_version > 0 else None
110
+ )
111
+ self.versions.append(version)
112
+ self.current_version = version.version_number
113
+ self.data = copy.deepcopy(data)
114
+ self.updated_at = datetime.utcnow()
115
+ return version
116
+
117
+
118
+ class SharedContextManager:
119
+ """
120
+ Manager for shared contexts in agent communities.
121
+ Provides versioning, conflict resolution, and streaming capabilities.
122
+ """
123
+
124
+ def __init__(
125
+ self,
126
+ default_conflict_strategy: ConflictResolutionStrategy = ConflictResolutionStrategy.LAST_WRITE_WINS
127
+ ):
128
+ """
129
+ Initialize the shared context manager.
130
+
131
+ Args:
132
+ default_conflict_strategy: Default strategy for conflict resolution
133
+ """
134
+ self.contexts: Dict[str, SharedContext] = {}
135
+ self.default_conflict_strategy = default_conflict_strategy
136
+
137
+ # Scope-based indexes
138
+ self.community_contexts: Dict[str, Set[str]] = defaultdict(set)
139
+ self.session_contexts: Dict[str, Set[str]] = defaultdict(set)
140
+ self.task_contexts: Dict[str, Set[str]] = defaultdict(set)
141
+ self.agent_contexts: Dict[str, Set[str]] = defaultdict(set)
142
+
143
+ # Update callbacks for streaming
144
+ self.update_callbacks: Dict[str, List[Callable]] = defaultdict(list)
145
+
146
+ logger.info("Shared context manager initialized")
147
+
148
+ async def create_context(
149
+ self,
150
+ scope: ContextScope,
151
+ owner_id: str,
152
+ scope_id: str,
153
+ initial_data: Optional[Dict[str, Any]] = None,
154
+ access_control: Optional[Set[str]] = None
155
+ ) -> str:
156
+ """
157
+ Create a new shared context.
158
+
159
+ Args:
160
+ scope: Scope level of the context
161
+ owner_id: ID of the context owner
162
+ scope_id: ID of the scope (community_id, session_id, task_id, or agent_id)
163
+ initial_data: Optional initial data
164
+ access_control: Optional set of agent IDs with access
165
+
166
+ Returns:
167
+ Context ID
168
+ """
169
+ context_id = str(uuid.uuid4())
170
+ context = SharedContext(context_id, scope, owner_id, initial_data)
171
+
172
+ if access_control:
173
+ context.access_control = access_control
174
+
175
+ self.contexts[context_id] = context
176
+
177
+ # Add to scope index
178
+ if scope == ContextScope.COMMUNITY:
179
+ self.community_contexts[scope_id].add(context_id)
180
+ elif scope == ContextScope.SESSION:
181
+ self.session_contexts[scope_id].add(context_id)
182
+ elif scope == ContextScope.TASK:
183
+ self.task_contexts[scope_id].add(context_id)
184
+ elif scope == ContextScope.AGENT:
185
+ self.agent_contexts[scope_id].add(context_id)
186
+
187
+ logger.info(f"Created {scope.value} context {context_id}")
188
+ return context_id
189
+
190
+ async def get_context(
191
+ self,
192
+ context_id: str,
193
+ requester_id: str,
194
+ version: Optional[int] = None
195
+ ) -> Optional[Dict[str, Any]]:
196
+ """
197
+ Get context data.
198
+
199
+ Args:
200
+ context_id: ID of the context
201
+ requester_id: ID of the requester
202
+ version: Optional specific version to retrieve
203
+
204
+ Returns:
205
+ Context data or None if not found/unauthorized
206
+ """
207
+ context = self.contexts.get(context_id)
208
+ if not context:
209
+ return None
210
+
211
+ # Check access control
212
+ if requester_id not in context.access_control:
213
+ logger.warning(f"Access denied for {requester_id} to context {context_id}")
214
+ return None
215
+
216
+ # Return specific version or current
217
+ if version is not None:
218
+ for v in context.versions:
219
+ if v.version_number == version:
220
+ return copy.deepcopy(v.data)
221
+ return None
222
+
223
+ return copy.deepcopy(context.data)
224
+
225
+ async def update_context(
226
+ self,
227
+ context_id: str,
228
+ updater_id: str,
229
+ updates: Dict[str, Any],
230
+ conflict_strategy: Optional[ConflictResolutionStrategy] = None,
231
+ create_version: bool = True
232
+ ) -> bool:
233
+ """
234
+ Update context data with conflict resolution.
235
+
236
+ Args:
237
+ context_id: ID of the context
238
+ updater_id: ID of the updater
239
+ updates: Data updates
240
+ conflict_strategy: Optional conflict resolution strategy
241
+ create_version: Whether to create a new version
242
+
243
+ Returns:
244
+ True if update was successful
245
+ """
246
+ context = self.contexts.get(context_id)
247
+ if not context:
248
+ logger.error(f"Context {context_id} not found")
249
+ return False
250
+
251
+ # Check access control
252
+ if updater_id not in context.access_control:
253
+ logger.warning(f"Access denied for {updater_id} to update context {context_id}")
254
+ return False
255
+
256
+ # Apply updates with conflict resolution
257
+ strategy = conflict_strategy or self.default_conflict_strategy
258
+ merged_data = await self._resolve_conflicts(
259
+ context.data, updates, strategy, context, updater_id
260
+ )
261
+
262
+ # Create new version if requested
263
+ if create_version:
264
+ context._create_version(merged_data, updater_id)
265
+ else:
266
+ context.data = merged_data
267
+ context.updated_at = datetime.utcnow()
268
+
269
+ # Notify subscribers via streaming
270
+ await self._notify_subscribers(context_id, merged_data, updater_id)
271
+
272
+ logger.debug(f"Updated context {context_id} by {updater_id}")
273
+ return True
274
+
275
+ async def _resolve_conflicts(
276
+ self,
277
+ current_data: Dict[str, Any],
278
+ updates: Dict[str, Any],
279
+ strategy: ConflictResolutionStrategy,
280
+ context: SharedContext,
281
+ updater_id: str
282
+ ) -> Dict[str, Any]:
283
+ """Resolve conflicts between current data and updates."""
284
+ if strategy == ConflictResolutionStrategy.LAST_WRITE_WINS:
285
+ # Simply apply updates over current data
286
+ merged = copy.deepcopy(current_data)
287
+ merged.update(updates)
288
+ return merged
289
+
290
+ elif strategy == ConflictResolutionStrategy.FIRST_WRITE_WINS:
291
+ # Only add new keys, don't override existing
292
+ merged = copy.deepcopy(current_data)
293
+ for key, value in updates.items():
294
+ if key not in merged:
295
+ merged[key] = value
296
+ return merged
297
+
298
+ elif strategy == ConflictResolutionStrategy.MERGE:
299
+ # Intelligent merge based on data types
300
+ merged = copy.deepcopy(current_data)
301
+ for key, new_value in updates.items():
302
+ if key in merged:
303
+ current_value = merged[key]
304
+ # Merge lists
305
+ if isinstance(current_value, list) and isinstance(new_value, list):
306
+ merged[key] = current_value + [item for item in new_value if item not in current_value]
307
+ # Merge dicts
308
+ elif isinstance(current_value, dict) and isinstance(new_value, dict):
309
+ merged[key] = {**current_value, **new_value}
310
+ # Otherwise, last write wins
311
+ else:
312
+ merged[key] = new_value
313
+ else:
314
+ merged[key] = new_value
315
+ return merged
316
+
317
+ elif strategy == ConflictResolutionStrategy.TIMESTAMP_BASED:
318
+ # Use timestamps to determine which update wins
319
+ merged = copy.deepcopy(current_data)
320
+ current_time = datetime.utcnow()
321
+ for key, value in updates.items():
322
+ if key not in merged or context.updated_at < current_time:
323
+ merged[key] = value
324
+ return merged
325
+
326
+ else: # MANUAL
327
+ # Return updates as-is and log conflict for manual resolution
328
+ logger.warning(f"Manual conflict resolution required for context {context.context_id}")
329
+ return copy.deepcopy(updates)
330
+
331
+ async def subscribe_to_context(
332
+ self,
333
+ context_id: str,
334
+ subscriber_id: str,
335
+ callback: Optional[Callable] = None
336
+ ) -> bool:
337
+ """
338
+ Subscribe to context updates (streaming).
339
+
340
+ Args:
341
+ context_id: ID of the context
342
+ subscriber_id: ID of the subscriber
343
+ callback: Optional callback for updates
344
+
345
+ Returns:
346
+ True if subscription was successful
347
+ """
348
+ context = self.contexts.get(context_id)
349
+ if not context:
350
+ return False
351
+
352
+ # Check access control
353
+ if subscriber_id not in context.access_control:
354
+ logger.warning(f"Access denied for {subscriber_id} to subscribe to context {context_id}")
355
+ return False
356
+
357
+ context.subscribers.add(subscriber_id)
358
+
359
+ if callback:
360
+ self.update_callbacks[context_id].append(callback)
361
+
362
+ logger.debug(f"Agent {subscriber_id} subscribed to context {context_id}")
363
+ return True
364
+
365
+ async def unsubscribe_from_context(
366
+ self,
367
+ context_id: str,
368
+ subscriber_id: str,
369
+ callback: Optional[Callable] = None
370
+ ) -> bool:
371
+ """
372
+ Unsubscribe from context updates.
373
+
374
+ Args:
375
+ context_id: ID of the context
376
+ subscriber_id: ID of the subscriber
377
+ callback: Optional callback to remove
378
+
379
+ Returns:
380
+ True if unsubscription was successful
381
+ """
382
+ context = self.contexts.get(context_id)
383
+ if not context:
384
+ return False
385
+
386
+ context.subscribers.discard(subscriber_id)
387
+
388
+ if callback and context_id in self.update_callbacks:
389
+ if callback in self.update_callbacks[context_id]:
390
+ self.update_callbacks[context_id].remove(callback)
391
+
392
+ logger.debug(f"Agent {subscriber_id} unsubscribed from context {context_id}")
393
+ return True
394
+
395
+ async def _notify_subscribers(
396
+ self,
397
+ context_id: str,
398
+ updated_data: Dict[str, Any],
399
+ updater_id: str
400
+ ) -> None:
401
+ """Notify subscribers of context updates."""
402
+ context = self.contexts.get(context_id)
403
+ if not context:
404
+ return
405
+
406
+ update_notification = {
407
+ "context_id": context_id,
408
+ "updater_id": updater_id,
409
+ "data": copy.deepcopy(updated_data),
410
+ "version": context.current_version,
411
+ "timestamp": datetime.utcnow().isoformat()
412
+ }
413
+
414
+ # Execute callbacks
415
+ if context_id in self.update_callbacks:
416
+ for callback in self.update_callbacks[context_id]:
417
+ try:
418
+ if asyncio.iscoroutinefunction(callback):
419
+ await callback(update_notification)
420
+ else:
421
+ callback(update_notification)
422
+ except Exception as e:
423
+ logger.error(f"Error executing context update callback: {e}")
424
+
425
+ async def grant_access(
426
+ self,
427
+ context_id: str,
428
+ granter_id: str,
429
+ grantee_id: str
430
+ ) -> bool:
431
+ """
432
+ Grant access to a context.
433
+
434
+ Args:
435
+ context_id: ID of the context
436
+ granter_id: ID of the agent granting access (must be owner)
437
+ grantee_id: ID of the agent being granted access
438
+
439
+ Returns:
440
+ True if access was granted
441
+ """
442
+ context = self.contexts.get(context_id)
443
+ if not context:
444
+ return False
445
+
446
+ # Only owner can grant access
447
+ if granter_id != context.owner_id:
448
+ logger.warning(f"Only owner can grant access to context {context_id}")
449
+ return False
450
+
451
+ context.access_control.add(grantee_id)
452
+ logger.info(f"Granted access to {grantee_id} for context {context_id}")
453
+ return True
454
+
455
+ async def revoke_access(
456
+ self,
457
+ context_id: str,
458
+ revoker_id: str,
459
+ revokee_id: str
460
+ ) -> bool:
461
+ """
462
+ Revoke access to a context.
463
+
464
+ Args:
465
+ context_id: ID of the context
466
+ revoker_id: ID of the agent revoking access (must be owner)
467
+ revokee_id: ID of the agent losing access
468
+
469
+ Returns:
470
+ True if access was revoked
471
+ """
472
+ context = self.contexts.get(context_id)
473
+ if not context:
474
+ return False
475
+
476
+ # Only owner can revoke access
477
+ if revoker_id != context.owner_id:
478
+ logger.warning(f"Only owner can revoke access to context {context_id}")
479
+ return False
480
+
481
+ # Can't revoke owner's access
482
+ if revokee_id == context.owner_id:
483
+ logger.warning("Cannot revoke owner's access to context")
484
+ return False
485
+
486
+ context.access_control.discard(revokee_id)
487
+ context.subscribers.discard(revokee_id)
488
+ logger.info(f"Revoked access from {revokee_id} for context {context_id}")
489
+ return True
490
+
491
+ async def get_version_history(
492
+ self,
493
+ context_id: str,
494
+ requester_id: str
495
+ ) -> Optional[List[Dict[str, Any]]]:
496
+ """
497
+ Get version history for a context.
498
+
499
+ Args:
500
+ context_id: ID of the context
501
+ requester_id: ID of the requester
502
+
503
+ Returns:
504
+ List of version information or None if unauthorized
505
+ """
506
+ context = self.contexts.get(context_id)
507
+ if not context:
508
+ return None
509
+
510
+ # Check access control
511
+ if requester_id not in context.access_control:
512
+ return None
513
+
514
+ history = []
515
+ for version in context.versions:
516
+ history.append({
517
+ "version_number": version.version_number,
518
+ "author_id": version.author_id,
519
+ "timestamp": version.timestamp.isoformat(),
520
+ "parent_version": version.parent_version,
521
+ "metadata": version.metadata
522
+ })
523
+
524
+ return history
525
+
526
+ async def rollback_to_version(
527
+ self,
528
+ context_id: str,
529
+ requester_id: str,
530
+ target_version: int
531
+ ) -> bool:
532
+ """
533
+ Rollback context to a previous version.
534
+
535
+ Args:
536
+ context_id: ID of the context
537
+ requester_id: ID of the requester (must be owner)
538
+ target_version: Version number to rollback to
539
+
540
+ Returns:
541
+ True if rollback was successful
542
+ """
543
+ context = self.contexts.get(context_id)
544
+ if not context:
545
+ return False
546
+
547
+ # Only owner can rollback
548
+ if requester_id != context.owner_id:
549
+ logger.warning(f"Only owner can rollback context {context_id}")
550
+ return False
551
+
552
+ # Find target version
553
+ target = None
554
+ for version in context.versions:
555
+ if version.version_number == target_version:
556
+ target = version
557
+ break
558
+
559
+ if not target:
560
+ logger.error(f"Version {target_version} not found for context {context_id}")
561
+ return False
562
+
563
+ # Create new version based on target (rollback is a new version)
564
+ context._create_version(target.data, requester_id)
565
+ context.metadata["rollback"] = {
566
+ "from_version": context.current_version - 1,
567
+ "to_version": target_version,
568
+ "timestamp": datetime.utcnow().isoformat()
569
+ }
570
+
571
+ # Notify subscribers
572
+ await self._notify_subscribers(context_id, context.data, requester_id)
573
+
574
+ logger.info(f"Rolled back context {context_id} to version {target_version}")
575
+ return True
576
+
577
+ def get_contexts_by_scope(
578
+ self,
579
+ scope: ContextScope,
580
+ scope_id: str
581
+ ) -> List[str]:
582
+ """
583
+ Get all contexts for a specific scope.
584
+
585
+ Args:
586
+ scope: Scope level
587
+ scope_id: ID of the scope
588
+
589
+ Returns:
590
+ List of context IDs
591
+ """
592
+ if scope == ContextScope.COMMUNITY:
593
+ return list(self.community_contexts.get(scope_id, set()))
594
+ elif scope == ContextScope.SESSION:
595
+ return list(self.session_contexts.get(scope_id, set()))
596
+ elif scope == ContextScope.TASK:
597
+ return list(self.task_contexts.get(scope_id, set()))
598
+ elif scope == ContextScope.AGENT:
599
+ return list(self.agent_contexts.get(scope_id, set()))
600
+ return []
601
+
602
+ def get_statistics(self) -> Dict[str, Any]:
603
+ """
604
+ Get context manager statistics.
605
+
606
+ Returns:
607
+ Statistics dictionary
608
+ """
609
+ total_versions = sum(len(ctx.versions) for ctx in self.contexts.values())
610
+ total_subscribers = sum(len(ctx.subscribers) for ctx in self.contexts.values())
611
+
612
+ return {
613
+ "total_contexts": len(self.contexts),
614
+ "total_versions": total_versions,
615
+ "total_subscribers": total_subscribers,
616
+ "community_contexts": sum(len(s) for s in self.community_contexts.values()),
617
+ "session_contexts": sum(len(s) for s in self.session_contexts.values()),
618
+ "task_contexts": sum(len(s) for s in self.task_contexts.values()),
619
+ "agent_contexts": sum(len(s) for s in self.agent_contexts.values())
620
+ }
621
+