autobyteus 1.1.3__py3-none-any.whl → 1.1.5__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 +1 -1
- autobyteus/agent/bootstrap_steps/system_prompt_processing_step.py +4 -2
- autobyteus/agent/context/__init__.py +4 -2
- autobyteus/agent/context/agent_config.py +35 -8
- autobyteus/agent/context/agent_context_registry.py +73 -0
- autobyteus/agent/events/notifiers.py +4 -0
- autobyteus/agent/events/worker_event_dispatcher.py +1 -2
- autobyteus/agent/handlers/inter_agent_message_event_handler.py +8 -3
- autobyteus/agent/handlers/llm_complete_response_received_event_handler.py +19 -19
- autobyteus/agent/handlers/llm_user_message_ready_event_handler.py +2 -2
- autobyteus/agent/handlers/tool_result_event_handler.py +48 -20
- autobyteus/agent/handlers/user_input_message_event_handler.py +16 -1
- autobyteus/agent/input_processor/__init__.py +1 -7
- autobyteus/agent/message/context_file_type.py +6 -0
- autobyteus/agent/message/send_message_to.py +74 -99
- autobyteus/agent/phases/discover.py +2 -1
- autobyteus/agent/runtime/agent_runtime.py +10 -2
- autobyteus/agent/runtime/agent_worker.py +1 -0
- autobyteus/agent/sender_type.py +15 -0
- autobyteus/agent/streaming/agent_event_stream.py +6 -0
- autobyteus/agent/streaming/stream_event_payloads.py +12 -0
- autobyteus/agent/streaming/stream_events.py +3 -0
- autobyteus/agent/system_prompt_processor/tool_manifest_injector_processor.py +7 -4
- autobyteus/agent/tool_execution_result_processor/__init__.py +9 -0
- autobyteus/agent/tool_execution_result_processor/base_processor.py +46 -0
- autobyteus/agent/tool_execution_result_processor/processor_definition.py +36 -0
- autobyteus/agent/tool_execution_result_processor/processor_meta.py +36 -0
- autobyteus/agent/tool_execution_result_processor/processor_registry.py +70 -0
- autobyteus/agent/workspace/base_workspace.py +17 -2
- autobyteus/agent_team/__init__.py +1 -0
- autobyteus/agent_team/agent_team.py +93 -0
- autobyteus/agent_team/agent_team_builder.py +184 -0
- autobyteus/agent_team/base_agent_team.py +86 -0
- autobyteus/agent_team/bootstrap_steps/__init__.py +24 -0
- autobyteus/agent_team/bootstrap_steps/agent_configuration_preparation_step.py +73 -0
- autobyteus/agent_team/bootstrap_steps/agent_team_bootstrapper.py +54 -0
- autobyteus/agent_team/bootstrap_steps/agent_team_runtime_queue_initialization_step.py +25 -0
- autobyteus/agent_team/bootstrap_steps/base_agent_team_bootstrap_step.py +23 -0
- autobyteus/agent_team/bootstrap_steps/coordinator_initialization_step.py +41 -0
- autobyteus/agent_team/bootstrap_steps/coordinator_prompt_preparation_step.py +85 -0
- autobyteus/agent_team/bootstrap_steps/task_notifier_initialization_step.py +51 -0
- autobyteus/agent_team/bootstrap_steps/team_context_initialization_step.py +45 -0
- autobyteus/agent_team/context/__init__.py +17 -0
- autobyteus/agent_team/context/agent_team_config.py +33 -0
- autobyteus/agent_team/context/agent_team_context.py +61 -0
- autobyteus/agent_team/context/agent_team_runtime_state.py +56 -0
- autobyteus/agent_team/context/team_manager.py +147 -0
- autobyteus/agent_team/context/team_node_config.py +76 -0
- autobyteus/agent_team/events/__init__.py +29 -0
- autobyteus/agent_team/events/agent_team_event_dispatcher.py +39 -0
- autobyteus/agent_team/events/agent_team_events.py +53 -0
- autobyteus/agent_team/events/agent_team_input_event_queue_manager.py +21 -0
- autobyteus/agent_team/exceptions.py +8 -0
- autobyteus/agent_team/factory/__init__.py +9 -0
- autobyteus/agent_team/factory/agent_team_factory.py +99 -0
- autobyteus/agent_team/handlers/__init__.py +19 -0
- autobyteus/agent_team/handlers/agent_team_event_handler_registry.py +23 -0
- autobyteus/agent_team/handlers/base_agent_team_event_handler.py +16 -0
- autobyteus/agent_team/handlers/inter_agent_message_request_event_handler.py +61 -0
- autobyteus/agent_team/handlers/lifecycle_agent_team_event_handler.py +27 -0
- autobyteus/agent_team/handlers/process_user_message_event_handler.py +46 -0
- autobyteus/agent_team/handlers/tool_approval_team_event_handler.py +48 -0
- autobyteus/agent_team/phases/__init__.py +11 -0
- autobyteus/agent_team/phases/agent_team_operational_phase.py +19 -0
- autobyteus/agent_team/phases/agent_team_phase_manager.py +48 -0
- autobyteus/agent_team/runtime/__init__.py +13 -0
- autobyteus/agent_team/runtime/agent_team_runtime.py +82 -0
- autobyteus/agent_team/runtime/agent_team_worker.py +117 -0
- autobyteus/agent_team/shutdown_steps/__init__.py +17 -0
- autobyteus/agent_team/shutdown_steps/agent_team_shutdown_orchestrator.py +35 -0
- autobyteus/agent_team/shutdown_steps/agent_team_shutdown_step.py +42 -0
- autobyteus/agent_team/shutdown_steps/base_agent_team_shutdown_step.py +16 -0
- autobyteus/agent_team/shutdown_steps/bridge_cleanup_step.py +28 -0
- autobyteus/agent_team/shutdown_steps/sub_team_shutdown_step.py +41 -0
- autobyteus/agent_team/streaming/__init__.py +26 -0
- autobyteus/agent_team/streaming/agent_event_bridge.py +48 -0
- autobyteus/agent_team/streaming/agent_event_multiplexer.py +70 -0
- autobyteus/agent_team/streaming/agent_team_event_notifier.py +64 -0
- autobyteus/agent_team/streaming/agent_team_event_stream.py +33 -0
- autobyteus/agent_team/streaming/agent_team_stream_event_payloads.py +32 -0
- autobyteus/agent_team/streaming/agent_team_stream_events.py +56 -0
- autobyteus/agent_team/streaming/team_event_bridge.py +50 -0
- autobyteus/agent_team/task_notification/__init__.py +11 -0
- autobyteus/agent_team/task_notification/system_event_driven_agent_task_notifier.py +164 -0
- autobyteus/agent_team/task_notification/task_notification_mode.py +24 -0
- autobyteus/agent_team/utils/__init__.py +9 -0
- autobyteus/agent_team/utils/wait_for_idle.py +46 -0
- autobyteus/cli/__init__.py +1 -1
- autobyteus/cli/agent_team_tui/__init__.py +4 -0
- autobyteus/cli/agent_team_tui/app.py +210 -0
- autobyteus/cli/agent_team_tui/state.py +180 -0
- autobyteus/cli/agent_team_tui/widgets/__init__.py +6 -0
- autobyteus/cli/agent_team_tui/widgets/agent_list_sidebar.py +149 -0
- autobyteus/cli/agent_team_tui/widgets/focus_pane.py +320 -0
- autobyteus/cli/agent_team_tui/widgets/logo.py +20 -0
- autobyteus/cli/agent_team_tui/widgets/renderables.py +77 -0
- autobyteus/cli/agent_team_tui/widgets/shared.py +60 -0
- autobyteus/cli/agent_team_tui/widgets/status_bar.py +14 -0
- autobyteus/cli/agent_team_tui/widgets/task_board_panel.py +82 -0
- autobyteus/cli/cli_display.py +1 -1
- autobyteus/cli/workflow_tui/__init__.py +4 -0
- autobyteus/cli/workflow_tui/app.py +210 -0
- autobyteus/cli/workflow_tui/state.py +189 -0
- autobyteus/cli/workflow_tui/widgets/__init__.py +6 -0
- autobyteus/cli/workflow_tui/widgets/agent_list_sidebar.py +149 -0
- autobyteus/cli/workflow_tui/widgets/focus_pane.py +335 -0
- autobyteus/cli/workflow_tui/widgets/logo.py +27 -0
- autobyteus/cli/workflow_tui/widgets/renderables.py +70 -0
- autobyteus/cli/workflow_tui/widgets/shared.py +51 -0
- autobyteus/cli/workflow_tui/widgets/status_bar.py +14 -0
- autobyteus/events/event_types.py +8 -0
- autobyteus/llm/api/autobyteus_llm.py +11 -12
- autobyteus/llm/api/lmstudio_llm.py +34 -0
- autobyteus/llm/api/ollama_llm.py +8 -13
- autobyteus/llm/api/openai_compatible_llm.py +20 -3
- autobyteus/llm/autobyteus_provider.py +73 -46
- autobyteus/llm/llm_factory.py +103 -139
- autobyteus/llm/lmstudio_provider.py +104 -0
- autobyteus/llm/models.py +83 -53
- autobyteus/llm/ollama_provider.py +69 -61
- autobyteus/llm/ollama_provider_resolver.py +1 -0
- autobyteus/llm/providers.py +13 -12
- autobyteus/llm/runtimes.py +11 -0
- autobyteus/llm/token_counter/token_counter_factory.py +2 -0
- autobyteus/task_management/__init__.py +43 -0
- autobyteus/task_management/base_task_board.py +68 -0
- autobyteus/task_management/converters/__init__.py +11 -0
- autobyteus/task_management/converters/task_board_converter.py +64 -0
- autobyteus/task_management/converters/task_plan_converter.py +48 -0
- autobyteus/task_management/deliverable.py +16 -0
- autobyteus/task_management/deliverables/__init__.py +8 -0
- autobyteus/task_management/deliverables/file_deliverable.py +15 -0
- autobyteus/task_management/events.py +27 -0
- autobyteus/task_management/in_memory_task_board.py +126 -0
- autobyteus/task_management/schemas/__init__.py +15 -0
- autobyteus/task_management/schemas/deliverable_schema.py +13 -0
- autobyteus/task_management/schemas/plan_definition.py +35 -0
- autobyteus/task_management/schemas/task_status_report.py +27 -0
- autobyteus/task_management/task_plan.py +110 -0
- autobyteus/task_management/tools/__init__.py +14 -0
- autobyteus/task_management/tools/get_task_board_status.py +68 -0
- autobyteus/task_management/tools/publish_task_plan.py +113 -0
- autobyteus/task_management/tools/update_task_status.py +135 -0
- autobyteus/tools/__init__.py +2 -0
- autobyteus/tools/ask_user_input.py +2 -1
- autobyteus/tools/bash/bash_executor.py +61 -15
- autobyteus/tools/browser/session_aware/browser_session_aware_navigate_to.py +2 -0
- autobyteus/tools/browser/session_aware/browser_session_aware_web_element_trigger.py +3 -0
- autobyteus/tools/browser/session_aware/browser_session_aware_webpage_reader.py +3 -0
- autobyteus/tools/browser/session_aware/browser_session_aware_webpage_screenshot_taker.py +3 -0
- autobyteus/tools/browser/standalone/google_search_ui.py +2 -0
- autobyteus/tools/browser/standalone/navigate_to.py +2 -0
- autobyteus/tools/browser/standalone/web_page_pdf_generator.py +3 -0
- autobyteus/tools/browser/standalone/webpage_image_downloader.py +3 -0
- autobyteus/tools/browser/standalone/webpage_reader.py +2 -0
- autobyteus/tools/browser/standalone/webpage_screenshot_taker.py +3 -0
- autobyteus/tools/file/file_reader.py +36 -9
- autobyteus/tools/file/file_writer.py +37 -9
- autobyteus/tools/functional_tool.py +5 -4
- autobyteus/tools/image_downloader.py +2 -0
- autobyteus/tools/mcp/config_service.py +63 -58
- autobyteus/tools/mcp/server/http_managed_mcp_server.py +14 -2
- autobyteus/tools/mcp/server/stdio_managed_mcp_server.py +14 -2
- autobyteus/tools/mcp/server_instance_manager.py +30 -4
- autobyteus/tools/mcp/tool_registrar.py +106 -51
- autobyteus/tools/parameter_schema.py +17 -11
- autobyteus/tools/pdf_downloader.py +2 -1
- autobyteus/tools/registry/tool_definition.py +36 -37
- autobyteus/tools/registry/tool_registry.py +50 -2
- autobyteus/tools/timer.py +2 -0
- autobyteus/tools/tool_category.py +15 -4
- autobyteus/tools/tool_meta.py +6 -1
- autobyteus/tools/tool_origin.py +10 -0
- autobyteus/tools/usage/formatters/default_json_example_formatter.py +78 -3
- autobyteus/tools/usage/formatters/default_xml_example_formatter.py +23 -3
- autobyteus/tools/usage/formatters/gemini_json_example_formatter.py +6 -0
- autobyteus/tools/usage/formatters/google_json_example_formatter.py +7 -0
- autobyteus/tools/usage/formatters/openai_json_example_formatter.py +6 -4
- autobyteus/tools/usage/parsers/gemini_json_tool_usage_parser.py +23 -7
- autobyteus/tools/usage/parsers/provider_aware_tool_usage_parser.py +14 -25
- autobyteus/tools/usage/providers/__init__.py +2 -12
- autobyteus/tools/usage/providers/tool_manifest_provider.py +36 -29
- autobyteus/tools/usage/registries/__init__.py +7 -12
- autobyteus/tools/usage/registries/tool_formatter_pair.py +15 -0
- autobyteus/tools/usage/registries/tool_formatting_registry.py +58 -0
- autobyteus/tools/usage/registries/tool_usage_parser_registry.py +55 -0
- autobyteus/workflow/agentic_workflow.py +93 -0
- autobyteus/{agent/workflow → workflow}/base_agentic_workflow.py +19 -27
- autobyteus/workflow/bootstrap_steps/__init__.py +20 -0
- autobyteus/workflow/bootstrap_steps/agent_tool_injection_step.py +34 -0
- autobyteus/workflow/bootstrap_steps/base_workflow_bootstrap_step.py +23 -0
- autobyteus/workflow/bootstrap_steps/coordinator_initialization_step.py +41 -0
- autobyteus/workflow/bootstrap_steps/coordinator_prompt_preparation_step.py +108 -0
- autobyteus/workflow/bootstrap_steps/workflow_bootstrapper.py +50 -0
- autobyteus/workflow/bootstrap_steps/workflow_runtime_queue_initialization_step.py +25 -0
- autobyteus/workflow/context/__init__.py +17 -0
- autobyteus/workflow/context/team_manager.py +147 -0
- autobyteus/workflow/context/workflow_config.py +30 -0
- autobyteus/workflow/context/workflow_context.py +61 -0
- autobyteus/workflow/context/workflow_node_config.py +76 -0
- autobyteus/workflow/context/workflow_runtime_state.py +53 -0
- autobyteus/workflow/events/__init__.py +29 -0
- autobyteus/workflow/events/workflow_event_dispatcher.py +39 -0
- autobyteus/workflow/events/workflow_events.py +53 -0
- autobyteus/workflow/events/workflow_input_event_queue_manager.py +21 -0
- autobyteus/workflow/exceptions.py +8 -0
- autobyteus/workflow/factory/__init__.py +9 -0
- autobyteus/workflow/factory/workflow_factory.py +99 -0
- autobyteus/workflow/handlers/__init__.py +19 -0
- autobyteus/workflow/handlers/base_workflow_event_handler.py +16 -0
- autobyteus/workflow/handlers/inter_agent_message_request_event_handler.py +61 -0
- autobyteus/workflow/handlers/lifecycle_workflow_event_handler.py +27 -0
- autobyteus/workflow/handlers/process_user_message_event_handler.py +46 -0
- autobyteus/workflow/handlers/tool_approval_workflow_event_handler.py +39 -0
- autobyteus/workflow/handlers/workflow_event_handler_registry.py +23 -0
- autobyteus/workflow/phases/__init__.py +11 -0
- autobyteus/workflow/phases/workflow_operational_phase.py +19 -0
- autobyteus/workflow/phases/workflow_phase_manager.py +48 -0
- autobyteus/workflow/runtime/__init__.py +13 -0
- autobyteus/workflow/runtime/workflow_runtime.py +82 -0
- autobyteus/workflow/runtime/workflow_worker.py +117 -0
- autobyteus/workflow/shutdown_steps/__init__.py +17 -0
- autobyteus/workflow/shutdown_steps/agent_team_shutdown_step.py +42 -0
- autobyteus/workflow/shutdown_steps/base_workflow_shutdown_step.py +16 -0
- autobyteus/workflow/shutdown_steps/bridge_cleanup_step.py +28 -0
- autobyteus/workflow/shutdown_steps/sub_workflow_shutdown_step.py +41 -0
- autobyteus/workflow/shutdown_steps/workflow_shutdown_orchestrator.py +35 -0
- autobyteus/workflow/streaming/__init__.py +26 -0
- autobyteus/workflow/streaming/agent_event_bridge.py +48 -0
- autobyteus/workflow/streaming/agent_event_multiplexer.py +70 -0
- autobyteus/workflow/streaming/workflow_event_bridge.py +50 -0
- autobyteus/workflow/streaming/workflow_event_notifier.py +83 -0
- autobyteus/workflow/streaming/workflow_event_stream.py +33 -0
- autobyteus/workflow/streaming/workflow_stream_event_payloads.py +28 -0
- autobyteus/workflow/streaming/workflow_stream_events.py +45 -0
- autobyteus/workflow/utils/__init__.py +9 -0
- autobyteus/workflow/utils/wait_for_idle.py +46 -0
- autobyteus/workflow/workflow_builder.py +151 -0
- {autobyteus-1.1.3.dist-info → autobyteus-1.1.5.dist-info}/METADATA +16 -14
- autobyteus-1.1.5.dist-info/RECORD +455 -0
- {autobyteus-1.1.3.dist-info → autobyteus-1.1.5.dist-info}/top_level.txt +1 -0
- examples/__init__.py +1 -0
- examples/agent_team/__init__.py +1 -0
- examples/discover_phase_transitions.py +104 -0
- examples/run_browser_agent.py +262 -0
- examples/run_google_slides_agent.py +287 -0
- examples/run_mcp_browser_client.py +174 -0
- examples/run_mcp_google_slides_client.py +270 -0
- examples/run_mcp_list_tools.py +189 -0
- examples/run_poem_writer.py +284 -0
- examples/run_sqlite_agent.py +295 -0
- autobyteus/agent/context/agent_phase_manager.py +0 -264
- autobyteus/agent/context/phases.py +0 -49
- autobyteus/agent/group/__init__.py +0 -0
- autobyteus/agent/group/agent_group.py +0 -164
- autobyteus/agent/group/agent_group_context.py +0 -81
- autobyteus/agent/input_processor/content_prefixing_input_processor.py +0 -41
- autobyteus/agent/input_processor/metadata_appending_input_processor.py +0 -34
- autobyteus/agent/input_processor/passthrough_input_processor.py +0 -33
- autobyteus/agent/workflow/__init__.py +0 -11
- autobyteus/agent/workflow/agentic_workflow.py +0 -89
- autobyteus/tools/mcp/call_handlers/__init__.py +0 -16
- autobyteus/tools/mcp/call_handlers/base_handler.py +0 -40
- autobyteus/tools/mcp/call_handlers/stdio_handler.py +0 -76
- autobyteus/tools/mcp/call_handlers/streamable_http_handler.py +0 -55
- autobyteus/tools/mcp/registrar.py +0 -202
- autobyteus/tools/usage/providers/json_example_provider.py +0 -32
- autobyteus/tools/usage/providers/json_schema_provider.py +0 -35
- autobyteus/tools/usage/providers/json_tool_usage_parser_provider.py +0 -28
- autobyteus/tools/usage/providers/xml_example_provider.py +0 -28
- autobyteus/tools/usage/providers/xml_schema_provider.py +0 -29
- autobyteus/tools/usage/providers/xml_tool_usage_parser_provider.py +0 -26
- autobyteus/tools/usage/registries/json_example_formatter_registry.py +0 -51
- autobyteus/tools/usage/registries/json_schema_formatter_registry.py +0 -51
- autobyteus/tools/usage/registries/json_tool_usage_parser_registry.py +0 -42
- autobyteus/tools/usage/registries/xml_example_formatter_registry.py +0 -30
- autobyteus/tools/usage/registries/xml_schema_formatter_registry.py +0 -33
- autobyteus/tools/usage/registries/xml_tool_usage_parser_registry.py +0 -30
- autobyteus/workflow/simple_task.py +0 -98
- autobyteus/workflow/task.py +0 -147
- autobyteus/workflow/workflow.py +0 -49
- autobyteus-1.1.3.dist-info/RECORD +0 -312
- {autobyteus-1.1.3.dist-info → autobyteus-1.1.5.dist-info}/WHEEL +0 -0
- {autobyteus-1.1.3.dist-info → autobyteus-1.1.5.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent_team/bootstrap_steps/coordinator_prompt_preparation_step.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING, List
|
|
4
|
+
|
|
5
|
+
from autobyteus.agent_team.bootstrap_steps.base_agent_team_bootstrap_step import BaseAgentTeamBootstrapStep
|
|
6
|
+
from autobyteus.agent.context import AgentConfig
|
|
7
|
+
from autobyteus.agent_team.context import AgentTeamConfig
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from autobyteus.agent_team.context.agent_team_context import AgentTeamContext
|
|
11
|
+
from autobyteus.agent_team.phases.agent_team_phase_manager import AgentTeamPhaseManager
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
class CoordinatorPromptPreparationStep(BaseAgentTeamBootstrapStep):
|
|
16
|
+
"""
|
|
17
|
+
Bootstrap step to finalize the coordinator's system prompt by injecting a
|
|
18
|
+
dynamically generated team manifest into a user-defined prompt template.
|
|
19
|
+
The user is expected to provide a `system_prompt` in the coordinator's
|
|
20
|
+
AgentConfig with a `{{team}}` placeholder.
|
|
21
|
+
"""
|
|
22
|
+
async def execute(self, context: 'AgentTeamContext', phase_manager: 'AgentTeamPhaseManager') -> bool:
|
|
23
|
+
team_id = context.team_id
|
|
24
|
+
logger.info(f"Team '{team_id}': Executing CoordinatorPromptPreparationStep.")
|
|
25
|
+
try:
|
|
26
|
+
coordinator_node_config_wrapper = context.config.coordinator_node
|
|
27
|
+
|
|
28
|
+
# The coordinator must be an agent with a defined config.
|
|
29
|
+
if not isinstance(coordinator_node_config_wrapper.node_definition, AgentConfig):
|
|
30
|
+
logger.error(f"Team '{team_id}': Coordinator node '{coordinator_node_config_wrapper.name}' is not defined by an AgentConfig. Cannot prepare prompt.")
|
|
31
|
+
return False
|
|
32
|
+
|
|
33
|
+
coordinator_agent_config: AgentConfig = coordinator_node_config_wrapper.node_definition
|
|
34
|
+
|
|
35
|
+
# Start with the user's provided prompt template.
|
|
36
|
+
prompt_template = coordinator_agent_config.system_prompt
|
|
37
|
+
if not prompt_template:
|
|
38
|
+
logger.warning(f"Team '{team_id}': Coordinator '{coordinator_agent_config.name}' has no system_prompt defined. No prompt will be applied.")
|
|
39
|
+
context.state.prepared_coordinator_prompt = ""
|
|
40
|
+
return True
|
|
41
|
+
|
|
42
|
+
team_manifest = self._generate_team_manifest(context)
|
|
43
|
+
|
|
44
|
+
# Inject the manifest into the template.
|
|
45
|
+
if "{{team}}" in prompt_template:
|
|
46
|
+
final_prompt = prompt_template.replace("{{team}}", team_manifest)
|
|
47
|
+
logger.debug(f"Team '{team_id}': Injected team manifest into coordinator's system prompt.")
|
|
48
|
+
else:
|
|
49
|
+
final_prompt = prompt_template
|
|
50
|
+
logger.warning(f"Team '{team_id}': The coordinator's system prompt does not contain a '{{team}}' placeholder. The team manifest will not be injected.")
|
|
51
|
+
|
|
52
|
+
# Store the finalized prompt in the state for the AgentToolInjectionStep to use.
|
|
53
|
+
context.state.prepared_coordinator_prompt = final_prompt
|
|
54
|
+
|
|
55
|
+
logger.info(f"Team '{team_id}': Coordinator prompt prepared successfully and stored in state.")
|
|
56
|
+
return True
|
|
57
|
+
except Exception as e:
|
|
58
|
+
logger.error(f"Team '{team_id}': Failed to prepare coordinator prompt: {e}", exc_info=True)
|
|
59
|
+
return False
|
|
60
|
+
|
|
61
|
+
def _generate_team_manifest(self, context: 'AgentTeamContext') -> str:
|
|
62
|
+
"""Generates a string manifest of all non-coordinator team members."""
|
|
63
|
+
prompt_parts: List[str] = []
|
|
64
|
+
coordinator_node = context.config.coordinator_node
|
|
65
|
+
member_nodes = {node for node in context.config.nodes if node != coordinator_node}
|
|
66
|
+
|
|
67
|
+
if not member_nodes:
|
|
68
|
+
return "You are working alone. You have no team members to delegate to."
|
|
69
|
+
|
|
70
|
+
# Sort for deterministic prompt generation
|
|
71
|
+
for node in sorted(list(member_nodes), key=lambda n: n.name):
|
|
72
|
+
node_def = node.node_definition
|
|
73
|
+
description = "No description available."
|
|
74
|
+
|
|
75
|
+
# --- THE FIX ---
|
|
76
|
+
# Use the 'description' for an AgentConfig and the 'role' for an AgentTeamConfig (sub-team).
|
|
77
|
+
if isinstance(node_def, AgentConfig):
|
|
78
|
+
description = node_def.description
|
|
79
|
+
elif isinstance(node_def, AgentTeamConfig):
|
|
80
|
+
# A sub-team's role is its most concise and relevant description for a parent coordinator.
|
|
81
|
+
description = node_def.role or node_def.description
|
|
82
|
+
|
|
83
|
+
prompt_parts.append(f"- name: {node.name}\n description: {description}")
|
|
84
|
+
|
|
85
|
+
return "\n".join(prompt_parts)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent_team/bootstrap_steps/task_notifier_initialization_step.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from autobyteus.agent_team.bootstrap_steps.base_agent_team_bootstrap_step import BaseAgentTeamBootstrapStep
|
|
6
|
+
from autobyteus.agent_team.task_notification.task_notification_mode import TaskNotificationMode
|
|
7
|
+
from autobyteus.agent_team.task_notification.system_event_driven_agent_task_notifier import SystemEventDrivenAgentTaskNotifier
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from autobyteus.agent_team.context.agent_team_context import AgentTeamContext
|
|
11
|
+
from autobyteus.agent_team.phases.agent_team_phase_manager import AgentTeamPhaseManager
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
class TaskNotifierInitializationStep(BaseAgentTeamBootstrapStep):
|
|
16
|
+
"""
|
|
17
|
+
Bootstrap step to initialize the SystemEventDrivenAgentTaskNotifier if the
|
|
18
|
+
team is configured for event-driven notifications.
|
|
19
|
+
"""
|
|
20
|
+
async def execute(self, context: 'AgentTeamContext', phase_manager: 'AgentTeamPhaseManager') -> bool:
|
|
21
|
+
team_id = context.team_id
|
|
22
|
+
logger.info(f"Team '{team_id}': Executing TaskNotifierInitializationStep.")
|
|
23
|
+
|
|
24
|
+
if context.config.task_notification_mode != TaskNotificationMode.SYSTEM_EVENT_DRIVEN:
|
|
25
|
+
logger.info(f"Team '{team_id}': Task notification mode is '{context.config.task_notification_mode.value}'. Skipping event-driven notifier setup.")
|
|
26
|
+
return True
|
|
27
|
+
|
|
28
|
+
logger.info(f"Team '{team_id}': Mode is SYSTEM_EVENT_DRIVEN. Initializing and activating task notifier.")
|
|
29
|
+
try:
|
|
30
|
+
task_board = context.state.task_board
|
|
31
|
+
if not task_board:
|
|
32
|
+
logger.error(f"Team '{team_id}': TaskBoard not found. Cannot initialize task notifier. This step should run after TeamContextInitializationStep.")
|
|
33
|
+
return False
|
|
34
|
+
|
|
35
|
+
team_manager = context.team_manager
|
|
36
|
+
if not team_manager:
|
|
37
|
+
logger.error(f"Team '{team_id}': TeamManager not found. Cannot initialize task notifier.")
|
|
38
|
+
return False
|
|
39
|
+
|
|
40
|
+
notifier = SystemEventDrivenAgentTaskNotifier(
|
|
41
|
+
task_board=task_board,
|
|
42
|
+
team_manager=team_manager
|
|
43
|
+
)
|
|
44
|
+
notifier.start_monitoring()
|
|
45
|
+
|
|
46
|
+
context.state.task_notifier = notifier
|
|
47
|
+
logger.info(f"Team '{team_id}': SystemEventDrivenAgentTaskNotifier initialized and monitoring started.")
|
|
48
|
+
return True
|
|
49
|
+
except Exception as e:
|
|
50
|
+
logger.error(f"Team '{team_id}': Critical failure during task notifier initialization: {e}", exc_info=True)
|
|
51
|
+
return False
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent_team/bootstrap_steps/team_context_initialization_step.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from autobyteus.agent_team.bootstrap_steps.base_agent_team_bootstrap_step import BaseAgentTeamBootstrapStep
|
|
6
|
+
from autobyteus.task_management import TaskBoard
|
|
7
|
+
from autobyteus.events.event_types import EventType
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from autobyteus.agent_team.context.agent_team_context import AgentTeamContext
|
|
11
|
+
from autobyteus.agent_team.phases.agent_team_phase_manager import AgentTeamPhaseManager
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
class TeamContextInitializationStep(BaseAgentTeamBootstrapStep):
|
|
16
|
+
"""
|
|
17
|
+
Bootstrap step to initialize shared team context components, such as the
|
|
18
|
+
TaskBoard, and bridges its events to the team's notifier.
|
|
19
|
+
"""
|
|
20
|
+
async def execute(self, context: 'AgentTeamContext', phase_manager: 'AgentTeamPhaseManager') -> bool:
|
|
21
|
+
team_id = context.team_id
|
|
22
|
+
logger.info(f"Team '{team_id}': Executing TeamContextInitializationStep.")
|
|
23
|
+
try:
|
|
24
|
+
if context.state.task_board is None:
|
|
25
|
+
task_board = TaskBoard(team_id=team_id)
|
|
26
|
+
context.state.task_board = task_board
|
|
27
|
+
logger.info(f"Team '{team_id}': TaskBoard initialized and attached to team state.")
|
|
28
|
+
|
|
29
|
+
notifier = phase_manager.notifier
|
|
30
|
+
if notifier:
|
|
31
|
+
# The notifier, a long-lived component, subscribes to events
|
|
32
|
+
# from the task_board, another long-lived component.
|
|
33
|
+
notifier.subscribe_from(sender=task_board, event=EventType.TASK_BOARD_PLAN_PUBLISHED, listener=notifier.handle_and_publish_task_board_event)
|
|
34
|
+
notifier.subscribe_from(sender=task_board, event=EventType.TASK_BOARD_STATUS_UPDATED, listener=notifier.handle_and_publish_task_board_event)
|
|
35
|
+
logger.info(f"Team '{team_id}': Successfully bridged TaskBoard events to the team notifier.")
|
|
36
|
+
else:
|
|
37
|
+
logger.warning(f"Team '{team_id}': Notifier not found in PhaseManager. Cannot bridge TaskBoard events.")
|
|
38
|
+
|
|
39
|
+
else:
|
|
40
|
+
logger.warning(f"Team '{team_id}': TaskBoard already exists. Skipping initialization.")
|
|
41
|
+
|
|
42
|
+
return True
|
|
43
|
+
except Exception as e:
|
|
44
|
+
logger.error(f"Team '{team_id}': Critical failure during team context initialization: {e}", exc_info=True)
|
|
45
|
+
return False
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent_team/context/__init__.py
|
|
2
|
+
"""
|
|
3
|
+
Components related to the agent team's runtime context, state, and configuration.
|
|
4
|
+
"""
|
|
5
|
+
from autobyteus.agent_team.context.team_manager import TeamManager
|
|
6
|
+
from autobyteus.agent_team.context.agent_team_config import AgentTeamConfig
|
|
7
|
+
from autobyteus.agent_team.context.agent_team_context import AgentTeamContext
|
|
8
|
+
from autobyteus.agent_team.context.team_node_config import TeamNodeConfig
|
|
9
|
+
from autobyteus.agent_team.context.agent_team_runtime_state import AgentTeamRuntimeState
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"TeamManager",
|
|
13
|
+
"AgentTeamConfig",
|
|
14
|
+
"AgentTeamContext",
|
|
15
|
+
"TeamNodeConfig",
|
|
16
|
+
"AgentTeamRuntimeState",
|
|
17
|
+
]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent_team/context/agent_team_config.py
|
|
2
|
+
import logging
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import List, Optional, Tuple
|
|
5
|
+
|
|
6
|
+
from autobyteus.agent_team.context.team_node_config import TeamNodeConfig
|
|
7
|
+
from autobyteus.agent_team.task_notification.task_notification_mode import TaskNotificationMode
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True)
|
|
12
|
+
class AgentTeamConfig:
|
|
13
|
+
"""
|
|
14
|
+
Represents the complete, static configuration for an AgentTeam instance.
|
|
15
|
+
This is the user's primary input for defining an agent team.
|
|
16
|
+
"""
|
|
17
|
+
name: str
|
|
18
|
+
description: str
|
|
19
|
+
nodes: Tuple[TeamNodeConfig, ...]
|
|
20
|
+
coordinator_node: TeamNodeConfig
|
|
21
|
+
role: Optional[str] = None
|
|
22
|
+
task_notification_mode: TaskNotificationMode = TaskNotificationMode.AGENT_MANUAL_NOTIFICATION
|
|
23
|
+
|
|
24
|
+
def __post_init__(self):
|
|
25
|
+
if not self.name or not isinstance(self.name, str):
|
|
26
|
+
raise ValueError("The 'name' in AgentTeamConfig must be a non-empty string.")
|
|
27
|
+
if not self.nodes:
|
|
28
|
+
raise ValueError("The 'nodes' collection in AgentTeamConfig cannot be empty.")
|
|
29
|
+
if self.coordinator_node not in self.nodes:
|
|
30
|
+
raise ValueError("The 'coordinator_node' must be one of the nodes in the 'nodes' collection.")
|
|
31
|
+
if not isinstance(self.task_notification_mode, TaskNotificationMode):
|
|
32
|
+
raise TypeError("The 'task_notification_mode' must be an instance of TaskNotificationMode enum.")
|
|
33
|
+
logger.debug(f"AgentTeamConfig validated for team: '{self.name}'.")
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent_team/context/agent_team_context.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING, List, Optional, Dict
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from autobyteus.agent_team.context.agent_team_config import AgentTeamConfig
|
|
7
|
+
from autobyteus.agent_team.context.agent_team_runtime_state import AgentTeamRuntimeState
|
|
8
|
+
from autobyteus.agent.agent import Agent
|
|
9
|
+
from autobyteus.agent_team.phases.agent_team_phase_manager import AgentTeamPhaseManager
|
|
10
|
+
from autobyteus.agent_team.context.team_manager import TeamManager
|
|
11
|
+
from autobyteus.agent_team.streaming.agent_event_multiplexer import AgentEventMultiplexer
|
|
12
|
+
from autobyteus.agent.context import AgentConfig
|
|
13
|
+
from autobyteus.agent_team.context.team_node_config import TeamNodeConfig
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
class AgentTeamContext:
|
|
18
|
+
"""Represents the complete operational context for a single agent team instance."""
|
|
19
|
+
def __init__(self, team_id: str, config: 'AgentTeamConfig', state: 'AgentTeamRuntimeState'):
|
|
20
|
+
if not team_id or not isinstance(team_id, str):
|
|
21
|
+
raise ValueError("AgentTeamContext requires a non-empty string 'team_id'.")
|
|
22
|
+
|
|
23
|
+
self.team_id: str = team_id
|
|
24
|
+
self.config: 'AgentTeamConfig' = config
|
|
25
|
+
self.state: 'AgentTeamRuntimeState' = state
|
|
26
|
+
self._node_config_map: Optional[Dict[str, 'TeamNodeConfig']] = None
|
|
27
|
+
|
|
28
|
+
logger.info(f"AgentTeamContext composed for team_id '{self.team_id}'.")
|
|
29
|
+
|
|
30
|
+
def get_node_config_by_name(self, name: str) -> Optional['TeamNodeConfig']:
|
|
31
|
+
"""Efficiently retrieves a node's config by its friendly name."""
|
|
32
|
+
if self._node_config_map is None:
|
|
33
|
+
# Build cache on first access
|
|
34
|
+
self._node_config_map = {node.name: node for node in self.config.nodes}
|
|
35
|
+
return self._node_config_map.get(name)
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def agents(self) -> List['Agent']:
|
|
39
|
+
"""Returns all agents managed by the TeamManager."""
|
|
40
|
+
if self.state.team_manager:
|
|
41
|
+
return self.state.team_manager.get_all_agents()
|
|
42
|
+
return []
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def coordinator_agent(self) -> Optional['Agent']:
|
|
46
|
+
"""Returns the coordinator agent from the TeamManager."""
|
|
47
|
+
if self.state.team_manager:
|
|
48
|
+
return self.state.team_manager.coordinator_agent
|
|
49
|
+
return None
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def phase_manager(self) -> Optional['AgentTeamPhaseManager']:
|
|
53
|
+
return self.state.phase_manager_ref
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def team_manager(self) -> Optional['TeamManager']:
|
|
57
|
+
return self.state.team_manager
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def multiplexer(self) -> Optional['AgentEventMultiplexer']:
|
|
61
|
+
return self.state.multiplexer_ref
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent_team/context/agent_team_runtime_state.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import List, Optional, TYPE_CHECKING, Dict
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
|
|
6
|
+
from autobyteus.agent_team.phases.agent_team_operational_phase import AgentTeamOperationalPhase
|
|
7
|
+
from autobyteus.agent.context import AgentConfig
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from autobyteus.agent.agent import Agent
|
|
11
|
+
from autobyteus.agent_team.events.agent_team_input_event_queue_manager import AgentTeamInputEventQueueManager
|
|
12
|
+
from autobyteus.agent_team.phases.agent_team_phase_manager import AgentTeamPhaseManager
|
|
13
|
+
from autobyteus.agent_team.context.team_node_config import TeamNodeConfig
|
|
14
|
+
from autobyteus.agent_team.context.team_manager import TeamManager
|
|
15
|
+
from autobyteus.agent_team.streaming.agent_event_multiplexer import AgentEventMultiplexer
|
|
16
|
+
from autobyteus.task_management.base_task_board import BaseTaskBoard
|
|
17
|
+
from autobyteus.task_management.artifacts.artifact_manifest import ArtifactManifest
|
|
18
|
+
from autobyteus.agent_team.task_notification.system_event_driven_agent_task_notifier import SystemEventDrivenAgentTaskNotifier
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class AgentTeamRuntimeState:
|
|
24
|
+
"""Encapsulates the dynamic, stateful data of a running agent team instance."""
|
|
25
|
+
team_id: str
|
|
26
|
+
current_phase: AgentTeamOperationalPhase = AgentTeamOperationalPhase.UNINITIALIZED
|
|
27
|
+
|
|
28
|
+
# State populated by bootstrap steps
|
|
29
|
+
prepared_coordinator_prompt: Optional[str] = None
|
|
30
|
+
final_agent_configs: Dict[str, 'AgentConfig'] = field(default_factory=dict)
|
|
31
|
+
|
|
32
|
+
# Core services
|
|
33
|
+
team_manager: Optional['TeamManager'] = None
|
|
34
|
+
task_notifier: Optional['SystemEventDrivenAgentTaskNotifier'] = None
|
|
35
|
+
|
|
36
|
+
# Runtime components and references
|
|
37
|
+
input_event_queues: Optional['AgentTeamInputEventQueueManager'] = None
|
|
38
|
+
phase_manager_ref: Optional['AgentTeamPhaseManager'] = None
|
|
39
|
+
multiplexer_ref: Optional['AgentEventMultiplexer'] = None
|
|
40
|
+
|
|
41
|
+
# Dynamic planning and artifact state
|
|
42
|
+
task_board: Optional['BaseTaskBoard'] = None
|
|
43
|
+
artifact_registry: Dict[str, 'ArtifactManifest'] = field(default_factory=dict)
|
|
44
|
+
|
|
45
|
+
def __post_init__(self):
|
|
46
|
+
if not self.team_id or not isinstance(self.team_id, str):
|
|
47
|
+
raise ValueError("AgentTeamRuntimeState requires a non-empty string 'team_id'.")
|
|
48
|
+
logger.info(f"AgentTeamRuntimeState initialized for team_id '{self.team_id}'.")
|
|
49
|
+
|
|
50
|
+
def __repr__(self) -> str:
|
|
51
|
+
agents_count = len(self.team_manager.get_all_agents()) if self.team_manager else 0
|
|
52
|
+
coordinator_set = self.team_manager.coordinator_agent is not None if self.team_manager else False
|
|
53
|
+
return (f"<AgentTeamRuntimeState id='{self.team_id}', phase='{self.current_phase.value}', "
|
|
54
|
+
f"agents_count={agents_count}, coordinator_set={coordinator_set}, "
|
|
55
|
+
f"final_configs_count={len(self.final_agent_configs)}, "
|
|
56
|
+
f"team_manager_set={self.team_manager is not None}>")
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent_team/context/team_manager.py
|
|
2
|
+
import asyncio
|
|
3
|
+
import logging
|
|
4
|
+
from typing import List, Dict, Optional, TYPE_CHECKING, Union
|
|
5
|
+
|
|
6
|
+
from autobyteus.agent.factory import AgentFactory
|
|
7
|
+
from autobyteus.agent.utils.wait_for_idle import wait_for_agent_to_be_idle
|
|
8
|
+
from autobyteus.agent_team.utils.wait_for_idle import wait_for_team_to_be_idle
|
|
9
|
+
from autobyteus.agent_team.exceptions import TeamNodeNotFoundException
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from autobyteus.agent.agent import Agent
|
|
13
|
+
from autobyteus.agent_team.agent_team import AgentTeam
|
|
14
|
+
from autobyteus.agent_team.events.agent_team_events import InterAgentMessageRequestEvent, ProcessUserMessageEvent
|
|
15
|
+
from autobyteus.agent_team.runtime.agent_team_runtime import AgentTeamRuntime
|
|
16
|
+
from autobyteus.agent_team.streaming.agent_event_multiplexer import AgentEventMultiplexer
|
|
17
|
+
from autobyteus.agent_team.context.agent_team_config import AgentTeamConfig
|
|
18
|
+
|
|
19
|
+
ManagedNode = Union['Agent', 'AgentTeam']
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
class TeamManager:
|
|
24
|
+
"""
|
|
25
|
+
Manages all nodes (agents and sub-teams) within an agent team. It handles
|
|
26
|
+
lazy creation, on-demand startup, and provides access to managed instances.
|
|
27
|
+
It assumes all node names are unique within the team.
|
|
28
|
+
"""
|
|
29
|
+
def __init__(self, team_id: str, runtime: 'AgentTeamRuntime', multiplexer: 'AgentEventMultiplexer'):
|
|
30
|
+
self.team_id = team_id
|
|
31
|
+
self._runtime = runtime
|
|
32
|
+
self._multiplexer = multiplexer
|
|
33
|
+
self._agent_factory = AgentFactory()
|
|
34
|
+
self._nodes_cache: Dict[str, ManagedNode] = {}
|
|
35
|
+
self._agent_id_to_name_map: Dict[str, str] = {}
|
|
36
|
+
self._coordinator_agent: Optional['Agent'] = None
|
|
37
|
+
logger.info(f"TeamManager created for team '{self.team_id}'.")
|
|
38
|
+
|
|
39
|
+
async def dispatch_inter_agent_message_request(self, event: 'InterAgentMessageRequestEvent'):
|
|
40
|
+
await self._runtime.submit_event(event)
|
|
41
|
+
|
|
42
|
+
async def dispatch_user_message_to_agent(self, event: 'ProcessUserMessageEvent'):
|
|
43
|
+
"""Submits a user message event (potentially system-generated) to the team's runtime."""
|
|
44
|
+
await self._runtime.submit_event(event)
|
|
45
|
+
|
|
46
|
+
async def ensure_node_is_ready(self, name_or_agent_id: str) -> ManagedNode:
|
|
47
|
+
"""
|
|
48
|
+
Retrieves a node by its unique name or ID. If not yet created, it
|
|
49
|
+
instantiates and starts it on-demand.
|
|
50
|
+
"""
|
|
51
|
+
unique_name: str
|
|
52
|
+
if name_or_agent_id in self._agent_id_to_name_map:
|
|
53
|
+
unique_name = self._agent_id_to_name_map[name_or_agent_id]
|
|
54
|
+
else:
|
|
55
|
+
unique_name = name_or_agent_id
|
|
56
|
+
|
|
57
|
+
node_instance = self._nodes_cache.get(unique_name)
|
|
58
|
+
|
|
59
|
+
was_created = False
|
|
60
|
+
if not node_instance:
|
|
61
|
+
logger.debug(f"Node '{unique_name}' not in cache for team '{self.team_id}'. Attempting lazy creation.")
|
|
62
|
+
|
|
63
|
+
node_config_wrapper = self._runtime.context.get_node_config_by_name(unique_name)
|
|
64
|
+
if not node_config_wrapper:
|
|
65
|
+
raise TeamNodeNotFoundException(node_name=name_or_agent_id, team_id=self.team_id)
|
|
66
|
+
|
|
67
|
+
if node_config_wrapper.is_sub_team:
|
|
68
|
+
from autobyteus.agent_team.factory.agent_team_factory import AgentTeamFactory
|
|
69
|
+
from autobyteus.agent_team.context.agent_team_config import AgentTeamConfig
|
|
70
|
+
|
|
71
|
+
team_factory = AgentTeamFactory()
|
|
72
|
+
node_definition = node_config_wrapper.node_definition
|
|
73
|
+
if not isinstance(node_definition, AgentTeamConfig):
|
|
74
|
+
raise TypeError(f"Expected AgentTeamConfig for node '{unique_name}', but found {type(node_definition)}")
|
|
75
|
+
logger.info(f"Lazily creating sub-team node '{unique_name}' in team '{self.team_id}'.")
|
|
76
|
+
node_instance = team_factory.create_team(config=node_definition)
|
|
77
|
+
else:
|
|
78
|
+
# Agent creation is now simpler: just retrieve the pre-made config.
|
|
79
|
+
final_config = self._runtime.context.state.final_agent_configs.get(unique_name)
|
|
80
|
+
if not final_config:
|
|
81
|
+
raise RuntimeError(f"No pre-prepared agent configuration found for '{unique_name}'. "
|
|
82
|
+
"Bootstrap step may have failed or skipped this agent.")
|
|
83
|
+
|
|
84
|
+
logger.info(f"Lazily creating agent node '{unique_name}' using pre-prepared configuration.")
|
|
85
|
+
node_instance = self._agent_factory.create_agent(config=final_config)
|
|
86
|
+
|
|
87
|
+
self._nodes_cache[unique_name] = node_instance
|
|
88
|
+
was_created = True
|
|
89
|
+
|
|
90
|
+
from autobyteus.agent.agent import Agent
|
|
91
|
+
if isinstance(node_instance, Agent):
|
|
92
|
+
self._agent_id_to_name_map[node_instance.agent_id] = unique_name
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
if was_created and node_instance:
|
|
96
|
+
from autobyteus.agent_team.agent_team import AgentTeam
|
|
97
|
+
from autobyteus.agent.agent import Agent
|
|
98
|
+
if isinstance(node_instance, AgentTeam):
|
|
99
|
+
self._multiplexer.start_bridging_team_events(node_instance, unique_name)
|
|
100
|
+
elif isinstance(node_instance, Agent):
|
|
101
|
+
self._multiplexer.start_bridging_agent_events(node_instance, unique_name)
|
|
102
|
+
|
|
103
|
+
# On-Demand Startup Logic
|
|
104
|
+
if not node_instance.is_running:
|
|
105
|
+
from autobyteus.agent_team.agent_team import AgentTeam
|
|
106
|
+
logger.info(f"Team '{self.team_id}': Node '{unique_name}' is not running. Starting on-demand.")
|
|
107
|
+
await self._start_node(node_instance, unique_name)
|
|
108
|
+
|
|
109
|
+
return node_instance
|
|
110
|
+
|
|
111
|
+
async def _start_node(self, node: ManagedNode, name: str):
|
|
112
|
+
"""Starts a node and waits for it to be idle."""
|
|
113
|
+
try:
|
|
114
|
+
node.start()
|
|
115
|
+
from autobyteus.agent_team.agent_team import AgentTeam
|
|
116
|
+
if isinstance(node, AgentTeam):
|
|
117
|
+
await wait_for_team_to_be_idle(node, timeout=120.0)
|
|
118
|
+
else:
|
|
119
|
+
await wait_for_agent_to_be_idle(node, timeout=60.0)
|
|
120
|
+
except Exception as e:
|
|
121
|
+
logger.error(f"Team '{self.team_id}': Failed to start node '{name}' on-demand: {e}", exc_info=True)
|
|
122
|
+
raise RuntimeError(f"Failed to start node '{name}' on-demand.") from e
|
|
123
|
+
|
|
124
|
+
def get_all_agents(self) -> List['Agent']:
|
|
125
|
+
from autobyteus.agent.agent import Agent
|
|
126
|
+
return [node for node in self._nodes_cache.values() if isinstance(node, Agent)]
|
|
127
|
+
|
|
128
|
+
def get_all_sub_teams(self) -> List['AgentTeam']:
|
|
129
|
+
from autobyteus.agent_team.agent_team import AgentTeam
|
|
130
|
+
return [node for node in self._nodes_cache.values() if isinstance(node, AgentTeam)]
|
|
131
|
+
|
|
132
|
+
@property
|
|
133
|
+
def coordinator_agent(self) -> Optional['Agent']:
|
|
134
|
+
return self._coordinator_agent
|
|
135
|
+
|
|
136
|
+
async def ensure_coordinator_is_ready(self, coordinator_name: str) -> 'Agent':
|
|
137
|
+
"""
|
|
138
|
+
Ensures the coordinator agent is created, started, and ready, then
|
|
139
|
+
designates it as the coordinator.
|
|
140
|
+
"""
|
|
141
|
+
from autobyteus.agent.agent import Agent
|
|
142
|
+
node = await self.ensure_node_is_ready(name_or_agent_id=coordinator_name)
|
|
143
|
+
if not isinstance(node, Agent):
|
|
144
|
+
raise TypeError(f"Coordinator node '{coordinator_name}' resolved to a non-agent type: {type(node).__name__}")
|
|
145
|
+
|
|
146
|
+
self._coordinator_agent = node
|
|
147
|
+
return node
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent_team/context/team_node_config.py
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
import logging
|
|
4
|
+
import uuid
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import List, TYPE_CHECKING, Union, Tuple
|
|
7
|
+
|
|
8
|
+
# The import is moved into the TYPE_CHECKING block to break the circular dependency at module load time.
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from autobyteus.agent.context import AgentConfig
|
|
11
|
+
from autobyteus.agent_team.context.agent_team_config import AgentTeamConfig
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class TeamNodeConfig:
|
|
17
|
+
"""
|
|
18
|
+
Represents a node in an agent team graph.
|
|
19
|
+
|
|
20
|
+
This is the core building block for defining agent teams. A node can be either
|
|
21
|
+
a single agent (defined by an AgentConfig) or an entire sub-team
|
|
22
|
+
(defined by an AgentTeamConfig).
|
|
23
|
+
|
|
24
|
+
Attributes:
|
|
25
|
+
node_definition: The configuration for the agent or sub-team at this node.
|
|
26
|
+
dependencies: A tuple of other TeamNodeConfig objects that must be
|
|
27
|
+
successfully executed before this node can be executed.
|
|
28
|
+
node_id: A unique identifier for this node instance.
|
|
29
|
+
"""
|
|
30
|
+
node_definition: Union["AgentConfig", "AgentTeamConfig"]
|
|
31
|
+
dependencies: Tuple[TeamNodeConfig, ...] = field(default_factory=tuple)
|
|
32
|
+
node_id: str = field(default_factory=lambda: f"node_{uuid.uuid4().hex}", init=False, repr=False)
|
|
33
|
+
|
|
34
|
+
def __post_init__(self):
|
|
35
|
+
"""Validates the node configuration."""
|
|
36
|
+
from autobyteus.agent.context import AgentConfig
|
|
37
|
+
from autobyteus.agent_team.context.agent_team_config import AgentTeamConfig
|
|
38
|
+
|
|
39
|
+
if not isinstance(self.node_definition, (AgentConfig, AgentTeamConfig)):
|
|
40
|
+
raise TypeError("The 'node_definition' attribute must be an instance of AgentConfig or AgentTeamConfig.")
|
|
41
|
+
|
|
42
|
+
if not all(isinstance(dep, TeamNodeConfig) for dep in self.dependencies):
|
|
43
|
+
raise TypeError("All items in 'dependencies' must be instances of TeamNodeConfig.")
|
|
44
|
+
|
|
45
|
+
logger.debug(f"TeamNodeConfig created for: '{self.name}' (NodeID: {self.node_id}). Dependencies: {[dep.name for dep in self.dependencies]}")
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def name(self) -> str:
|
|
49
|
+
"""A convenience property to get the node's name from its definition."""
|
|
50
|
+
return self.node_definition.name
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def effective_config(self) -> Union["AgentConfig", "AgentTeamConfig"]:
|
|
54
|
+
"""Returns the underlying AgentConfig or AgentTeamConfig."""
|
|
55
|
+
return self.node_definition
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def is_sub_team(self) -> bool:
|
|
59
|
+
"""Returns True if this node represents a sub-team."""
|
|
60
|
+
from autobyteus.agent_team.context.agent_team_config import AgentTeamConfig
|
|
61
|
+
return isinstance(self.node_definition, AgentTeamConfig)
|
|
62
|
+
|
|
63
|
+
def __hash__(self):
|
|
64
|
+
"""
|
|
65
|
+
Makes the node hashable based on its unique node_id, allowing it to be
|
|
66
|
+
used in sets and as dictionary keys.
|
|
67
|
+
"""
|
|
68
|
+
return hash(self.node_id)
|
|
69
|
+
|
|
70
|
+
def __eq__(self, other):
|
|
71
|
+
"""
|
|
72
|
+
Compares two nodes based on their unique node_id.
|
|
73
|
+
"""
|
|
74
|
+
if isinstance(other, TeamNodeConfig):
|
|
75
|
+
return self.node_id == other.node_id
|
|
76
|
+
return False
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent_team/events/__init__.py
|
|
2
|
+
"""
|
|
3
|
+
This package contains event definitions and dispatchers for the agent team runtime.
|
|
4
|
+
"""
|
|
5
|
+
from autobyteus.agent_team.events.agent_team_events import (
|
|
6
|
+
BaseAgentTeamEvent,
|
|
7
|
+
LifecycleAgentTeamEvent,
|
|
8
|
+
OperationalAgentTeamEvent,
|
|
9
|
+
AgentTeamReadyEvent,
|
|
10
|
+
AgentTeamErrorEvent,
|
|
11
|
+
ProcessUserMessageEvent,
|
|
12
|
+
InterAgentMessageRequestEvent,
|
|
13
|
+
ToolApprovalTeamEvent,
|
|
14
|
+
)
|
|
15
|
+
from autobyteus.agent_team.events.agent_team_event_dispatcher import AgentTeamEventDispatcher
|
|
16
|
+
from autobyteus.agent_team.events.agent_team_input_event_queue_manager import AgentTeamInputEventQueueManager
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"BaseAgentTeamEvent",
|
|
20
|
+
"LifecycleAgentTeamEvent",
|
|
21
|
+
"OperationalAgentTeamEvent",
|
|
22
|
+
"AgentTeamReadyEvent",
|
|
23
|
+
"AgentTeamErrorEvent",
|
|
24
|
+
"ProcessUserMessageEvent",
|
|
25
|
+
"InterAgentMessageRequestEvent",
|
|
26
|
+
"ToolApprovalTeamEvent",
|
|
27
|
+
"AgentTeamEventDispatcher",
|
|
28
|
+
"AgentTeamInputEventQueueManager",
|
|
29
|
+
]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/agent_team/events/agent_team_event_dispatcher.py
|
|
2
|
+
import logging
|
|
3
|
+
import traceback
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from autobyteus.agent_team.events.agent_team_events import BaseAgentTeamEvent, AgentTeamReadyEvent, ProcessUserMessageEvent
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from autobyteus.agent_team.context.agent_team_context import AgentTeamContext
|
|
10
|
+
from autobyteus.agent_team.handlers.agent_team_event_handler_registry import AgentTeamEventHandlerRegistry
|
|
11
|
+
from autobyteus.agent_team.phases.agent_team_phase_manager import AgentTeamPhaseManager
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
class AgentTeamEventDispatcher:
|
|
16
|
+
"""Dispatches agent team events to their appropriate handlers."""
|
|
17
|
+
|
|
18
|
+
def __init__(self,
|
|
19
|
+
event_handler_registry: 'AgentTeamEventHandlerRegistry',
|
|
20
|
+
phase_manager: 'AgentTeamPhaseManager'):
|
|
21
|
+
self.registry = event_handler_registry
|
|
22
|
+
self.phase_manager = phase_manager
|
|
23
|
+
|
|
24
|
+
async def dispatch(self, event: BaseAgentTeamEvent, context: 'AgentTeamContext'):
|
|
25
|
+
handler = self.registry.get_handler(type(event))
|
|
26
|
+
team_id = context.team_id
|
|
27
|
+
|
|
28
|
+
if not handler:
|
|
29
|
+
logger.warning(f"Team '{team_id}': No handler for event '{type(event).__name__}'.")
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
await handler.handle(event, context)
|
|
34
|
+
if isinstance(event, AgentTeamReadyEvent):
|
|
35
|
+
await self.phase_manager.notify_initialization_complete()
|
|
36
|
+
except Exception as e:
|
|
37
|
+
error_msg = f"Error handling '{type(event).__name__}' in team '{team_id}': {e}"
|
|
38
|
+
logger.error(error_msg, exc_info=True)
|
|
39
|
+
await self.phase_manager.notify_error_occurred(error_msg, traceback.format_exc())
|