aiecs 1.0.8__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 (81) hide show
  1. aiecs/__init__.py +1 -1
  2. aiecs/aiecs_client.py +159 -1
  3. aiecs/config/config.py +6 -0
  4. aiecs/domain/__init__.py +95 -0
  5. aiecs/domain/community/__init__.py +159 -0
  6. aiecs/domain/community/agent_adapter.py +516 -0
  7. aiecs/domain/community/analytics.py +465 -0
  8. aiecs/domain/community/collaborative_workflow.py +99 -7
  9. aiecs/domain/community/communication_hub.py +649 -0
  10. aiecs/domain/community/community_builder.py +322 -0
  11. aiecs/domain/community/community_integration.py +365 -12
  12. aiecs/domain/community/community_manager.py +481 -5
  13. aiecs/domain/community/decision_engine.py +459 -13
  14. aiecs/domain/community/exceptions.py +238 -0
  15. aiecs/domain/community/models/__init__.py +36 -0
  16. aiecs/domain/community/resource_manager.py +1 -1
  17. aiecs/domain/community/shared_context_manager.py +621 -0
  18. aiecs/domain/context/__init__.py +24 -0
  19. aiecs/domain/context/context_engine.py +37 -33
  20. aiecs/main.py +20 -2
  21. aiecs/scripts/aid/VERSION_MANAGEMENT.md +97 -0
  22. aiecs/scripts/aid/__init__.py +15 -0
  23. aiecs/scripts/aid/version_manager.py +224 -0
  24. aiecs/scripts/dependance_check/__init__.py +18 -0
  25. aiecs/scripts/{download_nlp_data.py → dependance_check/download_nlp_data.py} +51 -8
  26. aiecs/scripts/dependance_patch/__init__.py +8 -0
  27. aiecs/scripts/dependance_patch/fix_weasel/__init__.py +12 -0
  28. aiecs/scripts/tools_develop/README.md +340 -0
  29. aiecs/scripts/tools_develop/__init__.py +16 -0
  30. aiecs/scripts/tools_develop/check_type_annotations.py +263 -0
  31. aiecs/scripts/tools_develop/validate_tool_schemas.py +346 -0
  32. aiecs/tools/__init__.py +53 -34
  33. aiecs/tools/docs/__init__.py +106 -0
  34. aiecs/tools/docs/ai_document_orchestrator.py +556 -0
  35. aiecs/tools/docs/ai_document_writer_orchestrator.py +2222 -0
  36. aiecs/tools/docs/content_insertion_tool.py +1234 -0
  37. aiecs/tools/docs/document_creator_tool.py +1179 -0
  38. aiecs/tools/docs/document_layout_tool.py +1105 -0
  39. aiecs/tools/docs/document_parser_tool.py +924 -0
  40. aiecs/tools/docs/document_writer_tool.py +1636 -0
  41. aiecs/tools/langchain_adapter.py +102 -51
  42. aiecs/tools/schema_generator.py +265 -0
  43. aiecs/tools/statistics/__init__.py +82 -0
  44. aiecs/tools/statistics/ai_data_analysis_orchestrator.py +581 -0
  45. aiecs/tools/statistics/ai_insight_generator_tool.py +473 -0
  46. aiecs/tools/statistics/ai_report_orchestrator_tool.py +629 -0
  47. aiecs/tools/statistics/data_loader_tool.py +518 -0
  48. aiecs/tools/statistics/data_profiler_tool.py +599 -0
  49. aiecs/tools/statistics/data_transformer_tool.py +531 -0
  50. aiecs/tools/statistics/data_visualizer_tool.py +460 -0
  51. aiecs/tools/statistics/model_trainer_tool.py +470 -0
  52. aiecs/tools/statistics/statistical_analyzer_tool.py +426 -0
  53. aiecs/tools/task_tools/chart_tool.py +2 -1
  54. aiecs/tools/task_tools/image_tool.py +43 -43
  55. aiecs/tools/task_tools/office_tool.py +48 -36
  56. aiecs/tools/task_tools/pandas_tool.py +37 -33
  57. aiecs/tools/task_tools/report_tool.py +67 -56
  58. aiecs/tools/task_tools/research_tool.py +32 -31
  59. aiecs/tools/task_tools/scraper_tool.py +53 -46
  60. aiecs/tools/task_tools/search_tool.py +1123 -0
  61. aiecs/tools/task_tools/stats_tool.py +20 -15
  62. {aiecs-1.0.8.dist-info → aiecs-1.2.0.dist-info}/METADATA +5 -1
  63. aiecs-1.2.0.dist-info/RECORD +135 -0
  64. aiecs-1.2.0.dist-info/entry_points.txt +10 -0
  65. aiecs/tools/task_tools/search_api.py +0 -7
  66. aiecs-1.0.8.dist-info/RECORD +0 -98
  67. aiecs-1.0.8.dist-info/entry_points.txt +0 -7
  68. /aiecs/scripts/{DEPENDENCY_SYSTEM_SUMMARY.md → dependance_check/DEPENDENCY_SYSTEM_SUMMARY.md} +0 -0
  69. /aiecs/scripts/{README_DEPENDENCY_CHECKER.md → dependance_check/README_DEPENDENCY_CHECKER.md} +0 -0
  70. /aiecs/scripts/{dependency_checker.py → dependance_check/dependency_checker.py} +0 -0
  71. /aiecs/scripts/{dependency_fixer.py → dependance_check/dependency_fixer.py} +0 -0
  72. /aiecs/scripts/{quick_dependency_check.py → dependance_check/quick_dependency_check.py} +0 -0
  73. /aiecs/scripts/{setup_nlp_data.sh → dependance_check/setup_nlp_data.sh} +0 -0
  74. /aiecs/scripts/{README_WEASEL_PATCH.md → dependance_patch/fix_weasel/README_WEASEL_PATCH.md} +0 -0
  75. /aiecs/scripts/{fix_weasel_validator.py → dependance_patch/fix_weasel/fix_weasel_validator.py} +0 -0
  76. /aiecs/scripts/{fix_weasel_validator.sh → dependance_patch/fix_weasel/fix_weasel_validator.sh} +0 -0
  77. /aiecs/scripts/{patch_weasel_library.sh → dependance_patch/fix_weasel/patch_weasel_library.sh} +0 -0
  78. /aiecs/scripts/{run_weasel_patch.sh → dependance_patch/fix_weasel/run_weasel_patch.sh} +0 -0
  79. {aiecs-1.0.8.dist-info → aiecs-1.2.0.dist-info}/WHEEL +0 -0
  80. {aiecs-1.0.8.dist-info → aiecs-1.2.0.dist-info}/licenses/LICENSE +0 -0
  81. {aiecs-1.0.8.dist-info → aiecs-1.2.0.dist-info}/top_level.txt +0 -0
