aiecs 1.5.1__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 (302) hide show
  1. aiecs/__init__.py +72 -0
  2. aiecs/__main__.py +41 -0
  3. aiecs/aiecs_client.py +469 -0
  4. aiecs/application/__init__.py +10 -0
  5. aiecs/application/executors/__init__.py +10 -0
  6. aiecs/application/executors/operation_executor.py +363 -0
  7. aiecs/application/knowledge_graph/__init__.py +7 -0
  8. aiecs/application/knowledge_graph/builder/__init__.py +37 -0
  9. aiecs/application/knowledge_graph/builder/document_builder.py +375 -0
  10. aiecs/application/knowledge_graph/builder/graph_builder.py +356 -0
  11. aiecs/application/knowledge_graph/builder/schema_mapping.py +531 -0
  12. aiecs/application/knowledge_graph/builder/structured_pipeline.py +443 -0
  13. aiecs/application/knowledge_graph/builder/text_chunker.py +319 -0
  14. aiecs/application/knowledge_graph/extractors/__init__.py +27 -0
  15. aiecs/application/knowledge_graph/extractors/base.py +100 -0
  16. aiecs/application/knowledge_graph/extractors/llm_entity_extractor.py +327 -0
  17. aiecs/application/knowledge_graph/extractors/llm_relation_extractor.py +349 -0
  18. aiecs/application/knowledge_graph/extractors/ner_entity_extractor.py +244 -0
  19. aiecs/application/knowledge_graph/fusion/__init__.py +23 -0
  20. aiecs/application/knowledge_graph/fusion/entity_deduplicator.py +387 -0
  21. aiecs/application/knowledge_graph/fusion/entity_linker.py +343 -0
  22. aiecs/application/knowledge_graph/fusion/knowledge_fusion.py +580 -0
  23. aiecs/application/knowledge_graph/fusion/relation_deduplicator.py +189 -0
  24. aiecs/application/knowledge_graph/pattern_matching/__init__.py +21 -0
  25. aiecs/application/knowledge_graph/pattern_matching/pattern_matcher.py +344 -0
  26. aiecs/application/knowledge_graph/pattern_matching/query_executor.py +378 -0
  27. aiecs/application/knowledge_graph/profiling/__init__.py +12 -0
  28. aiecs/application/knowledge_graph/profiling/query_plan_visualizer.py +199 -0
  29. aiecs/application/knowledge_graph/profiling/query_profiler.py +223 -0
  30. aiecs/application/knowledge_graph/reasoning/__init__.py +27 -0
  31. aiecs/application/knowledge_graph/reasoning/evidence_synthesis.py +347 -0
  32. aiecs/application/knowledge_graph/reasoning/inference_engine.py +504 -0
  33. aiecs/application/knowledge_graph/reasoning/logic_form_parser.py +167 -0
  34. aiecs/application/knowledge_graph/reasoning/logic_parser/__init__.py +79 -0
  35. aiecs/application/knowledge_graph/reasoning/logic_parser/ast_builder.py +513 -0
  36. aiecs/application/knowledge_graph/reasoning/logic_parser/ast_nodes.py +630 -0
  37. aiecs/application/knowledge_graph/reasoning/logic_parser/ast_validator.py +654 -0
  38. aiecs/application/knowledge_graph/reasoning/logic_parser/error_handler.py +477 -0
  39. aiecs/application/knowledge_graph/reasoning/logic_parser/parser.py +390 -0
  40. aiecs/application/knowledge_graph/reasoning/logic_parser/query_context.py +217 -0
  41. aiecs/application/knowledge_graph/reasoning/logic_query_integration.py +169 -0
  42. aiecs/application/knowledge_graph/reasoning/query_planner.py +872 -0
  43. aiecs/application/knowledge_graph/reasoning/reasoning_engine.py +554 -0
  44. aiecs/application/knowledge_graph/retrieval/__init__.py +19 -0
  45. aiecs/application/knowledge_graph/retrieval/retrieval_strategies.py +596 -0
  46. aiecs/application/knowledge_graph/search/__init__.py +59 -0
  47. aiecs/application/knowledge_graph/search/hybrid_search.py +423 -0
  48. aiecs/application/knowledge_graph/search/reranker.py +295 -0
  49. aiecs/application/knowledge_graph/search/reranker_strategies.py +553 -0
  50. aiecs/application/knowledge_graph/search/text_similarity.py +398 -0
  51. aiecs/application/knowledge_graph/traversal/__init__.py +15 -0
  52. aiecs/application/knowledge_graph/traversal/enhanced_traversal.py +329 -0
  53. aiecs/application/knowledge_graph/traversal/path_scorer.py +269 -0
  54. aiecs/application/knowledge_graph/validators/__init__.py +13 -0
  55. aiecs/application/knowledge_graph/validators/relation_validator.py +189 -0
  56. aiecs/application/knowledge_graph/visualization/__init__.py +11 -0
  57. aiecs/application/knowledge_graph/visualization/graph_visualizer.py +321 -0
  58. aiecs/common/__init__.py +9 -0
  59. aiecs/common/knowledge_graph/__init__.py +17 -0
  60. aiecs/common/knowledge_graph/runnable.py +484 -0
  61. aiecs/config/__init__.py +16 -0
  62. aiecs/config/config.py +498 -0
  63. aiecs/config/graph_config.py +137 -0
  64. aiecs/config/registry.py +23 -0
  65. aiecs/core/__init__.py +46 -0
  66. aiecs/core/interface/__init__.py +34 -0
  67. aiecs/core/interface/execution_interface.py +152 -0
  68. aiecs/core/interface/storage_interface.py +171 -0
  69. aiecs/domain/__init__.py +289 -0
  70. aiecs/domain/agent/__init__.py +189 -0
  71. aiecs/domain/agent/base_agent.py +697 -0
  72. aiecs/domain/agent/exceptions.py +103 -0
  73. aiecs/domain/agent/graph_aware_mixin.py +559 -0
  74. aiecs/domain/agent/hybrid_agent.py +490 -0
  75. aiecs/domain/agent/integration/__init__.py +26 -0
  76. aiecs/domain/agent/integration/context_compressor.py +222 -0
  77. aiecs/domain/agent/integration/context_engine_adapter.py +252 -0
  78. aiecs/domain/agent/integration/retry_policy.py +219 -0
  79. aiecs/domain/agent/integration/role_config.py +213 -0
  80. aiecs/domain/agent/knowledge_aware_agent.py +646 -0
  81. aiecs/domain/agent/lifecycle.py +296 -0
  82. aiecs/domain/agent/llm_agent.py +300 -0
  83. aiecs/domain/agent/memory/__init__.py +12 -0
  84. aiecs/domain/agent/memory/conversation.py +197 -0
  85. aiecs/domain/agent/migration/__init__.py +14 -0
  86. aiecs/domain/agent/migration/conversion.py +160 -0
  87. aiecs/domain/agent/migration/legacy_wrapper.py +90 -0
  88. aiecs/domain/agent/models.py +317 -0
  89. aiecs/domain/agent/observability.py +407 -0
  90. aiecs/domain/agent/persistence.py +289 -0
  91. aiecs/domain/agent/prompts/__init__.py +29 -0
  92. aiecs/domain/agent/prompts/builder.py +161 -0
  93. aiecs/domain/agent/prompts/formatters.py +189 -0
  94. aiecs/domain/agent/prompts/template.py +255 -0
  95. aiecs/domain/agent/registry.py +260 -0
  96. aiecs/domain/agent/tool_agent.py +257 -0
  97. aiecs/domain/agent/tools/__init__.py +12 -0
  98. aiecs/domain/agent/tools/schema_generator.py +221 -0
  99. aiecs/domain/community/__init__.py +155 -0
  100. aiecs/domain/community/agent_adapter.py +477 -0
  101. aiecs/domain/community/analytics.py +481 -0
  102. aiecs/domain/community/collaborative_workflow.py +642 -0
  103. aiecs/domain/community/communication_hub.py +645 -0
  104. aiecs/domain/community/community_builder.py +320 -0
  105. aiecs/domain/community/community_integration.py +800 -0
  106. aiecs/domain/community/community_manager.py +813 -0
  107. aiecs/domain/community/decision_engine.py +879 -0
  108. aiecs/domain/community/exceptions.py +225 -0
  109. aiecs/domain/community/models/__init__.py +33 -0
  110. aiecs/domain/community/models/community_models.py +268 -0
  111. aiecs/domain/community/resource_manager.py +457 -0
  112. aiecs/domain/community/shared_context_manager.py +603 -0
  113. aiecs/domain/context/__init__.py +58 -0
  114. aiecs/domain/context/context_engine.py +989 -0
  115. aiecs/domain/context/conversation_models.py +354 -0
  116. aiecs/domain/context/graph_memory.py +467 -0
  117. aiecs/domain/execution/__init__.py +12 -0
  118. aiecs/domain/execution/model.py +57 -0
  119. aiecs/domain/knowledge_graph/__init__.py +19 -0
  120. aiecs/domain/knowledge_graph/models/__init__.py +52 -0
  121. aiecs/domain/knowledge_graph/models/entity.py +130 -0
  122. aiecs/domain/knowledge_graph/models/evidence.py +194 -0
  123. aiecs/domain/knowledge_graph/models/inference_rule.py +186 -0
  124. aiecs/domain/knowledge_graph/models/path.py +179 -0
  125. aiecs/domain/knowledge_graph/models/path_pattern.py +173 -0
  126. aiecs/domain/knowledge_graph/models/query.py +272 -0
  127. aiecs/domain/knowledge_graph/models/query_plan.py +187 -0
  128. aiecs/domain/knowledge_graph/models/relation.py +136 -0
  129. aiecs/domain/knowledge_graph/schema/__init__.py +23 -0
  130. aiecs/domain/knowledge_graph/schema/entity_type.py +135 -0
  131. aiecs/domain/knowledge_graph/schema/graph_schema.py +271 -0
  132. aiecs/domain/knowledge_graph/schema/property_schema.py +155 -0
  133. aiecs/domain/knowledge_graph/schema/relation_type.py +171 -0
  134. aiecs/domain/knowledge_graph/schema/schema_manager.py +496 -0
  135. aiecs/domain/knowledge_graph/schema/type_enums.py +205 -0
  136. aiecs/domain/task/__init__.py +13 -0
  137. aiecs/domain/task/dsl_processor.py +613 -0
  138. aiecs/domain/task/model.py +62 -0
  139. aiecs/domain/task/task_context.py +268 -0
  140. aiecs/infrastructure/__init__.py +24 -0
  141. aiecs/infrastructure/graph_storage/__init__.py +11 -0
  142. aiecs/infrastructure/graph_storage/base.py +601 -0
  143. aiecs/infrastructure/graph_storage/batch_operations.py +449 -0
  144. aiecs/infrastructure/graph_storage/cache.py +429 -0
  145. aiecs/infrastructure/graph_storage/distributed.py +226 -0
  146. aiecs/infrastructure/graph_storage/error_handling.py +390 -0
  147. aiecs/infrastructure/graph_storage/graceful_degradation.py +306 -0
  148. aiecs/infrastructure/graph_storage/health_checks.py +378 -0
  149. aiecs/infrastructure/graph_storage/in_memory.py +514 -0
  150. aiecs/infrastructure/graph_storage/index_optimization.py +483 -0
  151. aiecs/infrastructure/graph_storage/lazy_loading.py +410 -0
  152. aiecs/infrastructure/graph_storage/metrics.py +357 -0
  153. aiecs/infrastructure/graph_storage/migration.py +413 -0
  154. aiecs/infrastructure/graph_storage/pagination.py +471 -0
  155. aiecs/infrastructure/graph_storage/performance_monitoring.py +466 -0
  156. aiecs/infrastructure/graph_storage/postgres.py +871 -0
  157. aiecs/infrastructure/graph_storage/query_optimizer.py +635 -0
  158. aiecs/infrastructure/graph_storage/schema_cache.py +290 -0
  159. aiecs/infrastructure/graph_storage/sqlite.py +623 -0
  160. aiecs/infrastructure/graph_storage/streaming.py +495 -0
  161. aiecs/infrastructure/messaging/__init__.py +13 -0
  162. aiecs/infrastructure/messaging/celery_task_manager.py +383 -0
  163. aiecs/infrastructure/messaging/websocket_manager.py +298 -0
  164. aiecs/infrastructure/monitoring/__init__.py +34 -0
  165. aiecs/infrastructure/monitoring/executor_metrics.py +174 -0
  166. aiecs/infrastructure/monitoring/global_metrics_manager.py +213 -0
  167. aiecs/infrastructure/monitoring/structured_logger.py +48 -0
  168. aiecs/infrastructure/monitoring/tracing_manager.py +410 -0
  169. aiecs/infrastructure/persistence/__init__.py +24 -0
  170. aiecs/infrastructure/persistence/context_engine_client.py +187 -0
  171. aiecs/infrastructure/persistence/database_manager.py +333 -0
  172. aiecs/infrastructure/persistence/file_storage.py +754 -0
  173. aiecs/infrastructure/persistence/redis_client.py +220 -0
  174. aiecs/llm/__init__.py +86 -0
  175. aiecs/llm/callbacks/__init__.py +11 -0
  176. aiecs/llm/callbacks/custom_callbacks.py +264 -0
  177. aiecs/llm/client_factory.py +420 -0
  178. aiecs/llm/clients/__init__.py +33 -0
  179. aiecs/llm/clients/base_client.py +193 -0
  180. aiecs/llm/clients/googleai_client.py +181 -0
  181. aiecs/llm/clients/openai_client.py +131 -0
  182. aiecs/llm/clients/vertex_client.py +437 -0
  183. aiecs/llm/clients/xai_client.py +184 -0
  184. aiecs/llm/config/__init__.py +51 -0
  185. aiecs/llm/config/config_loader.py +275 -0
  186. aiecs/llm/config/config_validator.py +236 -0
  187. aiecs/llm/config/model_config.py +151 -0
  188. aiecs/llm/utils/__init__.py +10 -0
  189. aiecs/llm/utils/validate_config.py +91 -0
  190. aiecs/main.py +363 -0
  191. aiecs/scripts/__init__.py +3 -0
  192. aiecs/scripts/aid/VERSION_MANAGEMENT.md +97 -0
  193. aiecs/scripts/aid/__init__.py +19 -0
  194. aiecs/scripts/aid/version_manager.py +215 -0
  195. aiecs/scripts/dependance_check/DEPENDENCY_SYSTEM_SUMMARY.md +242 -0
  196. aiecs/scripts/dependance_check/README_DEPENDENCY_CHECKER.md +310 -0
  197. aiecs/scripts/dependance_check/__init__.py +17 -0
  198. aiecs/scripts/dependance_check/dependency_checker.py +938 -0
  199. aiecs/scripts/dependance_check/dependency_fixer.py +391 -0
  200. aiecs/scripts/dependance_check/download_nlp_data.py +396 -0
  201. aiecs/scripts/dependance_check/quick_dependency_check.py +270 -0
  202. aiecs/scripts/dependance_check/setup_nlp_data.sh +217 -0
  203. aiecs/scripts/dependance_patch/__init__.py +7 -0
  204. aiecs/scripts/dependance_patch/fix_weasel/README_WEASEL_PATCH.md +126 -0
  205. aiecs/scripts/dependance_patch/fix_weasel/__init__.py +11 -0
  206. aiecs/scripts/dependance_patch/fix_weasel/fix_weasel_validator.py +128 -0
  207. aiecs/scripts/dependance_patch/fix_weasel/fix_weasel_validator.sh +82 -0
  208. aiecs/scripts/dependance_patch/fix_weasel/patch_weasel_library.sh +188 -0
  209. aiecs/scripts/dependance_patch/fix_weasel/run_weasel_patch.sh +41 -0
  210. aiecs/scripts/tools_develop/README.md +449 -0
  211. aiecs/scripts/tools_develop/TOOL_AUTO_DISCOVERY.md +234 -0
  212. aiecs/scripts/tools_develop/__init__.py +21 -0
  213. aiecs/scripts/tools_develop/check_type_annotations.py +259 -0
  214. aiecs/scripts/tools_develop/validate_tool_schemas.py +422 -0
  215. aiecs/scripts/tools_develop/verify_tools.py +356 -0
  216. aiecs/tasks/__init__.py +1 -0
  217. aiecs/tasks/worker.py +172 -0
  218. aiecs/tools/__init__.py +299 -0
  219. aiecs/tools/apisource/__init__.py +99 -0
  220. aiecs/tools/apisource/intelligence/__init__.py +19 -0
  221. aiecs/tools/apisource/intelligence/data_fusion.py +381 -0
  222. aiecs/tools/apisource/intelligence/query_analyzer.py +413 -0
  223. aiecs/tools/apisource/intelligence/search_enhancer.py +388 -0
  224. aiecs/tools/apisource/monitoring/__init__.py +9 -0
  225. aiecs/tools/apisource/monitoring/metrics.py +303 -0
  226. aiecs/tools/apisource/providers/__init__.py +115 -0
  227. aiecs/tools/apisource/providers/base.py +664 -0
  228. aiecs/tools/apisource/providers/census.py +401 -0
  229. aiecs/tools/apisource/providers/fred.py +564 -0
  230. aiecs/tools/apisource/providers/newsapi.py +412 -0
  231. aiecs/tools/apisource/providers/worldbank.py +357 -0
  232. aiecs/tools/apisource/reliability/__init__.py +12 -0
  233. aiecs/tools/apisource/reliability/error_handler.py +375 -0
  234. aiecs/tools/apisource/reliability/fallback_strategy.py +391 -0
  235. aiecs/tools/apisource/tool.py +850 -0
  236. aiecs/tools/apisource/utils/__init__.py +9 -0
  237. aiecs/tools/apisource/utils/validators.py +338 -0
  238. aiecs/tools/base_tool.py +201 -0
  239. aiecs/tools/docs/__init__.py +121 -0
  240. aiecs/tools/docs/ai_document_orchestrator.py +599 -0
  241. aiecs/tools/docs/ai_document_writer_orchestrator.py +2403 -0
  242. aiecs/tools/docs/content_insertion_tool.py +1333 -0
  243. aiecs/tools/docs/document_creator_tool.py +1317 -0
  244. aiecs/tools/docs/document_layout_tool.py +1166 -0
  245. aiecs/tools/docs/document_parser_tool.py +994 -0
  246. aiecs/tools/docs/document_writer_tool.py +1818 -0
  247. aiecs/tools/knowledge_graph/__init__.py +17 -0
  248. aiecs/tools/knowledge_graph/graph_reasoning_tool.py +734 -0
  249. aiecs/tools/knowledge_graph/graph_search_tool.py +923 -0
  250. aiecs/tools/knowledge_graph/kg_builder_tool.py +476 -0
  251. aiecs/tools/langchain_adapter.py +542 -0
  252. aiecs/tools/schema_generator.py +275 -0
  253. aiecs/tools/search_tool/__init__.py +100 -0
  254. aiecs/tools/search_tool/analyzers.py +589 -0
  255. aiecs/tools/search_tool/cache.py +260 -0
  256. aiecs/tools/search_tool/constants.py +128 -0
  257. aiecs/tools/search_tool/context.py +216 -0
  258. aiecs/tools/search_tool/core.py +749 -0
  259. aiecs/tools/search_tool/deduplicator.py +123 -0
  260. aiecs/tools/search_tool/error_handler.py +271 -0
  261. aiecs/tools/search_tool/metrics.py +371 -0
  262. aiecs/tools/search_tool/rate_limiter.py +178 -0
  263. aiecs/tools/search_tool/schemas.py +277 -0
  264. aiecs/tools/statistics/__init__.py +80 -0
  265. aiecs/tools/statistics/ai_data_analysis_orchestrator.py +643 -0
  266. aiecs/tools/statistics/ai_insight_generator_tool.py +505 -0
  267. aiecs/tools/statistics/ai_report_orchestrator_tool.py +694 -0
  268. aiecs/tools/statistics/data_loader_tool.py +564 -0
  269. aiecs/tools/statistics/data_profiler_tool.py +658 -0
  270. aiecs/tools/statistics/data_transformer_tool.py +573 -0
  271. aiecs/tools/statistics/data_visualizer_tool.py +495 -0
  272. aiecs/tools/statistics/model_trainer_tool.py +487 -0
  273. aiecs/tools/statistics/statistical_analyzer_tool.py +459 -0
  274. aiecs/tools/task_tools/__init__.py +86 -0
  275. aiecs/tools/task_tools/chart_tool.py +732 -0
  276. aiecs/tools/task_tools/classfire_tool.py +922 -0
  277. aiecs/tools/task_tools/image_tool.py +447 -0
  278. aiecs/tools/task_tools/office_tool.py +684 -0
  279. aiecs/tools/task_tools/pandas_tool.py +635 -0
  280. aiecs/tools/task_tools/report_tool.py +635 -0
  281. aiecs/tools/task_tools/research_tool.py +392 -0
  282. aiecs/tools/task_tools/scraper_tool.py +715 -0
  283. aiecs/tools/task_tools/stats_tool.py +688 -0
  284. aiecs/tools/temp_file_manager.py +130 -0
  285. aiecs/tools/tool_executor/__init__.py +37 -0
  286. aiecs/tools/tool_executor/tool_executor.py +881 -0
  287. aiecs/utils/LLM_output_structor.py +445 -0
  288. aiecs/utils/__init__.py +34 -0
  289. aiecs/utils/base_callback.py +47 -0
  290. aiecs/utils/cache_provider.py +695 -0
  291. aiecs/utils/execution_utils.py +184 -0
  292. aiecs/utils/logging.py +1 -0
  293. aiecs/utils/prompt_loader.py +14 -0
  294. aiecs/utils/token_usage_repository.py +323 -0
  295. aiecs/ws/__init__.py +0 -0
  296. aiecs/ws/socket_server.py +52 -0
  297. aiecs-1.5.1.dist-info/METADATA +608 -0
  298. aiecs-1.5.1.dist-info/RECORD +302 -0
  299. aiecs-1.5.1.dist-info/WHEEL +5 -0
  300. aiecs-1.5.1.dist-info/entry_points.txt +10 -0
  301. aiecs-1.5.1.dist-info/licenses/LICENSE +225 -0
  302. aiecs-1.5.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,813 @@
