aiecs 1.0.1__py3-none-any.whl → 1.7.6__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.
- aiecs/__init__.py +13 -16
- aiecs/__main__.py +7 -7
- aiecs/aiecs_client.py +269 -75
- aiecs/application/executors/operation_executor.py +79 -54
- aiecs/application/knowledge_graph/__init__.py +7 -0
- aiecs/application/knowledge_graph/builder/__init__.py +37 -0
- aiecs/application/knowledge_graph/builder/data_quality.py +302 -0
- aiecs/application/knowledge_graph/builder/data_reshaping.py +293 -0
- aiecs/application/knowledge_graph/builder/document_builder.py +369 -0
- aiecs/application/knowledge_graph/builder/graph_builder.py +490 -0
- aiecs/application/knowledge_graph/builder/import_optimizer.py +396 -0
- aiecs/application/knowledge_graph/builder/schema_inference.py +462 -0
- aiecs/application/knowledge_graph/builder/schema_mapping.py +563 -0
- aiecs/application/knowledge_graph/builder/structured_pipeline.py +1384 -0
- aiecs/application/knowledge_graph/builder/text_chunker.py +317 -0
- aiecs/application/knowledge_graph/extractors/__init__.py +27 -0
- aiecs/application/knowledge_graph/extractors/base.py +98 -0
- aiecs/application/knowledge_graph/extractors/llm_entity_extractor.py +422 -0
- aiecs/application/knowledge_graph/extractors/llm_relation_extractor.py +347 -0
- aiecs/application/knowledge_graph/extractors/ner_entity_extractor.py +241 -0
- aiecs/application/knowledge_graph/fusion/__init__.py +78 -0
- aiecs/application/knowledge_graph/fusion/ab_testing.py +395 -0
- aiecs/application/knowledge_graph/fusion/abbreviation_expander.py +327 -0
- aiecs/application/knowledge_graph/fusion/alias_index.py +597 -0
- aiecs/application/knowledge_graph/fusion/alias_matcher.py +384 -0
- aiecs/application/knowledge_graph/fusion/cache_coordinator.py +343 -0
- aiecs/application/knowledge_graph/fusion/entity_deduplicator.py +433 -0
- aiecs/application/knowledge_graph/fusion/entity_linker.py +511 -0
- aiecs/application/knowledge_graph/fusion/evaluation_dataset.py +240 -0
- aiecs/application/knowledge_graph/fusion/knowledge_fusion.py +632 -0
- aiecs/application/knowledge_graph/fusion/matching_config.py +489 -0
- aiecs/application/knowledge_graph/fusion/name_normalizer.py +352 -0
- aiecs/application/knowledge_graph/fusion/relation_deduplicator.py +183 -0
- aiecs/application/knowledge_graph/fusion/semantic_name_matcher.py +464 -0
- aiecs/application/knowledge_graph/fusion/similarity_pipeline.py +534 -0
- aiecs/application/knowledge_graph/pattern_matching/__init__.py +21 -0
- aiecs/application/knowledge_graph/pattern_matching/pattern_matcher.py +342 -0
- aiecs/application/knowledge_graph/pattern_matching/query_executor.py +366 -0
- aiecs/application/knowledge_graph/profiling/__init__.py +12 -0
- aiecs/application/knowledge_graph/profiling/query_plan_visualizer.py +195 -0
- aiecs/application/knowledge_graph/profiling/query_profiler.py +223 -0
- aiecs/application/knowledge_graph/reasoning/__init__.py +27 -0
- aiecs/application/knowledge_graph/reasoning/evidence_synthesis.py +341 -0
- aiecs/application/knowledge_graph/reasoning/inference_engine.py +500 -0
- aiecs/application/knowledge_graph/reasoning/logic_form_parser.py +163 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/__init__.py +79 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/ast_builder.py +513 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/ast_nodes.py +913 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/ast_validator.py +866 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/error_handler.py +475 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/parser.py +396 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/query_context.py +208 -0
- aiecs/application/knowledge_graph/reasoning/logic_query_integration.py +170 -0
- aiecs/application/knowledge_graph/reasoning/query_planner.py +855 -0
- aiecs/application/knowledge_graph/reasoning/reasoning_engine.py +518 -0
- aiecs/application/knowledge_graph/retrieval/__init__.py +27 -0
- aiecs/application/knowledge_graph/retrieval/query_intent_classifier.py +211 -0
- aiecs/application/knowledge_graph/retrieval/retrieval_strategies.py +592 -0
- aiecs/application/knowledge_graph/retrieval/strategy_types.py +23 -0
- aiecs/application/knowledge_graph/search/__init__.py +59 -0
- aiecs/application/knowledge_graph/search/hybrid_search.py +457 -0
- aiecs/application/knowledge_graph/search/reranker.py +293 -0
- aiecs/application/knowledge_graph/search/reranker_strategies.py +535 -0
- aiecs/application/knowledge_graph/search/text_similarity.py +392 -0
- aiecs/application/knowledge_graph/traversal/__init__.py +15 -0
- aiecs/application/knowledge_graph/traversal/enhanced_traversal.py +305 -0
- aiecs/application/knowledge_graph/traversal/path_scorer.py +271 -0
- aiecs/application/knowledge_graph/validators/__init__.py +13 -0
- aiecs/application/knowledge_graph/validators/relation_validator.py +239 -0
- aiecs/application/knowledge_graph/visualization/__init__.py +11 -0
- aiecs/application/knowledge_graph/visualization/graph_visualizer.py +313 -0
- aiecs/common/__init__.py +9 -0
- aiecs/common/knowledge_graph/__init__.py +17 -0
- aiecs/common/knowledge_graph/runnable.py +471 -0
- aiecs/config/__init__.py +20 -5
- aiecs/config/config.py +762 -31
- aiecs/config/graph_config.py +131 -0
- aiecs/config/tool_config.py +399 -0
- aiecs/core/__init__.py +29 -13
- aiecs/core/interface/__init__.py +2 -2
- aiecs/core/interface/execution_interface.py +22 -22
- aiecs/core/interface/storage_interface.py +37 -88
- aiecs/core/registry/__init__.py +31 -0
- aiecs/core/registry/service_registry.py +92 -0
- aiecs/domain/__init__.py +270 -1
- aiecs/domain/agent/__init__.py +191 -0
- aiecs/domain/agent/base_agent.py +3870 -0
- aiecs/domain/agent/exceptions.py +99 -0
- aiecs/domain/agent/graph_aware_mixin.py +569 -0
- aiecs/domain/agent/hybrid_agent.py +1435 -0
- aiecs/domain/agent/integration/__init__.py +29 -0
- aiecs/domain/agent/integration/context_compressor.py +216 -0
- aiecs/domain/agent/integration/context_engine_adapter.py +587 -0
- aiecs/domain/agent/integration/protocols.py +281 -0
- aiecs/domain/agent/integration/retry_policy.py +218 -0
- aiecs/domain/agent/integration/role_config.py +213 -0
- aiecs/domain/agent/knowledge_aware_agent.py +1892 -0
- aiecs/domain/agent/lifecycle.py +291 -0
- aiecs/domain/agent/llm_agent.py +692 -0
- aiecs/domain/agent/memory/__init__.py +12 -0
- aiecs/domain/agent/memory/conversation.py +1124 -0
- aiecs/domain/agent/migration/__init__.py +14 -0
- aiecs/domain/agent/migration/conversion.py +163 -0
- aiecs/domain/agent/migration/legacy_wrapper.py +86 -0
- aiecs/domain/agent/models.py +884 -0
- aiecs/domain/agent/observability.py +479 -0
- aiecs/domain/agent/persistence.py +449 -0
- aiecs/domain/agent/prompts/__init__.py +29 -0
- aiecs/domain/agent/prompts/builder.py +159 -0
- aiecs/domain/agent/prompts/formatters.py +187 -0
- aiecs/domain/agent/prompts/template.py +255 -0
- aiecs/domain/agent/registry.py +253 -0
- aiecs/domain/agent/tool_agent.py +444 -0
- aiecs/domain/agent/tools/__init__.py +15 -0
- aiecs/domain/agent/tools/schema_generator.py +364 -0
- aiecs/domain/community/__init__.py +155 -0
- aiecs/domain/community/agent_adapter.py +469 -0
- aiecs/domain/community/analytics.py +432 -0
- aiecs/domain/community/collaborative_workflow.py +648 -0
- aiecs/domain/community/communication_hub.py +634 -0
- aiecs/domain/community/community_builder.py +320 -0
- aiecs/domain/community/community_integration.py +796 -0
- aiecs/domain/community/community_manager.py +803 -0
- aiecs/domain/community/decision_engine.py +849 -0
- aiecs/domain/community/exceptions.py +231 -0
- aiecs/domain/community/models/__init__.py +33 -0
- aiecs/domain/community/models/community_models.py +234 -0
- aiecs/domain/community/resource_manager.py +461 -0
- aiecs/domain/community/shared_context_manager.py +589 -0
- aiecs/domain/context/__init__.py +40 -10
- aiecs/domain/context/context_engine.py +1910 -0
- aiecs/domain/context/conversation_models.py +87 -53
- aiecs/domain/context/graph_memory.py +582 -0
- aiecs/domain/execution/model.py +12 -4
- aiecs/domain/knowledge_graph/__init__.py +19 -0
- aiecs/domain/knowledge_graph/models/__init__.py +52 -0
- aiecs/domain/knowledge_graph/models/entity.py +148 -0
- aiecs/domain/knowledge_graph/models/evidence.py +178 -0
- aiecs/domain/knowledge_graph/models/inference_rule.py +184 -0
- aiecs/domain/knowledge_graph/models/path.py +171 -0
- aiecs/domain/knowledge_graph/models/path_pattern.py +171 -0
- aiecs/domain/knowledge_graph/models/query.py +261 -0
- aiecs/domain/knowledge_graph/models/query_plan.py +181 -0
- aiecs/domain/knowledge_graph/models/relation.py +202 -0
- aiecs/domain/knowledge_graph/schema/__init__.py +23 -0
- aiecs/domain/knowledge_graph/schema/entity_type.py +131 -0
- aiecs/domain/knowledge_graph/schema/graph_schema.py +253 -0
- aiecs/domain/knowledge_graph/schema/property_schema.py +143 -0
- aiecs/domain/knowledge_graph/schema/relation_type.py +163 -0
- aiecs/domain/knowledge_graph/schema/schema_manager.py +691 -0
- aiecs/domain/knowledge_graph/schema/type_enums.py +209 -0
- aiecs/domain/task/dsl_processor.py +172 -56
- aiecs/domain/task/model.py +20 -8
- aiecs/domain/task/task_context.py +27 -24
- aiecs/infrastructure/__init__.py +0 -2
- aiecs/infrastructure/graph_storage/__init__.py +11 -0
- aiecs/infrastructure/graph_storage/base.py +837 -0
- aiecs/infrastructure/graph_storage/batch_operations.py +458 -0
- aiecs/infrastructure/graph_storage/cache.py +424 -0
- aiecs/infrastructure/graph_storage/distributed.py +223 -0
- aiecs/infrastructure/graph_storage/error_handling.py +380 -0
- aiecs/infrastructure/graph_storage/graceful_degradation.py +294 -0
- aiecs/infrastructure/graph_storage/health_checks.py +378 -0
- aiecs/infrastructure/graph_storage/in_memory.py +1197 -0
- aiecs/infrastructure/graph_storage/index_optimization.py +446 -0
- aiecs/infrastructure/graph_storage/lazy_loading.py +431 -0
- aiecs/infrastructure/graph_storage/metrics.py +344 -0
- aiecs/infrastructure/graph_storage/migration.py +400 -0
- aiecs/infrastructure/graph_storage/pagination.py +483 -0
- aiecs/infrastructure/graph_storage/performance_monitoring.py +456 -0
- aiecs/infrastructure/graph_storage/postgres.py +1563 -0
- aiecs/infrastructure/graph_storage/property_storage.py +353 -0
- aiecs/infrastructure/graph_storage/protocols.py +76 -0
- aiecs/infrastructure/graph_storage/query_optimizer.py +642 -0
- aiecs/infrastructure/graph_storage/schema_cache.py +290 -0
- aiecs/infrastructure/graph_storage/sqlite.py +1373 -0
- aiecs/infrastructure/graph_storage/streaming.py +487 -0
- aiecs/infrastructure/graph_storage/tenant.py +412 -0
- aiecs/infrastructure/messaging/celery_task_manager.py +92 -54
- aiecs/infrastructure/messaging/websocket_manager.py +51 -35
- aiecs/infrastructure/monitoring/__init__.py +22 -0
- aiecs/infrastructure/monitoring/executor_metrics.py +45 -11
- aiecs/infrastructure/monitoring/global_metrics_manager.py +212 -0
- aiecs/infrastructure/monitoring/structured_logger.py +3 -7
- aiecs/infrastructure/monitoring/tracing_manager.py +63 -35
- aiecs/infrastructure/persistence/__init__.py +14 -1
- aiecs/infrastructure/persistence/context_engine_client.py +184 -0
- aiecs/infrastructure/persistence/database_manager.py +67 -43
- aiecs/infrastructure/persistence/file_storage.py +180 -103
- aiecs/infrastructure/persistence/redis_client.py +74 -21
- aiecs/llm/__init__.py +73 -25
- aiecs/llm/callbacks/__init__.py +11 -0
- aiecs/llm/{custom_callbacks.py → callbacks/custom_callbacks.py} +26 -19
- aiecs/llm/client_factory.py +224 -36
- aiecs/llm/client_resolver.py +155 -0
- aiecs/llm/clients/__init__.py +38 -0
- aiecs/llm/clients/base_client.py +324 -0
- aiecs/llm/clients/google_function_calling_mixin.py +457 -0
- aiecs/llm/clients/googleai_client.py +241 -0
- aiecs/llm/clients/openai_client.py +158 -0
- aiecs/llm/clients/openai_compatible_mixin.py +367 -0
- aiecs/llm/clients/vertex_client.py +897 -0
- aiecs/llm/clients/xai_client.py +201 -0
- aiecs/llm/config/__init__.py +51 -0
- aiecs/llm/config/config_loader.py +272 -0
- aiecs/llm/config/config_validator.py +206 -0
- aiecs/llm/config/model_config.py +143 -0
- aiecs/llm/protocols.py +149 -0
- aiecs/llm/utils/__init__.py +10 -0
- aiecs/llm/utils/validate_config.py +89 -0
- aiecs/main.py +140 -121
- aiecs/scripts/aid/VERSION_MANAGEMENT.md +138 -0
- aiecs/scripts/aid/__init__.py +19 -0
- aiecs/scripts/aid/module_checker.py +499 -0
- aiecs/scripts/aid/version_manager.py +235 -0
- aiecs/scripts/{DEPENDENCY_SYSTEM_SUMMARY.md → dependance_check/DEPENDENCY_SYSTEM_SUMMARY.md} +1 -0
- aiecs/scripts/{README_DEPENDENCY_CHECKER.md → dependance_check/README_DEPENDENCY_CHECKER.md} +1 -0
- aiecs/scripts/dependance_check/__init__.py +15 -0
- aiecs/scripts/dependance_check/dependency_checker.py +1835 -0
- aiecs/scripts/{dependency_fixer.py → dependance_check/dependency_fixer.py} +192 -90
- aiecs/scripts/{download_nlp_data.py → dependance_check/download_nlp_data.py} +203 -71
- aiecs/scripts/dependance_patch/__init__.py +7 -0
- aiecs/scripts/dependance_patch/fix_weasel/__init__.py +11 -0
- aiecs/scripts/{fix_weasel_validator.py → dependance_patch/fix_weasel/fix_weasel_validator.py} +21 -14
- aiecs/scripts/{patch_weasel_library.sh → dependance_patch/fix_weasel/patch_weasel_library.sh} +1 -1
- aiecs/scripts/knowledge_graph/__init__.py +3 -0
- aiecs/scripts/knowledge_graph/run_threshold_experiments.py +212 -0
- aiecs/scripts/migrations/multi_tenancy/README.md +142 -0
- aiecs/scripts/tools_develop/README.md +671 -0
- aiecs/scripts/tools_develop/README_CONFIG_CHECKER.md +273 -0
- aiecs/scripts/tools_develop/TOOLS_CONFIG_GUIDE.md +1287 -0
- aiecs/scripts/tools_develop/TOOL_AUTO_DISCOVERY.md +234 -0
- aiecs/scripts/tools_develop/__init__.py +21 -0
- aiecs/scripts/tools_develop/check_all_tools_config.py +548 -0
- aiecs/scripts/tools_develop/check_type_annotations.py +257 -0
- aiecs/scripts/tools_develop/pre-commit-schema-coverage.sh +66 -0
- aiecs/scripts/tools_develop/schema_coverage.py +511 -0
- aiecs/scripts/tools_develop/validate_tool_schemas.py +475 -0
- aiecs/scripts/tools_develop/verify_executor_config_fix.py +98 -0
- aiecs/scripts/tools_develop/verify_tools.py +352 -0
- aiecs/tasks/__init__.py +0 -1
- aiecs/tasks/worker.py +115 -47
- aiecs/tools/__init__.py +194 -72
- aiecs/tools/apisource/__init__.py +99 -0
- aiecs/tools/apisource/intelligence/__init__.py +19 -0
- aiecs/tools/apisource/intelligence/data_fusion.py +632 -0
- aiecs/tools/apisource/intelligence/query_analyzer.py +417 -0
- aiecs/tools/apisource/intelligence/search_enhancer.py +385 -0
- aiecs/tools/apisource/monitoring/__init__.py +9 -0
- aiecs/tools/apisource/monitoring/metrics.py +330 -0
- aiecs/tools/apisource/providers/__init__.py +112 -0
- aiecs/tools/apisource/providers/base.py +671 -0
- aiecs/tools/apisource/providers/census.py +397 -0
- aiecs/tools/apisource/providers/fred.py +535 -0
- aiecs/tools/apisource/providers/newsapi.py +409 -0
- aiecs/tools/apisource/providers/worldbank.py +352 -0
- aiecs/tools/apisource/reliability/__init__.py +12 -0
- aiecs/tools/apisource/reliability/error_handler.py +363 -0
- aiecs/tools/apisource/reliability/fallback_strategy.py +376 -0
- aiecs/tools/apisource/tool.py +832 -0
- aiecs/tools/apisource/utils/__init__.py +9 -0
- aiecs/tools/apisource/utils/validators.py +334 -0
- aiecs/tools/base_tool.py +415 -21
- aiecs/tools/docs/__init__.py +121 -0
- aiecs/tools/docs/ai_document_orchestrator.py +607 -0
- aiecs/tools/docs/ai_document_writer_orchestrator.py +2350 -0
- aiecs/tools/docs/content_insertion_tool.py +1320 -0
- aiecs/tools/docs/document_creator_tool.py +1323 -0
- aiecs/tools/docs/document_layout_tool.py +1160 -0
- aiecs/tools/docs/document_parser_tool.py +1011 -0
- aiecs/tools/docs/document_writer_tool.py +1829 -0
- aiecs/tools/knowledge_graph/__init__.py +17 -0
- aiecs/tools/knowledge_graph/graph_reasoning_tool.py +807 -0
- aiecs/tools/knowledge_graph/graph_search_tool.py +944 -0
- aiecs/tools/knowledge_graph/kg_builder_tool.py +524 -0
- aiecs/tools/langchain_adapter.py +300 -138
- aiecs/tools/schema_generator.py +455 -0
- aiecs/tools/search_tool/__init__.py +100 -0
- aiecs/tools/search_tool/analyzers.py +581 -0
- aiecs/tools/search_tool/cache.py +264 -0
- aiecs/tools/search_tool/constants.py +128 -0
- aiecs/tools/search_tool/context.py +224 -0
- aiecs/tools/search_tool/core.py +778 -0
- aiecs/tools/search_tool/deduplicator.py +119 -0
- aiecs/tools/search_tool/error_handler.py +242 -0
- aiecs/tools/search_tool/metrics.py +343 -0
- aiecs/tools/search_tool/rate_limiter.py +172 -0
- aiecs/tools/search_tool/schemas.py +275 -0
- aiecs/tools/statistics/__init__.py +80 -0
- aiecs/tools/statistics/ai_data_analysis_orchestrator.py +646 -0
- aiecs/tools/statistics/ai_insight_generator_tool.py +508 -0
- aiecs/tools/statistics/ai_report_orchestrator_tool.py +684 -0
- aiecs/tools/statistics/data_loader_tool.py +555 -0
- aiecs/tools/statistics/data_profiler_tool.py +638 -0
- aiecs/tools/statistics/data_transformer_tool.py +580 -0
- aiecs/tools/statistics/data_visualizer_tool.py +498 -0
- aiecs/tools/statistics/model_trainer_tool.py +507 -0
- aiecs/tools/statistics/statistical_analyzer_tool.py +472 -0
- aiecs/tools/task_tools/__init__.py +49 -36
- aiecs/tools/task_tools/chart_tool.py +200 -184
- aiecs/tools/task_tools/classfire_tool.py +268 -267
- aiecs/tools/task_tools/image_tool.py +175 -131
- aiecs/tools/task_tools/office_tool.py +226 -146
- aiecs/tools/task_tools/pandas_tool.py +477 -121
- aiecs/tools/task_tools/report_tool.py +390 -142
- aiecs/tools/task_tools/research_tool.py +149 -79
- aiecs/tools/task_tools/scraper_tool.py +339 -145
- aiecs/tools/task_tools/stats_tool.py +448 -209
- aiecs/tools/temp_file_manager.py +26 -24
- aiecs/tools/tool_executor/__init__.py +18 -16
- aiecs/tools/tool_executor/tool_executor.py +364 -52
- aiecs/utils/LLM_output_structor.py +74 -48
- aiecs/utils/__init__.py +14 -3
- aiecs/utils/base_callback.py +0 -3
- aiecs/utils/cache_provider.py +696 -0
- aiecs/utils/execution_utils.py +50 -31
- aiecs/utils/prompt_loader.py +1 -0
- aiecs/utils/token_usage_repository.py +37 -11
- aiecs/ws/socket_server.py +14 -4
- {aiecs-1.0.1.dist-info → aiecs-1.7.6.dist-info}/METADATA +52 -15
- aiecs-1.7.6.dist-info/RECORD +337 -0
- aiecs-1.7.6.dist-info/entry_points.txt +13 -0
- aiecs/config/registry.py +0 -19
- aiecs/domain/context/content_engine.py +0 -982
- aiecs/llm/base_client.py +0 -99
- aiecs/llm/openai_client.py +0 -125
- aiecs/llm/vertex_client.py +0 -186
- aiecs/llm/xai_client.py +0 -184
- aiecs/scripts/dependency_checker.py +0 -857
- aiecs/scripts/quick_dependency_check.py +0 -269
- aiecs/tools/task_tools/search_api.py +0 -7
- aiecs-1.0.1.dist-info/RECORD +0 -90
- aiecs-1.0.1.dist-info/entry_points.txt +0 -7
- /aiecs/scripts/{setup_nlp_data.sh → dependance_check/setup_nlp_data.sh} +0 -0
- /aiecs/scripts/{README_WEASEL_PATCH.md → dependance_patch/fix_weasel/README_WEASEL_PATCH.md} +0 -0
- /aiecs/scripts/{fix_weasel_validator.sh → dependance_patch/fix_weasel/fix_weasel_validator.sh} +0 -0
- /aiecs/scripts/{run_weasel_patch.sh → dependance_patch/fix_weasel/run_weasel_patch.sh} +0 -0
- {aiecs-1.0.1.dist-info → aiecs-1.7.6.dist-info}/WHEEL +0 -0
- {aiecs-1.0.1.dist-info → aiecs-1.7.6.dist-info}/licenses/LICENSE +0 -0
- {aiecs-1.0.1.dist-info → aiecs-1.7.6.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,634 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Communication Hub
|
|
3
|
+
|
|
4
|
+
Provides agent-to-agent messaging, event bus, and pub/sub system
|
|
5
|
+
for community communication and collaboration.
|
|
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, deque
|
|
14
|
+
import uuid
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class MessageType(str, Enum):
|
|
20
|
+
"""Types of messages in the community."""
|
|
21
|
+
|
|
22
|
+
REQUEST = "request"
|
|
23
|
+
RESPONSE = "response"
|
|
24
|
+
NOTIFICATION = "notification"
|
|
25
|
+
SHARE = "share"
|
|
26
|
+
BROADCAST = "broadcast"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class EventType(str, Enum):
|
|
30
|
+
"""Types of community events."""
|
|
31
|
+
|
|
32
|
+
COMMUNITY_CREATED = "community_created"
|
|
33
|
+
COMMUNITY_UPDATED = "community_updated"
|
|
34
|
+
MEMBER_JOINED = "member_joined"
|
|
35
|
+
MEMBER_EXITED = "member_exited"
|
|
36
|
+
MEMBER_UPDATED = "member_updated"
|
|
37
|
+
DECISION_PROPOSED = "decision_proposed"
|
|
38
|
+
DECISION_VOTED = "decision_voted"
|
|
39
|
+
DECISION_APPROVED = "decision_approved"
|
|
40
|
+
DECISION_REJECTED = "decision_rejected"
|
|
41
|
+
RESOURCE_CREATED = "resource_created"
|
|
42
|
+
RESOURCE_UPDATED = "resource_updated"
|
|
43
|
+
RESOURCE_SHARED = "resource_shared"
|
|
44
|
+
SESSION_STARTED = "session_started"
|
|
45
|
+
SESSION_ENDED = "session_ended"
|
|
46
|
+
CUSTOM = "custom"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class Message:
|
|
50
|
+
"""Represents a message between agents."""
|
|
51
|
+
|
|
52
|
+
def __init__(
|
|
53
|
+
self,
|
|
54
|
+
sender_id: str,
|
|
55
|
+
recipient_ids: List[str],
|
|
56
|
+
message_type: MessageType,
|
|
57
|
+
content: Any,
|
|
58
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
59
|
+
):
|
|
60
|
+
"""
|
|
61
|
+
Initialize a message.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
sender_id: ID of the sending agent
|
|
65
|
+
recipient_ids: List of recipient agent IDs (empty for broadcast)
|
|
66
|
+
message_type: Type of message
|
|
67
|
+
content: Message content
|
|
68
|
+
metadata: Optional metadata
|
|
69
|
+
"""
|
|
70
|
+
self.message_id = str(uuid.uuid4())
|
|
71
|
+
self.sender_id = sender_id
|
|
72
|
+
self.recipient_ids = recipient_ids
|
|
73
|
+
self.message_type = message_type
|
|
74
|
+
self.content = content
|
|
75
|
+
self.metadata = metadata or {}
|
|
76
|
+
self.timestamp = datetime.utcnow()
|
|
77
|
+
self.delivered_to: Set[str] = set()
|
|
78
|
+
self.read_by: Set[str] = set()
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class Event:
|
|
82
|
+
"""Represents a community event."""
|
|
83
|
+
|
|
84
|
+
def __init__(
|
|
85
|
+
self,
|
|
86
|
+
event_type: EventType,
|
|
87
|
+
source_id: str,
|
|
88
|
+
data: Dict[str, Any],
|
|
89
|
+
community_id: Optional[str] = None,
|
|
90
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
91
|
+
):
|
|
92
|
+
"""
|
|
93
|
+
Initialize an event.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
event_type: Type of event
|
|
97
|
+
source_id: ID of the source (community, member, etc.)
|
|
98
|
+
data: Event data
|
|
99
|
+
community_id: Optional community ID
|
|
100
|
+
metadata: Optional metadata
|
|
101
|
+
"""
|
|
102
|
+
self.event_id = str(uuid.uuid4())
|
|
103
|
+
self.event_type = event_type
|
|
104
|
+
self.source_id = source_id
|
|
105
|
+
self.data = data
|
|
106
|
+
self.community_id = community_id
|
|
107
|
+
self.metadata = metadata or {}
|
|
108
|
+
self.timestamp = datetime.utcnow()
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class CommunicationHub:
|
|
112
|
+
"""
|
|
113
|
+
Central hub for agent communication and event distribution.
|
|
114
|
+
Provides messaging, pub/sub, and event broadcasting capabilities.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
def __init__(self, max_queue_size: int = 1000):
|
|
118
|
+
"""
|
|
119
|
+
Initialize the communication hub.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
max_queue_size: Maximum size for message queues
|
|
123
|
+
"""
|
|
124
|
+
self.max_queue_size = max_queue_size
|
|
125
|
+
|
|
126
|
+
# Message queues per agent
|
|
127
|
+
self.message_queues: Dict[str, deque] = defaultdict(lambda: deque(maxlen=max_queue_size))
|
|
128
|
+
|
|
129
|
+
# Event subscriptions: event_type -> set of subscriber_ids
|
|
130
|
+
self.event_subscriptions: Dict[EventType, Set[str]] = defaultdict(set)
|
|
131
|
+
|
|
132
|
+
# Topic subscriptions: topic -> set of subscriber_ids
|
|
133
|
+
self.topic_subscriptions: Dict[str, Set[str]] = defaultdict(set)
|
|
134
|
+
|
|
135
|
+
# Event handlers: subscriber_id -> handler_function
|
|
136
|
+
self.event_handlers: Dict[str, Callable] = {}
|
|
137
|
+
|
|
138
|
+
# Message history (limited)
|
|
139
|
+
self.message_history: deque = deque(maxlen=max_queue_size * 2)
|
|
140
|
+
self.event_history: deque = deque(maxlen=max_queue_size * 2)
|
|
141
|
+
|
|
142
|
+
# Output streams: agent_id -> list of subscriber_callback
|
|
143
|
+
self.output_streams: Dict[str, List[Callable]] = defaultdict(list)
|
|
144
|
+
|
|
145
|
+
# Active connections
|
|
146
|
+
self.active_agents: Set[str] = set()
|
|
147
|
+
|
|
148
|
+
logger.info("Communication hub initialized")
|
|
149
|
+
|
|
150
|
+
# ========== Agent Messaging ==========
|
|
151
|
+
|
|
152
|
+
async def send_message(
|
|
153
|
+
self,
|
|
154
|
+
sender_id: str,
|
|
155
|
+
recipient_ids: List[str],
|
|
156
|
+
message_type: MessageType,
|
|
157
|
+
content: Any,
|
|
158
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
159
|
+
) -> str:
|
|
160
|
+
"""
|
|
161
|
+
Send a message to one or more recipients (unicast/multicast).
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
sender_id: ID of the sending agent
|
|
165
|
+
recipient_ids: List of recipient agent IDs
|
|
166
|
+
message_type: Type of message
|
|
167
|
+
content: Message content
|
|
168
|
+
metadata: Optional metadata
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
Message ID
|
|
172
|
+
"""
|
|
173
|
+
message = Message(sender_id, recipient_ids, message_type, content, metadata)
|
|
174
|
+
|
|
175
|
+
# Deliver to each recipient's queue
|
|
176
|
+
for recipient_id in recipient_ids:
|
|
177
|
+
if recipient_id in self.active_agents or len(self.message_queues[recipient_id]) < self.max_queue_size:
|
|
178
|
+
self.message_queues[recipient_id].append(message)
|
|
179
|
+
message.delivered_to.add(recipient_id)
|
|
180
|
+
|
|
181
|
+
# Store in history
|
|
182
|
+
self.message_history.append(message)
|
|
183
|
+
|
|
184
|
+
logger.debug(f"Message {message.message_id} from {sender_id} to {len(recipient_ids)} recipients")
|
|
185
|
+
return message.message_id
|
|
186
|
+
|
|
187
|
+
async def broadcast_message(
|
|
188
|
+
self,
|
|
189
|
+
sender_id: str,
|
|
190
|
+
content: Any,
|
|
191
|
+
community_id: Optional[str] = None,
|
|
192
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
193
|
+
) -> str:
|
|
194
|
+
"""
|
|
195
|
+
Broadcast a message to all active agents or community members.
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
sender_id: ID of the sending agent
|
|
199
|
+
content: Message content
|
|
200
|
+
community_id: Optional community to limit broadcast
|
|
201
|
+
metadata: Optional metadata
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
Message ID
|
|
205
|
+
"""
|
|
206
|
+
# Get recipients (all active agents or community members)
|
|
207
|
+
recipients = list(self.active_agents)
|
|
208
|
+
|
|
209
|
+
message = Message(sender_id, recipients, MessageType.BROADCAST, content, metadata)
|
|
210
|
+
|
|
211
|
+
# Deliver to all recipients
|
|
212
|
+
for recipient_id in recipients:
|
|
213
|
+
if recipient_id != sender_id: # Don't send to self
|
|
214
|
+
self.message_queues[recipient_id].append(message)
|
|
215
|
+
message.delivered_to.add(recipient_id)
|
|
216
|
+
|
|
217
|
+
# Store in history
|
|
218
|
+
self.message_history.append(message)
|
|
219
|
+
|
|
220
|
+
logger.info(f"Broadcast message {message.message_id} from {sender_id} to {len(recipients)} agents")
|
|
221
|
+
return message.message_id
|
|
222
|
+
|
|
223
|
+
async def receive_messages(
|
|
224
|
+
self,
|
|
225
|
+
agent_id: str,
|
|
226
|
+
mark_as_read: bool = True,
|
|
227
|
+
limit: Optional[int] = None,
|
|
228
|
+
) -> List[Message]:
|
|
229
|
+
"""
|
|
230
|
+
Receive messages for an agent.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
agent_id: ID of the agent receiving messages
|
|
234
|
+
mark_as_read: Whether to mark messages as read
|
|
235
|
+
limit: Optional limit on number of messages to retrieve
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
List of messages
|
|
239
|
+
"""
|
|
240
|
+
queue = self.message_queues[agent_id]
|
|
241
|
+
|
|
242
|
+
if not queue:
|
|
243
|
+
return []
|
|
244
|
+
|
|
245
|
+
# Get messages
|
|
246
|
+
count = min(limit, len(queue)) if limit else len(queue)
|
|
247
|
+
messages = []
|
|
248
|
+
|
|
249
|
+
for _ in range(count):
|
|
250
|
+
if queue:
|
|
251
|
+
message = queue.popleft()
|
|
252
|
+
if mark_as_read:
|
|
253
|
+
message.read_by.add(agent_id)
|
|
254
|
+
messages.append(message)
|
|
255
|
+
|
|
256
|
+
logger.debug(f"Agent {agent_id} received {len(messages)} messages")
|
|
257
|
+
return messages
|
|
258
|
+
|
|
259
|
+
async def peek_messages(self, agent_id: str, limit: Optional[int] = None) -> List[Message]:
|
|
260
|
+
"""
|
|
261
|
+
Peek at messages without removing them from queue.
|
|
262
|
+
|
|
263
|
+
Args:
|
|
264
|
+
agent_id: ID of the agent
|
|
265
|
+
limit: Optional limit on number of messages
|
|
266
|
+
|
|
267
|
+
Returns:
|
|
268
|
+
List of messages
|
|
269
|
+
"""
|
|
270
|
+
queue = self.message_queues[agent_id]
|
|
271
|
+
count = min(limit, len(queue)) if limit else len(queue)
|
|
272
|
+
return list(queue)[:count] if count > 0 else []
|
|
273
|
+
|
|
274
|
+
def get_unread_count(self, agent_id: str) -> int:
|
|
275
|
+
"""
|
|
276
|
+
Get count of unread messages for an agent.
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
agent_id: ID of the agent
|
|
280
|
+
|
|
281
|
+
Returns:
|
|
282
|
+
Number of unread messages
|
|
283
|
+
"""
|
|
284
|
+
return len(self.message_queues[agent_id])
|
|
285
|
+
|
|
286
|
+
# ========== Event Bus & Pub/Sub ==========
|
|
287
|
+
|
|
288
|
+
async def subscribe_to_event(
|
|
289
|
+
self,
|
|
290
|
+
subscriber_id: str,
|
|
291
|
+
event_type: EventType,
|
|
292
|
+
handler: Optional[Callable] = None,
|
|
293
|
+
) -> bool:
|
|
294
|
+
"""
|
|
295
|
+
Subscribe to a specific event type.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
subscriber_id: ID of the subscriber
|
|
299
|
+
event_type: Type of event to subscribe to
|
|
300
|
+
handler: Optional handler function for the event
|
|
301
|
+
|
|
302
|
+
Returns:
|
|
303
|
+
True if subscription was successful
|
|
304
|
+
"""
|
|
305
|
+
self.event_subscriptions[event_type].add(subscriber_id)
|
|
306
|
+
|
|
307
|
+
if handler:
|
|
308
|
+
self.event_handlers[f"{subscriber_id}:{event_type.value}"] = handler
|
|
309
|
+
|
|
310
|
+
logger.debug(f"Agent {subscriber_id} subscribed to {event_type.value}")
|
|
311
|
+
return True
|
|
312
|
+
|
|
313
|
+
async def unsubscribe_from_event(self, subscriber_id: str, event_type: EventType) -> bool:
|
|
314
|
+
"""
|
|
315
|
+
Unsubscribe from an event type.
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
subscriber_id: ID of the subscriber
|
|
319
|
+
event_type: Type of event to unsubscribe from
|
|
320
|
+
|
|
321
|
+
Returns:
|
|
322
|
+
True if unsubscription was successful
|
|
323
|
+
"""
|
|
324
|
+
if event_type in self.event_subscriptions:
|
|
325
|
+
self.event_subscriptions[event_type].discard(subscriber_id)
|
|
326
|
+
|
|
327
|
+
handler_key = f"{subscriber_id}:{event_type.value}"
|
|
328
|
+
if handler_key in self.event_handlers:
|
|
329
|
+
del self.event_handlers[handler_key]
|
|
330
|
+
|
|
331
|
+
logger.debug(f"Agent {subscriber_id} unsubscribed from {event_type.value}")
|
|
332
|
+
return True
|
|
333
|
+
|
|
334
|
+
async def subscribe_to_topic(
|
|
335
|
+
self,
|
|
336
|
+
subscriber_id: str,
|
|
337
|
+
topic: str,
|
|
338
|
+
handler: Optional[Callable] = None,
|
|
339
|
+
) -> bool:
|
|
340
|
+
"""
|
|
341
|
+
Subscribe to a custom topic.
|
|
342
|
+
|
|
343
|
+
Args:
|
|
344
|
+
subscriber_id: ID of the subscriber
|
|
345
|
+
topic: Topic name
|
|
346
|
+
handler: Optional handler function
|
|
347
|
+
|
|
348
|
+
Returns:
|
|
349
|
+
True if subscription was successful
|
|
350
|
+
"""
|
|
351
|
+
self.topic_subscriptions[topic].add(subscriber_id)
|
|
352
|
+
|
|
353
|
+
if handler:
|
|
354
|
+
self.event_handlers[f"{subscriber_id}:topic:{topic}"] = handler
|
|
355
|
+
|
|
356
|
+
logger.debug(f"Agent {subscriber_id} subscribed to topic '{topic}'")
|
|
357
|
+
return True
|
|
358
|
+
|
|
359
|
+
async def unsubscribe_from_topic(self, subscriber_id: str, topic: str) -> bool:
|
|
360
|
+
"""
|
|
361
|
+
Unsubscribe from a topic.
|
|
362
|
+
|
|
363
|
+
Args:
|
|
364
|
+
subscriber_id: ID of the subscriber
|
|
365
|
+
topic: Topic name
|
|
366
|
+
|
|
367
|
+
Returns:
|
|
368
|
+
True if unsubscription was successful
|
|
369
|
+
"""
|
|
370
|
+
if topic in self.topic_subscriptions:
|
|
371
|
+
self.topic_subscriptions[topic].discard(subscriber_id)
|
|
372
|
+
|
|
373
|
+
handler_key = f"{subscriber_id}:topic:{topic}"
|
|
374
|
+
if handler_key in self.event_handlers:
|
|
375
|
+
del self.event_handlers[handler_key]
|
|
376
|
+
|
|
377
|
+
logger.debug(f"Agent {subscriber_id} unsubscribed from topic '{topic}'")
|
|
378
|
+
return True
|
|
379
|
+
|
|
380
|
+
async def publish_event(
|
|
381
|
+
self,
|
|
382
|
+
event_type: EventType,
|
|
383
|
+
source_id: str,
|
|
384
|
+
data: Dict[str, Any],
|
|
385
|
+
community_id: Optional[str] = None,
|
|
386
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
387
|
+
) -> str:
|
|
388
|
+
"""
|
|
389
|
+
Publish an event to all subscribers.
|
|
390
|
+
|
|
391
|
+
Args:
|
|
392
|
+
event_type: Type of event
|
|
393
|
+
source_id: ID of the event source
|
|
394
|
+
data: Event data
|
|
395
|
+
community_id: Optional community ID
|
|
396
|
+
metadata: Optional metadata
|
|
397
|
+
|
|
398
|
+
Returns:
|
|
399
|
+
Event ID
|
|
400
|
+
"""
|
|
401
|
+
event = Event(event_type, source_id, data, community_id, metadata)
|
|
402
|
+
|
|
403
|
+
# Store in history
|
|
404
|
+
self.event_history.append(event)
|
|
405
|
+
|
|
406
|
+
# Get subscribers
|
|
407
|
+
subscribers = self.event_subscriptions.get(event_type, set())
|
|
408
|
+
|
|
409
|
+
# Execute handlers
|
|
410
|
+
for subscriber_id in subscribers:
|
|
411
|
+
handler_key = f"{subscriber_id}:{event_type.value}"
|
|
412
|
+
handler = self.event_handlers.get(handler_key)
|
|
413
|
+
|
|
414
|
+
if handler:
|
|
415
|
+
try:
|
|
416
|
+
if asyncio.iscoroutinefunction(handler):
|
|
417
|
+
await handler(event)
|
|
418
|
+
else:
|
|
419
|
+
handler(event)
|
|
420
|
+
except Exception as e:
|
|
421
|
+
logger.error(f"Error executing event handler for {subscriber_id}: {e}")
|
|
422
|
+
|
|
423
|
+
logger.debug(f"Published event {event_type.value} to {len(subscribers)} subscribers")
|
|
424
|
+
return event.event_id
|
|
425
|
+
|
|
426
|
+
async def publish_to_topic(
|
|
427
|
+
self,
|
|
428
|
+
topic: str,
|
|
429
|
+
source_id: str,
|
|
430
|
+
data: Dict[str, Any],
|
|
431
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
432
|
+
) -> str:
|
|
433
|
+
"""
|
|
434
|
+
Publish data to a topic.
|
|
435
|
+
|
|
436
|
+
Args:
|
|
437
|
+
topic: Topic name
|
|
438
|
+
source_id: ID of the publisher
|
|
439
|
+
data: Data to publish
|
|
440
|
+
metadata: Optional metadata
|
|
441
|
+
|
|
442
|
+
Returns:
|
|
443
|
+
Event ID
|
|
444
|
+
"""
|
|
445
|
+
event = Event(EventType.CUSTOM, source_id, data, None, metadata)
|
|
446
|
+
event.metadata["topic"] = topic
|
|
447
|
+
|
|
448
|
+
# Store in history
|
|
449
|
+
self.event_history.append(event)
|
|
450
|
+
|
|
451
|
+
# Get subscribers
|
|
452
|
+
subscribers = self.topic_subscriptions.get(topic, set())
|
|
453
|
+
|
|
454
|
+
# Execute handlers
|
|
455
|
+
for subscriber_id in subscribers:
|
|
456
|
+
handler_key = f"{subscriber_id}:topic:{topic}"
|
|
457
|
+
handler = self.event_handlers.get(handler_key)
|
|
458
|
+
|
|
459
|
+
if handler:
|
|
460
|
+
try:
|
|
461
|
+
if asyncio.iscoroutinefunction(handler):
|
|
462
|
+
await handler(event)
|
|
463
|
+
else:
|
|
464
|
+
handler(event)
|
|
465
|
+
except Exception as e:
|
|
466
|
+
logger.error(f"Error executing topic handler for {subscriber_id}: {e}")
|
|
467
|
+
|
|
468
|
+
logger.debug(f"Published to topic '{topic}' for {len(subscribers)} subscribers")
|
|
469
|
+
return event.event_id
|
|
470
|
+
|
|
471
|
+
# ========== Output Streaming ==========
|
|
472
|
+
|
|
473
|
+
async def subscribe_to_output(
|
|
474
|
+
self,
|
|
475
|
+
publisher_id: str,
|
|
476
|
+
subscriber_callback: Callable[[Dict[str, Any]], None],
|
|
477
|
+
) -> bool:
|
|
478
|
+
"""
|
|
479
|
+
Subscribe to an agent's output stream.
|
|
480
|
+
|
|
481
|
+
Args:
|
|
482
|
+
publisher_id: ID of the agent publishing output
|
|
483
|
+
subscriber_callback: Callback function for output data
|
|
484
|
+
|
|
485
|
+
Returns:
|
|
486
|
+
True if subscription was successful
|
|
487
|
+
"""
|
|
488
|
+
self.output_streams[publisher_id].append(subscriber_callback)
|
|
489
|
+
logger.debug(f"Subscribed to output stream of {publisher_id}")
|
|
490
|
+
return True
|
|
491
|
+
|
|
492
|
+
async def unsubscribe_from_output(
|
|
493
|
+
self,
|
|
494
|
+
publisher_id: str,
|
|
495
|
+
subscriber_callback: Callable[[Dict[str, Any]], None],
|
|
496
|
+
) -> bool:
|
|
497
|
+
"""
|
|
498
|
+
Unsubscribe from an agent's output stream.
|
|
499
|
+
|
|
500
|
+
Args:
|
|
501
|
+
publisher_id: ID of the agent
|
|
502
|
+
subscriber_callback: Callback function to remove
|
|
503
|
+
|
|
504
|
+
Returns:
|
|
505
|
+
True if unsubscription was successful
|
|
506
|
+
"""
|
|
507
|
+
if publisher_id in self.output_streams:
|
|
508
|
+
if subscriber_callback in self.output_streams[publisher_id]:
|
|
509
|
+
self.output_streams[publisher_id].remove(subscriber_callback)
|
|
510
|
+
logger.debug(f"Unsubscribed from output stream of {publisher_id}")
|
|
511
|
+
return True
|
|
512
|
+
return False
|
|
513
|
+
|
|
514
|
+
async def stream_output(
|
|
515
|
+
self,
|
|
516
|
+
publisher_id: str,
|
|
517
|
+
output_data: Dict[str, Any],
|
|
518
|
+
stream_type: str = "result",
|
|
519
|
+
) -> int:
|
|
520
|
+
"""
|
|
521
|
+
Stream output to all subscribers with backpressure handling.
|
|
522
|
+
|
|
523
|
+
Args:
|
|
524
|
+
publisher_id: ID of the publishing agent
|
|
525
|
+
output_data: Output data to stream
|
|
526
|
+
stream_type: Type of output (result, progress, partial, etc.)
|
|
527
|
+
|
|
528
|
+
Returns:
|
|
529
|
+
Number of subscribers notified
|
|
530
|
+
"""
|
|
531
|
+
if publisher_id not in self.output_streams:
|
|
532
|
+
return 0
|
|
533
|
+
|
|
534
|
+
stream_data = {
|
|
535
|
+
"publisher_id": publisher_id,
|
|
536
|
+
"stream_type": stream_type,
|
|
537
|
+
"data": output_data,
|
|
538
|
+
"timestamp": datetime.utcnow().isoformat(),
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
subscribers = self.output_streams[publisher_id]
|
|
542
|
+
notified_count = 0
|
|
543
|
+
|
|
544
|
+
for callback in subscribers:
|
|
545
|
+
try:
|
|
546
|
+
if asyncio.iscoroutinefunction(callback):
|
|
547
|
+
await callback(stream_data)
|
|
548
|
+
else:
|
|
549
|
+
callback(stream_data)
|
|
550
|
+
notified_count += 1
|
|
551
|
+
except Exception as e:
|
|
552
|
+
logger.error(f"Error streaming output to subscriber: {e}")
|
|
553
|
+
|
|
554
|
+
return notified_count
|
|
555
|
+
|
|
556
|
+
# ========== Connection Management ==========
|
|
557
|
+
|
|
558
|
+
async def register_agent(self, agent_id: str) -> bool:
|
|
559
|
+
"""
|
|
560
|
+
Register an agent as active in the hub.
|
|
561
|
+
|
|
562
|
+
Args:
|
|
563
|
+
agent_id: ID of the agent
|
|
564
|
+
|
|
565
|
+
Returns:
|
|
566
|
+
True if registration was successful
|
|
567
|
+
"""
|
|
568
|
+
self.active_agents.add(agent_id)
|
|
569
|
+
logger.info(f"Registered agent {agent_id} in communication hub")
|
|
570
|
+
return True
|
|
571
|
+
|
|
572
|
+
async def unregister_agent(self, agent_id: str) -> bool:
|
|
573
|
+
"""
|
|
574
|
+
Unregister an agent from the hub.
|
|
575
|
+
|
|
576
|
+
Args:
|
|
577
|
+
agent_id: ID of the agent
|
|
578
|
+
|
|
579
|
+
Returns:
|
|
580
|
+
True if unregistration was successful
|
|
581
|
+
"""
|
|
582
|
+
self.active_agents.discard(agent_id)
|
|
583
|
+
|
|
584
|
+
# Clean up subscriptions
|
|
585
|
+
for event_type in self.event_subscriptions:
|
|
586
|
+
self.event_subscriptions[event_type].discard(agent_id)
|
|
587
|
+
|
|
588
|
+
for topic in self.topic_subscriptions:
|
|
589
|
+
self.topic_subscriptions[topic].discard(agent_id)
|
|
590
|
+
|
|
591
|
+
# Clean up output streams
|
|
592
|
+
if agent_id in self.output_streams:
|
|
593
|
+
del self.output_streams[agent_id]
|
|
594
|
+
|
|
595
|
+
logger.info(f"Unregistered agent {agent_id} from communication hub")
|
|
596
|
+
return True
|
|
597
|
+
|
|
598
|
+
# ========== Statistics & Monitoring ==========
|
|
599
|
+
|
|
600
|
+
def get_statistics(self) -> Dict[str, Any]:
|
|
601
|
+
"""
|
|
602
|
+
Get communication hub statistics.
|
|
603
|
+
|
|
604
|
+
Returns:
|
|
605
|
+
Statistics dictionary
|
|
606
|
+
"""
|
|
607
|
+
return {
|
|
608
|
+
"active_agents": len(self.active_agents),
|
|
609
|
+
"total_messages": len(self.message_history),
|
|
610
|
+
"total_events": len(self.event_history),
|
|
611
|
+
"pending_messages": sum(len(q) for q in self.message_queues.values()),
|
|
612
|
+
"event_subscriptions": sum(len(s) for s in self.event_subscriptions.values()),
|
|
613
|
+
"topic_subscriptions": sum(len(s) for s in self.topic_subscriptions.values()),
|
|
614
|
+
"output_streams": len(self.output_streams),
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
def get_agent_status(self, agent_id: str) -> Dict[str, Any]:
|
|
618
|
+
"""
|
|
619
|
+
Get status for a specific agent.
|
|
620
|
+
|
|
621
|
+
Args:
|
|
622
|
+
agent_id: ID of the agent
|
|
623
|
+
|
|
624
|
+
Returns:
|
|
625
|
+
Agent status dictionary
|
|
626
|
+
"""
|
|
627
|
+
return {
|
|
628
|
+
"agent_id": agent_id,
|
|
629
|
+
"is_active": agent_id in self.active_agents,
|
|
630
|
+
"unread_messages": len(self.message_queues[agent_id]),
|
|
631
|
+
"event_subscriptions": [et.value for et, subs in self.event_subscriptions.items() if agent_id in subs],
|
|
632
|
+
"topic_subscriptions": [topic for topic, subs in self.topic_subscriptions.items() if agent_id in subs],
|
|
633
|
+
"output_subscribers": len(self.output_streams.get(agent_id, [])),
|
|
634
|
+
}
|