autobyteus 1.0.5__py3-none-any.whl → 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- autobyteus/agent/agent.py +97 -222
- autobyteus/agent/bootstrap_steps/__init__.py +19 -0
- autobyteus/agent/bootstrap_steps/agent_bootstrapper.py +88 -0
- autobyteus/agent/bootstrap_steps/agent_runtime_queue_initialization_step.py +57 -0
- autobyteus/agent/bootstrap_steps/base_bootstrap_step.py +38 -0
- autobyteus/agent/bootstrap_steps/system_prompt_processing_step.py +93 -0
- autobyteus/agent/bootstrap_steps/workspace_context_initialization_step.py +47 -0
- autobyteus/agent/context/__init__.py +18 -0
- autobyteus/agent/context/agent_config.py +80 -0
- autobyteus/agent/context/agent_context.py +132 -0
- autobyteus/agent/context/agent_phase_manager.py +164 -0
- autobyteus/agent/context/agent_runtime_state.py +89 -0
- autobyteus/agent/context/phases.py +47 -0
- autobyteus/agent/events/__init__.py +63 -0
- autobyteus/agent/events/agent_events.py +147 -0
- autobyteus/agent/events/agent_input_event_queue_manager.py +174 -0
- autobyteus/agent/events/notifiers.py +104 -0
- autobyteus/agent/events/worker_event_dispatcher.py +118 -0
- autobyteus/agent/factory/__init__.py +9 -0
- autobyteus/agent/factory/agent_factory.py +126 -79
- autobyteus/agent/group/agent_group.py +155 -0
- autobyteus/agent/group/agent_group_context.py +81 -0
- autobyteus/agent/handlers/__init__.py +36 -0
- autobyteus/agent/handlers/approved_tool_invocation_event_handler.py +134 -0
- autobyteus/agent/handlers/base_event_handler.py +36 -0
- autobyteus/agent/handlers/event_handler_registry.py +76 -0
- autobyteus/agent/handlers/generic_event_handler.py +46 -0
- autobyteus/agent/handlers/inter_agent_message_event_handler.py +76 -0
- autobyteus/agent/handlers/lifecycle_event_logger.py +64 -0
- autobyteus/agent/handlers/llm_complete_response_received_event_handler.py +136 -0
- autobyteus/agent/handlers/llm_user_message_ready_event_handler.py +140 -0
- autobyteus/agent/handlers/tool_execution_approval_event_handler.py +85 -0
- autobyteus/agent/handlers/tool_invocation_request_event_handler.py +186 -0
- autobyteus/agent/handlers/tool_result_event_handler.py +96 -0
- autobyteus/agent/handlers/user_input_message_event_handler.py +77 -0
- autobyteus/agent/hooks/__init__.py +9 -0
- autobyteus/agent/hooks/base_phase_hook.py +52 -0
- autobyteus/agent/input_processor/__init__.py +18 -0
- autobyteus/agent/input_processor/base_user_input_processor.py +51 -0
- autobyteus/agent/input_processor/content_prefixing_input_processor.py +41 -0
- autobyteus/agent/input_processor/metadata_appending_input_processor.py +34 -0
- autobyteus/agent/input_processor/passthrough_input_processor.py +32 -0
- autobyteus/agent/input_processor/processor_definition.py +42 -0
- autobyteus/agent/input_processor/processor_meta.py +46 -0
- autobyteus/agent/input_processor/processor_registry.py +98 -0
- autobyteus/agent/llm_response_processor/__init__.py +16 -0
- autobyteus/agent/llm_response_processor/base_processor.py +50 -0
- autobyteus/agent/llm_response_processor/processor_definition.py +36 -0
- autobyteus/agent/llm_response_processor/processor_meta.py +37 -0
- autobyteus/agent/llm_response_processor/processor_registry.py +94 -0
- autobyteus/agent/llm_response_processor/provider_aware_tool_usage_processor.py +53 -0
- autobyteus/agent/message/__init__.py +20 -0
- autobyteus/agent/message/agent_input_user_message.py +96 -0
- autobyteus/agent/message/context_file.py +82 -0
- autobyteus/agent/message/context_file_type.py +64 -0
- autobyteus/agent/message/{message.py → inter_agent_message.py} +12 -12
- autobyteus/agent/message/{message_types.py → inter_agent_message_type.py} +8 -6
- autobyteus/agent/message/send_message_to.py +142 -36
- autobyteus/agent/remote_agent.py +240 -5
- autobyteus/agent/runtime/__init__.py +15 -0
- autobyteus/agent/runtime/agent_runtime.py +139 -0
- autobyteus/agent/runtime/agent_thread_pool_manager.py +88 -0
- autobyteus/agent/runtime/agent_worker.py +200 -0
- autobyteus/agent/streaming/__init__.py +15 -0
- autobyteus/agent/streaming/agent_event_stream.py +120 -0
- autobyteus/agent/streaming/queue_streamer.py +58 -0
- autobyteus/agent/streaming/stream_event_payloads.py +156 -0
- autobyteus/agent/streaming/stream_events.py +123 -0
- autobyteus/agent/system_prompt_processor/__init__.py +14 -0
- autobyteus/agent/system_prompt_processor/base_processor.py +45 -0
- autobyteus/agent/system_prompt_processor/processor_definition.py +40 -0
- autobyteus/agent/system_prompt_processor/processor_meta.py +47 -0
- autobyteus/agent/system_prompt_processor/processor_registry.py +119 -0
- autobyteus/agent/system_prompt_processor/tool_manifest_injector_processor.py +65 -0
- autobyteus/agent/tool_invocation.py +28 -5
- autobyteus/agent/utils/__init__.py +9 -0
- autobyteus/agent/utils/wait_for_idle.py +59 -0
- autobyteus/agent/workflow/__init__.py +11 -0
- autobyteus/agent/workflow/agentic_workflow.py +89 -0
- autobyteus/agent/workflow/base_agentic_workflow.py +98 -0
- autobyteus/agent/workspace/__init__.py +9 -0
- autobyteus/agent/workspace/base_workspace.py +55 -0
- autobyteus/cli/__init__.py +10 -0
- autobyteus/cli/agent_cli.py +299 -0
- autobyteus/events/event_emitter.py +33 -56
- autobyteus/events/event_manager.py +133 -66
- autobyteus/events/event_types.py +41 -14
- autobyteus/llm/api/autobyteus_llm.py +13 -15
- autobyteus/llm/api/bedrock_llm.py +9 -3
- autobyteus/llm/api/claude_llm.py +10 -5
- autobyteus/llm/api/deepseek_llm.py +53 -91
- autobyteus/llm/api/gemini_llm.py +10 -4
- autobyteus/llm/api/grok_llm.py +53 -77
- autobyteus/llm/api/groq_llm.py +10 -5
- autobyteus/llm/api/mistral_llm.py +10 -5
- autobyteus/llm/api/nvidia_llm.py +9 -4
- autobyteus/llm/api/ollama_llm.py +56 -48
- autobyteus/llm/api/openai_llm.py +20 -14
- autobyteus/llm/base_llm.py +95 -34
- autobyteus/llm/extensions/base_extension.py +3 -4
- autobyteus/llm/extensions/token_usage_tracking_extension.py +2 -3
- autobyteus/llm/llm_factory.py +12 -13
- autobyteus/llm/models.py +87 -8
- autobyteus/llm/user_message.py +73 -0
- autobyteus/llm/utils/llm_config.py +124 -27
- autobyteus/llm/utils/response_types.py +3 -2
- autobyteus/llm/utils/token_usage.py +7 -4
- autobyteus/rpc/__init__.py +73 -0
- autobyteus/rpc/client/__init__.py +17 -0
- autobyteus/rpc/client/abstract_client_connection.py +124 -0
- autobyteus/rpc/client/client_connection_manager.py +153 -0
- autobyteus/rpc/client/sse_client_connection.py +306 -0
- autobyteus/rpc/client/stdio_client_connection.py +280 -0
- autobyteus/rpc/config/__init__.py +13 -0
- autobyteus/rpc/config/agent_server_config.py +153 -0
- autobyteus/rpc/config/agent_server_registry.py +152 -0
- autobyteus/rpc/hosting.py +244 -0
- autobyteus/rpc/protocol.py +244 -0
- autobyteus/rpc/server/__init__.py +20 -0
- autobyteus/rpc/server/agent_server_endpoint.py +181 -0
- autobyteus/rpc/server/base_method_handler.py +40 -0
- autobyteus/rpc/server/method_handlers.py +259 -0
- autobyteus/rpc/server/sse_server_handler.py +182 -0
- autobyteus/rpc/server/stdio_server_handler.py +151 -0
- autobyteus/rpc/server_main.py +198 -0
- autobyteus/rpc/transport_type.py +13 -0
- autobyteus/tools/__init__.py +75 -0
- autobyteus/tools/ask_user_input.py +34 -77
- autobyteus/tools/base_tool.py +66 -37
- autobyteus/tools/bash/__init__.py +2 -0
- autobyteus/tools/bash/bash_executor.py +42 -79
- autobyteus/tools/browser/__init__.py +2 -0
- autobyteus/tools/browser/session_aware/browser_session_aware_navigate_to.py +50 -42
- autobyteus/tools/browser/session_aware/browser_session_aware_tool.py +7 -4
- autobyteus/tools/browser/session_aware/browser_session_aware_web_element_trigger.py +117 -125
- autobyteus/tools/browser/session_aware/browser_session_aware_webpage_reader.py +75 -22
- autobyteus/tools/browser/session_aware/browser_session_aware_webpage_screenshot_taker.py +94 -28
- autobyteus/tools/browser/session_aware/factory/browser_session_aware_web_element_trigger_factory.py +10 -2
- autobyteus/tools/browser/session_aware/factory/browser_session_aware_webpage_reader_factory.py +18 -2
- autobyteus/tools/browser/session_aware/factory/browser_session_aware_webpage_screenshot_taker_factory.py +10 -2
- autobyteus/tools/browser/session_aware/shared_browser_session_manager.py +4 -3
- autobyteus/tools/browser/standalone/__init__.py +7 -0
- autobyteus/tools/browser/standalone/factory/google_search_factory.py +17 -2
- autobyteus/tools/browser/standalone/factory/webpage_reader_factory.py +17 -2
- autobyteus/tools/browser/standalone/factory/webpage_screenshot_taker_factory.py +10 -2
- autobyteus/tools/browser/standalone/google_search_ui.py +104 -67
- autobyteus/tools/browser/standalone/navigate_to.py +52 -28
- autobyteus/tools/browser/standalone/web_page_pdf_generator.py +94 -0
- autobyteus/tools/browser/standalone/webpage_image_downloader.py +146 -61
- autobyteus/tools/browser/standalone/webpage_reader.py +80 -61
- autobyteus/tools/browser/standalone/webpage_screenshot_taker.py +91 -45
- autobyteus/tools/factory/__init__.py +9 -0
- autobyteus/tools/factory/tool_factory.py +25 -4
- autobyteus/tools/file/file_reader.py +22 -51
- autobyteus/tools/file/file_writer.py +25 -56
- autobyteus/tools/functional_tool.py +234 -0
- autobyteus/tools/image_downloader.py +49 -71
- autobyteus/tools/mcp/__init__.py +47 -0
- autobyteus/tools/mcp/call_handlers/__init__.py +18 -0
- autobyteus/tools/mcp/call_handlers/base_handler.py +40 -0
- autobyteus/tools/mcp/call_handlers/sse_handler.py +22 -0
- autobyteus/tools/mcp/call_handlers/stdio_handler.py +62 -0
- autobyteus/tools/mcp/call_handlers/streamable_http_handler.py +55 -0
- autobyteus/tools/mcp/config_service.py +258 -0
- autobyteus/tools/mcp/factory.py +70 -0
- autobyteus/tools/mcp/registrar.py +135 -0
- autobyteus/tools/mcp/schema_mapper.py +131 -0
- autobyteus/tools/mcp/tool.py +101 -0
- autobyteus/tools/mcp/types.py +96 -0
- autobyteus/tools/parameter_schema.py +268 -0
- autobyteus/tools/pdf_downloader.py +78 -79
- autobyteus/tools/registry/__init__.py +0 -2
- autobyteus/tools/registry/tool_definition.py +106 -34
- autobyteus/tools/registry/tool_registry.py +46 -22
- autobyteus/tools/timer.py +150 -102
- autobyteus/tools/tool_config.py +117 -0
- autobyteus/tools/tool_meta.py +48 -26
- autobyteus/tools/usage/__init__.py +6 -0
- autobyteus/tools/usage/formatters/__init__.py +31 -0
- autobyteus/tools/usage/formatters/anthropic_json_example_formatter.py +18 -0
- autobyteus/tools/usage/formatters/anthropic_json_schema_formatter.py +25 -0
- autobyteus/tools/usage/formatters/base_formatter.py +42 -0
- autobyteus/tools/usage/formatters/default_json_example_formatter.py +42 -0
- autobyteus/tools/usage/formatters/default_json_schema_formatter.py +28 -0
- autobyteus/tools/usage/formatters/default_xml_example_formatter.py +55 -0
- autobyteus/tools/usage/formatters/default_xml_schema_formatter.py +46 -0
- autobyteus/tools/usage/formatters/gemini_json_example_formatter.py +34 -0
- autobyteus/tools/usage/formatters/gemini_json_schema_formatter.py +25 -0
- autobyteus/tools/usage/formatters/google_json_example_formatter.py +34 -0
- autobyteus/tools/usage/formatters/google_json_schema_formatter.py +25 -0
- autobyteus/tools/usage/formatters/openai_json_example_formatter.py +49 -0
- autobyteus/tools/usage/formatters/openai_json_schema_formatter.py +28 -0
- autobyteus/tools/usage/parsers/__init__.py +22 -0
- autobyteus/tools/usage/parsers/anthropic_xml_tool_usage_parser.py +10 -0
- autobyteus/tools/usage/parsers/base_parser.py +41 -0
- autobyteus/tools/usage/parsers/default_json_tool_usage_parser.py +106 -0
- autobyteus/tools/usage/parsers/default_xml_tool_usage_parser.py +135 -0
- autobyteus/tools/usage/parsers/exceptions.py +13 -0
- autobyteus/tools/usage/parsers/gemini_json_tool_usage_parser.py +68 -0
- autobyteus/tools/usage/parsers/openai_json_tool_usage_parser.py +147 -0
- autobyteus/tools/usage/parsers/provider_aware_tool_usage_parser.py +67 -0
- autobyteus/tools/usage/providers/__init__.py +22 -0
- autobyteus/tools/usage/providers/json_example_provider.py +32 -0
- autobyteus/tools/usage/providers/json_schema_provider.py +35 -0
- autobyteus/tools/usage/providers/json_tool_usage_parser_provider.py +28 -0
- autobyteus/tools/usage/providers/tool_manifest_provider.py +68 -0
- autobyteus/tools/usage/providers/xml_example_provider.py +28 -0
- autobyteus/tools/usage/providers/xml_schema_provider.py +29 -0
- autobyteus/tools/usage/providers/xml_tool_usage_parser_provider.py +26 -0
- autobyteus/tools/usage/registries/__init__.py +20 -0
- autobyteus/tools/usage/registries/json_example_formatter_registry.py +51 -0
- autobyteus/tools/usage/registries/json_schema_formatter_registry.py +51 -0
- autobyteus/tools/usage/registries/json_tool_usage_parser_registry.py +42 -0
- autobyteus/tools/usage/registries/xml_example_formatter_registry.py +30 -0
- autobyteus/tools/usage/registries/xml_schema_formatter_registry.py +33 -0
- autobyteus/tools/usage/registries/xml_tool_usage_parser_registry.py +30 -0
- {autobyteus-1.0.5.dist-info → autobyteus-1.1.0.dist-info}/METADATA +21 -3
- autobyteus-1.1.0.dist-info/RECORD +279 -0
- {autobyteus-1.0.5.dist-info → autobyteus-1.1.0.dist-info}/WHEEL +1 -1
- autobyteus/agent/async_agent.py +0 -175
- autobyteus/agent/async_group_aware_agent.py +0 -136
- autobyteus/agent/group/async_group_aware_agent.py +0 -122
- autobyteus/agent/group/coordinator_agent.py +0 -36
- autobyteus/agent/group/group_aware_agent.py +0 -121
- autobyteus/agent/orchestrator/__init__.py +0 -0
- autobyteus/agent/orchestrator/base_agent_orchestrator.py +0 -82
- autobyteus/agent/orchestrator/multi_replica_agent_orchestrator.py +0 -72
- autobyteus/agent/orchestrator/single_replica_agent_orchestrator.py +0 -43
- autobyteus/agent/registry/__init__.py +0 -11
- autobyteus/agent/registry/agent_definition.py +0 -94
- autobyteus/agent/registry/agent_registry.py +0 -114
- autobyteus/agent/response_parser/__init__.py +0 -0
- autobyteus/agent/response_parser/tool_usage_command_parser.py +0 -100
- autobyteus/agent/status.py +0 -12
- autobyteus/conversation/__init__.py +0 -0
- autobyteus/conversation/conversation.py +0 -54
- autobyteus/conversation/user_message.py +0 -59
- autobyteus/events/decorators.py +0 -29
- autobyteus/prompt/prompt_version_manager.py +0 -58
- autobyteus/prompt/storage/__init__.py +0 -0
- autobyteus/prompt/storage/prompt_version_model.py +0 -29
- autobyteus/prompt/storage/prompt_version_repository.py +0 -83
- autobyteus/tools/bash/factory/__init__.py +0 -0
- autobyteus/tools/bash/factory/bash_executor_factory.py +0 -6
- autobyteus/tools/factory/ask_user_input_factory.py +0 -6
- autobyteus/tools/factory/image_downloader_factory.py +0 -9
- autobyteus/tools/factory/pdf_downloader_factory.py +0 -9
- autobyteus/tools/factory/webpage_image_downloader_factory.py +0 -6
- autobyteus/tools/file/factory/__init__.py +0 -0
- autobyteus/tools/file/factory/file_reader_factory.py +0 -6
- autobyteus/tools/file/factory/file_writer_factory.py +0 -6
- autobyteus/tools/mcp_remote_tool.py +0 -82
- autobyteus/tools/web_page_pdf_generator.py +0 -90
- autobyteus-1.0.5.dist-info/RECORD +0 -163
- {autobyteus-1.0.5.dist-info → autobyteus-1.1.0.dist-info}/licenses/LICENSE +0 -0
- {autobyteus-1.0.5.dist-info → autobyteus-1.1.0.dist-info}/top_level.txt +0 -0
autobyteus/agent/agent.py
CHANGED
|
@@ -1,231 +1,106 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/agent.py
|
|
1
2
|
import asyncio
|
|
2
3
|
import logging
|
|
3
|
-
from typing import List,
|
|
4
|
-
|
|
5
|
-
from autobyteus.
|
|
6
|
-
from autobyteus.
|
|
7
|
-
from autobyteus.
|
|
8
|
-
from autobyteus.
|
|
9
|
-
from autobyteus.events
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
from autobyteus.
|
|
4
|
+
from typing import AsyncIterator, Optional, List, Any, Dict, TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from autobyteus.agent.runtime.agent_runtime import AgentRuntime
|
|
7
|
+
from autobyteus.agent.context.phases import AgentOperationalPhase
|
|
8
|
+
from autobyteus.agent.message.agent_input_user_message import AgentInputUserMessage
|
|
9
|
+
from autobyteus.agent.message.inter_agent_message import InterAgentMessage
|
|
10
|
+
from autobyteus.agent.events import UserMessageReceivedEvent, InterAgentMessageReceivedEvent, ToolExecutionApprovalEvent, BaseEvent
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from autobyteus.agent.context import AgentContext
|
|
13
14
|
|
|
14
15
|
logger = logging.getLogger(__name__)
|
|
15
16
|
|
|
16
|
-
class Agent
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
self.
|
|
30
|
-
self.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
def get_task_completed(self):
|
|
56
|
-
if self.task_completed is None:
|
|
57
|
-
raise RuntimeError("task_completed Event accessed before initialization")
|
|
58
|
-
return self.task_completed
|
|
59
|
-
|
|
60
|
-
async def run(self):
|
|
61
|
-
try:
|
|
62
|
-
logger.info(f"Starting execution for agent: {self.role}")
|
|
63
|
-
self._initialize_queues()
|
|
64
|
-
self._initialize_task_completed()
|
|
65
|
-
await self.initialize_conversation()
|
|
66
|
-
|
|
67
|
-
user_message_handler = asyncio.create_task(self.handle_user_messages())
|
|
68
|
-
tool_result_handler = asyncio.create_task(self.handle_tool_result_messages())
|
|
69
|
-
|
|
70
|
-
# Once everything is ready, set the status to RUNNING
|
|
71
|
-
self.status = AgentStatus.RUNNING
|
|
72
|
-
|
|
73
|
-
await asyncio.gather(user_message_handler, tool_result_handler)
|
|
74
|
-
|
|
75
|
-
except Exception as e:
|
|
76
|
-
logger.error(f"Error in agent {self.role} execution: {str(e)}")
|
|
77
|
-
self.status = AgentStatus.ERROR
|
|
78
|
-
finally:
|
|
79
|
-
self.status = AgentStatus.ENDED
|
|
80
|
-
await self.cleanup()
|
|
81
|
-
|
|
82
|
-
async def handle_user_messages(self):
|
|
83
|
-
logger.info(f"Agent {self.role} started handling user messages")
|
|
84
|
-
while not self.task_completed.is_set() and self.status == AgentStatus.RUNNING:
|
|
85
|
-
try:
|
|
86
|
-
user_message: UserMessage = await asyncio.wait_for(self.user_messages.get(), timeout=1.0)
|
|
87
|
-
logger.info(f"Agent {self.role} handling user message")
|
|
88
|
-
response = await self.conversation.send_user_message(user_message.content, user_message.file_paths)
|
|
89
|
-
await self.process_llm_response(response)
|
|
90
|
-
except asyncio.TimeoutError:
|
|
91
|
-
continue
|
|
92
|
-
except asyncio.CancelledError:
|
|
93
|
-
logger.info(f"User message handler for agent {self.role} cancelled")
|
|
94
|
-
break
|
|
95
|
-
except Exception as e:
|
|
96
|
-
logger.error(f"Error handling user message for agent {self.role}: {str(e)}")
|
|
97
|
-
|
|
98
|
-
async def receive_user_message(self, message: UserMessage):
|
|
99
|
-
"""
|
|
100
|
-
This method gracefully waits for the agent to become RUNNING
|
|
101
|
-
if it's in the process of starting up, ensuring the queues are
|
|
102
|
-
initialized before we put a message into them.
|
|
103
|
-
"""
|
|
104
|
-
logger.info(f"Agent {self.agent_id} received user message")
|
|
105
|
-
|
|
106
|
-
# If the agent is not started (or ended), begin the start process
|
|
107
|
-
if self.status in [AgentStatus.NOT_STARTED, AgentStatus.ENDED]:
|
|
108
|
-
self.start()
|
|
109
|
-
|
|
110
|
-
# If the agent is still starting, wait until it transitions to RUNNING
|
|
111
|
-
while self.status == AgentStatus.STARTING:
|
|
112
|
-
await asyncio.sleep(0.1)
|
|
113
|
-
|
|
114
|
-
if self.status != AgentStatus.RUNNING:
|
|
115
|
-
logger.error(f"Agent is not in a running state: {self.status}")
|
|
116
|
-
return
|
|
17
|
+
class Agent:
|
|
18
|
+
"""
|
|
19
|
+
User-facing API for interacting with an agent's runtime.
|
|
20
|
+
It manages an underlying AgentRuntime instance and translates user actions
|
|
21
|
+
into events for the agent's event processing loop by submitting them
|
|
22
|
+
to AgentRuntime. Output is consumed via AgentEventStream which listens
|
|
23
|
+
to AgentExternalEventNotifier.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, runtime: AgentRuntime):
|
|
27
|
+
if not isinstance(runtime, AgentRuntime): # pragma: no cover
|
|
28
|
+
raise TypeError(f"Agent requires an AgentRuntime instance, got {type(runtime).__name__}")
|
|
29
|
+
|
|
30
|
+
self._runtime: AgentRuntime = runtime
|
|
31
|
+
self.agent_id: str = self._runtime.context.agent_id
|
|
32
|
+
|
|
33
|
+
logger.info(f"Agent facade initialized for agent_id '{self.agent_id}'.")
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def context(self) -> 'AgentContext':
|
|
37
|
+
return self._runtime.context
|
|
38
|
+
|
|
39
|
+
async def _submit_event_to_runtime(self, event: BaseEvent) -> None:
|
|
40
|
+
"""Internal helper to submit an event to the runtime and handle startup."""
|
|
41
|
+
if not self._runtime.is_running: # pragma: no cover
|
|
42
|
+
logger.info(f"Agent '{self.agent_id}' runtime is not running. Calling start() before submitting event.")
|
|
43
|
+
self.start()
|
|
44
|
+
await asyncio.sleep(0.05)
|
|
45
|
+
|
|
46
|
+
logger.debug(f"Agent '{self.agent_id}': Submitting {type(event).__name__} to runtime.")
|
|
47
|
+
await self._runtime.submit_event(event)
|
|
48
|
+
|
|
49
|
+
async def post_user_message(self, agent_input_user_message: AgentInputUserMessage) -> None:
|
|
50
|
+
if not isinstance(agent_input_user_message, AgentInputUserMessage): # pragma: no cover
|
|
51
|
+
raise TypeError(f"Agent for '{self.agent_id}' received invalid type for user_message. Expected AgentInputUserMessage, got {type(agent_input_user_message)}.")
|
|
52
|
+
|
|
53
|
+
event = UserMessageReceivedEvent(agent_input_user_message=agent_input_user_message)
|
|
54
|
+
await self._submit_event_to_runtime(event)
|
|
117
55
|
|
|
118
|
-
# Now that we are running, safely place the message in the queue
|
|
119
|
-
await self.user_messages.put(message)
|
|
120
|
-
|
|
121
|
-
async def handle_tool_result_messages(self):
|
|
122
|
-
logger.info(f"Agent {self.role} started handling tool result messages")
|
|
123
|
-
while not self.task_completed.is_set() and self.status == AgentStatus.RUNNING:
|
|
124
|
-
try:
|
|
125
|
-
message = await asyncio.wait_for(self.tool_result_messages.get(), timeout=1.0)
|
|
126
|
-
logger.info(f"Agent {self.role} handling tool result message: {message}")
|
|
127
|
-
response = await self.conversation.send_user_message(f"Tool execution result: {message}")
|
|
128
|
-
await self.process_llm_response(response)
|
|
129
|
-
except asyncio.TimeoutError:
|
|
130
|
-
continue
|
|
131
|
-
except asyncio.CancelledError:
|
|
132
|
-
logger.info(f"Tool result handler for agent {self.role} cancelled")
|
|
133
|
-
break
|
|
134
|
-
except Exception as e:
|
|
135
|
-
logger.error(f"Error handling tool result for agent {self.role}: {str(e)}")
|
|
136
|
-
|
|
137
|
-
async def initialize_conversation(self):
|
|
138
|
-
logger.info(f"Initializing conversation for agent: {self.role}")
|
|
139
|
-
self.conversation = Conversation(self.llm)
|
|
140
|
-
|
|
141
|
-
if self.initial_user_message:
|
|
142
|
-
initial_message = self.initial_user_message
|
|
143
|
-
else:
|
|
144
|
-
prompt_content = self.prompt_builder.set_variable_value(
|
|
145
|
-
"external_tools",
|
|
146
|
-
self._get_external_tools_section()
|
|
147
|
-
).build()
|
|
148
|
-
initial_message = UserMessage(content=prompt_content)
|
|
149
|
-
|
|
150
|
-
logger.debug(f"Initial user message for agent {self.role}: {initial_message}")
|
|
151
|
-
initial_llm_response = await self.conversation.send_user_message(
|
|
152
|
-
initial_message.content,
|
|
153
|
-
initial_message.file_paths
|
|
154
|
-
)
|
|
155
|
-
await self.process_llm_response(initial_llm_response)
|
|
156
56
|
|
|
157
|
-
async def
|
|
158
|
-
|
|
57
|
+
async def post_inter_agent_message(self, inter_agent_message: InterAgentMessage) -> None:
|
|
58
|
+
if not isinstance(inter_agent_message, InterAgentMessage): # pragma: no cover
|
|
59
|
+
raise TypeError(
|
|
60
|
+
f"Agent for '{self.agent_id}' received invalid type for inter_agent_message. "
|
|
61
|
+
f"Expected InterAgentMessage, got {type(inter_agent_message).__name__}."
|
|
62
|
+
)
|
|
159
63
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
"""
|
|
192
|
-
if self.status in [AgentStatus.NOT_STARTED, AgentStatus.ENDED]:
|
|
193
|
-
logger.info(f"Starting agent {self.role}")
|
|
194
|
-
self.status = AgentStatus.STARTING
|
|
195
|
-
self._run_task = asyncio.create_task(self.run())
|
|
196
|
-
elif self.status == AgentStatus.STARTING:
|
|
197
|
-
logger.info(f"Agent {self.role} is already in STARTING state.")
|
|
198
|
-
elif self.status == AgentStatus.RUNNING:
|
|
199
|
-
logger.info(f"Agent {self.role} is already running.")
|
|
200
|
-
else:
|
|
201
|
-
logger.warning(f"Agent {self.role} is in an unexpected state: {self.status}")
|
|
202
|
-
|
|
203
|
-
def stop(self):
|
|
204
|
-
if self._run_task and not self._run_task.done():
|
|
205
|
-
self._run_task.cancel()
|
|
206
|
-
|
|
207
|
-
async def cleanup(self):
|
|
208
|
-
while not self.tool_result_messages.empty():
|
|
209
|
-
self.tool_result_messages.get_nowait()
|
|
210
|
-
while not self.user_messages.empty():
|
|
211
|
-
self.user_messages.get_nowait()
|
|
212
|
-
await self.llm.cleanup()
|
|
213
|
-
logger.info(f"Cleanup completed for agent: {self.role}")
|
|
214
|
-
|
|
215
|
-
def set_agent_id_on_tools(self):
|
|
216
|
-
if self.tools:
|
|
217
|
-
for tool in self.tools:
|
|
218
|
-
tool.set_agent_id(self.agent_id)
|
|
219
|
-
|
|
220
|
-
def _get_external_tools_section(self) -> str:
|
|
221
|
-
if not self.tools:
|
|
222
|
-
return ""
|
|
64
|
+
event = InterAgentMessageReceivedEvent(inter_agent_message=inter_agent_message)
|
|
65
|
+
await self._submit_event_to_runtime(event)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
async def post_tool_execution_approval(self,
|
|
69
|
+
tool_invocation_id: str,
|
|
70
|
+
is_approved: bool,
|
|
71
|
+
reason: Optional[str] = None) -> None:
|
|
72
|
+
if not isinstance(tool_invocation_id, str) or not tool_invocation_id: # pragma: no cover
|
|
73
|
+
raise ValueError("tool_invocation_id must be a non-empty string.")
|
|
74
|
+
if not isinstance(is_approved, bool): # pragma: no cover
|
|
75
|
+
raise TypeError("is_approved must be a boolean.")
|
|
76
|
+
|
|
77
|
+
approval_event = ToolExecutionApprovalEvent(
|
|
78
|
+
tool_invocation_id=tool_invocation_id,
|
|
79
|
+
is_approved=is_approved,
|
|
80
|
+
reason=reason
|
|
81
|
+
)
|
|
82
|
+
await self._submit_event_to_runtime(approval_event)
|
|
83
|
+
|
|
84
|
+
def get_current_phase(self) -> AgentOperationalPhase:
|
|
85
|
+
return self._runtime.current_phase_property
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def is_running(self) -> bool:
|
|
89
|
+
return self._runtime.is_running
|
|
90
|
+
|
|
91
|
+
def start(self) -> None:
|
|
92
|
+
if self._runtime.is_running: # pragma: no cover
|
|
93
|
+
logger.info(f"Agent '{self.agent_id}' runtime is already running. Ignoring start command.")
|
|
94
|
+
return
|
|
223
95
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
96
|
+
logger.info(f"Agent '{self.agent_id}' requesting runtime to start.")
|
|
97
|
+
self._runtime.start()
|
|
98
|
+
|
|
99
|
+
async def stop(self, timeout: float = 10.0) -> None: # pragma: no cover
|
|
100
|
+
logger.info(f"Agent '{self.agent_id}' requesting runtime to stop (timeout: {timeout}s).")
|
|
101
|
+
await self._runtime.stop(timeout=timeout)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def __repr__(self) -> str:
|
|
105
|
+
phase_val = self._runtime.current_phase_property.value
|
|
106
|
+
return f"<Agent agent_id='{self.agent_id}', current_phase='{phase_val}'>"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/bootstrap_steps/__init__.py
|
|
2
|
+
"""
|
|
3
|
+
Defines individual, self-contained steps for the agent bootstrapping process.
|
|
4
|
+
These steps are orchestrated by the BootstrapAgentEventHandler.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .base_bootstrap_step import BaseBootstrapStep
|
|
8
|
+
from .agent_runtime_queue_initialization_step import AgentRuntimeQueueInitializationStep # UPDATED
|
|
9
|
+
from .workspace_context_initialization_step import WorkspaceContextInitializationStep
|
|
10
|
+
# ToolInitializationStep is no longer a bootstrap step.
|
|
11
|
+
from .system_prompt_processing_step import SystemPromptProcessingStep
|
|
12
|
+
# LLMConfigFinalizationStep and LLMInstanceCreationStep removed.
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"BaseBootstrapStep",
|
|
16
|
+
"AgentRuntimeQueueInitializationStep", # UPDATED
|
|
17
|
+
"WorkspaceContextInitializationStep",
|
|
18
|
+
"SystemPromptProcessingStep",
|
|
19
|
+
]
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/bootstrap_steps/agent_bootstrapper.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING, List, Optional
|
|
4
|
+
|
|
5
|
+
from .base_bootstrap_step import BaseBootstrapStep
|
|
6
|
+
from .agent_runtime_queue_initialization_step import AgentRuntimeQueueInitializationStep
|
|
7
|
+
from .workspace_context_initialization_step import WorkspaceContextInitializationStep
|
|
8
|
+
from .system_prompt_processing_step import SystemPromptProcessingStep
|
|
9
|
+
from autobyteus.agent.events import AgentReadyEvent
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from autobyteus.agent.context import AgentContext
|
|
13
|
+
from autobyteus.agent.context.agent_phase_manager import AgentPhaseManager
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
class AgentBootstrapper:
|
|
18
|
+
"""
|
|
19
|
+
Orchestrates the agent's bootstrapping process by executing a sequence of
|
|
20
|
+
self-contained bootstrap steps.
|
|
21
|
+
"""
|
|
22
|
+
def __init__(self, steps: Optional[List[BaseBootstrapStep]] = None):
|
|
23
|
+
"""
|
|
24
|
+
Initializes the AgentBootstrapper.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
steps: An optional list of bootstrap steps to execute. If not provided,
|
|
28
|
+
a default sequence will be used.
|
|
29
|
+
"""
|
|
30
|
+
if steps is None:
|
|
31
|
+
self.bootstrap_steps: List[BaseBootstrapStep] = [
|
|
32
|
+
AgentRuntimeQueueInitializationStep(),
|
|
33
|
+
WorkspaceContextInitializationStep(),
|
|
34
|
+
SystemPromptProcessingStep(),
|
|
35
|
+
]
|
|
36
|
+
logger.debug("AgentBootstrapper initialized with default steps.")
|
|
37
|
+
else:
|
|
38
|
+
self.bootstrap_steps = steps
|
|
39
|
+
logger.debug(f"AgentBootstrapper initialized with {len(steps)} custom steps.")
|
|
40
|
+
|
|
41
|
+
async def run(self, context: 'AgentContext', phase_manager: 'AgentPhaseManager') -> bool:
|
|
42
|
+
"""
|
|
43
|
+
Executes the configured sequence of bootstrap steps.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
context: The agent's context.
|
|
47
|
+
phase_manager: The agent's phase manager.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
True if all steps completed successfully, False otherwise.
|
|
51
|
+
"""
|
|
52
|
+
agent_id = context.agent_id
|
|
53
|
+
|
|
54
|
+
# Set the agent phase to BOOTSTRAPPING and wait for any associated hooks.
|
|
55
|
+
await phase_manager.notify_bootstrapping_started()
|
|
56
|
+
logger.info(f"Agent '{agent_id}': AgentBootstrapper starting execution. Phase set to BOOTSTRAPPING.")
|
|
57
|
+
|
|
58
|
+
for step_index, step_instance in enumerate(self.bootstrap_steps):
|
|
59
|
+
step_name = step_instance.__class__.__name__
|
|
60
|
+
logger.debug(f"Agent '{agent_id}': Executing bootstrap step {step_index + 1}/{len(self.bootstrap_steps)}: {step_name}")
|
|
61
|
+
|
|
62
|
+
success = await step_instance.execute(context, phase_manager)
|
|
63
|
+
|
|
64
|
+
if not success:
|
|
65
|
+
error_message = f"Bootstrap step {step_name} failed."
|
|
66
|
+
logger.error(f"Agent '{agent_id}': {error_message} Halting bootstrap process.")
|
|
67
|
+
# The step itself is responsible for detailed error logging.
|
|
68
|
+
# We are responsible for notifying the phase manager to set the agent to an error state.
|
|
69
|
+
await phase_manager.notify_error_occurred(
|
|
70
|
+
error_message=f"Critical bootstrap failure at {step_name}",
|
|
71
|
+
error_details=f"Agent '{agent_id}' failed during bootstrap step '{step_name}'. Check logs for details."
|
|
72
|
+
)
|
|
73
|
+
return False
|
|
74
|
+
|
|
75
|
+
logger.info(f"Agent '{agent_id}': All bootstrap steps completed successfully. Enqueuing AgentReadyEvent.")
|
|
76
|
+
# After successful bootstrapping, enqueue the ready event.
|
|
77
|
+
if context.state.input_event_queues:
|
|
78
|
+
await context.state.input_event_queues.enqueue_internal_system_event(AgentReadyEvent())
|
|
79
|
+
else: # pragma: no cover
|
|
80
|
+
# Should not happen if AgentRuntimeQueueInitializationStep is present and successful
|
|
81
|
+
logger.critical(f"Agent '{agent_id}': Bootstrap succeeded but input queues are not available to enqueue AgentReadyEvent.")
|
|
82
|
+
await phase_manager.notify_error_occurred(
|
|
83
|
+
error_message="Input queues unavailable after bootstrap",
|
|
84
|
+
error_details=f"Agent '{agent_id}' bootstrap process seemed to succeed, but input event queues are missing."
|
|
85
|
+
)
|
|
86
|
+
return False
|
|
87
|
+
|
|
88
|
+
return True
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/bootstrap_steps/agent_runtime_queue_initialization_step.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from .base_bootstrap_step import BaseBootstrapStep
|
|
6
|
+
from autobyteus.agent.events import AgentErrorEvent, AgentInputEventQueueManager
|
|
7
|
+
# AgentOutputDataManager is no longer initialized here.
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from autobyteus.agent.context import AgentContext
|
|
11
|
+
from autobyteus.agent.context.agent_phase_manager import AgentPhaseManager
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
class AgentRuntimeQueueInitializationStep(BaseBootstrapStep):
|
|
16
|
+
"""
|
|
17
|
+
Bootstrap step for initializing the agent's runtime INPUT event queues.
|
|
18
|
+
These queues are created within the AgentWorker's event loop.
|
|
19
|
+
Output data is now handled by emitting events via AgentExternalEventNotifier.
|
|
20
|
+
"""
|
|
21
|
+
def __init__(self, input_queue_size: int = 0): # Removed output_queue_size
|
|
22
|
+
self.input_queue_size = input_queue_size
|
|
23
|
+
logger.debug(f"AgentRuntimeQueueInitializationStep initialized with input_q_size={input_queue_size}.")
|
|
24
|
+
|
|
25
|
+
async def execute(self,
|
|
26
|
+
context: 'AgentContext',
|
|
27
|
+
phase_manager: 'AgentPhaseManager') -> bool:
|
|
28
|
+
agent_id = context.agent_id
|
|
29
|
+
logger.info(f"Agent '{agent_id}': Executing AgentRuntimeQueueInitializationStep (for input queues).")
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
if context.state.input_event_queues is not None: # Check only input queues
|
|
33
|
+
logger.warning(f"Agent '{agent_id}': Input runtime queues seem to be already initialized. Overwriting. This might indicate a logic error.")
|
|
34
|
+
|
|
35
|
+
input_queues = AgentInputEventQueueManager(queue_size=self.input_queue_size)
|
|
36
|
+
context.state.input_event_queues = input_queues
|
|
37
|
+
# context.state.output_data_queues is no longer set here.
|
|
38
|
+
|
|
39
|
+
logger.info(f"Agent '{agent_id}': AgentInputEventQueueManager initialized and set in agent state.")
|
|
40
|
+
if context.state.input_event_queues is None: # pragma: no cover
|
|
41
|
+
raise RuntimeError("Input event queue manager was not successfully set in agent state during AgentRuntimeQueueInitializationStep.")
|
|
42
|
+
|
|
43
|
+
return True
|
|
44
|
+
except Exception as e:
|
|
45
|
+
error_message = f"Agent '{agent_id}': Critical failure during AgentRuntimeQueueInitializationStep (input queues): {e}"
|
|
46
|
+
logger.error(error_message, exc_info=True)
|
|
47
|
+
|
|
48
|
+
# Attempt to enqueue an error event if input_event_queues was partially created
|
|
49
|
+
# This check itself might be problematic if input_queues is the thing that failed.
|
|
50
|
+
# However, if it failed *after* assigning self.input_event_queues, this might work.
|
|
51
|
+
if context.state.input_event_queues and context.state.input_event_queues.internal_system_event_queue: # pragma: no cover
|
|
52
|
+
await context.state.input_event_queues.enqueue_internal_system_event(
|
|
53
|
+
AgentErrorEvent(error_message=error_message, exception_details=str(e))
|
|
54
|
+
)
|
|
55
|
+
else: # pragma: no cover
|
|
56
|
+
logger.error(f"Agent '{agent_id}': Cannot enqueue AgentErrorEvent as input_event_queues are not available after AgentRuntimeQueueInitializationStep failure.")
|
|
57
|
+
return False
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/bootstrap_steps/base_bootstrap_step.py
|
|
2
|
+
import logging
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from autobyteus.agent.context import AgentContext
|
|
8
|
+
from autobyteus.agent.context.agent_phase_manager import AgentPhaseManager
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
class BaseBootstrapStep(ABC):
|
|
13
|
+
"""
|
|
14
|
+
Abstract base class for individual steps in the agent bootstrapping process.
|
|
15
|
+
Each step is responsible for a specific part of the initialization and
|
|
16
|
+
for reporting its success or failure.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
@abstractmethod
|
|
20
|
+
async def execute(self,
|
|
21
|
+
context: 'AgentContext',
|
|
22
|
+
phase_manager: 'AgentPhaseManager') -> bool:
|
|
23
|
+
"""
|
|
24
|
+
Executes the bootstrap step.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
context: The agent's context, providing access to configuration and state.
|
|
28
|
+
phase_manager: The agent's phase manager for notifying phase transitions.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
True if the step completed successfully, False otherwise.
|
|
32
|
+
If False, the step is expected to have handled logging and enqueuing
|
|
33
|
+
an AgentErrorEvent.
|
|
34
|
+
"""
|
|
35
|
+
raise NotImplementedError("Subclasses must implement the 'execute' method.")
|
|
36
|
+
|
|
37
|
+
def __repr__(self) -> str:
|
|
38
|
+
return f"<{self.__class__.__name__}>"
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/bootstrap_steps/system_prompt_processing_step.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from .base_bootstrap_step import BaseBootstrapStep
|
|
6
|
+
from autobyteus.agent.events import AgentErrorEvent
|
|
7
|
+
from autobyteus.agent.system_prompt_processor.base_processor import BaseSystemPromptProcessor
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from autobyteus.agent.context import AgentContext
|
|
11
|
+
from autobyteus.agent.context.agent_phase_manager import AgentPhaseManager
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
class SystemPromptProcessingStep(BaseBootstrapStep):
|
|
16
|
+
"""
|
|
17
|
+
Bootstrap step for processing the agent's system prompt and setting it
|
|
18
|
+
on the pre-initialized LLM instance.
|
|
19
|
+
If any configured processor fails, this entire step is considered failed.
|
|
20
|
+
"""
|
|
21
|
+
def __init__(self):
|
|
22
|
+
logger.debug("SystemPromptProcessingStep initialized.")
|
|
23
|
+
|
|
24
|
+
async def execute(self,
|
|
25
|
+
context: 'AgentContext',
|
|
26
|
+
phase_manager: 'AgentPhaseManager') -> bool:
|
|
27
|
+
agent_id = context.agent_id
|
|
28
|
+
# The phase is now managed by the AgentBootstrapper.
|
|
29
|
+
logger.info(f"Agent '{agent_id}': Executing SystemPromptProcessingStep.")
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
# The LLM instance is now expected to be present from the start.
|
|
33
|
+
llm_instance = context.llm_instance
|
|
34
|
+
if not llm_instance:
|
|
35
|
+
raise ValueError("LLM instance not found in agent state. It must be provided in AgentConfig.")
|
|
36
|
+
|
|
37
|
+
current_system_prompt = context.config.system_prompt
|
|
38
|
+
logger.debug(f"Agent '{agent_id}': Retrieved base system prompt from agent config.")
|
|
39
|
+
|
|
40
|
+
processor_instances = context.config.system_prompt_processors
|
|
41
|
+
tool_instances_for_processor = context.tool_instances
|
|
42
|
+
|
|
43
|
+
if not processor_instances:
|
|
44
|
+
logger.debug(f"Agent '{agent_id}': No system prompt processors configured. Using system prompt as is.")
|
|
45
|
+
else:
|
|
46
|
+
logger.debug(f"Agent '{agent_id}': Found {len(processor_instances)} configured system prompt processors. Applying sequentially.")
|
|
47
|
+
for processor_instance in processor_instances:
|
|
48
|
+
if not isinstance(processor_instance, BaseSystemPromptProcessor):
|
|
49
|
+
error_message = f"Agent '{agent_id}': Invalid system prompt processor configuration type: {type(processor_instance)}. Expected BaseSystemPromptProcessor."
|
|
50
|
+
logger.error(error_message)
|
|
51
|
+
raise TypeError(error_message)
|
|
52
|
+
|
|
53
|
+
processor_name = processor_instance.get_name()
|
|
54
|
+
try:
|
|
55
|
+
logger.debug(f"Agent '{agent_id}': Applying system prompt processor '{processor_name}'.")
|
|
56
|
+
current_system_prompt = processor_instance.process(
|
|
57
|
+
system_prompt=current_system_prompt,
|
|
58
|
+
tool_instances=tool_instances_for_processor,
|
|
59
|
+
agent_id=agent_id,
|
|
60
|
+
context=context
|
|
61
|
+
)
|
|
62
|
+
logger.info(f"Agent '{agent_id}': System prompt processor '{processor_name}' applied successfully.")
|
|
63
|
+
except Exception as e_proc:
|
|
64
|
+
error_message = f"Agent '{agent_id}': Error applying system prompt processor '{processor_name}': {e_proc}"
|
|
65
|
+
logger.error(error_message, exc_info=True)
|
|
66
|
+
if context.state.input_event_queues:
|
|
67
|
+
await context.state.input_event_queues.enqueue_internal_system_event(
|
|
68
|
+
AgentErrorEvent(error_message=error_message, exception_details=str(e_proc))
|
|
69
|
+
)
|
|
70
|
+
return False # Signal failure of the entire step
|
|
71
|
+
|
|
72
|
+
context.state.processed_system_prompt = current_system_prompt
|
|
73
|
+
|
|
74
|
+
# --- New Logic: Set the prompt on the existing LLM instance ---
|
|
75
|
+
if hasattr(llm_instance, 'configure_system_prompt') and callable(getattr(llm_instance, 'configure_system_prompt')):
|
|
76
|
+
llm_instance.configure_system_prompt(current_system_prompt)
|
|
77
|
+
logger.info(f"Agent '{agent_id}': Final processed system prompt configured on LLM instance. Final length: {len(current_system_prompt)}.")
|
|
78
|
+
else:
|
|
79
|
+
# This path should ideally not be taken if all LLMs inherit from the updated BaseLLM.
|
|
80
|
+
# It's kept as a fallback with a strong warning.
|
|
81
|
+
logger.warning(f"Agent '{agent_id}': LLM instance ({llm_instance.__class__.__name__}) does not have a 'configure_system_prompt' method. "
|
|
82
|
+
f"The system prompt cannot be dynamically updated on the LLM instance after initialization. This may lead to incorrect agent behavior.")
|
|
83
|
+
|
|
84
|
+
logger.info(f"Agent '{agent_id}': Final processed system prompt:\n---\n{current_system_prompt}\n---")
|
|
85
|
+
return True
|
|
86
|
+
except Exception as e: # Catches other errors in the step setup itself
|
|
87
|
+
error_message = f"Agent '{agent_id}': Critical failure during system prompt processing step: {e}"
|
|
88
|
+
logger.error(error_message, exc_info=True)
|
|
89
|
+
if context.state.input_event_queues:
|
|
90
|
+
await context.input_event_queues.enqueue_internal_system_event(
|
|
91
|
+
AgentErrorEvent(error_message=error_message, exception_details=str(e))
|
|
92
|
+
)
|
|
93
|
+
return False
|