1
+ """
2
+ Community Manager
3
+
4
+ Core component for managing agent communities, including governance,
5
+ resource sharing, and collaborative decision-making.
6
+ """
7
+
8
+ import logging
9
+ from datetime import datetime, timedelta
10
+ from typing import Dict, List, Any, Optional, Set, Protocol
11
+ import uuid
12
+
13
+ from .models.community_models import (
14
+ AgentCommunity,
15
+ CommunityMember,
16
+ CommunityResource,
17
+ CommunityDecision,
18
+ CollaborationSession,
19
+ CommunityRole,
20
+ GovernanceType,
21
+ DecisionStatus,
22
+ ResourceType,
23
+ )
24
+ from .exceptions import CommunityValidationError as TaskValidationError
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ class MemberLifecycleHooks(Protocol):
30
+ """
31
+ Protocol defining lifecycle hooks for community member events.
32
+ Implement this protocol to receive notifications about member lifecycle events.
33
+ """
34
+
35
+ async def on_member_join(
36
+ self, community_id: str, member_id: str, member: CommunityMember
37
+ ) -> None:
38
+ """Called when a member joins a community."""
39
+ ...
40
+
41
+ async def on_member_exit(
42
+ self,
43
+ community_id: str,
44
+ member_id: str,
45
+ member: CommunityMember,
46
+ reason: Optional[str] = None,
47
+ ) -> None:
48
+ """Called when a member exits/is removed from a community."""
49
+ ...
50
+
51
+ async def on_member_update(
52
+ self,
53
+ community_id: str,
54
+ member_id: str,
55
+ member: CommunityMember,
56
+ changes: Dict[str, Any],
57
+ ) -> None:
58
+ """Called when a member's properties are updated."""
59
+ ...
60
+
61
+ async def on_member_inactive(
62
+ self,
63
+ community_id: str,
64
+ member_id: str,
65
+ member: CommunityMember,
66
+ reason: Optional[str] = None,
67
+ ) -> None:
68
+ """Called when a member becomes inactive."""
69
+ ...
70
+
71
+
72
+ class CommunityManager:
73
+ """
74
+ Manager for agent communities, handling governance, collaboration, and resource sharing.
75
+ """
76
+
77
+ def __init__(self, context_engine=None):
78
+ """
79
+ Initialize the community manager.
80
+
81
+ Args:
82
+ context_engine: Optional context engine for persistent storage
83
+ """
84
+ self.context_engine = context_engine
85
+
86
+ # In-memory storage (should be replaced with persistent storage)
87
+ self.communities: Dict[str, AgentCommunity] = {}
88
+ self.members: Dict[str, CommunityMember] = {}
89
+ self.resources: Dict[str, CommunityResource] = {}
90
+ self.decisions: Dict[str, CommunityDecision] = {}
91
+ self.sessions: Dict[str, CollaborationSession] = {}
92
+
93
+ # Community relationships
94
+ # member_id -> set of community_ids
95
+ self.member_communities: Dict[str, Set[str]] = {}
96
+ # community_id -> set of member_ids
97
+ self.community_members: Dict[str, Set[str]] = {}
98
+
99
+ # Lifecycle hooks
100
+ self.lifecycle_hooks: List[MemberLifecycleHooks] = []
101
+
102
+ self._initialized = False
103
+ logger.info("Community manager initialized")
104
+
105
+ async def initialize(self) -> None:
106
+ """Initialize the community manager."""
107
+ if self._initialized:
108
+ return
109
+
110
+ # Load existing communities and members from persistent storage if
111
+ # available
112
+ if self.context_engine:
113
+ await self._load_from_storage()
114
+
115
+ self._initialized = True
116
+ logger.info("Community manager initialization completed")
117
+
118
+ async def create_community(
119
+ self,
120
+ name: str,
121
+ description: Optional[str] = None,
122
+ governance_type: GovernanceType = GovernanceType.DEMOCRATIC,
123
+ governance_rules: Optional[Dict[str, Any]] = None,
124
+ creator_agent_id: Optional[str] = None,
125
+ ) -> str:
126
+ """
127
+ Create a new agent community.
128
+
129
+ Args:
130
+ name: Name of the community
131
+ description: Optional description
132
+ governance_type: Type of governance
133
+ governance_rules: Governance rules and policies
134
+ creator_agent_id: ID of the agent creating the community
135
+
136
+ Returns:
137
+ Community ID
138
+ """
139
+ community = AgentCommunity(
140
+ name=name,
141
+ description=description,
142
+ governance_type=governance_type,
143
+ governance_rules=governance_rules or {},
144
+ )
145
+
146
+ self.communities[community.community_id] = community
147
+ self.community_members[community.community_id] = set()
148
+
149
+ # Add creator as the first leader if provided
150
+ if creator_agent_id:
151
+ await self.add_member_to_community(
152
+ community.community_id,
153
+ creator_agent_id,
154
+ community_role=CommunityRole.LEADER,
155
+ )
156
+
157
+ # Auto-save to storage
158
+ await self._save_to_storage()
159
+
160
+ logger.info(f"Created community: {name} ({community.community_id})")
161
+ return community.community_id
162
+
163
+ async def add_member_to_community(
164
+ self,
165
+ community_id: str,
166
+ agent_id: str,
167
+ agent_role: str = "general",
168
+ community_role: CommunityRole = CommunityRole.CONTRIBUTOR,
169
+ specializations: Optional[List[str]] = None,
170
+ ) -> str:
171
+ """
172
+ Add a member to a community.
173
+
174
+ Args:
175
+ community_id: ID of the community
176
+ agent_id: ID of the agent to add
177
+ agent_role: Functional role of the agent
178
+ community_role: Role within the community
179
+ specializations: Areas of specialization
180
+
181
+ Returns:
182
+ Member ID
183
+ """
184
+ if community_id not in self.communities:
185
+ raise TaskValidationError(f"Community not found: {community_id}")
186
+
187
+ # Check if agent is already a member
188
+ existing_member = self._find_member_by_agent_id(community_id, agent_id)
189
+ if existing_member:
190
+ logger.warning(f"Agent {agent_id} is already a member of community {community_id}")
191
+ return existing_member.member_id
192
+
193
+ member = CommunityMember(
194
+ member_id=str(uuid.uuid4()),
195
+ agent_id=agent_id,
196
+ agent_role=agent_role,
197
+ community_role=community_role,
198
+ specializations=specializations or [],
199
+ )
200
+
201
+ self.members[member.member_id] = member
202
+
203
+ # Update relationships
204
+ if agent_id not in self.member_communities:
205
+ self.member_communities[agent_id] = set()
206
+ self.member_communities[agent_id].add(community_id)
207
+ self.community_members[community_id].add(member.member_id)
208
+
209
+ # Update community
210
+ community = self.communities[community_id]
211
+ community.members.append(member.member_id)
212
+
213
+ if community_role == CommunityRole.LEADER:
214
+ community.leaders.append(member.member_id)
215
+ elif community_role == CommunityRole.COORDINATOR:
216
+ community.coordinators.append(member.member_id)
217
+
218
+ # Auto-save to storage
219
+ await self._save_to_storage()
220
+
221
+ # Execute lifecycle hooks
222
+ await self._execute_hook("on_member_join", community_id, member.member_id, member)
223
+
224
+ logger.info(f"Added member {agent_id} to community {community_id} as {community_role}")
225
+ return member.member_id
226
+
227
+ async def create_community_resource(
228
+ self,
229
+ community_id: str,
230
+ owner_member_id: str,
231
+ name: str,
232
+ resource_type: ResourceType,
233
+ content: Dict[str, Any],
234
+ description: Optional[str] = None,
235
+ access_level: str = "public",
236
+ tags: Optional[List[str]] = None,
237
+ ) -> str:
238
+ """
239
+ Create a shared community resource.
240
+
241
+ Args:
242
+ community_id: ID of the community
243
+ owner_member_id: ID of the member creating the resource
244
+ name: Name of the resource
245
+ resource_type: Type of resource
246
+ content: Resource content/data
247
+ description: Optional description
248
+ access_level: Access level (public, restricted, private)
249
+ tags: Tags for categorization
250
+
251
+ Returns:
252
+ Resource ID
253
+ """
254
+ if community_id not in self.communities:
255
+ raise TaskValidationError(f"Community not found: {community_id}")
256
+
257
+ if owner_member_id not in self.members:
258
+ raise TaskValidationError(f"Member not found: {owner_member_id}")
259
+
260
+ resource = CommunityResource(
261
+ name=name,
262
+ resource_type=resource_type,
263
+ description=description,
264
+ owner_id=owner_member_id,
265
+ access_level=access_level,
266
+ content=content,
267
+ tags=tags or [],
268
+ )
269
+
270
+ self.resources[resource.resource_id] = resource
271
+
272
+ # Update community
273
+ community = self.communities[community_id]
274
+ community.shared_resources.append(resource.resource_id)
275
+ community.resource_count += 1
276
+
277
+ # Auto-save to storage
278
+ await self._save_to_storage()
279
+
280
+ logger.info(f"Created resource {name} in community {community_id}")
281
+ return resource.resource_id
282
+
283
+ async def propose_decision(
284
+ self,
285
+ community_id: str,
286
+ proposer_member_id: str,
287
+ title: str,
288
+ description: str,
289
+ decision_type: str,
290
+ implementation_plan: Optional[str] = None,
291
+ deadline: Optional[datetime] = None,
292
+ ) -> str:
293
+ """
294
+ Propose a decision for community consideration.
295
+
296
+ Args:
297
+ community_id: ID of the community
298
+ proposer_member_id: ID of the member proposing
299
+ title: Title of the proposal
300
+ description: Detailed description
301
+ decision_type: Type of decision
302
+ implementation_plan: Plan for implementation
303
+ deadline: Implementation deadline
304
+
305
+ Returns:
306
+ Decision ID
307
+ """
308
+ if community_id not in self.communities:
309
+ raise TaskValidationError(f"Community not found: {community_id}")
310
+
311
+ if proposer_member_id not in self.members:
312
+ raise TaskValidationError(f"Member not found: {proposer_member_id}")
313
+
314
+ decision = CommunityDecision(
315
+ title=title,
316
+ description=description,
317
+ proposer_id=proposer_member_id,
318
+ decision_type=decision_type,
319
+ implementation_plan=implementation_plan,
320
+ deadline=deadline,
321
+ voting_ends_at=datetime.utcnow() + timedelta(days=3), # Default 3-day voting period
322
+ )
323
+
324
+ self.decisions[decision.decision_id] = decision
325
+
326
+ # Update community
327
+ community = self.communities[community_id]
328
+ community.decision_count += 1
329
+
330
+ # Auto-save to storage
331
+ await self._save_to_storage()
332
+
333
+ logger.info(f"Proposed decision '{title}' in community {community_id}")
334
+ return decision.decision_id
335
+
336
+ async def vote_on_decision(
337
+ self,
338
+ decision_id: str,
339
+ member_id: str,
340
+ vote: str, # "for", "against", "abstain"
341
+ ) -> bool:
342
+ """
343
+ Cast a vote on a community decision.
344
+
345
+ Args:
346
+ decision_id: ID of the decision
347
+ member_id: ID of the voting member
348
+ vote: Vote choice ("for", "against", "abstain")
349
+
350
+ Returns:
351
+ True if vote was cast successfully
352
+ """
353
+ if decision_id not in self.decisions:
354
+ raise TaskValidationError(f"Decision not found: {decision_id}")
355
+
356
+ if member_id not in self.members:
357
+ raise TaskValidationError(f"Member not found: {member_id}")
358
+
359
+ decision = self.decisions[decision_id]
360
+
361
+ # Check if voting is still open
362
+ if decision.status != DecisionStatus.VOTING and decision.status != DecisionStatus.PROPOSED:
363
+ raise TaskValidationError(f"Voting is closed for decision {decision_id}")
364
+
365
+ if decision.voting_ends_at and datetime.utcnow() > decision.voting_ends_at:
366
+ raise TaskValidationError(f"Voting period has ended for decision {decision_id}")
367
+
368
+ # Remove previous vote if exists
369
+ if member_id in decision.votes_for:
370
+ decision.votes_for.remove(member_id)
371
+ if member_id in decision.votes_against:
372
+ decision.votes_against.remove(member_id)
373
+ if member_id in decision.abstentions:
374
+ decision.abstentions.remove(member_id)
375
+
376
+ # Cast new vote
377
+ if vote.lower() == "for":
378
+ decision.votes_for.append(member_id)
379
+ elif vote.lower() == "against":
380
+ decision.votes_against.append(member_id)
381
+ elif vote.lower() == "abstain":
382
+ decision.abstentions.append(member_id)
383
+ else:
384
+ raise TaskValidationError(f"Invalid vote choice: {vote}")
385
+
386
+ # Update decision status
387
+ if decision.status == DecisionStatus.PROPOSED:
388
+ decision.status = DecisionStatus.VOTING
389
+
390
+ # Auto-save to storage
391
+ await self._save_to_storage()
392
+
393
+ logger.info(f"Member {member_id} voted '{vote}' on decision {decision_id}")
394
+ return True
395
+
396
+ async def remove_member_from_community(
397
+ self,
398
+ community_id: str,
399
+ member_id: str,
400
+ transfer_resources: bool = True,
401
+ new_owner_id: Optional[str] = None,
402
+ ) -> bool:
403
+ """
404
+ Remove a member from a community with graceful cleanup.
405
+
406
+ Args:
407
+ community_id: ID of the community
408
+ member_id: ID of the member to remove
409
+ transfer_resources: Whether to transfer member's resources
410
+ new_owner_id: Optional new owner for transferred resources
411
+
412
+ Returns:
413
+ True if member was removed successfully
414
+ """
415
+ if community_id not in self.communities:
416
+ raise TaskValidationError(f"Community not found: {community_id}")
417
+
418
+ if member_id not in self.members:
419
+ raise TaskValidationError(f"Member not found: {member_id}")
420
+
421
+ member = self.members[member_id]
422
+ community = self.communities[community_id]
423
+
424
+ # Transfer or orphan resources
425
+ if transfer_resources:
426
+ await self.transfer_member_resources(
427
+ member_id=member_id,
428
+ new_owner_id=(new_owner_id or community.leaders[0] if community.leaders else None),
429
+ community_id=community_id,
430
+ )
431
+
432
+ # Remove from community member list
433
+ if member_id in community.members:
434
+ community.members.remove(member_id)
435
+
436
+ # Remove from leadership positions
437
+ if member_id in community.leaders:
438
+ community.leaders.remove(member_id)
439
+
440
+ if member_id in community.coordinators:
441
+ community.coordinators.remove(member_id)
442
+
443
+ # Update relationships
444
+ if community_id in self.community_members:
445
+ self.community_members[community_id].discard(member_id)
446
+
447
+ if member.agent_id in self.member_communities:
448
+ self.member_communities[member.agent_id].discard(community_id)
449
+
450
+ # Mark member as inactive
451
+ member.is_active = False
452
+ member.last_active_at = datetime.utcnow()
453
+
454
+ # Auto-save to storage
455
+ await self._save_to_storage()
456
+
457
+ # Execute lifecycle hooks
458
+ await self._execute_hook(
459
+ "on_member_exit", community_id, member_id, member, reason="removed"
460
+ )
461
+
462
+ logger.info(f"Removed member {member_id} from community {community_id}")
463
+ return True
464
+
465
+ async def transfer_member_resources(
466
+ self, member_id: str, new_owner_id: Optional[str], community_id: str
467
+ ) -> List[str]:
468
+ """
469
+ Transfer ownership of member's resources to another member.
470
+
471
+ Args:
472
+ member_id: ID of the member whose resources to transfer
473
+ new_owner_id: ID of the new owner (None = make resources orphaned/community-owned)
474
+ community_id: ID of the community
475
+
476
+ Returns:
477
+ List of transferred resource IDs
478
+ """
479
+ if member_id not in self.members:
480
+ raise TaskValidationError(f"Member not found: {member_id}")
481
+
482
+ transferred_resources = []
483
+
484
+ # Find all resources owned by this member
485
+ for resource_id, resource in self.resources.items():
486
+ if resource.owner_id == member_id:
487
+ if new_owner_id:
488
+ # Transfer to new owner
489
+ resource.owner_id = new_owner_id
490
+ resource.metadata["transferred_from"] = member_id
491
+ resource.metadata["transferred_at"] = datetime.utcnow().isoformat()
492
+ resource.updated_at = datetime.utcnow()
493
+ else:
494
+ # Make community-owned (orphaned)
495
+ resource.owner_id = "community"
496
+ resource.metadata["orphaned_from"] = member_id
497
+ resource.metadata["orphaned_at"] = datetime.utcnow().isoformat()
498
+ resource.access_level = "public" # Make public for community access
499
+
500
+ transferred_resources.append(resource_id)
501
+
502
+ # Auto-save to storage
503
+ if transferred_resources:
504
+ await self._save_to_storage()
505
+
506
+ logger.info(f"Transferred {len(transferred_resources)} resources from member {member_id}")
507
+ return transferred_resources
508
+
509
+ async def deactivate_member(self, member_id: str, reason: Optional[str] = None) -> bool:
510
+ """
511
+ Soft deactivation of a member (doesn't remove, just marks inactive).
512
+
513
+ Args:
514
+ member_id: ID of the member to deactivate
515
+ reason: Optional reason for deactivation
516
+
517
+ Returns:
518
+ True if member was deactivated successfully
519
+ """
520
+ if member_id not in self.members:
521
+ raise TaskValidationError(f"Member not found: {member_id}")
522
+
523
+ member = self.members[member_id]
524
+ member.is_active = False
525
+ member.last_active_at = datetime.utcnow()
526
+ member.participation_level = "inactive"
527
+
528
+ if reason:
529
+ member.metadata["deactivation_reason"] = reason
530
+ member.metadata["deactivated_at"] = datetime.utcnow().isoformat()
531
+
532
+ # Auto-save to storage
533
+ await self._save_to_storage()
534
+
535
+ # Execute lifecycle hooks
536
+ await self._execute_hook("on_member_inactive", None, member_id, member, reason=reason)
537
+
538
+ logger.info(f"Deactivated member {member_id}")
539
+ return True
540
+
541
+ async def reactivate_member(self, member_id: str, restore_roles: bool = True) -> bool:
542
+ """
543
+ Reactivate a previously deactivated member.
544
+
545
+ Args:
546
+ member_id: ID of the member to reactivate
547
+ restore_roles: Whether to restore previous roles
548
+
549
+ Returns:
550
+ True if member was reactivated successfully
551
+ """
552
+ if member_id not in self.members:
553
+ raise TaskValidationError(f"Member not found: {member_id}")
554
+
555
+ member = self.members[member_id]
556
+ member.is_active = True
557
+ member.last_active_at = datetime.utcnow()
558
+ member.participation_level = "active"
559
+
560
+ # Clear deactivation metadata
561
+ if "deactivation_reason" in member.metadata:
562
+ del member.metadata["deactivation_reason"]
563
+ if "deactivated_at" in member.metadata:
564
+ member.metadata["previous_deactivation"] = member.metadata["deactivated_at"]
565
+ del member.metadata["deactivated_at"]
566
+
567
+ member.metadata["reactivated_at"] = datetime.utcnow().isoformat()
568
+
569
+ # Auto-save to storage
570
+ await self._save_to_storage()
571
+
572
+ logger.info(f"Reactivated member {member_id}")
573
+ return True
574
+
575
+ def _find_member_by_agent_id(
576
+ self, community_id: str, agent_id: str
577
+ ) -> Optional[CommunityMember]:
578
+ """Find a community member by agent ID."""
579
+ if community_id not in self.community_members:
580
+ return None
581
+
582
+ for member_id in self.community_members[community_id]:
583
+ member = self.members.get(member_id)
584
+ if member and member.agent_id == agent_id:
585
+ return member
586
+
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}")
643
+
644
+ async def _load_from_storage(self) -> None:
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(
721
+ f"Loaded {len(self.communities)} communities, {len(self.members)} members from storage"
722
+ )
723
+
724
+ except Exception as e:
725
+ logger.error(f"Error loading from storage: {e}")
726
+
727
+ async def _load_data_by_key(self, key: str) -> Optional[Dict[str, Any]]:
728
+ """Load data from context engine by key."""
729
+ if not self.context_engine:
730
+ return None
731
+
732
+ try:
733
+ # Try to get data from context engine
734
+ # The exact method depends on the context engine implementation
735
+ if hasattr(self.context_engine, "get_context"):
736
+ data = await self.context_engine.get_context(key)
737
+ return data
738
+ elif hasattr(self.context_engine, "get"):
739
+ data = await self.context_engine.get(key)
740
+ return data
741
+ else:
742
+ logger.warning("Context engine does not support get operations")
743
+ return None
744
+ except Exception as e:
745
+ logger.debug(f"No data found for key {key}: {e}")
746
+ return None
747
+
748
+ async def _save_to_storage(self) -> None:
749
+ """
750
+ Save all communities and members to persistent storage.
751
+
752
+ Saves:
753
+ - Communities
754
+ - Members
755
+ - Resources
756
+ - Decisions
757
+ - Sessions
758
+ """
759
+ if not self.context_engine:
760
+ logger.debug("No context engine available for saving")
761
+ return
762
+
763
+ try:
764
+ # Save communities
765
+ communities_data = {
766
+ cid: community.model_dump() for cid, community in self.communities.items()
767
+ }
768
+ await self._save_data_by_key("communities", communities_data)
769
+
770
+ # Save members
771
+ members_data = {mid: member.model_dump() for mid, member in self.members.items()}
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 = {sid: session.model_dump() for sid, session in self.sessions.items()}
788
+ await self._save_data_by_key("community_sessions", sessions_data)
789
+
790
+ logger.debug(
791
+ f"Saved {len(self.communities)} communities, {len(self.members)} members to storage"
792
+ )
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("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}")