autobyteus 1.0.6__py3-none-any.whl → 1.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +13 -0
- autobyteus/agent/context/agent_config.py +84 -0
- autobyteus/agent/context/agent_context.py +129 -0
- autobyteus/agent/context/agent_phase_manager.py +264 -0
- autobyteus/agent/context/agent_runtime_state.py +89 -0
- autobyteus/agent/context/phases.py +49 -0
- autobyteus/agent/events/__init__.py +52 -0
- autobyteus/agent/events/agent_events.py +110 -0
- autobyteus/agent/events/agent_input_event_queue_manager.py +174 -0
- autobyteus/agent/events/notifiers.py +122 -0
- autobyteus/agent/events/worker_event_dispatcher.py +118 -0
- autobyteus/agent/factory/__init__.py +9 -0
- autobyteus/agent/factory/agent_factory.py +145 -0
- autobyteus/agent/group/agent_group.py +164 -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 +148 -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 +138 -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 +211 -0
- autobyteus/agent/handlers/tool_result_event_handler.py +101 -0
- autobyteus/agent/handlers/user_input_message_event_handler.py +77 -0
- autobyteus/agent/hooks/__init__.py +16 -0
- autobyteus/agent/hooks/base_phase_hook.py +61 -0
- autobyteus/agent/hooks/hook_definition.py +36 -0
- autobyteus/agent/hooks/hook_meta.py +37 -0
- autobyteus/agent/hooks/hook_registry.py +118 -0
- autobyteus/agent/input_processor/__init__.py +18 -0
- autobyteus/agent/input_processor/base_user_input_processor.py +54 -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 +33 -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 +117 -0
- autobyteus/agent/llm_response_processor/__init__.py +16 -0
- autobyteus/agent/llm_response_processor/base_processor.py +53 -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 +113 -0
- autobyteus/agent/llm_response_processor/provider_aware_tool_usage_processor.py +54 -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 +63 -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/phases/__init__.py +18 -0
- autobyteus/agent/phases/discover.py +52 -0
- autobyteus/agent/phases/manager.py +265 -0
- autobyteus/agent/phases/phase_enum.py +49 -0
- autobyteus/agent/phases/transition_decorator.py +40 -0
- autobyteus/agent/phases/transition_info.py +33 -0
- autobyteus/agent/remote_agent.py +244 -0
- autobyteus/agent/runtime/__init__.py +15 -0
- autobyteus/agent/runtime/agent_runtime.py +137 -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 +173 -0
- autobyteus/agent/streaming/queue_streamer.py +58 -0
- autobyteus/agent/streaming/stream_event_payloads.py +167 -0
- autobyteus/agent/streaming/stream_events.py +126 -0
- autobyteus/agent/system_prompt_processor/__init__.py +14 -0
- autobyteus/agent/system_prompt_processor/base_processor.py +48 -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 +79 -0
- autobyteus/agent/tool_invocation.py +54 -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 +11 -0
- autobyteus/agent/workspace/base_workspace.py +77 -0
- autobyteus/agent/workspace/workspace_config.py +160 -0
- autobyteus/agent/workspace/workspace_definition.py +36 -0
- autobyteus/agent/workspace/workspace_meta.py +37 -0
- autobyteus/agent/workspace/workspace_registry.py +72 -0
- autobyteus/cli/__init__.py +11 -0
- autobyteus/cli/agent_cli.py +117 -0
- autobyteus/cli/cli_display.py +205 -0
- autobyteus/events/event_emitter.py +33 -56
- autobyteus/events/event_manager.py +134 -66
- autobyteus/events/event_types.py +43 -14
- autobyteus/llm/api/autobyteus_llm.py +13 -25
- autobyteus/llm/api/bedrock_llm.py +9 -3
- autobyteus/llm/api/claude_llm.py +10 -5
- autobyteus/llm/api/deepseek_llm.py +55 -93
- autobyteus/llm/api/gemini_llm.py +10 -4
- autobyteus/llm/api/grok_llm.py +55 -79
- autobyteus/llm/api/groq_llm.py +10 -5
- autobyteus/llm/api/mistral_llm.py +13 -8
- autobyteus/llm/api/nvidia_llm.py +9 -4
- autobyteus/llm/api/ollama_llm.py +58 -50
- 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 +13 -4
- autobyteus/llm/llm_factory.py +116 -53
- autobyteus/llm/models.py +92 -17
- autobyteus/llm/ollama_provider.py +6 -2
- autobyteus/llm/ollama_provider_resolver.py +44 -0
- 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 +77 -0
- autobyteus/tools/ask_user_input.py +34 -77
- autobyteus/tools/base_tool.py +73 -38
- 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 +249 -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 +76 -0
- autobyteus/tools/mcp/call_handlers/streamable_http_handler.py +55 -0
- autobyteus/tools/mcp/config_service.py +237 -0
- autobyteus/tools/mcp/factory.py +70 -0
- autobyteus/tools/mcp/registrar.py +323 -0
- autobyteus/tools/mcp/schema_mapper.py +131 -0
- autobyteus/tools/mcp/tool.py +101 -0
- autobyteus/tools/mcp/types.py +98 -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 +113 -34
- autobyteus/tools/registry/tool_registry.py +64 -22
- autobyteus/tools/timer.py +150 -102
- autobyteus/tools/tool_category.py +11 -0
- autobyteus/tools/tool_config.py +117 -0
- autobyteus/tools/tool_meta.py +50 -26
- autobyteus/tools/tool_state.py +20 -0
- 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 +136 -0
- autobyteus/tools/usage/parsers/exceptions.py +13 -0
- autobyteus/tools/usage/parsers/gemini_json_tool_usage_parser.py +66 -0
- autobyteus/tools/usage/parsers/openai_json_tool_usage_parser.py +196 -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.6.dist-info → autobyteus-1.1.1.dist-info}/METADATA +23 -5
- autobyteus-1.1.1.dist-info/RECORD +296 -0
- {autobyteus-1.0.6.dist-info → autobyteus-1.1.1.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/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/web_page_pdf_generator.py +0 -90
- autobyteus-1.0.6.dist-info/RECORD +0 -157
- {autobyteus-1.0.6.dist-info → autobyteus-1.1.1.dist-info}/licenses/LICENSE +0 -0
- {autobyteus-1.0.6.dist-info → autobyteus-1.1.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/llm_response_processor/provider_aware_tool_usage_processor.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from .base_processor import BaseLLMResponseProcessor
|
|
6
|
+
from autobyteus.tools.usage.parsers import ProviderAwareToolUsageParser
|
|
7
|
+
from autobyteus.tools.usage.parsers.exceptions import ToolUsageParseException
|
|
8
|
+
from autobyteus.agent.events import PendingToolInvocationEvent
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from autobyteus.agent.context import AgentContext
|
|
12
|
+
from autobyteus.agent.events import LLMCompleteResponseReceivedEvent
|
|
13
|
+
from autobyteus.llm.utils.response_types import CompleteResponse
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
class ProviderAwareToolUsageProcessor(BaseLLMResponseProcessor):
|
|
18
|
+
"""
|
|
19
|
+
A "master" tool usage processor that uses a high-level parser from the
|
|
20
|
+
`tools` module to extract tool invocations, and then enqueues the
|
|
21
|
+
necessary agent events based on the parsed results.
|
|
22
|
+
"""
|
|
23
|
+
def __init__(self):
|
|
24
|
+
self._parser = ProviderAwareToolUsageParser()
|
|
25
|
+
logger.debug("ProviderAwareToolUsageProcessor initialized.")
|
|
26
|
+
|
|
27
|
+
@classmethod
|
|
28
|
+
def get_name(cls) -> str:
|
|
29
|
+
return "provider_aware_tool_usage"
|
|
30
|
+
|
|
31
|
+
async def process_response(self, response: 'CompleteResponse', context: 'AgentContext', triggering_event: 'LLMCompleteResponseReceivedEvent') -> bool:
|
|
32
|
+
"""
|
|
33
|
+
Uses a ProviderAwareToolUsageParser to get a list of tool invocations,
|
|
34
|
+
and then enqueues a PendingToolInvocationEvent for each one.
|
|
35
|
+
Propagates ToolUsageParseException if parsing fails.
|
|
36
|
+
"""
|
|
37
|
+
try:
|
|
38
|
+
# Delegate parsing to the high-level parser
|
|
39
|
+
tool_invocations = self._parser.parse(response, context)
|
|
40
|
+
except ToolUsageParseException:
|
|
41
|
+
# Re-raise the exception to be caught by the event handler
|
|
42
|
+
raise
|
|
43
|
+
|
|
44
|
+
if not tool_invocations:
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
logger.info(f"Agent '{context.agent_id}': Parsed {len(tool_invocations)} tool invocations. Enqueuing events.")
|
|
48
|
+
for invocation in tool_invocations:
|
|
49
|
+
logger.info(f"Agent '{context.agent_id}' ({self.get_name()}) identified tool invocation: {invocation.name}. Enqueuing event.")
|
|
50
|
+
await context.input_event_queues.enqueue_tool_invocation_request(
|
|
51
|
+
PendingToolInvocationEvent(tool_invocation=invocation)
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
return True
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/message/__init__.py
|
|
2
|
+
"""
|
|
3
|
+
Components related to messaging for and between agents.
|
|
4
|
+
Includes inter-agent messages, user input messages, context files, and related tools.
|
|
5
|
+
"""
|
|
6
|
+
from .inter_agent_message_type import InterAgentMessageType
|
|
7
|
+
from .inter_agent_message import InterAgentMessage
|
|
8
|
+
from .agent_input_user_message import AgentInputUserMessage
|
|
9
|
+
from .send_message_to import SendMessageTo
|
|
10
|
+
from .context_file import ContextFile
|
|
11
|
+
from .context_file_type import ContextFileType
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"InterAgentMessage",
|
|
15
|
+
"InterAgentMessageType",
|
|
16
|
+
"AgentInputUserMessage",
|
|
17
|
+
"SendMessageTo",
|
|
18
|
+
"ContextFile",
|
|
19
|
+
"ContextFileType",
|
|
20
|
+
]
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/message/agent_input_user_message.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Optional, List, Dict, Any
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
|
|
6
|
+
from .context_file import ContextFile # Import the new ContextFile dataclass
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class AgentInputUserMessage:
|
|
12
|
+
"""
|
|
13
|
+
Represents a message received from an external user interacting with the agent system.
|
|
14
|
+
This is a simple dataclass. It includes support for a list of ContextFile objects,
|
|
15
|
+
allowing users to provide various documents as context.
|
|
16
|
+
"""
|
|
17
|
+
content: str
|
|
18
|
+
image_urls: Optional[List[str]] = field(default=None) # Basic list of strings
|
|
19
|
+
context_files: Optional[List[ContextFile]] = field(default=None)
|
|
20
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
21
|
+
|
|
22
|
+
def __post_init__(self):
|
|
23
|
+
# Basic type validation that dataclasses don't do automatically for mutable defaults or complex types
|
|
24
|
+
if self.image_urls is not None and not (isinstance(self.image_urls, list) and all(isinstance(url, str) for url in self.image_urls)):
|
|
25
|
+
raise TypeError("AgentInputUserMessage 'image_urls' must be a list of strings if provided.")
|
|
26
|
+
if self.context_files is not None and not (isinstance(self.context_files, list) and all(isinstance(cf, ContextFile) for cf in self.context_files)):
|
|
27
|
+
raise TypeError("AgentInputUserMessage 'context_files' must be a list of ContextFile objects if provided.")
|
|
28
|
+
if not isinstance(self.metadata, dict): # Should be caught by default_factory, but good practice
|
|
29
|
+
raise TypeError("AgentInputUserMessage 'metadata' must be a dictionary.")
|
|
30
|
+
if not isinstance(self.content, str):
|
|
31
|
+
raise TypeError("AgentInputUserMessage 'content' must be a string.")
|
|
32
|
+
|
|
33
|
+
if logger.isEnabledFor(logging.DEBUG):
|
|
34
|
+
num_context_files = len(self.context_files) if self.context_files else 0
|
|
35
|
+
logger.debug(
|
|
36
|
+
f"AgentInputUserMessage initialized. Content: '{self.content[:50]}...', "
|
|
37
|
+
f"Image URLs: {self.image_urls}, Num ContextFiles: {num_context_files}, "
|
|
38
|
+
f"Metadata keys: {list(self.metadata.keys())}"
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
42
|
+
"""Serializes the AgentInputUserMessage to a dictionary."""
|
|
43
|
+
# Manually handle serialization of list of ContextFile objects
|
|
44
|
+
context_files_dict_list = None
|
|
45
|
+
if self.context_files:
|
|
46
|
+
context_files_dict_list = [cf.to_dict() for cf in self.context_files]
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
"content": self.content,
|
|
50
|
+
"image_urls": self.image_urls,
|
|
51
|
+
"context_files": context_files_dict_list,
|
|
52
|
+
"metadata": self.metadata,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'AgentInputUserMessage':
|
|
57
|
+
"""Deserializes an AgentInputUserMessage from a dictionary."""
|
|
58
|
+
content = data.get("content")
|
|
59
|
+
if not isinstance(content, str): # Ensure content is string
|
|
60
|
+
raise ValueError("AgentInputUserMessage 'content' in dictionary must be a string.")
|
|
61
|
+
|
|
62
|
+
image_urls = data.get("image_urls")
|
|
63
|
+
if image_urls is not None and not (isinstance(image_urls, list) and all(isinstance(url, str) for url in image_urls)):
|
|
64
|
+
raise ValueError("AgentInputUserMessage 'image_urls' in dictionary must be a list of strings if provided.")
|
|
65
|
+
|
|
66
|
+
context_files_data = data.get("context_files")
|
|
67
|
+
context_files_list: Optional[List[ContextFile]] = None
|
|
68
|
+
if context_files_data is not None:
|
|
69
|
+
if not isinstance(context_files_data, list):
|
|
70
|
+
raise ValueError("AgentInputUserMessage 'context_files' in dictionary must be a list if provided.")
|
|
71
|
+
context_files_list = [ContextFile.from_dict(cf_data) for cf_data in context_files_data]
|
|
72
|
+
|
|
73
|
+
metadata = data.get("metadata", {})
|
|
74
|
+
if not isinstance(metadata, dict):
|
|
75
|
+
raise ValueError("AgentInputUserMessage 'metadata' in dictionary must be a dict if provided.")
|
|
76
|
+
|
|
77
|
+
return cls(
|
|
78
|
+
content=content,
|
|
79
|
+
image_urls=image_urls,
|
|
80
|
+
context_files=context_files_list,
|
|
81
|
+
metadata=metadata
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
def __repr__(self) -> str:
|
|
85
|
+
content_preview = f"{self.content[:100]}..." if len(self.content) > 100 else self.content
|
|
86
|
+
images_repr = f", image_urls={self.image_urls}" if self.image_urls else ""
|
|
87
|
+
|
|
88
|
+
if self.context_files:
|
|
89
|
+
context_repr = f", context_files=[{len(self.context_files)} ContextFile(s)]"
|
|
90
|
+
else:
|
|
91
|
+
context_repr = ""
|
|
92
|
+
|
|
93
|
+
meta_repr = f", metadata_keys={list(self.metadata.keys())}" if self.metadata else ""
|
|
94
|
+
|
|
95
|
+
return (f"AgentInputUserMessage(content='{content_preview}'"
|
|
96
|
+
f"{images_repr}{context_repr}{meta_repr})")
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/message/context_file.py
|
|
2
|
+
import os
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Optional, Dict, Any
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
|
|
7
|
+
from .context_file_type import ContextFileType
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class ContextFile:
|
|
13
|
+
"""
|
|
14
|
+
Represents a single context file provided to an agent.
|
|
15
|
+
This is a simple dataclass, deferring path validation and file access
|
|
16
|
+
to input processors.
|
|
17
|
+
"""
|
|
18
|
+
path: str
|
|
19
|
+
file_type: ContextFileType = ContextFileType.UNKNOWN
|
|
20
|
+
file_name: Optional[str] = None
|
|
21
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
22
|
+
|
|
23
|
+
def __post_init__(self):
|
|
24
|
+
"""
|
|
25
|
+
Called after the dataclass's __init__ method.
|
|
26
|
+
Used here to infer file_name and file_type if not provided or UNKNOWN.
|
|
27
|
+
"""
|
|
28
|
+
if self.file_name is None and self.path:
|
|
29
|
+
try:
|
|
30
|
+
self.file_name = os.path.basename(self.path)
|
|
31
|
+
except Exception as e:
|
|
32
|
+
logger.warning(f"Could not determine basename for path '{self.path}': {e}")
|
|
33
|
+
self.file_name = "unknown_file"
|
|
34
|
+
|
|
35
|
+
if self.file_type == ContextFileType.UNKNOWN and self.path:
|
|
36
|
+
inferred_type = ContextFileType.from_path(self.path)
|
|
37
|
+
if inferred_type != ContextFileType.UNKNOWN:
|
|
38
|
+
self.file_type = inferred_type
|
|
39
|
+
logger.debug(f"Inferred file type for '{self.path}' as {self.file_type.value}")
|
|
40
|
+
else:
|
|
41
|
+
logger.debug(f"Could not infer specific file type for '{self.path}', remaining UNKNOWN.")
|
|
42
|
+
|
|
43
|
+
# Ensure path is a string
|
|
44
|
+
if not isinstance(self.path, str):
|
|
45
|
+
# This ideally should be caught by type hints earlier, but as a runtime safeguard:
|
|
46
|
+
raise TypeError(f"ContextFile path must be a string, got {type(self.path)}")
|
|
47
|
+
|
|
48
|
+
if logger.isEnabledFor(logging.DEBUG):
|
|
49
|
+
logger.debug(f"ContextFile initialized: path='{self.path}', type='{self.file_type.value}', name='{self.file_name}'")
|
|
50
|
+
|
|
51
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
52
|
+
"""Serializes the ContextFile to a dictionary."""
|
|
53
|
+
return {
|
|
54
|
+
"path": self.path,
|
|
55
|
+
"file_type": self.file_type.value, # Serialize enum to its value
|
|
56
|
+
"file_name": self.file_name,
|
|
57
|
+
"metadata": self.metadata,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@classmethod
|
|
61
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'ContextFile':
|
|
62
|
+
"""Deserializes a ContextFile from a dictionary."""
|
|
63
|
+
if not isinstance(data.get("path"), str):
|
|
64
|
+
raise ValueError("ContextFile 'path' in dictionary must be a string.")
|
|
65
|
+
|
|
66
|
+
file_type_str = data.get("file_type", ContextFileType.UNKNOWN.value)
|
|
67
|
+
try:
|
|
68
|
+
file_type = ContextFileType(file_type_str)
|
|
69
|
+
except ValueError:
|
|
70
|
+
logger.warning(f"Invalid file_type string '{file_type_str}' in ContextFile data. Defaulting to UNKNOWN.")
|
|
71
|
+
file_type = ContextFileType.UNKNOWN
|
|
72
|
+
|
|
73
|
+
return cls(
|
|
74
|
+
path=data["path"],
|
|
75
|
+
file_type=file_type,
|
|
76
|
+
file_name=data.get("file_name"),
|
|
77
|
+
metadata=data.get("metadata", {})
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
def __repr__(self) -> str:
|
|
81
|
+
return (f"ContextFile(path='{self.path}', file_name='{self.file_name}', "
|
|
82
|
+
f"file_type='{self.file_type.value}', metadata_keys={list(self.metadata.keys())})")
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
class ContextFileType(str, Enum):
|
|
5
|
+
"""
|
|
6
|
+
Enumerates the types of context files that can be provided to an agent.
|
|
7
|
+
"""
|
|
8
|
+
TEXT = "text" # .txt, .md (if treated as plain text initially)
|
|
9
|
+
MARKDOWN = "markdown" # .md (if specific markdown processing is intended)
|
|
10
|
+
PDF = "pdf" # .pdf
|
|
11
|
+
DOCX = "docx" # .docx (Microsoft Word)
|
|
12
|
+
PPTX = "pptx" # .pptx (Microsoft PowerPoint)
|
|
13
|
+
XLSX = "xlsx" # .xlsx (Microsoft Excel)
|
|
14
|
+
CSV = "csv" # .csv
|
|
15
|
+
JSON = "json" # .json
|
|
16
|
+
XML = "xml" # .xml
|
|
17
|
+
HTML = "html" # .html, .htm
|
|
18
|
+
PYTHON = "python" # .py
|
|
19
|
+
JAVASCRIPT = "javascript" # .js
|
|
20
|
+
IMAGE = "image" # .png, .jpg, .jpeg, .gif, .webp (when image is for contextual analysis, not direct LLM vision input)
|
|
21
|
+
UNKNOWN = "unknown" # Fallback for unrecognized types
|
|
22
|
+
|
|
23
|
+
@classmethod
|
|
24
|
+
def from_path(cls, file_path: str) -> 'ContextFileType':
|
|
25
|
+
"""
|
|
26
|
+
Infers the ContextFileType from a file path based on its extension.
|
|
27
|
+
"""
|
|
28
|
+
if not file_path or not isinstance(file_path, str):
|
|
29
|
+
return cls.UNKNOWN
|
|
30
|
+
|
|
31
|
+
_, extension = os.path.splitext(file_path.lower())
|
|
32
|
+
|
|
33
|
+
if extension == ".txt":
|
|
34
|
+
return cls.TEXT
|
|
35
|
+
elif extension == ".md":
|
|
36
|
+
return cls.MARKDOWN
|
|
37
|
+
elif extension == ".pdf":
|
|
38
|
+
return cls.PDF
|
|
39
|
+
elif extension == ".docx":
|
|
40
|
+
return cls.DOCX
|
|
41
|
+
elif extension == ".pptx":
|
|
42
|
+
return cls.PPTX
|
|
43
|
+
elif extension == ".xlsx":
|
|
44
|
+
return cls.XLSX
|
|
45
|
+
elif extension == ".csv":
|
|
46
|
+
return cls.CSV
|
|
47
|
+
elif extension == ".json":
|
|
48
|
+
return cls.JSON
|
|
49
|
+
elif extension == ".xml":
|
|
50
|
+
return cls.XML
|
|
51
|
+
elif extension in [".html", ".htm"]:
|
|
52
|
+
return cls.HTML
|
|
53
|
+
elif extension == ".py":
|
|
54
|
+
return cls.PYTHON
|
|
55
|
+
elif extension == ".js":
|
|
56
|
+
return cls.JAVASCRIPT
|
|
57
|
+
elif extension in [".png", ".jpg", ".jpeg", ".gif", ".webp"]:
|
|
58
|
+
return cls.IMAGE
|
|
59
|
+
else:
|
|
60
|
+
return cls.UNKNOWN
|
|
61
|
+
|
|
62
|
+
def __str__(self) -> str:
|
|
63
|
+
return self.value
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
from autobyteus.agent.message.
|
|
1
|
+
from autobyteus.agent.message.inter_agent_message_type import InterAgentMessageType
|
|
2
2
|
|
|
3
|
-
class
|
|
3
|
+
class InterAgentMessage:
|
|
4
4
|
def __init__(self, recipient_role_name: str, recipient_agent_id: str, content: str,
|
|
5
|
-
message_type:
|
|
5
|
+
message_type: InterAgentMessageType, sender_agent_id: str): # Updated type hint for message_type
|
|
6
6
|
self.recipient_role_name = recipient_role_name
|
|
7
7
|
self.recipient_agent_id = recipient_agent_id
|
|
8
8
|
self.content = content
|
|
9
|
-
self.message_type = message_type
|
|
9
|
+
self.message_type: InterAgentMessageType = message_type # Updated type hint for attribute
|
|
10
10
|
self.sender_agent_id = sender_agent_id
|
|
11
11
|
|
|
12
12
|
def __eq__(self, other):
|
|
13
|
-
if not isinstance(other,
|
|
13
|
+
if not isinstance(other, InterAgentMessage): # Updated class check
|
|
14
14
|
return False
|
|
15
15
|
return (self.recipient_role_name == other.recipient_role_name and
|
|
16
16
|
self.recipient_agent_id == other.recipient_agent_id and
|
|
@@ -19,23 +19,23 @@ class Message:
|
|
|
19
19
|
self.sender_agent_id == other.sender_agent_id)
|
|
20
20
|
|
|
21
21
|
def __repr__(self):
|
|
22
|
-
return (f"
|
|
22
|
+
return (f"InterAgentMessage(recipient_role_name='{self.recipient_role_name}', " # Updated class name
|
|
23
23
|
f"recipient_agent_id='{self.recipient_agent_id}', "
|
|
24
24
|
f"content='{self.content}', "
|
|
25
|
-
f"message_type=<{self.message_type.__class__.__name__}.{self.message_type.name}: '{self.message_type.value}'>, "
|
|
25
|
+
f"message_type=<{self.message_type.__class__.__name__}.{self.message_type.name}: '{self.message_type.value}'>, " # message_type class name will be InterAgentMessageType
|
|
26
26
|
f"sender_agent_id='{self.sender_agent_id}')")
|
|
27
27
|
|
|
28
28
|
@classmethod
|
|
29
29
|
def create_with_dynamic_message_type(cls, recipient_role_name: str, recipient_agent_id: str,
|
|
30
|
-
content: str, message_type: str, sender_agent_id: str):
|
|
30
|
+
content: str, message_type: str, sender_agent_id: str) -> 'InterAgentMessage': # Updated return type hint
|
|
31
31
|
if not message_type:
|
|
32
32
|
raise ValueError("message_type cannot be empty")
|
|
33
33
|
|
|
34
34
|
try:
|
|
35
|
-
msg_type =
|
|
35
|
+
msg_type = InterAgentMessageType(message_type.lower()) # Use renamed InterAgentMessageType
|
|
36
36
|
except ValueError:
|
|
37
|
-
msg_type =
|
|
37
|
+
msg_type = InterAgentMessageType.add_type(message_type.upper(), message_type.lower()) # Use renamed InterAgentMessageType
|
|
38
38
|
if msg_type is None:
|
|
39
|
-
raise ValueError(f"Failed to create or find
|
|
39
|
+
raise ValueError(f"Failed to create or find InterAgentMessageType: {message_type}") # Updated message
|
|
40
40
|
|
|
41
|
-
return cls(recipient_role_name, recipient_agent_id, content, msg_type, sender_agent_id)
|
|
41
|
+
return cls(recipient_role_name, recipient_agent_id, content, msg_type, sender_agent_id)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from autobyteus.utils.dynamic_enum import DynamicEnum
|
|
2
2
|
|
|
3
|
-
class
|
|
3
|
+
class InterAgentMessageType(DynamicEnum):
|
|
4
4
|
TASK_ASSIGNMENT = "task_assignment"
|
|
5
5
|
TASK_RESULT = "task_result"
|
|
6
6
|
TASK_COMPLETED = "task_completed"
|
|
@@ -8,16 +8,16 @@ class MessageType(DynamicEnum):
|
|
|
8
8
|
ERROR = "error"
|
|
9
9
|
|
|
10
10
|
@classmethod
|
|
11
|
-
def add_type(cls, name: str, value: str) -> '
|
|
11
|
+
def add_type(cls, name: str, value: str) -> 'InterAgentMessageType': # Updated return type hint
|
|
12
12
|
"""
|
|
13
|
-
Add a new message type dynamically.
|
|
13
|
+
Add a new inter-agent message type dynamically.
|
|
14
14
|
|
|
15
15
|
Args:
|
|
16
16
|
name (str): The name of the new message type.
|
|
17
17
|
value (str): The value of the new message type.
|
|
18
18
|
|
|
19
19
|
Returns:
|
|
20
|
-
|
|
20
|
+
InterAgentMessageType: The newly created InterAgentMessageType. # Updated return type in docstring
|
|
21
21
|
|
|
22
22
|
Raises:
|
|
23
23
|
ValueError: If the name or value already exists.
|
|
@@ -25,5 +25,7 @@ class MessageType(DynamicEnum):
|
|
|
25
25
|
try:
|
|
26
26
|
return cls.add(name, value)
|
|
27
27
|
except ValueError as e:
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
# Consider logging a warning here if a logger is available and appropriate
|
|
29
|
+
# For now, print as in original code
|
|
30
|
+
print(f"Warning: Failed to add new inter-agent message type. {str(e)}")
|
|
31
|
+
return None
|
|
@@ -1,44 +1,150 @@
|
|
|
1
|
-
# file: autobyteus/agent/
|
|
2
|
-
|
|
1
|
+
# file: autobyteus/autobyteus/agent/message/send_message_to.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Optional
|
|
4
|
+
|
|
5
|
+
from autobyteus.agent.message.inter_agent_message import InterAgentMessage
|
|
3
6
|
from autobyteus.tools.base_tool import BaseTool
|
|
4
|
-
from
|
|
7
|
+
from autobyteus.agent.group.agent_group_context import AgentGroupContext
|
|
8
|
+
# Updated imports for schema
|
|
9
|
+
from autobyteus.tools.parameter_schema import ParameterSchema, ParameterDefinition, ParameterType
|
|
5
10
|
|
|
6
11
|
if TYPE_CHECKING:
|
|
7
|
-
from autobyteus.agent.
|
|
12
|
+
from autobyteus.agent.context import AgentContext
|
|
13
|
+
from autobyteus.agent.agent import Agent
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
8
16
|
|
|
9
17
|
class SendMessageTo(BaseTool):
|
|
10
|
-
|
|
18
|
+
"""
|
|
19
|
+
A tool for sending messages to other agents within the same AgentGroup.
|
|
20
|
+
It utilizes AgentGroupContext injected into the calling agent's AgentContext
|
|
21
|
+
to resolve recipient agents.
|
|
22
|
+
"""
|
|
23
|
+
TOOL_NAME = "SendMessageTo" # Class attribute for the name
|
|
24
|
+
|
|
25
|
+
def __init__(self):
|
|
11
26
|
super().__init__()
|
|
12
|
-
self.
|
|
27
|
+
logger.debug(f"{self.get_name()} tool initialized.") # Use get_name()
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def get_name(cls) -> str: # Implemented as per BaseTool requirement
|
|
31
|
+
return cls.TOOL_NAME
|
|
32
|
+
|
|
33
|
+
@classmethod
|
|
34
|
+
def get_description(cls) -> str:
|
|
35
|
+
return ("Sends a message to another agent within the same group. "
|
|
36
|
+
"Can target by role or specific agent ID.")
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def get_argument_schema(cls) -> Optional[ParameterSchema]:
|
|
40
|
+
schema = ParameterSchema()
|
|
41
|
+
schema.add_parameter(ParameterDefinition(
|
|
42
|
+
name="recipient_role_name",
|
|
43
|
+
param_type=ParameterType.STRING,
|
|
44
|
+
description="The general role name of the recipient agent (e.g., 'worker', 'reviewer').",
|
|
45
|
+
required=True
|
|
46
|
+
))
|
|
47
|
+
schema.add_parameter(ParameterDefinition(
|
|
48
|
+
name="content",
|
|
49
|
+
param_type=ParameterType.STRING,
|
|
50
|
+
description="The actual message text.",
|
|
51
|
+
required=True
|
|
52
|
+
))
|
|
53
|
+
schema.add_parameter(ParameterDefinition(
|
|
54
|
+
name="message_type",
|
|
55
|
+
param_type=ParameterType.STRING, # Or ENUM if InterAgentMessageType values are known and fixed for schema
|
|
56
|
+
description="Type of the message (e.g., TASK_ASSIGNMENT, CLARIFICATION). Custom types allowed.",
|
|
57
|
+
required=True
|
|
58
|
+
))
|
|
59
|
+
schema.add_parameter(ParameterDefinition(
|
|
60
|
+
name="recipient_agent_id",
|
|
61
|
+
param_type=ParameterType.STRING,
|
|
62
|
+
description='Optional. Specific ID of the recipient agent. If "unknown" or omitted, resolves by role.',
|
|
63
|
+
required=False,
|
|
64
|
+
default_value=None # Explicitly no default, truly optional
|
|
65
|
+
))
|
|
66
|
+
return schema
|
|
67
|
+
|
|
68
|
+
# tool_usage_xml() and tool_usage_json() are inherited from BaseTool
|
|
69
|
+
# get_config_schema() for instantiation defaults to None
|
|
70
|
+
|
|
71
|
+
async def _execute(self,
|
|
72
|
+
context: 'AgentContext',
|
|
73
|
+
recipient_role_name: str,
|
|
74
|
+
content: str,
|
|
75
|
+
message_type: str,
|
|
76
|
+
recipient_agent_id: Optional[str] = None) -> str: # Named parameters
|
|
77
|
+
"""
|
|
78
|
+
Sends a message to another agent in the group.
|
|
79
|
+
Arguments are validated by BaseTool.execute().
|
|
80
|
+
"""
|
|
81
|
+
sender_agent_id = context.agent_id
|
|
82
|
+
logger.info(f"Tool '{self.get_name()}': Sender '{sender_agent_id}' attempting to send message. "
|
|
83
|
+
f"Recipient Role: '{recipient_role_name}', Recipient ID: '{recipient_agent_id}', Type: '{message_type}'.")
|
|
84
|
+
|
|
85
|
+
group_context_any = context.custom_data.get('agent_group_context')
|
|
86
|
+
|
|
87
|
+
if not isinstance(group_context_any, AgentGroupContext):
|
|
88
|
+
error_msg = f"Tool '{self.get_name()}' critical error: AgentGroupContext not found or invalid in AgentContext.custom_data for agent '{sender_agent_id}'. Cannot send message."
|
|
89
|
+
logger.error(error_msg)
|
|
90
|
+
return f"Error: {error_msg}"
|
|
91
|
+
|
|
92
|
+
group_context: AgentGroupContext = group_context_any # Type cast after check
|
|
93
|
+
|
|
94
|
+
target_agent: Optional['Agent'] = None
|
|
95
|
+
|
|
96
|
+
# Use recipient_agent_id if provided and not explicitly "unknown" (case-insensitive)
|
|
97
|
+
if recipient_agent_id and recipient_agent_id.lower() != "unknown":
|
|
98
|
+
target_agent = group_context.get_agent(recipient_agent_id)
|
|
99
|
+
if not target_agent:
|
|
100
|
+
logger.warning(f"Tool '{self.get_name()}': Agent with ID '{recipient_agent_id}' not found in group '{group_context.group_id}'. "
|
|
101
|
+
f"Attempting to find by role '{recipient_role_name}'.")
|
|
102
|
+
|
|
103
|
+
if not target_agent:
|
|
104
|
+
agents_with_role = group_context.get_agents_by_role(recipient_role_name)
|
|
105
|
+
if not agents_with_role:
|
|
106
|
+
error_msg = f"No agent found with role '{recipient_role_name}' (and specific ID '{recipient_agent_id}' if provided was not found) in group '{group_context.group_id}'."
|
|
107
|
+
logger.error(f"Tool '{self.get_name()}': {error_msg}")
|
|
108
|
+
return f"Error: {error_msg}"
|
|
109
|
+
|
|
110
|
+
if len(agents_with_role) > 1:
|
|
111
|
+
logger.warning(f"Tool '{self.get_name()}': Multiple agents ({len(agents_with_role)}) found for role '{recipient_role_name}'. "
|
|
112
|
+
f"Sending to the first one: {agents_with_role[0].agent_id}. "
|
|
113
|
+
"Consider using specific recipient_agent_id for clarity.")
|
|
114
|
+
target_agent = agents_with_role[0]
|
|
115
|
+
# Update recipient_agent_id to the one resolved by role if it was initially None or "unknown"
|
|
116
|
+
recipient_agent_id = target_agent.agent_id
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
if not target_agent:
|
|
120
|
+
error_msg = f"Could not resolve recipient agent with role '{recipient_role_name}' or ID '{recipient_agent_id}'." # recipient_agent_id would be updated here
|
|
121
|
+
logger.error(f"Tool '{self.get_name()}': {error_msg}")
|
|
122
|
+
return f"Error: {error_msg}"
|
|
13
123
|
|
|
14
|
-
async def _execute(self, recipient_role_name: str, recipient_agent_id: str, content: str, message_type: str, sender_agent_id: str) -> Any:
|
|
15
124
|
try:
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
<arg name="sender_agent_id">coordinator_001</arg>
|
|
43
|
-
</command>
|
|
44
|
-
'''
|
|
125
|
+
message_to_send = InterAgentMessage.create_with_dynamic_message_type(
|
|
126
|
+
recipient_role_name=target_agent.context.config.role,
|
|
127
|
+
recipient_agent_id=target_agent.agent_id, # Use the definitively resolved agent ID
|
|
128
|
+
content=content,
|
|
129
|
+
message_type=message_type,
|
|
130
|
+
sender_agent_id=sender_agent_id
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
await target_agent.post_inter_agent_message(message_to_send)
|
|
134
|
+
success_msg = (f"Message successfully sent from '{sender_agent_id}' to agent "
|
|
135
|
+
f"'{target_agent.agent_id}' (Role: '{target_agent.context.config.role}').")
|
|
136
|
+
logger.info(f"Tool '{self.get_name()}': {success_msg}")
|
|
137
|
+
return success_msg
|
|
138
|
+
except ValueError as ve:
|
|
139
|
+
error_msg = f"Error creating message: {str(ve)}"
|
|
140
|
+
logger.error(f"Tool '{self.get_name()}': {error_msg}", exc_info=True)
|
|
141
|
+
return f"Error: {error_msg}"
|
|
142
|
+
except Exception as e:
|
|
143
|
+
error_msg = f"An unexpected error occurred while sending message: {str(e)}"
|
|
144
|
+
logger.error(f"Tool '{self.get_name()}': {error_msg}", exc_info=True)
|
|
145
|
+
return f"Error: {error_msg}"
|
|
146
|
+
|
|
147
|
+
# tool_usage_xml was defined directly here, this is now inherited from BaseTool
|
|
148
|
+
# BaseTool.tool_usage_xml() will use get_argument_schema() to generate it.
|
|
149
|
+
# If a custom XML format was desired that differs from the auto-generated one,
|
|
150
|
+
# then this method could be overridden. For now, assume auto-generated is fine.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent/phases/__init__.py
|
|
2
|
+
"""
|
|
3
|
+
This package contains components for defining and describing agent operational phases
|
|
4
|
+
and their transitions.
|
|
5
|
+
"""
|
|
6
|
+
from .phase_enum import AgentOperationalPhase
|
|
7
|
+
from .transition_info import PhaseTransitionInfo
|
|
8
|
+
from .transition_decorator import phase_transition
|
|
9
|
+
from .discover import PhaseTransitionDiscoverer
|
|
10
|
+
from .manager import AgentPhaseManager
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"AgentOperationalPhase",
|
|
14
|
+
"PhaseTransitionInfo",
|
|
15
|
+
"phase_transition",
|
|
16
|
+
"PhaseTransitionDiscoverer",
|
|
17
|
+
"AgentPhaseManager",
|
|
18
|
+
]
|