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.
- aiecs/__init__.py +72 -0
- aiecs/__main__.py +41 -0
- aiecs/aiecs_client.py +469 -0
- aiecs/application/__init__.py +10 -0
- aiecs/application/executors/__init__.py +10 -0
- aiecs/application/executors/operation_executor.py +363 -0
- aiecs/application/knowledge_graph/__init__.py +7 -0
- aiecs/application/knowledge_graph/builder/__init__.py +37 -0
- aiecs/application/knowledge_graph/builder/document_builder.py +375 -0
- aiecs/application/knowledge_graph/builder/graph_builder.py +356 -0
- aiecs/application/knowledge_graph/builder/schema_mapping.py +531 -0
- aiecs/application/knowledge_graph/builder/structured_pipeline.py +443 -0
- aiecs/application/knowledge_graph/builder/text_chunker.py +319 -0
- aiecs/application/knowledge_graph/extractors/__init__.py +27 -0
- aiecs/application/knowledge_graph/extractors/base.py +100 -0
- aiecs/application/knowledge_graph/extractors/llm_entity_extractor.py +327 -0
- aiecs/application/knowledge_graph/extractors/llm_relation_extractor.py +349 -0
- aiecs/application/knowledge_graph/extractors/ner_entity_extractor.py +244 -0
- aiecs/application/knowledge_graph/fusion/__init__.py +23 -0
- aiecs/application/knowledge_graph/fusion/entity_deduplicator.py +387 -0
- aiecs/application/knowledge_graph/fusion/entity_linker.py +343 -0
- aiecs/application/knowledge_graph/fusion/knowledge_fusion.py +580 -0
- aiecs/application/knowledge_graph/fusion/relation_deduplicator.py +189 -0
- aiecs/application/knowledge_graph/pattern_matching/__init__.py +21 -0
- aiecs/application/knowledge_graph/pattern_matching/pattern_matcher.py +344 -0
- aiecs/application/knowledge_graph/pattern_matching/query_executor.py +378 -0
- aiecs/application/knowledge_graph/profiling/__init__.py +12 -0
- aiecs/application/knowledge_graph/profiling/query_plan_visualizer.py +199 -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 +347 -0
- aiecs/application/knowledge_graph/reasoning/inference_engine.py +504 -0
- aiecs/application/knowledge_graph/reasoning/logic_form_parser.py +167 -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 +630 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/ast_validator.py +654 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/error_handler.py +477 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/parser.py +390 -0
- aiecs/application/knowledge_graph/reasoning/logic_parser/query_context.py +217 -0
- aiecs/application/knowledge_graph/reasoning/logic_query_integration.py +169 -0
- aiecs/application/knowledge_graph/reasoning/query_planner.py +872 -0
- aiecs/application/knowledge_graph/reasoning/reasoning_engine.py +554 -0
- aiecs/application/knowledge_graph/retrieval/__init__.py +19 -0
- aiecs/application/knowledge_graph/retrieval/retrieval_strategies.py +596 -0
- aiecs/application/knowledge_graph/search/__init__.py +59 -0
- aiecs/application/knowledge_graph/search/hybrid_search.py +423 -0
- aiecs/application/knowledge_graph/search/reranker.py +295 -0
- aiecs/application/knowledge_graph/search/reranker_strategies.py +553 -0
- aiecs/application/knowledge_graph/search/text_similarity.py +398 -0
- aiecs/application/knowledge_graph/traversal/__init__.py +15 -0
- aiecs/application/knowledge_graph/traversal/enhanced_traversal.py +329 -0
- aiecs/application/knowledge_graph/traversal/path_scorer.py +269 -0
- aiecs/application/knowledge_graph/validators/__init__.py +13 -0
- aiecs/application/knowledge_graph/validators/relation_validator.py +189 -0
- aiecs/application/knowledge_graph/visualization/__init__.py +11 -0
- aiecs/application/knowledge_graph/visualization/graph_visualizer.py +321 -0
- aiecs/common/__init__.py +9 -0
- aiecs/common/knowledge_graph/__init__.py +17 -0
- aiecs/common/knowledge_graph/runnable.py +484 -0
- aiecs/config/__init__.py +16 -0
- aiecs/config/config.py +498 -0
- aiecs/config/graph_config.py +137 -0
- aiecs/config/registry.py +23 -0
- aiecs/core/__init__.py +46 -0
- aiecs/core/interface/__init__.py +34 -0
- aiecs/core/interface/execution_interface.py +152 -0
- aiecs/core/interface/storage_interface.py +171 -0
- aiecs/domain/__init__.py +289 -0
- aiecs/domain/agent/__init__.py +189 -0
- aiecs/domain/agent/base_agent.py +697 -0
- aiecs/domain/agent/exceptions.py +103 -0
- aiecs/domain/agent/graph_aware_mixin.py +559 -0
- aiecs/domain/agent/hybrid_agent.py +490 -0
- aiecs/domain/agent/integration/__init__.py +26 -0
- aiecs/domain/agent/integration/context_compressor.py +222 -0
- aiecs/domain/agent/integration/context_engine_adapter.py +252 -0
- aiecs/domain/agent/integration/retry_policy.py +219 -0
- aiecs/domain/agent/integration/role_config.py +213 -0
- aiecs/domain/agent/knowledge_aware_agent.py +646 -0
- aiecs/domain/agent/lifecycle.py +296 -0
- aiecs/domain/agent/llm_agent.py +300 -0
- aiecs/domain/agent/memory/__init__.py +12 -0
- aiecs/domain/agent/memory/conversation.py +197 -0
- aiecs/domain/agent/migration/__init__.py +14 -0
- aiecs/domain/agent/migration/conversion.py +160 -0
- aiecs/domain/agent/migration/legacy_wrapper.py +90 -0
- aiecs/domain/agent/models.py +317 -0
- aiecs/domain/agent/observability.py +407 -0
- aiecs/domain/agent/persistence.py +289 -0
- aiecs/domain/agent/prompts/__init__.py +29 -0
- aiecs/domain/agent/prompts/builder.py +161 -0
- aiecs/domain/agent/prompts/formatters.py +189 -0
- aiecs/domain/agent/prompts/template.py +255 -0
- aiecs/domain/agent/registry.py +260 -0
- aiecs/domain/agent/tool_agent.py +257 -0
- aiecs/domain/agent/tools/__init__.py +12 -0
- aiecs/domain/agent/tools/schema_generator.py +221 -0
- aiecs/domain/community/__init__.py +155 -0
- aiecs/domain/community/agent_adapter.py +477 -0
- aiecs/domain/community/analytics.py +481 -0
- aiecs/domain/community/collaborative_workflow.py +642 -0
- aiecs/domain/community/communication_hub.py +645 -0
- aiecs/domain/community/community_builder.py +320 -0
- aiecs/domain/community/community_integration.py +800 -0
- aiecs/domain/community/community_manager.py +813 -0
- aiecs/domain/community/decision_engine.py +879 -0
- aiecs/domain/community/exceptions.py +225 -0
- aiecs/domain/community/models/__init__.py +33 -0
- aiecs/domain/community/models/community_models.py +268 -0
- aiecs/domain/community/resource_manager.py +457 -0
- aiecs/domain/community/shared_context_manager.py +603 -0
- aiecs/domain/context/__init__.py +58 -0
- aiecs/domain/context/context_engine.py +989 -0
- aiecs/domain/context/conversation_models.py +354 -0
- aiecs/domain/context/graph_memory.py +467 -0
- aiecs/domain/execution/__init__.py +12 -0
- aiecs/domain/execution/model.py +57 -0
- aiecs/domain/knowledge_graph/__init__.py +19 -0
- aiecs/domain/knowledge_graph/models/__init__.py +52 -0
- aiecs/domain/knowledge_graph/models/entity.py +130 -0
- aiecs/domain/knowledge_graph/models/evidence.py +194 -0
- aiecs/domain/knowledge_graph/models/inference_rule.py +186 -0
- aiecs/domain/knowledge_graph/models/path.py +179 -0
- aiecs/domain/knowledge_graph/models/path_pattern.py +173 -0
- aiecs/domain/knowledge_graph/models/query.py +272 -0
- aiecs/domain/knowledge_graph/models/query_plan.py +187 -0
- aiecs/domain/knowledge_graph/models/relation.py +136 -0
- aiecs/domain/knowledge_graph/schema/__init__.py +23 -0
- aiecs/domain/knowledge_graph/schema/entity_type.py +135 -0
- aiecs/domain/knowledge_graph/schema/graph_schema.py +271 -0
- aiecs/domain/knowledge_graph/schema/property_schema.py +155 -0
- aiecs/domain/knowledge_graph/schema/relation_type.py +171 -0
- aiecs/domain/knowledge_graph/schema/schema_manager.py +496 -0
- aiecs/domain/knowledge_graph/schema/type_enums.py +205 -0
- aiecs/domain/task/__init__.py +13 -0
- aiecs/domain/task/dsl_processor.py +613 -0
- aiecs/domain/task/model.py +62 -0
- aiecs/domain/task/task_context.py +268 -0
- aiecs/infrastructure/__init__.py +24 -0
- aiecs/infrastructure/graph_storage/__init__.py +11 -0
- aiecs/infrastructure/graph_storage/base.py +601 -0
- aiecs/infrastructure/graph_storage/batch_operations.py +449 -0
- aiecs/infrastructure/graph_storage/cache.py +429 -0
- aiecs/infrastructure/graph_storage/distributed.py +226 -0
- aiecs/infrastructure/graph_storage/error_handling.py +390 -0
- aiecs/infrastructure/graph_storage/graceful_degradation.py +306 -0
- aiecs/infrastructure/graph_storage/health_checks.py +378 -0
- aiecs/infrastructure/graph_storage/in_memory.py +514 -0
- aiecs/infrastructure/graph_storage/index_optimization.py +483 -0
- aiecs/infrastructure/graph_storage/lazy_loading.py +410 -0
- aiecs/infrastructure/graph_storage/metrics.py +357 -0
- aiecs/infrastructure/graph_storage/migration.py +413 -0
- aiecs/infrastructure/graph_storage/pagination.py +471 -0
- aiecs/infrastructure/graph_storage/performance_monitoring.py +466 -0
- aiecs/infrastructure/graph_storage/postgres.py +871 -0
- aiecs/infrastructure/graph_storage/query_optimizer.py +635 -0
- aiecs/infrastructure/graph_storage/schema_cache.py +290 -0
- aiecs/infrastructure/graph_storage/sqlite.py +623 -0
- aiecs/infrastructure/graph_storage/streaming.py +495 -0
- aiecs/infrastructure/messaging/__init__.py +13 -0
- aiecs/infrastructure/messaging/celery_task_manager.py +383 -0
- aiecs/infrastructure/messaging/websocket_manager.py +298 -0
- aiecs/infrastructure/monitoring/__init__.py +34 -0
- aiecs/infrastructure/monitoring/executor_metrics.py +174 -0
- aiecs/infrastructure/monitoring/global_metrics_manager.py +213 -0
- aiecs/infrastructure/monitoring/structured_logger.py +48 -0
- aiecs/infrastructure/monitoring/tracing_manager.py +410 -0
- aiecs/infrastructure/persistence/__init__.py +24 -0
- aiecs/infrastructure/persistence/context_engine_client.py +187 -0
- aiecs/infrastructure/persistence/database_manager.py +333 -0
- aiecs/infrastructure/persistence/file_storage.py +754 -0
- aiecs/infrastructure/persistence/redis_client.py +220 -0
- aiecs/llm/__init__.py +86 -0
- aiecs/llm/callbacks/__init__.py +11 -0
- aiecs/llm/callbacks/custom_callbacks.py +264 -0
- aiecs/llm/client_factory.py +420 -0
- aiecs/llm/clients/__init__.py +33 -0
- aiecs/llm/clients/base_client.py +193 -0
- aiecs/llm/clients/googleai_client.py +181 -0
- aiecs/llm/clients/openai_client.py +131 -0
- aiecs/llm/clients/vertex_client.py +437 -0
- aiecs/llm/clients/xai_client.py +184 -0
- aiecs/llm/config/__init__.py +51 -0
- aiecs/llm/config/config_loader.py +275 -0
- aiecs/llm/config/config_validator.py +236 -0
- aiecs/llm/config/model_config.py +151 -0
- aiecs/llm/utils/__init__.py +10 -0
- aiecs/llm/utils/validate_config.py +91 -0
- aiecs/main.py +363 -0
- aiecs/scripts/__init__.py +3 -0
- aiecs/scripts/aid/VERSION_MANAGEMENT.md +97 -0
- aiecs/scripts/aid/__init__.py +19 -0
- aiecs/scripts/aid/version_manager.py +215 -0
- aiecs/scripts/dependance_check/DEPENDENCY_SYSTEM_SUMMARY.md +242 -0
- aiecs/scripts/dependance_check/README_DEPENDENCY_CHECKER.md +310 -0
- aiecs/scripts/dependance_check/__init__.py +17 -0
- aiecs/scripts/dependance_check/dependency_checker.py +938 -0
- aiecs/scripts/dependance_check/dependency_fixer.py +391 -0
- aiecs/scripts/dependance_check/download_nlp_data.py +396 -0
- aiecs/scripts/dependance_check/quick_dependency_check.py +270 -0
- aiecs/scripts/dependance_check/setup_nlp_data.sh +217 -0
- aiecs/scripts/dependance_patch/__init__.py +7 -0
- aiecs/scripts/dependance_patch/fix_weasel/README_WEASEL_PATCH.md +126 -0
- aiecs/scripts/dependance_patch/fix_weasel/__init__.py +11 -0
- aiecs/scripts/dependance_patch/fix_weasel/fix_weasel_validator.py +128 -0
- aiecs/scripts/dependance_patch/fix_weasel/fix_weasel_validator.sh +82 -0
- aiecs/scripts/dependance_patch/fix_weasel/patch_weasel_library.sh +188 -0
- aiecs/scripts/dependance_patch/fix_weasel/run_weasel_patch.sh +41 -0
- aiecs/scripts/tools_develop/README.md +449 -0
- aiecs/scripts/tools_develop/TOOL_AUTO_DISCOVERY.md +234 -0
- aiecs/scripts/tools_develop/__init__.py +21 -0
- aiecs/scripts/tools_develop/check_type_annotations.py +259 -0
- aiecs/scripts/tools_develop/validate_tool_schemas.py +422 -0
- aiecs/scripts/tools_develop/verify_tools.py +356 -0
- aiecs/tasks/__init__.py +1 -0
- aiecs/tasks/worker.py +172 -0
- aiecs/tools/__init__.py +299 -0
- aiecs/tools/apisource/__init__.py +99 -0
- aiecs/tools/apisource/intelligence/__init__.py +19 -0
- aiecs/tools/apisource/intelligence/data_fusion.py +381 -0
- aiecs/tools/apisource/intelligence/query_analyzer.py +413 -0
- aiecs/tools/apisource/intelligence/search_enhancer.py +388 -0
- aiecs/tools/apisource/monitoring/__init__.py +9 -0
- aiecs/tools/apisource/monitoring/metrics.py +303 -0
- aiecs/tools/apisource/providers/__init__.py +115 -0
- aiecs/tools/apisource/providers/base.py +664 -0
- aiecs/tools/apisource/providers/census.py +401 -0
- aiecs/tools/apisource/providers/fred.py +564 -0
- aiecs/tools/apisource/providers/newsapi.py +412 -0
- aiecs/tools/apisource/providers/worldbank.py +357 -0
- aiecs/tools/apisource/reliability/__init__.py +12 -0
- aiecs/tools/apisource/reliability/error_handler.py +375 -0
- aiecs/tools/apisource/reliability/fallback_strategy.py +391 -0
- aiecs/tools/apisource/tool.py +850 -0
- aiecs/tools/apisource/utils/__init__.py +9 -0
- aiecs/tools/apisource/utils/validators.py +338 -0
- aiecs/tools/base_tool.py +201 -0
- aiecs/tools/docs/__init__.py +121 -0
- aiecs/tools/docs/ai_document_orchestrator.py +599 -0
- aiecs/tools/docs/ai_document_writer_orchestrator.py +2403 -0
- aiecs/tools/docs/content_insertion_tool.py +1333 -0
- aiecs/tools/docs/document_creator_tool.py +1317 -0
- aiecs/tools/docs/document_layout_tool.py +1166 -0
- aiecs/tools/docs/document_parser_tool.py +994 -0
- aiecs/tools/docs/document_writer_tool.py +1818 -0
- aiecs/tools/knowledge_graph/__init__.py +17 -0
- aiecs/tools/knowledge_graph/graph_reasoning_tool.py +734 -0
- aiecs/tools/knowledge_graph/graph_search_tool.py +923 -0
- aiecs/tools/knowledge_graph/kg_builder_tool.py +476 -0
- aiecs/tools/langchain_adapter.py +542 -0
- aiecs/tools/schema_generator.py +275 -0
- aiecs/tools/search_tool/__init__.py +100 -0
- aiecs/tools/search_tool/analyzers.py +589 -0
- aiecs/tools/search_tool/cache.py +260 -0
- aiecs/tools/search_tool/constants.py +128 -0
- aiecs/tools/search_tool/context.py +216 -0
- aiecs/tools/search_tool/core.py +749 -0
- aiecs/tools/search_tool/deduplicator.py +123 -0
- aiecs/tools/search_tool/error_handler.py +271 -0
- aiecs/tools/search_tool/metrics.py +371 -0
- aiecs/tools/search_tool/rate_limiter.py +178 -0
- aiecs/tools/search_tool/schemas.py +277 -0
- aiecs/tools/statistics/__init__.py +80 -0
- aiecs/tools/statistics/ai_data_analysis_orchestrator.py +643 -0
- aiecs/tools/statistics/ai_insight_generator_tool.py +505 -0
- aiecs/tools/statistics/ai_report_orchestrator_tool.py +694 -0
- aiecs/tools/statistics/data_loader_tool.py +564 -0
- aiecs/tools/statistics/data_profiler_tool.py +658 -0
- aiecs/tools/statistics/data_transformer_tool.py +573 -0
- aiecs/tools/statistics/data_visualizer_tool.py +495 -0
- aiecs/tools/statistics/model_trainer_tool.py +487 -0
- aiecs/tools/statistics/statistical_analyzer_tool.py +459 -0
- aiecs/tools/task_tools/__init__.py +86 -0
- aiecs/tools/task_tools/chart_tool.py +732 -0
- aiecs/tools/task_tools/classfire_tool.py +922 -0
- aiecs/tools/task_tools/image_tool.py +447 -0
- aiecs/tools/task_tools/office_tool.py +684 -0
- aiecs/tools/task_tools/pandas_tool.py +635 -0
- aiecs/tools/task_tools/report_tool.py +635 -0
- aiecs/tools/task_tools/research_tool.py +392 -0
- aiecs/tools/task_tools/scraper_tool.py +715 -0
- aiecs/tools/task_tools/stats_tool.py +688 -0
- aiecs/tools/temp_file_manager.py +130 -0
- aiecs/tools/tool_executor/__init__.py +37 -0
- aiecs/tools/tool_executor/tool_executor.py +881 -0
- aiecs/utils/LLM_output_structor.py +445 -0
- aiecs/utils/__init__.py +34 -0
- aiecs/utils/base_callback.py +47 -0
- aiecs/utils/cache_provider.py +695 -0
- aiecs/utils/execution_utils.py +184 -0
- aiecs/utils/logging.py +1 -0
- aiecs/utils/prompt_loader.py +14 -0
- aiecs/utils/token_usage_repository.py +323 -0
- aiecs/ws/__init__.py +0 -0
- aiecs/ws/socket_server.py +52 -0
- aiecs-1.5.1.dist-info/METADATA +608 -0
- aiecs-1.5.1.dist-info/RECORD +302 -0
- aiecs-1.5.1.dist-info/WHEEL +5 -0
- aiecs-1.5.1.dist-info/entry_points.txt +10 -0
- aiecs-1.5.1.dist-info/licenses/LICENSE +225 -0
- aiecs-1.5.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Hybrid Agent
|
|
3
|
+
|
|
4
|
+
Agent implementation combining LLM reasoning with tool execution capabilities.
|
|
5
|
+
Implements the ReAct (Reasoning + Acting) pattern.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from typing import Dict, List, Any, Optional
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
|
|
12
|
+
from aiecs.llm import BaseLLMClient, LLMMessage
|
|
13
|
+
from aiecs.tools import get_tool, BaseTool
|
|
14
|
+
|
|
15
|
+
from .base_agent import BaseAIAgent
|
|
16
|
+
from .models import AgentType, AgentConfiguration
|
|
17
|
+
from .exceptions import TaskExecutionError, ToolAccessDeniedError
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class HybridAgent(BaseAIAgent):
|
|
23
|
+
"""
|
|
24
|
+
Hybrid agent combining LLM reasoning with tool execution.
|
|
25
|
+
|
|
26
|
+
Implements ReAct pattern: Reason → Act → Observe loop.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
agent_id: str,
|
|
32
|
+
name: str,
|
|
33
|
+
llm_client: BaseLLMClient,
|
|
34
|
+
tools: List[str],
|
|
35
|
+
config: AgentConfiguration,
|
|
36
|
+
description: Optional[str] = None,
|
|
37
|
+
version: str = "1.0.0",
|
|
38
|
+
max_iterations: int = 10,
|
|
39
|
+
):
|
|
40
|
+
"""
|
|
41
|
+
Initialize Hybrid agent.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
agent_id: Unique agent identifier
|
|
45
|
+
name: Agent name
|
|
46
|
+
llm_client: LLM client for reasoning
|
|
47
|
+
tools: List of tool names
|
|
48
|
+
config: Agent configuration
|
|
49
|
+
description: Optional description
|
|
50
|
+
version: Agent version
|
|
51
|
+
max_iterations: Maximum ReAct iterations
|
|
52
|
+
"""
|
|
53
|
+
super().__init__(
|
|
54
|
+
agent_id=agent_id,
|
|
55
|
+
name=name,
|
|
56
|
+
agent_type=AgentType.DEVELOPER, # Can be adjusted based on use case
|
|
57
|
+
config=config,
|
|
58
|
+
description=description or "Hybrid agent with LLM reasoning and tool execution",
|
|
59
|
+
version=version,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
self.llm_client = llm_client
|
|
63
|
+
self._available_tools = tools
|
|
64
|
+
self._max_iterations = max_iterations
|
|
65
|
+
self._tool_instances: Dict[str, BaseTool] = {}
|
|
66
|
+
self._system_prompt: Optional[str] = None
|
|
67
|
+
self._conversation_history: List[LLMMessage] = []
|
|
68
|
+
|
|
69
|
+
logger.info(
|
|
70
|
+
f"HybridAgent initialized: {agent_id} with LLM ({llm_client.provider_name}) "
|
|
71
|
+
f"and tools: {', '.join(tools)}"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
async def _initialize(self) -> None:
|
|
75
|
+
"""Initialize Hybrid agent - build system prompt and load tools."""
|
|
76
|
+
# Build system prompt
|
|
77
|
+
self._system_prompt = self._build_system_prompt()
|
|
78
|
+
|
|
79
|
+
# Load tool instances
|
|
80
|
+
for tool_name in self._available_tools:
|
|
81
|
+
try:
|
|
82
|
+
self._tool_instances[tool_name] = get_tool(tool_name)
|
|
83
|
+
logger.debug(f"HybridAgent {self.agent_id} loaded tool: {tool_name}")
|
|
84
|
+
except Exception as e:
|
|
85
|
+
logger.warning(f"Failed to load tool {tool_name}: {e}")
|
|
86
|
+
|
|
87
|
+
logger.info(
|
|
88
|
+
f"HybridAgent {self.agent_id} initialized with {len(self._tool_instances)} tools"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
async def _shutdown(self) -> None:
|
|
92
|
+
"""Shutdown Hybrid agent."""
|
|
93
|
+
self._conversation_history.clear()
|
|
94
|
+
self._tool_instances.clear()
|
|
95
|
+
|
|
96
|
+
if hasattr(self.llm_client, "close"):
|
|
97
|
+
await self.llm_client.close()
|
|
98
|
+
|
|
99
|
+
logger.info(f"HybridAgent {self.agent_id} shut down")
|
|
100
|
+
|
|
101
|
+
def _build_system_prompt(self) -> str:
|
|
102
|
+
"""Build system prompt including tool descriptions."""
|
|
103
|
+
parts = []
|
|
104
|
+
|
|
105
|
+
# Add goal and backstory
|
|
106
|
+
if self._config.goal:
|
|
107
|
+
parts.append(f"Goal: {self._config.goal}")
|
|
108
|
+
|
|
109
|
+
if self._config.backstory:
|
|
110
|
+
parts.append(f"Background: {self._config.backstory}")
|
|
111
|
+
|
|
112
|
+
# Add ReAct instructions
|
|
113
|
+
parts.append(
|
|
114
|
+
"You are a reasoning agent that can use tools to complete tasks. "
|
|
115
|
+
"Follow the ReAct pattern:\n"
|
|
116
|
+
"1. THOUGHT: Analyze the task and decide what to do\n"
|
|
117
|
+
"2. ACTION: Use a tool if needed, or provide final answer\n"
|
|
118
|
+
"3. OBSERVATION: Review the tool result and continue reasoning\n\n"
|
|
119
|
+
"When you need to use a tool, respond with:\n"
|
|
120
|
+
"TOOL: <tool_name>\n"
|
|
121
|
+
"OPERATION: <operation_name>\n"
|
|
122
|
+
"PARAMETERS: <json_parameters>\n\n"
|
|
123
|
+
"When you have the final answer, respond with:\n"
|
|
124
|
+
"FINAL ANSWER: <your_answer>"
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# Add available tools
|
|
128
|
+
if self._available_tools:
|
|
129
|
+
parts.append(f"\nAvailable tools: {', '.join(self._available_tools)}")
|
|
130
|
+
|
|
131
|
+
if self._config.domain_knowledge:
|
|
132
|
+
parts.append(f"\nDomain Knowledge: {self._config.domain_knowledge}")
|
|
133
|
+
|
|
134
|
+
return "\n\n".join(parts)
|
|
135
|
+
|
|
136
|
+
async def execute_task(self, task: Dict[str, Any], context: Dict[str, Any]) -> Dict[str, Any]:
|
|
137
|
+
"""
|
|
138
|
+
Execute a task using ReAct loop.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
task: Task specification with 'description' or 'prompt'
|
|
142
|
+
context: Execution context
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
Execution result with 'output', 'reasoning_steps', 'tool_calls'
|
|
146
|
+
|
|
147
|
+
Raises:
|
|
148
|
+
TaskExecutionError: If task execution fails
|
|
149
|
+
"""
|
|
150
|
+
start_time = datetime.utcnow()
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
# Extract task description
|
|
154
|
+
task_description = task.get("description") or task.get("prompt") or task.get("task")
|
|
155
|
+
if not task_description:
|
|
156
|
+
raise TaskExecutionError(
|
|
157
|
+
"Task must contain 'description', 'prompt', or 'task' field",
|
|
158
|
+
agent_id=self.agent_id,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
# Transition to busy state
|
|
162
|
+
self._transition_state(self.state.__class__.BUSY)
|
|
163
|
+
self._current_task_id = task.get("task_id")
|
|
164
|
+
|
|
165
|
+
# Execute ReAct loop
|
|
166
|
+
result = await self._react_loop(task_description, context)
|
|
167
|
+
|
|
168
|
+
# Calculate execution time
|
|
169
|
+
execution_time = (datetime.utcnow() - start_time).total_seconds()
|
|
170
|
+
|
|
171
|
+
# Update metrics
|
|
172
|
+
self.update_metrics(
|
|
173
|
+
execution_time=execution_time,
|
|
174
|
+
success=True,
|
|
175
|
+
tokens_used=result.get("total_tokens"),
|
|
176
|
+
tool_calls=result.get("tool_calls_count", 0),
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
# Transition back to active
|
|
180
|
+
self._transition_state(self.state.__class__.ACTIVE)
|
|
181
|
+
self._current_task_id = None
|
|
182
|
+
self.last_active_at = datetime.utcnow()
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
"success": True,
|
|
186
|
+
"output": result.get("final_answer"),
|
|
187
|
+
"reasoning_steps": result.get("steps"),
|
|
188
|
+
"tool_calls_count": result.get("tool_calls_count"),
|
|
189
|
+
"iterations": result.get("iterations"),
|
|
190
|
+
"execution_time": execution_time,
|
|
191
|
+
"timestamp": datetime.utcnow().isoformat(),
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
except Exception as e:
|
|
195
|
+
logger.error(f"Task execution failed for {self.agent_id}: {e}")
|
|
196
|
+
|
|
197
|
+
# Update metrics for failure
|
|
198
|
+
execution_time = (datetime.utcnow() - start_time).total_seconds()
|
|
199
|
+
self.update_metrics(execution_time=execution_time, success=False)
|
|
200
|
+
|
|
201
|
+
# Transition to error state
|
|
202
|
+
self._transition_state(self.state.__class__.ERROR)
|
|
203
|
+
self._current_task_id = None
|
|
204
|
+
|
|
205
|
+
raise TaskExecutionError(
|
|
206
|
+
f"Task execution failed: {str(e)}",
|
|
207
|
+
agent_id=self.agent_id,
|
|
208
|
+
task_id=task.get("task_id"),
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
async def process_message(
|
|
212
|
+
self, message: str, sender_id: Optional[str] = None
|
|
213
|
+
) -> Dict[str, Any]:
|
|
214
|
+
"""
|
|
215
|
+
Process an incoming message using ReAct loop.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
message: Message content
|
|
219
|
+
sender_id: Optional sender identifier
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
Response dictionary with 'response', 'reasoning_steps'
|
|
223
|
+
"""
|
|
224
|
+
try:
|
|
225
|
+
# Build task from message
|
|
226
|
+
task = {
|
|
227
|
+
"description": message,
|
|
228
|
+
"task_id": f"msg_{datetime.utcnow().timestamp()}",
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
# Execute as task
|
|
232
|
+
result = await self.execute_task(task, {"sender_id": sender_id})
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
"response": result.get("output"),
|
|
236
|
+
"reasoning_steps": result.get("reasoning_steps"),
|
|
237
|
+
"timestamp": result.get("timestamp"),
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
except Exception as e:
|
|
241
|
+
logger.error(f"Message processing failed for {self.agent_id}: {e}")
|
|
242
|
+
raise
|
|
243
|
+
|
|
244
|
+
async def _react_loop(self, task: str, context: Dict[str, Any]) -> Dict[str, Any]:
|
|
245
|
+
"""
|
|
246
|
+
Execute ReAct loop: Reason → Act → Observe.
|
|
247
|
+
|
|
248
|
+
Args:
|
|
249
|
+
task: Task description
|
|
250
|
+
context: Context dictionary
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
Result dictionary with 'final_answer', 'steps', 'iterations'
|
|
254
|
+
"""
|
|
255
|
+
steps = []
|
|
256
|
+
tool_calls_count = 0
|
|
257
|
+
total_tokens = 0
|
|
258
|
+
|
|
259
|
+
# Build initial messages
|
|
260
|
+
messages = self._build_initial_messages(task, context)
|
|
261
|
+
|
|
262
|
+
for iteration in range(self._max_iterations):
|
|
263
|
+
logger.debug(f"HybridAgent {self.agent_id} - ReAct iteration {iteration + 1}")
|
|
264
|
+
|
|
265
|
+
# THINK: LLM reasons about next action
|
|
266
|
+
response = await self.llm_client.generate_text(
|
|
267
|
+
messages=messages,
|
|
268
|
+
model=self._config.llm_model,
|
|
269
|
+
temperature=self._config.temperature,
|
|
270
|
+
max_tokens=self._config.max_tokens,
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
thought = response.content
|
|
274
|
+
total_tokens += getattr(response, "total_tokens", 0)
|
|
275
|
+
|
|
276
|
+
steps.append(
|
|
277
|
+
{
|
|
278
|
+
"type": "thought",
|
|
279
|
+
"content": thought,
|
|
280
|
+
"iteration": iteration + 1,
|
|
281
|
+
}
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
# Check if final answer
|
|
285
|
+
if "FINAL ANSWER:" in thought:
|
|
286
|
+
final_answer = self._extract_final_answer(thought)
|
|
287
|
+
return {
|
|
288
|
+
"final_answer": final_answer,
|
|
289
|
+
"steps": steps,
|
|
290
|
+
"iterations": iteration + 1,
|
|
291
|
+
"tool_calls_count": tool_calls_count,
|
|
292
|
+
"total_tokens": total_tokens,
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
# Check if tool call
|
|
296
|
+
if "TOOL:" in thought:
|
|
297
|
+
# ACT: Execute tool
|
|
298
|
+
try:
|
|
299
|
+
tool_info = self._parse_tool_call(thought)
|
|
300
|
+
tool_result = await self._execute_tool(
|
|
301
|
+
tool_info["tool"],
|
|
302
|
+
tool_info.get("operation"),
|
|
303
|
+
tool_info.get("parameters", {}),
|
|
304
|
+
)
|
|
305
|
+
tool_calls_count += 1
|
|
306
|
+
|
|
307
|
+
steps.append(
|
|
308
|
+
{
|
|
309
|
+
"type": "action",
|
|
310
|
+
"tool": tool_info["tool"],
|
|
311
|
+
"operation": tool_info.get("operation"),
|
|
312
|
+
"parameters": tool_info.get("parameters"),
|
|
313
|
+
"iteration": iteration + 1,
|
|
314
|
+
}
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
# OBSERVE: Add tool result to conversation
|
|
318
|
+
observation = f"OBSERVATION: Tool '{tool_info['tool']}' returned: {tool_result}"
|
|
319
|
+
steps.append(
|
|
320
|
+
{
|
|
321
|
+
"type": "observation",
|
|
322
|
+
"content": observation,
|
|
323
|
+
"iteration": iteration + 1,
|
|
324
|
+
}
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
# Add to messages for next iteration
|
|
328
|
+
messages.append(LLMMessage(role="assistant", content=thought))
|
|
329
|
+
messages.append(LLMMessage(role="user", content=observation))
|
|
330
|
+
|
|
331
|
+
except Exception as e:
|
|
332
|
+
error_msg = f"OBSERVATION: Tool execution failed: {str(e)}"
|
|
333
|
+
steps.append(
|
|
334
|
+
{
|
|
335
|
+
"type": "observation",
|
|
336
|
+
"content": error_msg,
|
|
337
|
+
"iteration": iteration + 1,
|
|
338
|
+
"error": True,
|
|
339
|
+
}
|
|
340
|
+
)
|
|
341
|
+
messages.append(LLMMessage(role="assistant", content=thought))
|
|
342
|
+
messages.append(LLMMessage(role="user", content=error_msg))
|
|
343
|
+
|
|
344
|
+
else:
|
|
345
|
+
# LLM didn't provide clear action - treat as final answer
|
|
346
|
+
return {
|
|
347
|
+
"final_answer": thought,
|
|
348
|
+
"steps": steps,
|
|
349
|
+
"iterations": iteration + 1,
|
|
350
|
+
"tool_calls_count": tool_calls_count,
|
|
351
|
+
"total_tokens": total_tokens,
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
# Max iterations reached
|
|
355
|
+
logger.warning(f"HybridAgent {self.agent_id} reached max iterations")
|
|
356
|
+
return {
|
|
357
|
+
"final_answer": "Max iterations reached. Unable to complete task fully.",
|
|
358
|
+
"steps": steps,
|
|
359
|
+
"iterations": self._max_iterations,
|
|
360
|
+
"tool_calls_count": tool_calls_count,
|
|
361
|
+
"total_tokens": total_tokens,
|
|
362
|
+
"max_iterations_reached": True,
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
def _build_initial_messages(self, task: str, context: Dict[str, Any]) -> List[LLMMessage]:
|
|
366
|
+
"""Build initial messages for ReAct loop."""
|
|
367
|
+
messages = []
|
|
368
|
+
|
|
369
|
+
# Add system prompt
|
|
370
|
+
if self._system_prompt:
|
|
371
|
+
messages.append(LLMMessage(role="system", content=self._system_prompt))
|
|
372
|
+
|
|
373
|
+
# Add context if provided
|
|
374
|
+
if context:
|
|
375
|
+
context_str = self._format_context(context)
|
|
376
|
+
if context_str:
|
|
377
|
+
messages.append(
|
|
378
|
+
LLMMessage(
|
|
379
|
+
role="system",
|
|
380
|
+
content=f"Additional Context:\n{context_str}",
|
|
381
|
+
)
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
# Add task
|
|
385
|
+
messages.append(LLMMessage(role="user", content=f"Task: {task}"))
|
|
386
|
+
|
|
387
|
+
return messages
|
|
388
|
+
|
|
389
|
+
def _format_context(self, context: Dict[str, Any]) -> str:
|
|
390
|
+
"""Format context dictionary as string."""
|
|
391
|
+
relevant_fields = []
|
|
392
|
+
for key, value in context.items():
|
|
393
|
+
if not key.startswith("_") and value is not None:
|
|
394
|
+
relevant_fields.append(f"{key}: {value}")
|
|
395
|
+
return "\n".join(relevant_fields) if relevant_fields else ""
|
|
396
|
+
|
|
397
|
+
def _extract_final_answer(self, thought: str) -> str:
|
|
398
|
+
"""Extract final answer from thought."""
|
|
399
|
+
if "FINAL ANSWER:" in thought:
|
|
400
|
+
return thought.split("FINAL ANSWER:", 1)[1].strip()
|
|
401
|
+
return thought
|
|
402
|
+
|
|
403
|
+
def _parse_tool_call(self, thought: str) -> Dict[str, Any]:
|
|
404
|
+
"""
|
|
405
|
+
Parse tool call from LLM thought.
|
|
406
|
+
|
|
407
|
+
Expected format:
|
|
408
|
+
TOOL: <tool_name>
|
|
409
|
+
OPERATION: <operation_name>
|
|
410
|
+
PARAMETERS: <json_parameters>
|
|
411
|
+
|
|
412
|
+
Args:
|
|
413
|
+
thought: LLM thought containing tool call
|
|
414
|
+
|
|
415
|
+
Returns:
|
|
416
|
+
Dictionary with 'tool', 'operation', 'parameters'
|
|
417
|
+
"""
|
|
418
|
+
import json
|
|
419
|
+
|
|
420
|
+
result = {}
|
|
421
|
+
|
|
422
|
+
# Extract tool
|
|
423
|
+
if "TOOL:" in thought:
|
|
424
|
+
tool_line = [line for line in thought.split("\n") if line.startswith("TOOL:")][0]
|
|
425
|
+
result["tool"] = tool_line.split("TOOL:", 1)[1].strip()
|
|
426
|
+
|
|
427
|
+
# Extract operation (optional)
|
|
428
|
+
if "OPERATION:" in thought:
|
|
429
|
+
op_line = [line for line in thought.split("\n") if line.startswith("OPERATION:")][0]
|
|
430
|
+
result["operation"] = op_line.split("OPERATION:", 1)[1].strip()
|
|
431
|
+
|
|
432
|
+
# Extract parameters (optional)
|
|
433
|
+
if "PARAMETERS:" in thought:
|
|
434
|
+
param_line = [line for line in thought.split("\n") if line.startswith("PARAMETERS:")][0]
|
|
435
|
+
param_str = param_line.split("PARAMETERS:", 1)[1].strip()
|
|
436
|
+
try:
|
|
437
|
+
result["parameters"] = json.loads(param_str)
|
|
438
|
+
except json.JSONDecodeError:
|
|
439
|
+
logger.warning(f"Failed to parse parameters: {param_str}")
|
|
440
|
+
result["parameters"] = {}
|
|
441
|
+
|
|
442
|
+
return result
|
|
443
|
+
|
|
444
|
+
async def _execute_tool(
|
|
445
|
+
self,
|
|
446
|
+
tool_name: str,
|
|
447
|
+
operation: Optional[str],
|
|
448
|
+
parameters: Dict[str, Any],
|
|
449
|
+
) -> Any:
|
|
450
|
+
"""Execute a tool operation."""
|
|
451
|
+
# Check access
|
|
452
|
+
if tool_name not in self._available_tools:
|
|
453
|
+
raise ToolAccessDeniedError(self.agent_id, tool_name)
|
|
454
|
+
|
|
455
|
+
tool = self._tool_instances.get(tool_name)
|
|
456
|
+
if not tool:
|
|
457
|
+
raise ValueError(f"Tool {tool_name} not loaded")
|
|
458
|
+
|
|
459
|
+
# Execute tool
|
|
460
|
+
if operation:
|
|
461
|
+
result = await tool.run_async(operation, **parameters)
|
|
462
|
+
else:
|
|
463
|
+
if hasattr(tool, "run_async"):
|
|
464
|
+
result = await tool.run_async(**parameters)
|
|
465
|
+
else:
|
|
466
|
+
raise ValueError(f"Tool {tool_name} requires operation to be specified")
|
|
467
|
+
|
|
468
|
+
return result
|
|
469
|
+
|
|
470
|
+
def get_available_tools(self) -> List[str]:
|
|
471
|
+
"""Get list of available tools."""
|
|
472
|
+
return self._available_tools.copy()
|
|
473
|
+
|
|
474
|
+
@classmethod
|
|
475
|
+
def from_dict(cls, data: Dict[str, Any]) -> "HybridAgent":
|
|
476
|
+
"""
|
|
477
|
+
Deserialize HybridAgent from dictionary.
|
|
478
|
+
|
|
479
|
+
Note: LLM client must be provided separately.
|
|
480
|
+
|
|
481
|
+
Args:
|
|
482
|
+
data: Dictionary representation
|
|
483
|
+
|
|
484
|
+
Returns:
|
|
485
|
+
HybridAgent instance
|
|
486
|
+
"""
|
|
487
|
+
raise NotImplementedError(
|
|
488
|
+
"HybridAgent.from_dict requires LLM client to be provided separately. "
|
|
489
|
+
"Use constructor instead."
|
|
490
|
+
)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Integration Module
|
|
3
|
+
|
|
4
|
+
Integration adapters for external systems.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .context_engine_adapter import ContextEngineAdapter
|
|
8
|
+
from .retry_policy import EnhancedRetryPolicy, ErrorClassifier, ErrorType
|
|
9
|
+
from .role_config import RoleConfiguration, load_role_config
|
|
10
|
+
from .context_compressor import (
|
|
11
|
+
ContextCompressor,
|
|
12
|
+
compress_messages,
|
|
13
|
+
CompressionStrategy,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"ContextEngineAdapter",
|
|
18
|
+
"EnhancedRetryPolicy",
|
|
19
|
+
"ErrorClassifier",
|
|
20
|
+
"ErrorType",
|
|
21
|
+
"RoleConfiguration",
|
|
22
|
+
"load_role_config",
|
|
23
|
+
"ContextCompressor",
|
|
24
|
+
"compress_messages",
|
|
25
|
+
"CompressionStrategy",
|
|
26
|
+
]
|