@@ -7,18 +7,64 @@ resource sharing, and collaborative decision-making.
7
7
 
8
8
  import logging
9
9
  from datetime import datetime, timedelta
10
- from typing import Dict, List, Any, Optional, Set
10
+ from typing import Dict, List, Any, Optional, Set, Protocol, Callable
11
11
  import uuid
12
12
 
13
13
  from .models.community_models import (
14
14
  AgentCommunity, CommunityMember, CommunityResource, CommunityDecision,
15
15
  CollaborationSession, CommunityRole, GovernanceType, DecisionStatus, ResourceType
16
16
  )
17
- from ..core.exceptions.task_exceptions import TaskValidationError
17
+ from .exceptions import CommunityValidationError as TaskValidationError
18
18
 
19
19
  logger = logging.getLogger(__name__)
20
20
 
21
21
 
22
+ class MemberLifecycleHooks(Protocol):
23
+ """
24
+ Protocol defining lifecycle hooks for community member events.
25
+ Implement this protocol to receive notifications about member lifecycle events.
26
+ """
27
+
28
+ async def on_member_join(
29
+ self,
30
+ community_id: str,
31
+ member_id: str,
32
+ member: CommunityMember
33
+ ) -> None:
34
+ """Called when a member joins a community."""
35
+ ...
36
+
37
+ async def on_member_exit(
38
+ self,
39
+ community_id: str,
40
+ member_id: str,
41
+ member: CommunityMember,
42
+ reason: Optional[str] = None
43
+ ) -> None:
44
+ """Called when a member exits/is removed from a community."""
45
+ ...
46
+
47
+ async def on_member_update(
48
+ self,
49
+ community_id: str,
50
+ member_id: str,
51
+ member: CommunityMember,
52
+ changes: Dict[str, Any]
53
+ ) -> None:
54
+ """Called when a member's properties are updated."""
55
+ ...
56
+
57
+ async def on_member_inactive(
58
+ self,
59
+ community_id: str,
60
+ member_id: str,
61
+ member: CommunityMember,
62
+ reason: Optional[str] = None
63
+ ) -> None:
64
+ """Called when a member becomes inactive."""
65
+ ...
66
+
67
+
22
68
  class CommunityManager:
23
69
  """
24
70
  Manager for agent communities, handling governance, collaboration, and resource sharing.
@@ -44,6 +90,9 @@ class CommunityManager:
44
90
  self.member_communities: Dict[str, Set[str]] = {} # member_id -> set of community_ids
45
91
  self.community_members: Dict[str, Set[str]] = {} # community_id -> set of member_ids
46
92
 
93
+ # Lifecycle hooks
94
+ self.lifecycle_hooks: List[MemberLifecycleHooks] = []
95
+
47
96
  self._initialized = False
48
97
  logger.info("Community manager initialized")
49
98
 
@@ -98,6 +147,9 @@ class CommunityManager:
98
147
  community_role=CommunityRole.LEADER
99
148
  )
100
149
 
150
+ # Auto-save to storage
151
+ await self._save_to_storage()
152
+
101
153
  logger.info(f"Created community: {name} ({community.community_id})")
102
154
  return community.community_id
103
155
 
@@ -156,6 +208,12 @@ class CommunityManager:
156
208
  elif community_role == CommunityRole.COORDINATOR:
157
209
  community.coordinators.append(member.member_id)
158
210
 
211
+ # Auto-save to storage
212
+ await self._save_to_storage()
213
+
214
+ # Execute lifecycle hooks
215
+ await self._execute_hook("on_member_join", community_id, member.member_id, member)
216
+
159
217
  logger.info(f"Added member {agent_id} to community {community_id} as {community_role}")
160
218
  return member.member_id
161
219
 
@@ -209,6 +267,9 @@ class CommunityManager:
209
267
  community.shared_resources.append(resource.resource_id)
210
268
  community.resource_count += 1
211
269
 
270
+ # Auto-save to storage
271
+ await self._save_to_storage()
272
+
212
273
  logger.info(f"Created resource {name} in community {community_id}")
213
274
  return resource.resource_id
214
275
 
@@ -259,6 +320,9 @@ class CommunityManager:
259
320
  community = self.communities[community_id]
260
321
  community.decision_count += 1
261
322
 
323
+ # Auto-save to storage
324
+ await self._save_to_storage()
325
+
262
326
  logger.info(f"Proposed decision '{title}' in community {community_id}")
263
327
  return decision.decision_id
264
328
 
@@ -316,9 +380,200 @@ class CommunityManager:
316
380
  if decision.status == DecisionStatus.PROPOSED:
317
381
  decision.status = DecisionStatus.VOTING
318
382
 
383
+ # Auto-save to storage
384
+ await self._save_to_storage()
385
+
319
386
  logger.info(f"Member {member_id} voted '{vote}' on decision {decision_id}")
320
387
  return True
321
388
 
389
+ async def remove_member_from_community(
390
+ self,
391
+ community_id: str,
392
+ member_id: str,
393
+ transfer_resources: bool = True,
394
+ new_owner_id: Optional[str] = None
395
+ ) -> bool:
396
+ """
397
+ Remove a member from a community with graceful cleanup.
398
+
399
+ Args:
400
+ community_id: ID of the community
401
+ member_id: ID of the member to remove
402
+ transfer_resources: Whether to transfer member's resources
403
+ new_owner_id: Optional new owner for transferred resources
404
+
405
+ Returns:
406
+ True if member was removed successfully
407
+ """
408
+ if community_id not in self.communities:
409
+ raise TaskValidationError(f"Community not found: {community_id}")
410
+
411
+ if member_id not in self.members:
412
+ raise TaskValidationError(f"Member not found: {member_id}")
413
+
414
+ member = self.members[member_id]
415
+ community = self.communities[community_id]
416
+
417
+ # Transfer or orphan resources
418
+ if transfer_resources:
419
+ await self.transfer_member_resources(
420
+ member_id=member_id,
421
+ new_owner_id=new_owner_id or community.leaders[0] if community.leaders else None,
422
+ community_id=community_id
423
+ )
424
+
425
+ # Remove from community member list
426
+ if member_id in community.members:
427
+ community.members.remove(member_id)
428
+
429
+ # Remove from leadership positions
430
+ if member_id in community.leaders:
431
+ community.leaders.remove(member_id)
432
+
433
+ if member_id in community.coordinators:
434
+ community.coordinators.remove(member_id)
435
+
436
+ # Update relationships
437
+ if community_id in self.community_members:
438
+ self.community_members[community_id].discard(member_id)
439
+
440
+ if member.agent_id in self.member_communities:
441
+ self.member_communities[member.agent_id].discard(community_id)
442
+
443
+ # Mark member as inactive
444
+ member.is_active = False
445
+ member.last_active_at = datetime.utcnow()
446
+
447
+ # Auto-save to storage
448
+ await self._save_to_storage()
449
+
450
+ # Execute lifecycle hooks
451
+ await self._execute_hook("on_member_exit", community_id, member_id, member, reason="removed")
452
+
453
+ logger.info(f"Removed member {member_id} from community {community_id}")
454
+ return True
455
+
456
+ async def transfer_member_resources(
457
+ self,
458
+ member_id: str,
459
+ new_owner_id: Optional[str],
460
+ community_id: str
461
+ ) -> List[str]:
462
+ """
463
+ Transfer ownership of member's resources to another member.
464
+
465
+ Args:
466
+ member_id: ID of the member whose resources to transfer
467
+ new_owner_id: ID of the new owner (None = make resources orphaned/community-owned)
468
+ community_id: ID of the community
469
+
470
+ Returns:
471
+ List of transferred resource IDs
472
+ """
473
+ if member_id not in self.members:
474
+ raise TaskValidationError(f"Member not found: {member_id}")
475
+
476
+ transferred_resources = []
477
+
478
+ # Find all resources owned by this member
479
+ for resource_id, resource in self.resources.items():
480
+ if resource.owner_id == member_id:
481
+ if new_owner_id:
482
+ # Transfer to new owner
483
+ resource.owner_id = new_owner_id
484
+ resource.metadata["transferred_from"] = member_id
485
+ resource.metadata["transferred_at"] = datetime.utcnow().isoformat()
486
+ resource.updated_at = datetime.utcnow()
487
+ else:
488
+ # Make community-owned (orphaned)
489
+ resource.owner_id = "community"
490
+ resource.metadata["orphaned_from"] = member_id
491
+ resource.metadata["orphaned_at"] = datetime.utcnow().isoformat()
492
+ resource.access_level = "public" # Make public for community access
493
+
494
+ transferred_resources.append(resource_id)
495
+
496
+ # Auto-save to storage
497
+ if transferred_resources:
498
+ await self._save_to_storage()
499
+
500
+ logger.info(f"Transferred {len(transferred_resources)} resources from member {member_id}")
501
+ return transferred_resources
502
+
503
+ async def deactivate_member(
504
+ self,
505
+ member_id: str,
506
+ reason: Optional[str] = None
507
+ ) -> bool:
508
+ """
509
+ Soft deactivation of a member (doesn't remove, just marks inactive).
510
+
511
+ Args:
512
+ member_id: ID of the member to deactivate
513
+ reason: Optional reason for deactivation
514
+
515
+ Returns:
516
+ True if member was deactivated successfully
517
+ """
518
+ if member_id not in self.members:
519
+ raise TaskValidationError(f"Member not found: {member_id}")
520
+
521
+ member = self.members[member_id]
522
+ member.is_active = False
523
+ member.last_active_at = datetime.utcnow()
524
+ member.participation_level = "inactive"
525
+
526
+ if reason:
527
+ member.metadata["deactivation_reason"] = reason
528
+ member.metadata["deactivated_at"] = datetime.utcnow().isoformat()
529
+
530
+ # Auto-save to storage
531
+ await self._save_to_storage()
532
+
533
+ # Execute lifecycle hooks
534
+ await self._execute_hook("on_member_inactive", None, member_id, member, reason=reason)
535
+
536
+ logger.info(f"Deactivated member {member_id}")
537
+ return True
538
+
539
+ async def reactivate_member(
540
+ self,
541
+ member_id: str,
542
+ restore_roles: bool = True
543
+ ) -> bool:
544
+ """
545
+ Reactivate a previously deactivated member.
546
+
547
+ Args:
548
+ member_id: ID of the member to reactivate
549
+ restore_roles: Whether to restore previous roles
550
+
551
+ Returns:
552
+ True if member was reactivated successfully
553
+ """
554
+ if member_id not in self.members:
555
+ raise TaskValidationError(f"Member not found: {member_id}")
556
+
557
+ member = self.members[member_id]
558
+ member.is_active = True
559
+ member.last_active_at = datetime.utcnow()
560
+ member.participation_level = "active"
561
+
562
+ # Clear deactivation metadata
563
+ if "deactivation_reason" in member.metadata:
564
+ del member.metadata["deactivation_reason"]
565
+ if "deactivated_at" in member.metadata:
566
+ member.metadata["previous_deactivation"] = member.metadata["deactivated_at"]
567
+ del member.metadata["deactivated_at"]
568
+
569
+ member.metadata["reactivated_at"] = datetime.utcnow().isoformat()
570
+
571
+ # Auto-save to storage
572
+ await self._save_to_storage()
573
+
574
+ logger.info(f"Reactivated member {member_id}")
575
+ return True
576
+
322
577
  def _find_member_by_agent_id(self, community_id: str, agent_id: str) -> Optional[CommunityMember]:
323
578
  """Find a community member by agent ID."""
324
579
  if community_id not in self.community_members:
@@ -330,8 +585,229 @@ class CommunityManager:
330
585
  return member
331
586
 
332
587
  return None
588
+
589
+ def register_lifecycle_hook(self, hook: MemberLifecycleHooks) -> None:
590
+ """
591
+ Register a lifecycle hook handler.
592
+
593
+ Args:
594
+ hook: Hook handler implementing MemberLifecycleHooks protocol
595
+ """
596
+ self.lifecycle_hooks.append(hook)
597
+ logger.info(f"Registered lifecycle hook: {hook.__class__.__name__}")
598
+
599
+ def unregister_lifecycle_hook(self, hook: MemberLifecycleHooks) -> bool:
600
+ """
601
+ Unregister a lifecycle hook handler.
602
+
603
+ Args:
604
+ hook: Hook handler to remove
605
+
606
+ Returns:
607
+ True if hook was removed
608
+ """
609
+ if hook in self.lifecycle_hooks:
610
+ self.lifecycle_hooks.remove(hook)
611
+ logger.info(f"Unregistered lifecycle hook: {hook.__class__.__name__}")
612
+ return True
613
+ return False
614
+
615
+ async def _execute_hook(
616
+ self,
617
+ hook_name: str,
618
+ community_id: Optional[str],
619
+ member_id: str,
620
+ member: CommunityMember,
621
+ **kwargs
622
+ ) -> None:
623
+ """
624
+ Execute all registered hooks for a specific event.
625
+
626
+ Args:
627
+ hook_name: Name of the hook method to call
628
+ community_id: ID of the community (optional for some hooks)
629
+ member_id: ID of the member
630
+ member: Member object
631
+ **kwargs: Additional arguments to pass to the hook
632
+ """
633
+ for hook in self.lifecycle_hooks:
634
+ try:
635
+ hook_method = getattr(hook, hook_name, None)
636
+ if hook_method:
637
+ if community_id:
638
+ await hook_method(community_id, member_id, member, **kwargs)
639
+ else:
640
+ await hook_method(member_id, member, **kwargs)
641
+ except Exception as e:
642
+ logger.error(f"Error executing lifecycle hook {hook_name}: {e}")
333
643
 
334
644
  async def _load_from_storage(self) -> None:
335
- """Load communities and members from persistent storage."""
336
- # TODO: Implement loading from context_engine or database
337
- pass
645
+ """
646
+ Load communities and members from persistent storage.
647
+
648
+ Loads:
649
+ - Communities
650
+ - Members
651
+ - Resources
652
+ - Decisions
653
+ - Sessions
654
+ - Relationships
655
+ """
656
+ if not self.context_engine:
657
+ logger.warning("No context engine available for loading")
658
+ return
659
+
660
+ try:
661
+ # Load communities
662
+ communities_data = await self._load_data_by_key("communities")
663
+ if communities_data:
664
+ for community_id, community_dict in communities_data.items():
665
+ try:
666
+ community = AgentCommunity(**community_dict)
667
+ self.communities[community_id] = community
668
+ self.community_members[community_id] = set(community.members)
669
+ except Exception as e:
670
+ logger.error(f"Failed to load community {community_id}: {e}")
671
+
672
+ # Load members
673
+ members_data = await self._load_data_by_key("community_members")
674
+ if members_data:
675
+ for member_id, member_dict in members_data.items():
676
+ try:
677
+ member = CommunityMember(**member_dict)
678
+ self.members[member_id] = member
679
+ except Exception as e:
680
+ logger.error(f"Failed to load member {member_id}: {e}")
681
+
682
+ # Load resources
683
+ resources_data = await self._load_data_by_key("community_resources")
684
+ if resources_data:
685
+ for resource_id, resource_dict in resources_data.items():
686
+ try:
687
+ resource = CommunityResource(**resource_dict)
688
+ self.resources[resource_id] = resource
689
+ except Exception as e:
690
+ logger.error(f"Failed to load resource {resource_id}: {e}")
691
+
692
+ # Load decisions
693
+ decisions_data = await self._load_data_by_key("community_decisions")
694
+ if decisions_data:
695
+ for decision_id, decision_dict in decisions_data.items():
696
+ try:
697
+ decision = CommunityDecision(**decision_dict)
698
+ self.decisions[decision_id] = decision
699
+ except Exception as e:
700
+ logger.error(f"Failed to load decision {decision_id}: {e}")
701
+
702
+ # Load sessions
703
+ sessions_data = await self._load_data_by_key("community_sessions")
704
+ if sessions_data:
705
+ for session_id, session_dict in sessions_data.items():
706
+ try:
707
+ session = CollaborationSession(**session_dict)
708
+ self.sessions[session_id] = session
709
+ except Exception as e:
710
+ logger.error(f"Failed to load session {session_id}: {e}")
711
+
712
+ # Rebuild member_communities relationships
713
+ for member_id, member in self.members.items():
714
+ for community_id, community in self.communities.items():
715
+ if member_id in community.members:
716
+ if member.agent_id not in self.member_communities:
717
+ self.member_communities[member.agent_id] = set()
718
+ self.member_communities[member.agent_id].add(community_id)
719
+
720
+ logger.info(f"Loaded {len(self.communities)} communities, {len(self.members)} members from storage")
721
+
722
+ except Exception as e:
723
+ logger.error(f"Error loading from storage: {e}")
724
+
725
+ async def _load_data_by_key(self, key: str) -> Optional[Dict[str, Any]]:
726
+ """Load data from context engine by key."""
727
+ if not self.context_engine:
728
+ return None
729
+
730
+ try:
731
+ # Try to get data from context engine
732
+ # The exact method depends on the context engine implementation
733
+ if hasattr(self.context_engine, 'get_context'):
734
+ data = await self.context_engine.get_context(key)
735
+ return data
736
+ elif hasattr(self.context_engine, 'get'):
737
+ data = await self.context_engine.get(key)
738
+ return data
739
+ else:
740
+ logger.warning(f"Context engine does not support get operations")
741
+ return None
742
+ except Exception as e:
743
+ logger.debug(f"No data found for key {key}: {e}")
744
+ return None
745
+
746
+ async def _save_to_storage(self) -> None:
747
+ """
748
+ Save all communities and members to persistent storage.
749
+
750
+ Saves:
751
+ - Communities
752
+ - Members
753
+ - Resources
754
+ - Decisions
755
+ - Sessions
756
+ """
757
+ if not self.context_engine:
758
+ logger.debug("No context engine available for saving")
759
+ return
760
+
761
+ try:
762
+ # Save communities
763
+ communities_data = {
764
+ cid: community.model_dump() for cid, community in self.communities.items()
765
+ }
766
+ await self._save_data_by_key("communities", communities_data)
767
+
768
+ # Save members
769
+ members_data = {
770
+ mid: member.model_dump() for mid, member in self.members.items()
771
+ }
772
+ await self._save_data_by_key("community_members", members_data)
773
+
774
+ # Save resources
775
+ resources_data = {
776
+ rid: resource.model_dump() for rid, resource in self.resources.items()
777
+ }
778
+ await self._save_data_by_key("community_resources", resources_data)
779
+
780
+ # Save decisions
781
+ decisions_data = {
782
+ did: decision.model_dump() for did, decision in self.decisions.items()
783
+ }
784
+ await self._save_data_by_key("community_decisions", decisions_data)
785
+
786
+ # Save sessions
787
+ sessions_data = {
788
+ sid: session.model_dump() for sid, session in self.sessions.items()
789
+ }
790
+ await self._save_data_by_key("community_sessions", sessions_data)
791
+
792
+ logger.debug(f"Saved {len(self.communities)} communities, {len(self.members)} members to storage")
793
+
794
+ except Exception as e:
795
+ logger.error(f"Error saving to storage: {e}")
796
+
797
+ async def _save_data_by_key(self, key: str, data: Dict[str, Any]) -> None:
798
+ """Save data to context engine by key."""
799
+ if not self.context_engine:
800
+ return
801
+
802
+ try:
803
+ # The exact method depends on the context engine implementation
804
+ if hasattr(self.context_engine, 'set_context'):
805
+ await self.context_engine.set_context(key, data)
806
+ elif hasattr(self.context_engine, 'set'):
807
+ await self.context_engine.set(key, data)
808
+ elif hasattr(self.context_engine, 'save'):
809
+ await self.context_engine.save(key, data)
810
+ else:
811
+ logger.warning(f"Context engine does not support set/save operations")
812
+ except Exception as e:
813
+ logger.error(f"Failed to save data for key {key}: {e}")