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
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/bootstrap_steps/workspace_context_initialization_step.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from .base_bootstrap_step import BaseBootstrapStep
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from autobyteus.agent.context import AgentContext
|
|
9
|
+
from autobyteus.agent.context.agent_phase_manager import AgentPhaseManager
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
class WorkspaceContextInitializationStep(BaseBootstrapStep):
|
|
14
|
+
"""
|
|
15
|
+
Bootstrap step for injecting the AgentContext into the agent's workspace instance.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self):
|
|
19
|
+
logger.debug("WorkspaceContextInitializationStep initialized.")
|
|
20
|
+
|
|
21
|
+
async def execute(self,
|
|
22
|
+
context: 'AgentContext',
|
|
23
|
+
phase_manager: 'AgentPhaseManager') -> bool:
|
|
24
|
+
agent_id = context.agent_id
|
|
25
|
+
logger.info(f"Agent '{agent_id}': Executing WorkspaceContextInitializationStep.")
|
|
26
|
+
|
|
27
|
+
workspace = context.workspace
|
|
28
|
+
|
|
29
|
+
if not workspace:
|
|
30
|
+
logger.debug(f"Agent '{agent_id}': No workspace configured. Skipping context injection.")
|
|
31
|
+
return True
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
if hasattr(workspace, 'set_context') and callable(getattr(workspace, 'set_context')):
|
|
35
|
+
workspace.set_context(context)
|
|
36
|
+
logger.info(f"Agent '{agent_id}': AgentContext successfully injected into workspace instance of type '{type(workspace).__name__}'.")
|
|
37
|
+
else:
|
|
38
|
+
logger.warning(f"Agent '{agent_id}': Configured workspace of type '{type(workspace).__name__}' does not have a 'set_context' method. "
|
|
39
|
+
"Workspace will not have access to the agent's context.")
|
|
40
|
+
|
|
41
|
+
return True
|
|
42
|
+
except Exception as e:
|
|
43
|
+
error_message = f"Agent '{agent_id}': Critical failure during WorkspaceContextInitializationStep: {e}"
|
|
44
|
+
logger.error(error_message, exc_info=True)
|
|
45
|
+
# No easy way to enqueue an error event here if queues aren't even initialized yet.
|
|
46
|
+
# The failure of a bootstrap step is handled by the bootstrapper, which will log and set error phase.
|
|
47
|
+
return False
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/context/__init__.py
|
|
2
|
+
"""
|
|
3
|
+
Components related to the agent's runtime context, state, config, and status management.
|
|
4
|
+
"""
|
|
5
|
+
from .agent_config import AgentConfig
|
|
6
|
+
from .agent_runtime_state import AgentRuntimeState
|
|
7
|
+
from .agent_context import AgentContext
|
|
8
|
+
from .agent_phase_manager import AgentPhaseManager
|
|
9
|
+
from .phases import AgentOperationalPhase
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"AgentContext",
|
|
14
|
+
"AgentConfig",
|
|
15
|
+
"AgentRuntimeState",
|
|
16
|
+
"AgentPhaseManager",
|
|
17
|
+
"AgentOperationalPhase",
|
|
18
|
+
]
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/context/agent_config.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import List, Optional, Union, Tuple, TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
# Correctly import the new master processor and the base class
|
|
6
|
+
from autobyteus.agent.system_prompt_processor import ToolManifestInjectorProcessor, BaseSystemPromptProcessor
|
|
7
|
+
from autobyteus.agent.llm_response_processor import ProviderAwareToolUsageProcessor, BaseLLMResponseProcessor
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from autobyteus.tools.base_tool import BaseTool
|
|
12
|
+
from autobyteus.agent.input_processor import BaseAgentUserInputMessageProcessor
|
|
13
|
+
from autobyteus.llm.base_llm import BaseLLM
|
|
14
|
+
from autobyteus.agent.workspace.base_workspace import BaseAgentWorkspace
|
|
15
|
+
from autobyteus.agent.hooks.base_phase_hook import BasePhaseHook
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
class AgentConfig:
|
|
20
|
+
"""
|
|
21
|
+
Represents the complete, static configuration for an agent instance.
|
|
22
|
+
This is the single source of truth for an agent's definition, including
|
|
23
|
+
its identity, capabilities, and default behaviors.
|
|
24
|
+
"""
|
|
25
|
+
# Use the new ProviderAwareToolUsageProcessor as the default
|
|
26
|
+
DEFAULT_LLM_RESPONSE_PROCESSORS = [ProviderAwareToolUsageProcessor()]
|
|
27
|
+
# Use the new, single, unified processor as the default
|
|
28
|
+
DEFAULT_SYSTEM_PROMPT_PROCESSORS = [ToolManifestInjectorProcessor()]
|
|
29
|
+
|
|
30
|
+
def __init__(self,
|
|
31
|
+
name: str,
|
|
32
|
+
role: str,
|
|
33
|
+
description: str,
|
|
34
|
+
llm_instance: 'BaseLLM',
|
|
35
|
+
system_prompt: str,
|
|
36
|
+
tools: List['BaseTool'],
|
|
37
|
+
auto_execute_tools: bool = True,
|
|
38
|
+
use_xml_tool_format: bool = True,
|
|
39
|
+
input_processors: Optional[List['BaseAgentUserInputMessageProcessor']] = None,
|
|
40
|
+
llm_response_processors: Optional[List['BaseLLMResponseProcessor']] = None,
|
|
41
|
+
system_prompt_processors: Optional[List['BaseSystemPromptProcessor']] = None,
|
|
42
|
+
workspace: Optional['BaseAgentWorkspace'] = None,
|
|
43
|
+
phase_hooks: Optional[List['BasePhaseHook']] = None):
|
|
44
|
+
"""
|
|
45
|
+
Initializes the AgentConfig.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
name: The agent's name.
|
|
49
|
+
role: The agent's role.
|
|
50
|
+
description: A description of the agent.
|
|
51
|
+
llm_instance: A pre-initialized LLM instance (subclass of BaseLLM).
|
|
52
|
+
The user is responsible for creating and configuring this instance.
|
|
53
|
+
system_prompt: The base system prompt.
|
|
54
|
+
tools: A list of pre-initialized tool instances (subclasses of BaseTool).
|
|
55
|
+
auto_execute_tools: If True, the agent will execute tools without approval.
|
|
56
|
+
use_xml_tool_format: Whether to use XML for tool descriptions and examples.
|
|
57
|
+
input_processors: A list of input processor instances.
|
|
58
|
+
llm_response_processors: A list of LLM response processor instances.
|
|
59
|
+
system_prompt_processors: A list of system prompt processor instances.
|
|
60
|
+
workspace: An optional pre-initialized workspace instance for the agent.
|
|
61
|
+
phase_hooks: An optional list of phase transition hook instances.
|
|
62
|
+
"""
|
|
63
|
+
self.name = name
|
|
64
|
+
self.role = role
|
|
65
|
+
self.description = description
|
|
66
|
+
self.llm_instance = llm_instance
|
|
67
|
+
self.system_prompt = system_prompt
|
|
68
|
+
self.tools = tools
|
|
69
|
+
self.workspace = workspace
|
|
70
|
+
self.auto_execute_tools = auto_execute_tools
|
|
71
|
+
self.use_xml_tool_format = use_xml_tool_format
|
|
72
|
+
self.input_processors = input_processors or []
|
|
73
|
+
self.llm_response_processors = llm_response_processors if llm_response_processors is not None else list(self.DEFAULT_LLM_RESPONSE_PROCESSORS)
|
|
74
|
+
self.system_prompt_processors = system_prompt_processors if system_prompt_processors is not None else list(self.DEFAULT_SYSTEM_PROMPT_PROCESSORS)
|
|
75
|
+
self.phase_hooks = phase_hooks or []
|
|
76
|
+
|
|
77
|
+
logger.debug(f"AgentConfig created for name '{self.name}', role '{self.role}'.")
|
|
78
|
+
|
|
79
|
+
def __repr__(self) -> str:
|
|
80
|
+
return (f"AgentConfig(name='{self.name}', role='{self.role}', llm_instance='{self.llm_instance.__class__.__name__}', workspace_configured={self.workspace is not None})")
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/context/agent_context.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING, List, Dict, Any, Optional
|
|
4
|
+
|
|
5
|
+
from .phases import AgentOperationalPhase
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from .agent_config import AgentConfig
|
|
9
|
+
from .agent_runtime_state import AgentRuntimeState
|
|
10
|
+
from autobyteus.llm.base_llm import BaseLLM
|
|
11
|
+
from autobyteus.tools.base_tool import BaseTool
|
|
12
|
+
from autobyteus.agent.events.agent_input_event_queue_manager import AgentInputEventQueueManager
|
|
13
|
+
from autobyteus.agent.tool_invocation import ToolInvocation
|
|
14
|
+
# LLMConfig no longer needed here
|
|
15
|
+
from autobyteus.agent.workspace.base_workspace import BaseAgentWorkspace
|
|
16
|
+
from autobyteus.agent.context.agent_phase_manager import AgentPhaseManager
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
class AgentContext:
|
|
21
|
+
"""
|
|
22
|
+
Represents the complete operational context for a single agent instance.
|
|
23
|
+
"""
|
|
24
|
+
def __init__(self, agent_id: str, config: 'AgentConfig', state: 'AgentRuntimeState'):
|
|
25
|
+
from .agent_config import AgentConfig as AgentConfigClass
|
|
26
|
+
from .agent_runtime_state import AgentRuntimeState as AgentRuntimeStateClass
|
|
27
|
+
|
|
28
|
+
if not agent_id or not isinstance(agent_id, str):
|
|
29
|
+
raise ValueError("AgentContext requires a non-empty string 'agent_id'.")
|
|
30
|
+
if not isinstance(config, AgentConfigClass):
|
|
31
|
+
raise TypeError(f"AgentContext 'config' must be an AgentConfig instance. Got {type(config)}")
|
|
32
|
+
if not isinstance(state, AgentRuntimeStateClass):
|
|
33
|
+
raise TypeError(f"AgentContext 'state' must be an AgentRuntimeState instance. Got {type(state)}")
|
|
34
|
+
|
|
35
|
+
if agent_id != state.agent_id: # pragma: no cover
|
|
36
|
+
logger.warning(f"AgentContext created with mismatched agent_id ('{agent_id}') and state's ID ('{state.agent_id}'). Using context's ID for logging.")
|
|
37
|
+
|
|
38
|
+
self.agent_id: str = agent_id
|
|
39
|
+
self.config: 'AgentConfig' = config
|
|
40
|
+
self.state: 'AgentRuntimeState' = state
|
|
41
|
+
|
|
42
|
+
logger.info(f"AgentContext composed for agent_id '{self.agent_id}'. Config and State linked.")
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def tool_instances(self) -> Dict[str, 'BaseTool']:
|
|
46
|
+
return self.state.tool_instances if self.state.tool_instances is not None else {}
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def auto_execute_tools(self) -> bool:
|
|
50
|
+
return self.config.auto_execute_tools
|
|
51
|
+
|
|
52
|
+
# llm_model_name property removed
|
|
53
|
+
# custom_llm_config property removed
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def llm_instance(self) -> Optional['BaseLLM']:
|
|
57
|
+
return self.state.llm_instance
|
|
58
|
+
|
|
59
|
+
@llm_instance.setter
|
|
60
|
+
def llm_instance(self, value: Optional['BaseLLM']):
|
|
61
|
+
self.state.llm_instance = value
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def input_event_queues(self) -> 'AgentInputEventQueueManager':
|
|
65
|
+
if self.state.input_event_queues is None:
|
|
66
|
+
logger.critical(f"AgentContext for '{self.agent_id}': Attempted to access 'input_event_queues' before they were initialized by AgentWorker.")
|
|
67
|
+
raise RuntimeError(f"Agent '{self.agent_id}': Input event queues have not been initialized. This typically occurs during agent bootstrapping.")
|
|
68
|
+
return self.state.input_event_queues
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def current_phase(self) -> 'AgentOperationalPhase':
|
|
72
|
+
return self.state.current_phase
|
|
73
|
+
|
|
74
|
+
@current_phase.setter
|
|
75
|
+
def current_phase(self, value: 'AgentOperationalPhase'):
|
|
76
|
+
if not isinstance(value, AgentOperationalPhase): # pragma: no cover
|
|
77
|
+
raise TypeError(f"current_phase must be an AgentOperationalPhase instance. Got {type(value)}")
|
|
78
|
+
self.state.current_phase = value
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def phase_manager(self) -> Optional['AgentPhaseManager']:
|
|
82
|
+
return self.state.phase_manager_ref
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def conversation_history(self) -> List[Dict[str, Any]]:
|
|
86
|
+
return self.state.conversation_history
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def pending_tool_approvals(self) -> Dict[str, 'ToolInvocation']:
|
|
90
|
+
return self.state.pending_tool_approvals
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def custom_data(self) -> Dict[str, Any]:
|
|
94
|
+
return self.state.custom_data
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def workspace(self) -> Optional['BaseAgentWorkspace']:
|
|
98
|
+
return self.state.workspace
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def processed_system_prompt(self) -> Optional[str]:
|
|
102
|
+
return self.state.processed_system_prompt
|
|
103
|
+
|
|
104
|
+
@processed_system_prompt.setter
|
|
105
|
+
def processed_system_prompt(self, value: Optional[str]):
|
|
106
|
+
self.state.processed_system_prompt = value
|
|
107
|
+
|
|
108
|
+
# final_llm_config_for_creation property removed
|
|
109
|
+
|
|
110
|
+
def add_message_to_history(self, message: Dict[str, Any]) -> None:
|
|
111
|
+
self.state.add_message_to_history(message)
|
|
112
|
+
|
|
113
|
+
def get_tool(self, tool_name: str) -> Optional['BaseTool']:
|
|
114
|
+
tool = self.tool_instances.get(tool_name)
|
|
115
|
+
if not tool: # pragma: no cover
|
|
116
|
+
logger.warning(f"Tool '{tool_name}' not found in AgentContext.state.tool_instances for agent '{self.agent_id}'. "
|
|
117
|
+
f"Available tools: {list(self.tool_instances.keys())}")
|
|
118
|
+
return tool
|
|
119
|
+
|
|
120
|
+
def store_pending_tool_invocation(self, invocation: 'ToolInvocation') -> None:
|
|
121
|
+
self.state.store_pending_tool_invocation(invocation)
|
|
122
|
+
|
|
123
|
+
def retrieve_pending_tool_invocation(self, invocation_id: str) -> Optional['ToolInvocation']:
|
|
124
|
+
return self.state.retrieve_pending_tool_invocation(invocation_id)
|
|
125
|
+
|
|
126
|
+
def __repr__(self) -> str:
|
|
127
|
+
input_q_status = "Initialized" if self.state.input_event_queues is not None else "Pending Init"
|
|
128
|
+
return (f"AgentContext(agent_id='{self.agent_id}', "
|
|
129
|
+
f"current_phase='{self.state.current_phase.value}', "
|
|
130
|
+
f"llm_initialized={self.state.llm_instance is not None}, "
|
|
131
|
+
f"tools_initialized={self.state.tool_instances is not None}, "
|
|
132
|
+
f"input_queues_status='{input_q_status}')")
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/context/agent_phase_manager.py
|
|
2
|
+
import asyncio
|
|
3
|
+
import logging
|
|
4
|
+
from typing import TYPE_CHECKING, Optional, Dict, Any
|
|
5
|
+
|
|
6
|
+
from .phases import AgentOperationalPhase
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from autobyteus.agent.context.agent_context import AgentContext
|
|
10
|
+
from autobyteus.agent.tool_invocation import ToolInvocation
|
|
11
|
+
from autobyteus.agent.events.notifiers import AgentExternalEventNotifier
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
class AgentPhaseManager:
|
|
17
|
+
"""
|
|
18
|
+
Manages the operational phase of an agent, uses an AgentExternalEventNotifier
|
|
19
|
+
to signal phase changes externally, and executes phase transition hooks.
|
|
20
|
+
"""
|
|
21
|
+
def __init__(self, context: 'AgentContext', notifier: 'AgentExternalEventNotifier'):
|
|
22
|
+
self.context: 'AgentContext' = context
|
|
23
|
+
self.notifier: 'AgentExternalEventNotifier' = notifier
|
|
24
|
+
|
|
25
|
+
self.context.current_phase = AgentOperationalPhase.UNINITIALIZED
|
|
26
|
+
|
|
27
|
+
logger.debug(f"AgentPhaseManager initialized for agent_id '{self.context.agent_id}'. "
|
|
28
|
+
f"Initial phase: {self.context.current_phase.value}. Uses provided notifier.")
|
|
29
|
+
|
|
30
|
+
async def _execute_hooks(self, old_phase: AgentOperationalPhase, new_phase: AgentOperationalPhase):
|
|
31
|
+
"""Asynchronously executes hooks that match the given phase transition."""
|
|
32
|
+
hooks_to_run = [
|
|
33
|
+
hook for hook in self.context.config.phase_hooks
|
|
34
|
+
if hook.source_phase == old_phase and hook.target_phase == new_phase
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
if not hooks_to_run:
|
|
38
|
+
return
|
|
39
|
+
|
|
40
|
+
hook_names = [hook.__class__.__name__ for hook in hooks_to_run]
|
|
41
|
+
logger.info(f"Agent '{self.context.agent_id}': Executing {len(hooks_to_run)} hooks for transition "
|
|
42
|
+
f"'{old_phase.value}' -> '{new_phase.value}': {hook_names}")
|
|
43
|
+
|
|
44
|
+
for hook in hooks_to_run:
|
|
45
|
+
try:
|
|
46
|
+
await hook.execute(self.context)
|
|
47
|
+
logger.debug(f"Agent '{self.context.agent_id}': Hook '{hook.__class__.__name__}' executed successfully.")
|
|
48
|
+
except Exception as e:
|
|
49
|
+
logger.error(f"Agent '{self.context.agent_id}': Error executing phase transition hook "
|
|
50
|
+
f"'{hook.__class__.__name__}' for '{old_phase.value}' -> '{new_phase.value}': {e}",
|
|
51
|
+
exc_info=True)
|
|
52
|
+
# We log the error but do not halt the agent's phase transition.
|
|
53
|
+
|
|
54
|
+
async def _transition_phase(self, new_phase: AgentOperationalPhase,
|
|
55
|
+
notify_method_name: str,
|
|
56
|
+
additional_data: Optional[Dict[str, Any]] = None):
|
|
57
|
+
"""
|
|
58
|
+
Private async helper to change the agent's phase, execute hooks, and then
|
|
59
|
+
call the appropriate notifier method. Hooks are now awaited.
|
|
60
|
+
"""
|
|
61
|
+
if not isinstance(new_phase, AgentOperationalPhase):
|
|
62
|
+
logger.error(f"AgentPhaseManager for '{self.context.agent_id}' received invalid type for new_phase: {type(new_phase)}. Must be AgentOperationalPhase.")
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
old_phase = self.context.current_phase
|
|
66
|
+
|
|
67
|
+
if old_phase == new_phase:
|
|
68
|
+
logger.debug(f"AgentPhaseManager for '{self.context.agent_id}': already in phase {new_phase.value}. No transition.")
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
logger.info(f"Agent '{self.context.agent_id}' phase transitioning from {old_phase.value} to {new_phase.value}.")
|
|
72
|
+
self.context.current_phase = new_phase
|
|
73
|
+
|
|
74
|
+
# Execute and wait for hooks to complete *before* notifying externally.
|
|
75
|
+
await self._execute_hooks(old_phase, new_phase)
|
|
76
|
+
|
|
77
|
+
notifier_method = getattr(self.notifier, notify_method_name, None)
|
|
78
|
+
if notifier_method and callable(notifier_method):
|
|
79
|
+
notify_args = {"old_phase": old_phase}
|
|
80
|
+
if additional_data:
|
|
81
|
+
notify_args.update(additional_data)
|
|
82
|
+
|
|
83
|
+
notifier_method(**notify_args)
|
|
84
|
+
else:
|
|
85
|
+
logger.error(f"AgentPhaseManager for '{self.context.agent_id}': Notifier method '{notify_method_name}' not found or not callable on {type(self.notifier).__name__}.")
|
|
86
|
+
|
|
87
|
+
async def notify_runtime_starting_and_uninitialized(self) -> None:
|
|
88
|
+
if self.context.current_phase == AgentOperationalPhase.UNINITIALIZED:
|
|
89
|
+
await self._transition_phase(AgentOperationalPhase.UNINITIALIZED, "notify_phase_uninitialized_entered")
|
|
90
|
+
elif self.context.current_phase.is_terminal():
|
|
91
|
+
await self._transition_phase(AgentOperationalPhase.UNINITIALIZED, "notify_phase_uninitialized_entered")
|
|
92
|
+
else:
|
|
93
|
+
logger.warning(f"Agent '{self.context.agent_id}' notify_runtime_starting_and_uninitialized called in unexpected phase: {self.context.current_phase.value}")
|
|
94
|
+
|
|
95
|
+
async def notify_bootstrapping_started(self) -> None:
|
|
96
|
+
await self._transition_phase(AgentOperationalPhase.BOOTSTRAPPING, "notify_phase_bootstrapping_started")
|
|
97
|
+
|
|
98
|
+
async def notify_initialization_complete(self) -> None:
|
|
99
|
+
if self.context.current_phase.is_initializing() or self.context.current_phase == AgentOperationalPhase.UNINITIALIZED:
|
|
100
|
+
# This will now be a BOOTSTRAPPING -> IDLE transition
|
|
101
|
+
await self._transition_phase(AgentOperationalPhase.IDLE, "notify_phase_idle_entered")
|
|
102
|
+
else:
|
|
103
|
+
logger.warning(f"Agent '{self.context.agent_id}' notify_initialization_complete called in unexpected phase: {self.context.current_phase.value}")
|
|
104
|
+
|
|
105
|
+
async def notify_processing_input_started(self, trigger_info: Optional[str] = None) -> None:
|
|
106
|
+
if self.context.current_phase in [AgentOperationalPhase.IDLE, AgentOperationalPhase.ANALYZING_LLM_RESPONSE, AgentOperationalPhase.PROCESSING_TOOL_RESULT, AgentOperationalPhase.EXECUTING_TOOL]:
|
|
107
|
+
data = {"trigger_info": trigger_info} if trigger_info else {}
|
|
108
|
+
await self._transition_phase(AgentOperationalPhase.PROCESSING_USER_INPUT, "notify_phase_processing_user_input_started", additional_data=data)
|
|
109
|
+
elif self.context.current_phase == AgentOperationalPhase.PROCESSING_USER_INPUT:
|
|
110
|
+
logger.debug(f"Agent '{self.context.agent_id}' already in PROCESSING_USER_INPUT phase.")
|
|
111
|
+
else:
|
|
112
|
+
logger.warning(f"Agent '{self.context.agent_id}' notify_processing_input_started called in unexpected phase: {self.context.current_phase.value}")
|
|
113
|
+
|
|
114
|
+
async def notify_awaiting_llm_response(self) -> None:
|
|
115
|
+
await self._transition_phase(AgentOperationalPhase.AWAITING_LLM_RESPONSE, "notify_phase_awaiting_llm_response_started")
|
|
116
|
+
|
|
117
|
+
async def notify_analyzing_llm_response(self) -> None:
|
|
118
|
+
await self._transition_phase(AgentOperationalPhase.ANALYZING_LLM_RESPONSE, "notify_phase_analyzing_llm_response_started")
|
|
119
|
+
|
|
120
|
+
async def notify_tool_execution_pending_approval(self, tool_invocation: 'ToolInvocation') -> None:
|
|
121
|
+
# The notifier's notify_phase_awaiting_tool_approval_started method no longer takes tool_details.
|
|
122
|
+
# The phase event itself is the signal. Tool data comes via queue.
|
|
123
|
+
await self._transition_phase(AgentOperationalPhase.AWAITING_TOOL_APPROVAL, "notify_phase_awaiting_tool_approval_started")
|
|
124
|
+
|
|
125
|
+
async def notify_tool_execution_resumed_after_approval(self, approved: bool, tool_name: Optional[str]) -> None:
|
|
126
|
+
if approved and tool_name:
|
|
127
|
+
await self._transition_phase(AgentOperationalPhase.EXECUTING_TOOL, "notify_phase_executing_tool_started", additional_data={"tool_name": tool_name})
|
|
128
|
+
else:
|
|
129
|
+
logger.info(f"Agent '{self.context.agent_id}' tool execution denied for '{tool_name}'. Transitioning to allow LLM to process denial.")
|
|
130
|
+
await self._transition_phase(AgentOperationalPhase.ANALYZING_LLM_RESPONSE, "notify_phase_analyzing_llm_response_started", additional_data={"denial_for_tool": tool_name})
|
|
131
|
+
|
|
132
|
+
async def notify_tool_execution_started(self, tool_name: str) -> None:
|
|
133
|
+
await self._transition_phase(AgentOperationalPhase.EXECUTING_TOOL, "notify_phase_executing_tool_started", additional_data={"tool_name": tool_name})
|
|
134
|
+
|
|
135
|
+
async def notify_processing_tool_result(self, tool_name: str) -> None:
|
|
136
|
+
await self._transition_phase(AgentOperationalPhase.PROCESSING_TOOL_RESULT, "notify_phase_processing_tool_result_started", additional_data={"tool_name": tool_name})
|
|
137
|
+
|
|
138
|
+
async def notify_processing_complete_and_idle(self) -> None:
|
|
139
|
+
if not self.context.current_phase.is_terminal() and self.context.current_phase != AgentOperationalPhase.IDLE:
|
|
140
|
+
await self._transition_phase(AgentOperationalPhase.IDLE, "notify_phase_idle_entered")
|
|
141
|
+
elif self.context.current_phase == AgentOperationalPhase.IDLE:
|
|
142
|
+
logger.debug(f"Agent '{self.context.agent_id}' processing complete, already IDLE.")
|
|
143
|
+
else:
|
|
144
|
+
logger.warning(f"Agent '{self.context.agent_id}' notify_processing_complete_and_idle called in unexpected phase: {self.context.current_phase.value}")
|
|
145
|
+
|
|
146
|
+
async def notify_error_occurred(self, error_message: str, error_details: Optional[str] = None) -> None:
|
|
147
|
+
if self.context.current_phase != AgentOperationalPhase.ERROR:
|
|
148
|
+
data = {"error_message": error_message, "error_details": error_details}
|
|
149
|
+
await self._transition_phase(AgentOperationalPhase.ERROR, "notify_phase_error_entered", additional_data=data)
|
|
150
|
+
else:
|
|
151
|
+
logger.debug(f"Agent '{self.context.agent_id}' already in ERROR phase when another error notified: {error_message}")
|
|
152
|
+
|
|
153
|
+
async def notify_shutdown_initiated(self) -> None:
|
|
154
|
+
if not self.context.current_phase.is_terminal():
|
|
155
|
+
await self._transition_phase(AgentOperationalPhase.SHUTTING_DOWN, "notify_phase_shutting_down_started")
|
|
156
|
+
else:
|
|
157
|
+
logger.debug(f"Agent '{self.context.agent_id}' shutdown initiated but already in a terminal phase: {self.context.current_phase.value}")
|
|
158
|
+
|
|
159
|
+
async def notify_final_shutdown_complete(self) -> None:
|
|
160
|
+
final_phase = AgentOperationalPhase.ERROR if self.context.current_phase == AgentOperationalPhase.ERROR else AgentOperationalPhase.SHUTDOWN_COMPLETE
|
|
161
|
+
if final_phase == AgentOperationalPhase.ERROR:
|
|
162
|
+
await self._transition_phase(AgentOperationalPhase.ERROR, "notify_phase_error_entered", additional_data={"error_message": "Shutdown completed with agent in error state."})
|
|
163
|
+
else:
|
|
164
|
+
await self._transition_phase(AgentOperationalPhase.SHUTDOWN_COMPLETE, "notify_phase_shutdown_completed")
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/context/agent_runtime_state.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import List, Dict, Any, Optional, TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from autobyteus.agent.events.agent_input_event_queue_manager import AgentInputEventQueueManager
|
|
6
|
+
# AgentOutputDataManager is no longer part of AgentRuntimeState
|
|
7
|
+
# from autobyteus.agent.events.agent_output_data_manager import AgentOutputDataManager
|
|
8
|
+
|
|
9
|
+
from autobyteus.llm.base_llm import BaseLLM
|
|
10
|
+
from .phases import AgentOperationalPhase
|
|
11
|
+
from autobyteus.agent.workspace.base_workspace import BaseAgentWorkspace
|
|
12
|
+
from autobyteus.agent.tool_invocation import ToolInvocation
|
|
13
|
+
# LLMConfig is no longer needed here
|
|
14
|
+
# from autobyteus.llm.utils.llm_config import LLMConfig
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from autobyteus.agent.context.agent_phase_manager import AgentPhaseManager
|
|
18
|
+
from autobyteus.tools.base_tool import BaseTool
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
class AgentRuntimeState:
|
|
23
|
+
"""
|
|
24
|
+
Encapsulates the dynamic, stateful data of an agent instance.
|
|
25
|
+
Input event queues are initialized by the AgentWorker via a bootstrap step.
|
|
26
|
+
Output data is now handled by emitting events via AgentExternalEventNotifier.
|
|
27
|
+
"""
|
|
28
|
+
def __init__(self,
|
|
29
|
+
agent_id: str,
|
|
30
|
+
workspace: Optional[BaseAgentWorkspace] = None,
|
|
31
|
+
conversation_history: Optional[List[Dict[str, Any]]] = None,
|
|
32
|
+
custom_data: Optional[Dict[str, Any]] = None):
|
|
33
|
+
if not agent_id or not isinstance(agent_id, str):
|
|
34
|
+
raise ValueError("AgentRuntimeState requires a non-empty string 'agent_id'.")
|
|
35
|
+
if workspace is not None and not isinstance(workspace, BaseAgentWorkspace): # pragma: no cover
|
|
36
|
+
raise TypeError(f"AgentRuntimeState 'workspace' must be a BaseAgentWorkspace or None. Got {type(workspace)}")
|
|
37
|
+
|
|
38
|
+
self.agent_id: str = agent_id
|
|
39
|
+
self.current_phase: AgentOperationalPhase = AgentOperationalPhase.UNINITIALIZED
|
|
40
|
+
self.llm_instance: Optional[BaseLLM] = None
|
|
41
|
+
self.tool_instances: Optional[Dict[str, 'BaseTool']] = None
|
|
42
|
+
|
|
43
|
+
self.input_event_queues: Optional[AgentInputEventQueueManager] = None
|
|
44
|
+
# REMOVED: self.output_data_queues attribute
|
|
45
|
+
|
|
46
|
+
self.workspace: Optional[BaseAgentWorkspace] = workspace
|
|
47
|
+
self.conversation_history: List[Dict[str, Any]] = conversation_history or []
|
|
48
|
+
self.pending_tool_approvals: Dict[str, ToolInvocation] = {}
|
|
49
|
+
self.custom_data: Dict[str, Any] = custom_data or {}
|
|
50
|
+
|
|
51
|
+
self.processed_system_prompt: Optional[str] = None
|
|
52
|
+
# self.final_llm_config_for_creation removed
|
|
53
|
+
|
|
54
|
+
self.phase_manager_ref: Optional['AgentPhaseManager'] = None
|
|
55
|
+
|
|
56
|
+
logger.info(f"AgentRuntimeState initialized for agent_id '{self.agent_id}'. Initial phase: {self.current_phase.value}. Workspace linked. InputQueues pending initialization. Output data via notifier.")
|
|
57
|
+
|
|
58
|
+
def add_message_to_history(self, message: Dict[str, Any]) -> None:
|
|
59
|
+
if not isinstance(message, dict) or "role" not in message: # pragma: no cover
|
|
60
|
+
logger.warning(f"Attempted to add malformed message to history for agent '{self.agent_id}': {message}")
|
|
61
|
+
return
|
|
62
|
+
self.conversation_history.append(message)
|
|
63
|
+
logger.debug(f"Message added to history for agent '{self.agent_id}': role={message['role']}")
|
|
64
|
+
|
|
65
|
+
def store_pending_tool_invocation(self, invocation: ToolInvocation) -> None:
|
|
66
|
+
if not isinstance(invocation, ToolInvocation) or not invocation.id: # pragma: no cover
|
|
67
|
+
logger.error(f"Agent '{self.agent_id}': Attempted to store invalid ToolInvocation for approval: {invocation}")
|
|
68
|
+
return
|
|
69
|
+
self.pending_tool_approvals[invocation.id] = invocation
|
|
70
|
+
logger.info(f"Agent '{self.agent_id}': Stored pending tool invocation '{invocation.id}' ({invocation.name}).")
|
|
71
|
+
|
|
72
|
+
def retrieve_pending_tool_invocation(self, invocation_id: str) -> Optional[ToolInvocation]:
|
|
73
|
+
invocation = self.pending_tool_approvals.pop(invocation_id, None)
|
|
74
|
+
if invocation:
|
|
75
|
+
logger.info(f"Agent '{self.agent_id}': Retrieved pending tool invocation '{invocation_id}' ({invocation.name}).")
|
|
76
|
+
else: # pragma: no cover
|
|
77
|
+
logger.warning(f"Agent '{self.agent_id}': Pending tool invocation '{invocation_id}' not found.")
|
|
78
|
+
return invocation
|
|
79
|
+
|
|
80
|
+
def __repr__(self) -> str:
|
|
81
|
+
phase_repr = self.current_phase.value
|
|
82
|
+
llm_status = "Initialized" if self.llm_instance else "Not Initialized"
|
|
83
|
+
tools_status = f"{len(self.tool_instances)} Initialized" if self.tool_instances is not None else "Not Initialized"
|
|
84
|
+
input_queues_status = "Initialized" if self.input_event_queues else "Not Initialized"
|
|
85
|
+
# REMOVED output_queues_status from repr
|
|
86
|
+
return (f"AgentRuntimeState(agent_id='{self.agent_id}', current_phase='{phase_repr}', "
|
|
87
|
+
f"llm_status='{llm_status}', tools_status='{tools_status}', "
|
|
88
|
+
f"input_queues_status='{input_queues_status}', "
|
|
89
|
+
f"pending_approvals={len(self.pending_tool_approvals)}, history_len={len(self.conversation_history)})")
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/context/phases.py
|
|
2
|
+
from enum import Enum
|
|
3
|
+
|
|
4
|
+
class AgentOperationalPhase(str, Enum):
|
|
5
|
+
"""
|
|
6
|
+
Defines the fine-grained operational phases of an agent.
|
|
7
|
+
This is the single source of truth for an agent's current state of operation.
|
|
8
|
+
"""
|
|
9
|
+
UNINITIALIZED = "uninitialized" # Agent object created, but runtime not started or fully set up.
|
|
10
|
+
BOOTSTRAPPING = "bootstrapping" # Agent is running its internal initialization/bootstrap sequence.
|
|
11
|
+
IDLE = "idle" # Fully initialized and ready for new input.
|
|
12
|
+
|
|
13
|
+
PROCESSING_USER_INPUT = "processing_user_input" # Actively processing a user message, typically preparing for an LLM call.
|
|
14
|
+
AWAITING_LLM_RESPONSE = "awaiting_llm_response" # Sent a request to LLM, waiting for the full response or stream.
|
|
15
|
+
ANALYZING_LLM_RESPONSE = "analyzing_llm_response" # Received LLM response, analyzing it for next actions (e.g., tool use, direct reply).
|
|
16
|
+
|
|
17
|
+
AWAITING_TOOL_APPROVAL = "awaiting_tool_approval" # Paused, needs external (user) approval for a tool invocation.
|
|
18
|
+
EXECUTING_TOOL = "executing_tool" # Tool has been approved (or auto-approved) and is currently running.
|
|
19
|
+
PROCESSING_TOOL_RESULT = "processing_tool_result" # Received a tool's result, actively processing it (often leading to another LLM call).
|
|
20
|
+
|
|
21
|
+
SHUTTING_DOWN = "shutting_down" # Shutdown sequence has been initiated.
|
|
22
|
+
SHUTDOWN_COMPLETE = "shutdown_complete" # Agent has fully stopped and released resources.
|
|
23
|
+
ERROR = "error" # An unrecoverable error has occurred. Agent might be non-operational.
|
|
24
|
+
|
|
25
|
+
def __str__(self) -> str:
|
|
26
|
+
return self.value
|
|
27
|
+
|
|
28
|
+
def is_initializing(self) -> bool:
|
|
29
|
+
"""Checks if the agent is in any of the initializing phases."""
|
|
30
|
+
return self in [
|
|
31
|
+
AgentOperationalPhase.BOOTSTRAPPING,
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
def is_processing(self) -> bool:
|
|
35
|
+
"""Checks if the agent is in any active processing phase (post-initialization, pre-shutdown)."""
|
|
36
|
+
return self in [
|
|
37
|
+
AgentOperationalPhase.PROCESSING_USER_INPUT,
|
|
38
|
+
AgentOperationalPhase.AWAITING_LLM_RESPONSE,
|
|
39
|
+
AgentOperationalPhase.ANALYZING_LLM_RESPONSE,
|
|
40
|
+
AgentOperationalPhase.AWAITING_TOOL_APPROVAL,
|
|
41
|
+
AgentOperationalPhase.EXECUTING_TOOL,
|
|
42
|
+
AgentOperationalPhase.PROCESSING_TOOL_RESULT,
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
def is_terminal(self) -> bool:
|
|
46
|
+
"""Checks if the phase is a terminal state (shutdown or error)."""
|
|
47
|
+
return self in [AgentOperationalPhase.SHUTDOWN_COMPLETE, AgentOperationalPhase.ERROR]
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/events/__init__.py
|
|
2
|
+
"""
|
|
3
|
+
Event definitions and event queue management for agents.
|
|
4
|
+
Also includes the WorkerEventDispatcher for routing events within an agent's worker loop.
|
|
5
|
+
"""
|
|
6
|
+
from .agent_input_event_queue_manager import AgentInputEventQueueManager
|
|
7
|
+
from .worker_event_dispatcher import WorkerEventDispatcher
|
|
8
|
+
|
|
9
|
+
from .agent_events import (
|
|
10
|
+
BaseEvent,
|
|
11
|
+
# Categorical Base Events
|
|
12
|
+
LifecycleEvent,
|
|
13
|
+
AgentProcessingEvent,
|
|
14
|
+
# Agent Phase-Specific Base Events
|
|
15
|
+
AgentPreparationEvent,
|
|
16
|
+
AgentOperationalEvent,
|
|
17
|
+
# Specific Lifecycle Events
|
|
18
|
+
AgentReadyEvent,
|
|
19
|
+
AgentStoppedEvent,
|
|
20
|
+
AgentErrorEvent,
|
|
21
|
+
# DEPRECATED Initialization Events
|
|
22
|
+
CreateToolInstancesEvent,
|
|
23
|
+
ProcessSystemPromptEvent,
|
|
24
|
+
FinalizeLLMConfigEvent,
|
|
25
|
+
CreateLLMInstanceEvent,
|
|
26
|
+
# Regular Agent Processing Events
|
|
27
|
+
UserMessageReceivedEvent,
|
|
28
|
+
InterAgentMessageReceivedEvent,
|
|
29
|
+
LLMUserMessageReadyEvent,
|
|
30
|
+
LLMCompleteResponseReceivedEvent,
|
|
31
|
+
PendingToolInvocationEvent,
|
|
32
|
+
ToolResultEvent,
|
|
33
|
+
ToolExecutionApprovalEvent,
|
|
34
|
+
ApprovedToolInvocationEvent,
|
|
35
|
+
# General Purpose Event
|
|
36
|
+
GenericEvent
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
__all__ = [
|
|
40
|
+
"AgentInputEventQueueManager",
|
|
41
|
+
"WorkerEventDispatcher",
|
|
42
|
+
"BaseEvent",
|
|
43
|
+
"LifecycleEvent",
|
|
44
|
+
"AgentProcessingEvent",
|
|
45
|
+
"AgentPreparationEvent",
|
|
46
|
+
"AgentOperationalEvent",
|
|
47
|
+
"AgentReadyEvent",
|
|
48
|
+
"AgentStoppedEvent",
|
|
49
|
+
"AgentErrorEvent",
|
|
50
|
+
"CreateToolInstancesEvent",
|
|
51
|
+
"ProcessSystemPromptEvent",
|
|
52
|
+
"FinalizeLLMConfigEvent",
|
|
53
|
+
"CreateLLMInstanceEvent",
|
|
54
|
+
"UserMessageReceivedEvent",
|
|
55
|
+
"InterAgentMessageReceivedEvent",
|
|
56
|
+
"LLMUserMessageReadyEvent",
|
|
57
|
+
"LLMCompleteResponseReceivedEvent",
|
|
58
|
+
"PendingToolInvocationEvent",
|
|
59
|
+
"ToolResultEvent",
|
|
60
|
+
"ToolExecutionApprovalEvent",
|
|
61
|
+
"ApprovedToolInvocationEvent",
|
|
62
|
+
"GenericEvent",
|
|
63
|
+
]
|