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,186 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/handlers/tool_invocation_request_event_handler.py
|
|
2
|
+
import logging
|
|
3
|
+
import json
|
|
4
|
+
import traceback
|
|
5
|
+
from typing import TYPE_CHECKING, Optional
|
|
6
|
+
|
|
7
|
+
from autobyteus.agent.handlers.base_event_handler import AgentEventHandler
|
|
8
|
+
from autobyteus.agent.events import PendingToolInvocationEvent, ToolResultEvent
|
|
9
|
+
from autobyteus.agent.tool_invocation import ToolInvocation
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from autobyteus.agent.context import AgentContext
|
|
13
|
+
from autobyteus.agent.events.notifiers import AgentExternalEventNotifier
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
class ToolInvocationRequestEventHandler(AgentEventHandler):
|
|
18
|
+
"""
|
|
19
|
+
Handles PendingToolInvocationEvents.
|
|
20
|
+
If 'auto_execute_tools' (from AgentConfig) is False, it stores the invocation,
|
|
21
|
+
updates history, and emits an AGENT_REQUEST_TOOL_INVOCATION_APPROVAL event via the notifier.
|
|
22
|
+
If 'auto_execute_tools' is True, it executes the tool directly, emits
|
|
23
|
+
AGENT_DATA_TOOL_LOG events for call and result/error,
|
|
24
|
+
and queues a ToolResultEvent.
|
|
25
|
+
"""
|
|
26
|
+
def __init__(self): # pragma: no cover
|
|
27
|
+
logger.info("ToolInvocationRequestEventHandler initialized.")
|
|
28
|
+
|
|
29
|
+
async def _execute_tool_directly(self,
|
|
30
|
+
tool_invocation: ToolInvocation,
|
|
31
|
+
context: 'AgentContext',
|
|
32
|
+
notifier: Optional['AgentExternalEventNotifier']) -> None: # pragma: no cover
|
|
33
|
+
agent_id = context.agent_id
|
|
34
|
+
tool_name = tool_invocation.name
|
|
35
|
+
arguments = tool_invocation.arguments
|
|
36
|
+
invocation_id = tool_invocation.id
|
|
37
|
+
|
|
38
|
+
logger.info(f"Agent '{agent_id}' executing tool directly: '{tool_name}' (ID: {invocation_id}) with args: {arguments}")
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
args_str = json.dumps(arguments)
|
|
42
|
+
except TypeError:
|
|
43
|
+
args_str = str(arguments)
|
|
44
|
+
|
|
45
|
+
log_msg_call = f"[TOOL_CALL_DIRECT] Agent_ID: {agent_id}, Tool: {tool_name}, Invocation_ID: {invocation_id}, Arguments: {args_str}"
|
|
46
|
+
if notifier:
|
|
47
|
+
try:
|
|
48
|
+
notifier.notify_agent_data_tool_log(log_msg_call) # USE RENAMED METHOD
|
|
49
|
+
except Exception as e_notify:
|
|
50
|
+
logger.error(f"Agent '{agent_id}': Error notifying tool call log: {e_notify}", exc_info=True)
|
|
51
|
+
|
|
52
|
+
tool_instance = context.get_tool(tool_name)
|
|
53
|
+
result_event: ToolResultEvent
|
|
54
|
+
|
|
55
|
+
if not tool_instance:
|
|
56
|
+
error_message = f"Tool '{tool_name}' not found or configured for agent '{agent_id}'."
|
|
57
|
+
logger.error(error_message)
|
|
58
|
+
result_event = ToolResultEvent(tool_name=tool_name, result=None, error=error_message, tool_invocation_id=invocation_id)
|
|
59
|
+
context.add_message_to_history({
|
|
60
|
+
"role": "tool",
|
|
61
|
+
"tool_call_id": invocation_id,
|
|
62
|
+
"name": tool_name,
|
|
63
|
+
"content": f"Error: Tool '{tool_name}' execution failed. Reason: {error_message}",
|
|
64
|
+
})
|
|
65
|
+
log_msg_error = f"[TOOL_ERROR_DIRECT] Agent_ID: {agent_id}, Tool: {tool_name}, Invocation_ID: {invocation_id}, Error: {error_message}"
|
|
66
|
+
if notifier:
|
|
67
|
+
try:
|
|
68
|
+
notifier.notify_agent_data_tool_log(log_msg_error) # USE RENAMED METHOD
|
|
69
|
+
notifier.notify_agent_error_output_generation( # USE RENAMED METHOD
|
|
70
|
+
error_source=f"ToolExecutionDirect.ToolNotFound.{tool_name}",
|
|
71
|
+
error_message=error_message
|
|
72
|
+
)
|
|
73
|
+
except Exception as e_notify:
|
|
74
|
+
logger.error(f"Agent '{agent_id}': Error notifying tool error log/output error: {e_notify}", exc_info=True)
|
|
75
|
+
else:
|
|
76
|
+
try:
|
|
77
|
+
logger.debug(f"Executing tool '{tool_name}' for agent '{agent_id}'. Invocation ID: {invocation_id}")
|
|
78
|
+
execution_result = await tool_instance.execute(context=context, **arguments)
|
|
79
|
+
|
|
80
|
+
try:
|
|
81
|
+
result_str_for_log = json.dumps(execution_result)
|
|
82
|
+
except TypeError:
|
|
83
|
+
result_str_for_log = str(execution_result)
|
|
84
|
+
|
|
85
|
+
logger.info(f"Tool '{tool_name}' (ID: {invocation_id}) executed by agent '{agent_id}'. Result: {result_str_for_log[:200]}...")
|
|
86
|
+
result_event = ToolResultEvent(tool_name=tool_name, result=execution_result, error=None, tool_invocation_id=invocation_id)
|
|
87
|
+
|
|
88
|
+
history_content = str(execution_result)
|
|
89
|
+
context.add_message_to_history({
|
|
90
|
+
"role": "tool",
|
|
91
|
+
"tool_call_id": invocation_id,
|
|
92
|
+
"name": tool_name,
|
|
93
|
+
"content": history_content,
|
|
94
|
+
})
|
|
95
|
+
log_msg_result = f"[TOOL_RESULT_DIRECT] Agent_ID: {agent_id}, Tool: {tool_name}, Invocation_ID: {invocation_id}, Outcome (first 200 chars): {result_str_for_log[:200]}"
|
|
96
|
+
if notifier:
|
|
97
|
+
try:
|
|
98
|
+
notifier.notify_agent_data_tool_log(log_msg_result) # USE RENAMED METHOD
|
|
99
|
+
except Exception as e_notify:
|
|
100
|
+
logger.error(f"Agent '{agent_id}': Error notifying tool result log: {e_notify}", exc_info=True)
|
|
101
|
+
|
|
102
|
+
except Exception as e:
|
|
103
|
+
error_message = f"Error executing tool '{tool_name}' (ID: {invocation_id}): {str(e)}"
|
|
104
|
+
logger.error(f"Agent '{agent_id}' {error_message}", exc_info=True)
|
|
105
|
+
result_event = ToolResultEvent(tool_name=tool_name, result=None, error=error_message, tool_invocation_id=invocation_id)
|
|
106
|
+
context.add_message_to_history({
|
|
107
|
+
"role": "tool",
|
|
108
|
+
"tool_call_id": invocation_id,
|
|
109
|
+
"name": tool_name,
|
|
110
|
+
"content": f"Error: Tool '{tool_name}' execution failed. Reason: {error_message}",
|
|
111
|
+
})
|
|
112
|
+
log_msg_exception = f"[TOOL_EXCEPTION_DIRECT] Agent_ID: {agent_id}, Tool: {tool_name}, Invocation_ID: {invocation_id}, Exception: {error_message}"
|
|
113
|
+
if notifier:
|
|
114
|
+
try:
|
|
115
|
+
notifier.notify_agent_data_tool_log(log_msg_exception) # USE RENAMED METHOD
|
|
116
|
+
notifier.notify_agent_error_output_generation( # USE RENAMED METHOD
|
|
117
|
+
error_source=f"ToolExecutionDirect.Exception.{tool_name}",
|
|
118
|
+
error_message=error_message,
|
|
119
|
+
error_details=traceback.format_exc()
|
|
120
|
+
)
|
|
121
|
+
except Exception as e_notify:
|
|
122
|
+
logger.error(f"Agent '{agent_id}': Error notifying tool exception log/output error: {e_notify}", exc_info=True)
|
|
123
|
+
|
|
124
|
+
await context.input_event_queues.enqueue_tool_result(result_event)
|
|
125
|
+
logger.debug(f"Agent '{agent_id}' enqueued ToolResultEvent (direct exec) for '{tool_name}' (ID: {invocation_id}).")
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
async def handle(self,
|
|
129
|
+
event: PendingToolInvocationEvent,
|
|
130
|
+
context: 'AgentContext') -> None: # pragma: no cover
|
|
131
|
+
if not isinstance(event, PendingToolInvocationEvent):
|
|
132
|
+
logger.warning(f"ToolInvocationRequestEventHandler received non-PendingToolInvocationEvent: {type(event)}. Skipping.")
|
|
133
|
+
return
|
|
134
|
+
|
|
135
|
+
tool_invocation: ToolInvocation = event.tool_invocation
|
|
136
|
+
agent_id = context.agent_id
|
|
137
|
+
|
|
138
|
+
notifier: Optional['AgentExternalEventNotifier'] = None
|
|
139
|
+
if context.phase_manager:
|
|
140
|
+
notifier = context.phase_manager.notifier
|
|
141
|
+
|
|
142
|
+
if not notifier:
|
|
143
|
+
logger.error(f"Agent '{agent_id}': Notifier not available in ToolInvocationRequestEventHandler. Output events for tool approval/logging will be lost.")
|
|
144
|
+
if not context.auto_execute_tools:
|
|
145
|
+
logger.critical(f"Agent '{agent_id}': Notifier is REQUIRED for manual tool approval flow but is unavailable. Tool '{tool_invocation.name}' cannot be processed for approval.")
|
|
146
|
+
return
|
|
147
|
+
|
|
148
|
+
if not context.auto_execute_tools:
|
|
149
|
+
logger.info(f"Agent '{agent_id}': Tool '{tool_invocation.name}' (ID: {tool_invocation.id}) requires approval. Storing pending invocation and emitting request.")
|
|
150
|
+
|
|
151
|
+
context.store_pending_tool_invocation(tool_invocation)
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
arguments_json_str = json.dumps(tool_invocation.arguments or {})
|
|
155
|
+
except TypeError:
|
|
156
|
+
logger.warning(f"Could not serialize args for history tool_call for '{tool_invocation.name}'. Using empty dict string.")
|
|
157
|
+
arguments_json_str = "{}"
|
|
158
|
+
|
|
159
|
+
context.add_message_to_history({
|
|
160
|
+
"role": "assistant",
|
|
161
|
+
"content": None,
|
|
162
|
+
"tool_calls": [{
|
|
163
|
+
"id": tool_invocation.id,
|
|
164
|
+
"type": "function",
|
|
165
|
+
"function": {
|
|
166
|
+
"name": tool_invocation.name,
|
|
167
|
+
"arguments": arguments_json_str
|
|
168
|
+
}
|
|
169
|
+
}]
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
approval_data = {
|
|
173
|
+
"invocation_id": tool_invocation.id,
|
|
174
|
+
"tool_name": tool_invocation.name,
|
|
175
|
+
"arguments": tool_invocation.arguments,
|
|
176
|
+
}
|
|
177
|
+
if notifier:
|
|
178
|
+
try:
|
|
179
|
+
notifier.notify_agent_request_tool_invocation_approval(approval_data) # USE RENAMED METHOD
|
|
180
|
+
logger.debug(f"Agent '{agent_id}': Emitted AGENT_REQUEST_TOOL_INVOCATION_APPROVAL for '{tool_invocation.name}' (ID: {tool_invocation.id}).")
|
|
181
|
+
except Exception as e_notify:
|
|
182
|
+
logger.error(f"Agent '{agent_id}': Error emitting AGENT_REQUEST_TOOL_INVOCATION_APPROVAL: {e_notify}", exc_info=True)
|
|
183
|
+
|
|
184
|
+
else:
|
|
185
|
+
logger.info(f"Agent '{agent_id}': Tool '{tool_invocation.name}' (ID: {tool_invocation.id}) executing automatically (auto_execute_tools=True).")
|
|
186
|
+
await self._execute_tool_directly(tool_invocation, context, notifier)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/handlers/tool_result_event_handler.py
|
|
2
|
+
import logging
|
|
3
|
+
import json
|
|
4
|
+
from typing import TYPE_CHECKING, Optional
|
|
5
|
+
|
|
6
|
+
from autobyteus.agent.handlers.base_event_handler import AgentEventHandler
|
|
7
|
+
from autobyteus.agent.events import ToolResultEvent, LLMUserMessageReadyEvent
|
|
8
|
+
from autobyteus.llm.user_message import LLMUserMessage
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from autobyteus.agent.context import AgentContext
|
|
12
|
+
from autobyteus.agent.events.notifiers import AgentExternalEventNotifier
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
class ToolResultEventHandler(AgentEventHandler):
|
|
17
|
+
"""
|
|
18
|
+
Handles ToolResultEvents by formatting the tool's output (or error)
|
|
19
|
+
as a new LLMUserMessage, emitting AGENT_DATA_TOOL_LOG event for this outcome,
|
|
20
|
+
and enqueuing an LLMUserMessageReadyEvent for further LLM processing.
|
|
21
|
+
"""
|
|
22
|
+
def __init__(self):
|
|
23
|
+
logger.info("ToolResultEventHandler initialized.")
|
|
24
|
+
|
|
25
|
+
async def handle(self,
|
|
26
|
+
event: ToolResultEvent,
|
|
27
|
+
context: 'AgentContext') -> None:
|
|
28
|
+
if not isinstance(event, ToolResultEvent):
|
|
29
|
+
logger.warning(f"ToolResultEventHandler received non-ToolResultEvent: {type(event)}. Skipping.")
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
agent_id = context.agent_id
|
|
33
|
+
tool_invocation_id = event.tool_invocation_id if event.tool_invocation_id else 'N/A'
|
|
34
|
+
|
|
35
|
+
logger.info(f"Agent '{agent_id}' handling ToolResultEvent from tool: '{event.tool_name}' (Invocation ID: {tool_invocation_id}). Error: {event.error is not None}")
|
|
36
|
+
|
|
37
|
+
notifier: Optional['AgentExternalEventNotifier'] = None
|
|
38
|
+
if context.phase_manager:
|
|
39
|
+
notifier = context.phase_manager.notifier
|
|
40
|
+
|
|
41
|
+
if not notifier: # pragma: no cover
|
|
42
|
+
logger.error(f"Agent '{agent_id}': Notifier not available in ToolResultEventHandler. Tool result processing logs will not be emitted.")
|
|
43
|
+
|
|
44
|
+
if event.error:
|
|
45
|
+
logger.debug(f"Agent '{agent_id}' tool '{event.tool_name}' (ID: {tool_invocation_id}) raw error details: {event.error}")
|
|
46
|
+
else:
|
|
47
|
+
try:
|
|
48
|
+
raw_result_str_for_debug_log = json.dumps(event.result, indent=2)
|
|
49
|
+
except TypeError: # pragma: no cover
|
|
50
|
+
raw_result_str_for_debug_log = str(event.result)
|
|
51
|
+
logger.debug(f"Agent '{agent_id}' tool '{event.tool_name}' (ID: {tool_invocation_id}) raw result:\n---\n{raw_result_str_for_debug_log}\n---")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
content_for_llm: str
|
|
55
|
+
if event.error:
|
|
56
|
+
content_for_llm = (
|
|
57
|
+
f"The tool '{event.tool_name}' (invocation ID: {tool_invocation_id}) encountered an error.\n"
|
|
58
|
+
f"Error details: {event.error}\n"
|
|
59
|
+
f"Please analyze this error and decide the next course of action."
|
|
60
|
+
)
|
|
61
|
+
log_msg_error_processed = f"[TOOL_RESULT_ERROR_PROCESSED] Agent_ID: {agent_id}, Tool: {event.tool_name}, Invocation_ID: {tool_invocation_id}, Error: {event.error}"
|
|
62
|
+
if notifier:
|
|
63
|
+
try:
|
|
64
|
+
notifier.notify_agent_data_tool_log(log_msg_error_processed) # USE RENAMED METHOD
|
|
65
|
+
except Exception as e_notify:
|
|
66
|
+
logger.error(f"Agent '{agent_id}': Error notifying tool result error log: {e_notify}", exc_info=True)
|
|
67
|
+
else:
|
|
68
|
+
try:
|
|
69
|
+
result_str_for_llm = json.dumps(event.result, indent=2) if not isinstance(event.result, str) else event.result
|
|
70
|
+
except TypeError: # pragma: no cover
|
|
71
|
+
result_str_for_llm = str(event.result)
|
|
72
|
+
|
|
73
|
+
max_len = 2000
|
|
74
|
+
if len(result_str_for_llm) > max_len: # pragma: no cover
|
|
75
|
+
original_len = len(str(event.result))
|
|
76
|
+
result_str_for_llm = result_str_for_llm[:max_len] + f"... (result truncated, original length {original_len})"
|
|
77
|
+
|
|
78
|
+
content_for_llm = (
|
|
79
|
+
f"The tool '{event.tool_name}' (invocation ID: {tool_invocation_id}) has executed.\n"
|
|
80
|
+
f"Result:\n{result_str_for_llm}\n"
|
|
81
|
+
f"Based on this result, what is the next step or final answer?"
|
|
82
|
+
)
|
|
83
|
+
log_msg_success_processed = f"[TOOL_RESULT_SUCCESS_PROCESSED] Agent_ID: {agent_id}, Tool: {event.tool_name}, Invocation_ID: {tool_invocation_id}, Result (first 200 chars of stringified): {str(event.result)[:200]}"
|
|
84
|
+
if notifier:
|
|
85
|
+
try:
|
|
86
|
+
notifier.notify_agent_data_tool_log(log_msg_success_processed) # USE RENAMED METHOD
|
|
87
|
+
except Exception as e_notify:
|
|
88
|
+
logger.error(f"Agent '{agent_id}': Error notifying tool result success log: {e_notify}", exc_info=True)
|
|
89
|
+
|
|
90
|
+
logger.debug(f"Agent '{agent_id}' preparing message for LLM based on tool '{event.tool_name}' (ID: {tool_invocation_id}) result:\n---\n{content_for_llm}\n---")
|
|
91
|
+
llm_user_message = LLMUserMessage(content=content_for_llm)
|
|
92
|
+
|
|
93
|
+
next_event = LLMUserMessageReadyEvent(llm_user_message=llm_user_message)
|
|
94
|
+
await context.input_event_queues.enqueue_internal_system_event(next_event)
|
|
95
|
+
|
|
96
|
+
logger.info(f"Agent '{agent_id}' enqueued LLMUserMessageReadyEvent for LLM based on tool '{event.tool_name}' (ID: {tool_invocation_id}) result summary.")
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/handlers/user_input_message_event_handler.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from autobyteus.agent.handlers.base_event_handler import AgentEventHandler
|
|
6
|
+
from autobyteus.agent.events import UserMessageReceivedEvent, LLMUserMessageReadyEvent
|
|
7
|
+
from autobyteus.agent.message.agent_input_user_message import AgentInputUserMessage
|
|
8
|
+
from autobyteus.agent.input_processor import BaseAgentUserInputMessageProcessor
|
|
9
|
+
from autobyteus.llm.user_message import LLMUserMessage
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from autobyteus.agent.context import AgentContext
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
class UserInputMessageEventHandler(AgentEventHandler):
|
|
18
|
+
"""
|
|
19
|
+
Handles UserMessageReceivedEvents by first applying any configured
|
|
20
|
+
AgentUserInputMessageProcessors (provided as instances) to the AgentInputUserMessage,
|
|
21
|
+
then converting the processed message into an LLMUserMessage, and finally
|
|
22
|
+
enqueuing an LLMUserMessageReadyEvent for further processing by the LLM.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self):
|
|
26
|
+
logger.info("UserInputMessageEventHandler initialized.")
|
|
27
|
+
|
|
28
|
+
async def handle(self,
|
|
29
|
+
event: UserMessageReceivedEvent,
|
|
30
|
+
context: 'AgentContext') -> None:
|
|
31
|
+
if not isinstance(event, UserMessageReceivedEvent):
|
|
32
|
+
logger.warning(f"UserInputMessageEventHandler received non-UserMessageReceivedEvent: {type(event)}. Skipping.")
|
|
33
|
+
return
|
|
34
|
+
|
|
35
|
+
original_agent_input_user_msg: AgentInputUserMessage = event.agent_input_user_message
|
|
36
|
+
processed_agent_input_user_msg: AgentInputUserMessage = original_agent_input_user_msg
|
|
37
|
+
|
|
38
|
+
logger.info(f"Agent '{context.agent_id}' handling UserMessageReceivedEvent: '{original_agent_input_user_msg.content[:100]}...'")
|
|
39
|
+
|
|
40
|
+
processor_instances = context.config.input_processors
|
|
41
|
+
if processor_instances:
|
|
42
|
+
processor_names = [p.get_name() for p in processor_instances]
|
|
43
|
+
logger.debug(f"Agent '{context.agent_id}': Applying input processors: {processor_names}")
|
|
44
|
+
for processor_instance in processor_instances:
|
|
45
|
+
processor_name_for_log = "unknown"
|
|
46
|
+
try:
|
|
47
|
+
if not isinstance(processor_instance, BaseAgentUserInputMessageProcessor):
|
|
48
|
+
logger.error(f"Agent '{context.agent_id}': Invalid input processor type in config: {type(processor_instance)}. Skipping.")
|
|
49
|
+
continue
|
|
50
|
+
|
|
51
|
+
processor_name_for_log = processor_instance.get_name()
|
|
52
|
+
logger.debug(f"Agent '{context.agent_id}': Applying input processor '{processor_name_for_log}'.")
|
|
53
|
+
msg_before_this_processor = processed_agent_input_user_msg
|
|
54
|
+
# Pass the original event to the processor
|
|
55
|
+
processed_agent_input_user_msg = await processor_instance.process(
|
|
56
|
+
message=msg_before_this_processor,
|
|
57
|
+
context=context,
|
|
58
|
+
triggering_event=event
|
|
59
|
+
)
|
|
60
|
+
logger.info(f"Agent '{context.agent_id}': Input processor '{processor_name_for_log}' applied successfully.")
|
|
61
|
+
|
|
62
|
+
except Exception as e:
|
|
63
|
+
logger.error(f"Agent '{context.agent_id}': Error applying input processor '{processor_name_for_log}': {e}. "
|
|
64
|
+
f"Skipping this processor and continuing with message from before this processor.", exc_info=True)
|
|
65
|
+
processed_agent_input_user_msg = msg_before_this_processor
|
|
66
|
+
else:
|
|
67
|
+
logger.debug(f"Agent '{context.agent_id}': No input processors configured in agent config.")
|
|
68
|
+
|
|
69
|
+
llm_user_message = LLMUserMessage(
|
|
70
|
+
content=processed_agent_input_user_msg.content,
|
|
71
|
+
image_urls=processed_agent_input_user_msg.image_urls
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
llm_user_message_ready_event = LLMUserMessageReadyEvent(llm_user_message=llm_user_message)
|
|
75
|
+
await context.input_event_queues.enqueue_internal_system_event(llm_user_message_ready_event)
|
|
76
|
+
|
|
77
|
+
logger.info(f"Agent '{context.agent_id}' processed AgentInputUserMessage and enqueued LLMUserMessageReadyEvent.")
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/hooks/base_phase_hook.py
|
|
2
|
+
import logging
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from autobyteus.agent.context.phases import AgentOperationalPhase
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from autobyteus.agent.context import AgentContext
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
class BasePhaseHook(ABC):
|
|
14
|
+
"""
|
|
15
|
+
Abstract base class for creating hooks that execute on specific agent
|
|
16
|
+
phase transitions.
|
|
17
|
+
|
|
18
|
+
Subclasses must define the `source_phase` and `target_phase` to specify
|
|
19
|
+
the exact transition they are interested in, and implement the `execute`
|
|
20
|
+
method for their custom logic.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
@abstractmethod
|
|
25
|
+
def source_phase(self) -> AgentOperationalPhase:
|
|
26
|
+
"""The source phase for the transition this hook targets."""
|
|
27
|
+
raise NotImplementedError
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
@abstractmethod
|
|
31
|
+
def target_phase(self) -> AgentOperationalPhase:
|
|
32
|
+
"""The target phase for the transition this hook targets."""
|
|
33
|
+
raise NotImplementedError
|
|
34
|
+
|
|
35
|
+
@abstractmethod
|
|
36
|
+
async def execute(self, context: 'AgentContext') -> None:
|
|
37
|
+
"""
|
|
38
|
+
The method executed when the specified phase transition occurs.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
context: The agent's context at the time of the transition.
|
|
42
|
+
"""
|
|
43
|
+
raise NotImplementedError
|
|
44
|
+
|
|
45
|
+
def __repr__(self) -> str:
|
|
46
|
+
# Use try-except in case properties are not yet implemented during introspection
|
|
47
|
+
try:
|
|
48
|
+
return (f"<{self.__class__.__name__} "
|
|
49
|
+
f"source='{self.source_phase.value}' "
|
|
50
|
+
f"target='{self.target_phase.value}'>")
|
|
51
|
+
except (NotImplementedError, AttributeError):
|
|
52
|
+
return f"<{self.__class__.__name__} (unconfigured)>"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/input_processor/__init__.py
|
|
2
|
+
"""
|
|
3
|
+
Components for pre-processing AgentUserMessage objects.
|
|
4
|
+
"""
|
|
5
|
+
from .base_user_input_processor import BaseAgentUserInputMessageProcessor
|
|
6
|
+
|
|
7
|
+
# Import concrete processors to make them easily accessible for instantiation
|
|
8
|
+
from .passthrough_input_processor import PassthroughInputProcessor
|
|
9
|
+
from .metadata_appending_input_processor import MetadataAppendingInputProcessor
|
|
10
|
+
from .content_prefixing_input_processor import ContentPrefixingInputProcessor
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"BaseAgentUserInputMessageProcessor",
|
|
15
|
+
"PassthroughInputProcessor",
|
|
16
|
+
"MetadataAppendingInputProcessor",
|
|
17
|
+
"ContentPrefixingInputProcessor",
|
|
18
|
+
]
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/input_processor/base_user_input_processor.py
|
|
2
|
+
import logging
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import TYPE_CHECKING, Optional
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from autobyteus.agent.message.agent_input_user_message import AgentInputUserMessage
|
|
8
|
+
from autobyteus.agent.context import AgentContext # Composite AgentContext
|
|
9
|
+
from autobyteus.agent.events import UserMessageReceivedEvent
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
class BaseAgentUserInputMessageProcessor(ABC):
|
|
14
|
+
"""
|
|
15
|
+
Abstract base class for agent user input message processors.
|
|
16
|
+
These processors can modify an AgentInputUserMessage, specifically from a user,
|
|
17
|
+
before it is converted to an LLMUserMessage.
|
|
18
|
+
Subclasses should be instantiated and passed to the AgentSpecification.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def get_name(self) -> str:
|
|
22
|
+
"""
|
|
23
|
+
Returns the unique registration name for this processor.
|
|
24
|
+
Defaults to the class name. Can be overridden by subclasses.
|
|
25
|
+
"""
|
|
26
|
+
return self.__class__.__name__
|
|
27
|
+
|
|
28
|
+
@abstractmethod
|
|
29
|
+
async def process(self,
|
|
30
|
+
message: 'AgentInputUserMessage',
|
|
31
|
+
context: 'AgentContext',
|
|
32
|
+
triggering_event: 'UserMessageReceivedEvent') -> 'AgentInputUserMessage':
|
|
33
|
+
"""
|
|
34
|
+
Processes the given AgentInputUserMessage.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
message: The AgentInputUserMessage to process.
|
|
38
|
+
context: The composite AgentContext, providing access to agent's config and state.
|
|
39
|
+
triggering_event: The original UserMessageReceivedEvent that triggered this processing.
|
|
40
|
+
This provides access to the full event payload for more complex processors.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
The processed (potentially modified) AgentInputUserMessage.
|
|
44
|
+
|
|
45
|
+
Raises:
|
|
46
|
+
NotImplementedError: If the subclass does not implement this method.
|
|
47
|
+
"""
|
|
48
|
+
raise NotImplementedError("Subclasses must implement the 'process' method.")
|
|
49
|
+
|
|
50
|
+
def __repr__(self) -> str:
|
|
51
|
+
return f"<{self.__class__.__name__}>"
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/input_processor/content_prefixing_input_processor.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from .base_user_input_processor import BaseAgentUserInputMessageProcessor
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from autobyteus.agent.message.agent_input_user_message import AgentInputUserMessage
|
|
9
|
+
from autobyteus.agent.context import AgentContext # Composite AgentContext
|
|
10
|
+
from autobyteus.agent.events import UserMessageReceivedEvent
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
class ContentPrefixingInputProcessor(BaseAgentUserInputMessageProcessor):
|
|
15
|
+
"""
|
|
16
|
+
A processor that adds a predefined prefix to the message content.
|
|
17
|
+
The prefix is defined by the agent's custom_data (in AgentRuntimeState) or a default.
|
|
18
|
+
Example prefix key in custom_data: "content_prefix"
|
|
19
|
+
"""
|
|
20
|
+
DEFAULT_PREFIX = "[Processed Message] "
|
|
21
|
+
|
|
22
|
+
async def process(self,
|
|
23
|
+
message: 'AgentInputUserMessage',
|
|
24
|
+
context: 'AgentContext',
|
|
25
|
+
triggering_event: 'UserMessageReceivedEvent') -> 'AgentInputUserMessage':
|
|
26
|
+
"""
|
|
27
|
+
Handles the message by prefixing its content.
|
|
28
|
+
The 'triggering_event' parameter is ignored by this processor.
|
|
29
|
+
"""
|
|
30
|
+
agent_id = context.agent_id # Convenience property
|
|
31
|
+
logger.debug(f"Agent '{agent_id}': ContentPrefixingInputProcessor processing message.")
|
|
32
|
+
|
|
33
|
+
# Access custom_data via convenience property (or context.state.custom_data)
|
|
34
|
+
prefix = context.custom_data.get("content_prefix", self.DEFAULT_PREFIX)
|
|
35
|
+
if not isinstance(prefix, str):
|
|
36
|
+
logger.warning(f"Agent '{agent_id}': 'content_prefix' in custom_data is not a string. Using default prefix. Found: {type(prefix)}")
|
|
37
|
+
prefix = self.DEFAULT_PREFIX
|
|
38
|
+
|
|
39
|
+
message.content = prefix + message.content
|
|
40
|
+
logger.info(f"Agent '{agent_id}': Prefixed message content with '{prefix}'.")
|
|
41
|
+
return message
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/input_processor/metadata_appending_input_processor.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from .base_user_input_processor import BaseAgentUserInputMessageProcessor
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from autobyteus.agent.message.agent_input_user_message import AgentInputUserMessage
|
|
9
|
+
from autobyteus.agent.context import AgentContext # Composite AgentContext
|
|
10
|
+
from autobyteus.agent.events import UserMessageReceivedEvent
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
class MetadataAppendingInputProcessor(BaseAgentUserInputMessageProcessor):
|
|
15
|
+
"""
|
|
16
|
+
A processor that appends fixed metadata to the message.
|
|
17
|
+
Example: Appends agent_id and config_name to metadata.
|
|
18
|
+
"""
|
|
19
|
+
async def process(self,
|
|
20
|
+
message: 'AgentInputUserMessage',
|
|
21
|
+
context: 'AgentContext',
|
|
22
|
+
triggering_event: 'UserMessageReceivedEvent') -> 'AgentInputUserMessage':
|
|
23
|
+
"""
|
|
24
|
+
Handles the message by appending metadata.
|
|
25
|
+
The 'triggering_event' parameter is ignored by this processor.
|
|
26
|
+
"""
|
|
27
|
+
agent_id = context.agent_id
|
|
28
|
+
config_name = context.config.name
|
|
29
|
+
|
|
30
|
+
logger.debug(f"Agent '{agent_id}': MetadataAppendingInputProcessor processing message.")
|
|
31
|
+
message.metadata["processed_by_agent_id"] = agent_id
|
|
32
|
+
message.metadata["processed_with_config_name"] = config_name
|
|
33
|
+
logger.info(f"Agent '{agent_id}': Appended 'processed_by_agent_id' and 'processed_with_config_name' to message metadata.")
|
|
34
|
+
return message
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/input_processor/passthrough_input_processor.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from .base_user_input_processor import BaseAgentUserInputMessageProcessor
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from autobyteus.agent.message.agent_input_user_message import AgentInputUserMessage
|
|
9
|
+
from autobyteus.agent.context import AgentContext # Composite AgentContext
|
|
10
|
+
from autobyteus.agent.events import UserMessageReceivedEvent
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
class PassthroughInputProcessor(BaseAgentUserInputMessageProcessor):
|
|
15
|
+
"""
|
|
16
|
+
A processor that returns the message unchanged.
|
|
17
|
+
Can be used as a default or for testing.
|
|
18
|
+
"""
|
|
19
|
+
def get_name(self) -> str:
|
|
20
|
+
return "PassthroughInputProcessor"
|
|
21
|
+
|
|
22
|
+
async def process(self,
|
|
23
|
+
message: 'AgentInputUserMessage',
|
|
24
|
+
context: 'AgentContext',
|
|
25
|
+
triggering_event: 'UserMessageReceivedEvent') -> 'AgentInputUserMessage':
|
|
26
|
+
"""
|
|
27
|
+
Handles the message by returning it without modification.
|
|
28
|
+
The 'triggering_event' parameter is ignored by this processor.
|
|
29
|
+
"""
|
|
30
|
+
agent_id = context.agent_id # Convenience property
|
|
31
|
+
logger.debug(f"Agent '{agent_id}': PassthroughInputProcessor received message, returning as is.")
|
|
32
|
+
return message
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/input_processor/processor_definition.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Type, TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from .base_user_input_processor import BaseAgentUserInputMessageProcessor
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
class AgentUserInputMessageProcessorDefinition:
|
|
11
|
+
"""
|
|
12
|
+
Represents the definition of an agent user input message processor.
|
|
13
|
+
Contains its registered name and the class itself.
|
|
14
|
+
"""
|
|
15
|
+
def __init__(self, name: str, processor_class: Type['BaseAgentUserInputMessageProcessor']):
|
|
16
|
+
"""
|
|
17
|
+
Initializes the AgentUserInputMessageProcessorDefinition.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
name: The unique registered name of the processor.
|
|
21
|
+
processor_class: The class of the input processor.
|
|
22
|
+
|
|
23
|
+
Raises:
|
|
24
|
+
ValueError: If name is empty or processor_class is not a type.
|
|
25
|
+
"""
|
|
26
|
+
if not name or not isinstance(name, str):
|
|
27
|
+
raise ValueError("Processor name must be a non-empty string.")
|
|
28
|
+
if not isinstance(processor_class, type): # Check if it's actually a class
|
|
29
|
+
raise ValueError("processor_class must be a class type.")
|
|
30
|
+
|
|
31
|
+
# Further check if it's a subclass of BaseAgentUserInputMessageProcessor might be too restrictive
|
|
32
|
+
# here if base class is not yet defined due to import cycles, metaclass handles this better.
|
|
33
|
+
# from .base_user_input_processor import BaseAgentUserInputMessageProcessor # Delayed import for check
|
|
34
|
+
# if not issubclass(processor_class, BaseAgentUserInputMessageProcessor):
|
|
35
|
+
# raise ValueError(f"processor_class '{processor_class.__name__}' must be a subclass of BaseAgentUserInputMessageProcessor.")
|
|
36
|
+
|
|
37
|
+
self.name: str = name
|
|
38
|
+
self.processor_class: Type['BaseAgentUserInputMessageProcessor'] = processor_class
|
|
39
|
+
logger.debug(f"AgentUserInputMessageProcessorDefinition created: name='{name}', class='{processor_class.__name__}'.")
|
|
40
|
+
|
|
41
|
+
def __repr__(self) -> str:
|
|
42
|
+
return f"<AgentUserInputMessageProcessorDefinition name='{self.name}', class='{self.processor_class.__name__}'>"
|