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,34 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/workflow/bootstrap_steps/agent_tool_injection_step.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING, Dict, Set
|
|
4
|
+
|
|
5
|
+
from autobyteus.workflow.bootstrap_steps.base_workflow_bootstrap_step import BaseWorkflowBootstrapStep
|
|
6
|
+
from autobyteus.agent.context import AgentConfig
|
|
7
|
+
from autobyteus.agent.message.send_message_to import SendMessageTo
|
|
8
|
+
from autobyteus.workflow.context.workflow_node_config import WorkflowNodeConfig
|
|
9
|
+
from autobyteus.tools.registry import default_tool_registry
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from autobyteus.workflow.context.workflow_context import WorkflowContext
|
|
13
|
+
from autobyteus.workflow.phases.workflow_phase_manager import WorkflowPhaseManager
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
class AgentToolInjectionStep(BaseWorkflowBootstrapStep):
|
|
18
|
+
"""
|
|
19
|
+
Bootstrap step to inject workflow-aware tools like SendMessageTo into
|
|
20
|
+
agent configurations just before they are used. This step is now effectively
|
|
21
|
+
a placeholder as tool injection is handled just-in-time by the TeamManager,
|
|
22
|
+
but it is kept for potential future use and to maintain the bootstrap sequence structure.
|
|
23
|
+
The primary logic of applying the coordinator prompt has been moved to the TeamManager
|
|
24
|
+
to ensure it happens just before the coordinator is created.
|
|
25
|
+
"""
|
|
26
|
+
async def execute(self, context: 'WorkflowContext', phase_manager: 'WorkflowPhaseManager') -> bool:
|
|
27
|
+
workflow_id = context.workflow_id
|
|
28
|
+
logger.info(f"Workflow '{workflow_id}': Executing AgentToolInjectionStep (now a placeholder).")
|
|
29
|
+
# The logic for injecting SendMessageTo and setting the coordinator prompt is now
|
|
30
|
+
# handled just-in-time by the TeamManager to better support lazy-loading of nodes.
|
|
31
|
+
# This step is preserved in the bootstrap sequence for clarity and future expansion.
|
|
32
|
+
logger.debug(f"Workflow '{workflow_id}': Tool injection and prompt setting are deferred to TeamManager.")
|
|
33
|
+
return True
|
|
34
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/workflow/bootstrap_steps/base_workflow_bootstrap_step.py
|
|
2
|
+
import logging
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from autobyteus.workflow.context.workflow_context import WorkflowContext
|
|
8
|
+
from autobyteus.workflow.phases.workflow_phase_manager import WorkflowPhaseManager
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
class BaseWorkflowBootstrapStep(ABC):
|
|
13
|
+
"""Abstract base class for individual steps in the workflow bootstrapping process."""
|
|
14
|
+
|
|
15
|
+
@abstractmethod
|
|
16
|
+
async def execute(self, context: 'WorkflowContext', phase_manager: 'WorkflowPhaseManager') -> bool:
|
|
17
|
+
"""
|
|
18
|
+
Executes the bootstrap step.
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
True if the step completed successfully, False otherwise.
|
|
22
|
+
"""
|
|
23
|
+
raise NotImplementedError("Subclasses must implement the 'execute' method.")
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/workflow/bootstrap_steps/coordinator_initialization_step.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from autobyteus.workflow.bootstrap_steps.base_workflow_bootstrap_step import BaseWorkflowBootstrapStep
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from autobyteus.workflow.context.workflow_context import WorkflowContext
|
|
9
|
+
from autobyteus.workflow.phases.workflow_phase_manager import WorkflowPhaseManager
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
class CoordinatorInitializationStep(BaseWorkflowBootstrapStep):
|
|
14
|
+
"""
|
|
15
|
+
Bootstrap step that eagerly instantiates and starts the coordinator agent
|
|
16
|
+
using the TeamManager. This ensures the coordinator is ready before the
|
|
17
|
+
workflow becomes idle.
|
|
18
|
+
"""
|
|
19
|
+
async def execute(self, context: 'WorkflowContext', phase_manager: 'WorkflowPhaseManager') -> bool:
|
|
20
|
+
workflow_id = context.workflow_id
|
|
21
|
+
logger.info(f"Workflow '{workflow_id}': Executing CoordinatorInitializationStep.")
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
team_manager = context.team_manager
|
|
25
|
+
if not team_manager:
|
|
26
|
+
raise RuntimeError("TeamManager not found in workflow context. It should be created by the factory.")
|
|
27
|
+
|
|
28
|
+
coordinator_name = context.config.coordinator_node.name
|
|
29
|
+
|
|
30
|
+
# This call now ensures the coordinator agent is fully created and ready.
|
|
31
|
+
coordinator = await team_manager.ensure_coordinator_is_ready(coordinator_name)
|
|
32
|
+
|
|
33
|
+
if not coordinator:
|
|
34
|
+
raise RuntimeError(f"TeamManager failed to return a ready coordinator agent for '{coordinator_name}'.")
|
|
35
|
+
|
|
36
|
+
logger.info(f"Workflow '{workflow_id}': Coordinator '{coordinator_name}' initialized and started via TeamManager.")
|
|
37
|
+
return True
|
|
38
|
+
|
|
39
|
+
except Exception as e:
|
|
40
|
+
logger.error(f"Workflow '{workflow_id}': Failed to initialize coordinator agent: {e}", exc_info=True)
|
|
41
|
+
return False
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/workflow/bootstrap_steps/coordinator_prompt_preparation_step.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING, Dict, Set, List
|
|
4
|
+
|
|
5
|
+
from autobyteus.workflow.bootstrap_steps.base_workflow_bootstrap_step import BaseWorkflowBootstrapStep
|
|
6
|
+
from autobyteus.agent.context import AgentConfig
|
|
7
|
+
from autobyteus.workflow.context.workflow_node_config import WorkflowNodeConfig
|
|
8
|
+
from autobyteus.workflow.context.workflow_config import WorkflowConfig
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from autobyteus.workflow.context.workflow_context import WorkflowContext
|
|
12
|
+
from autobyteus.workflow.phases.workflow_phase_manager import WorkflowPhaseManager
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
class CoordinatorPromptPreparationStep(BaseWorkflowBootstrapStep):
|
|
17
|
+
"""
|
|
18
|
+
Bootstrap step to dynamically generate the coordinator's system prompt
|
|
19
|
+
based on the workflow's structure and store it in the workflow's state.
|
|
20
|
+
"""
|
|
21
|
+
async def execute(self, context: 'WorkflowContext', phase_manager: 'WorkflowPhaseManager') -> bool:
|
|
22
|
+
workflow_id = context.workflow_id
|
|
23
|
+
logger.info(f"Workflow '{workflow_id}': Executing CoordinatorPromptPreparationStep.")
|
|
24
|
+
try:
|
|
25
|
+
coordinator_node = context.config.coordinator_node
|
|
26
|
+
member_nodes = {node for node in context.config.nodes if node != coordinator_node}
|
|
27
|
+
|
|
28
|
+
member_node_ids = self._generate_unique_node_ids(member_nodes)
|
|
29
|
+
dynamic_prompt = self._generate_prompt(context, member_node_ids)
|
|
30
|
+
|
|
31
|
+
context.state.prepared_coordinator_prompt = dynamic_prompt
|
|
32
|
+
|
|
33
|
+
logger.info(f"Workflow '{workflow_id}': Coordinator prompt prepared successfully and stored in state.")
|
|
34
|
+
return True
|
|
35
|
+
except Exception as e:
|
|
36
|
+
logger.error(f"Workflow '{workflow_id}': Failed to prepare coordinator prompt: {e}", exc_info=True)
|
|
37
|
+
return False
|
|
38
|
+
|
|
39
|
+
def _generate_unique_node_ids(self, member_nodes: Set[WorkflowNodeConfig]) -> Dict[WorkflowNodeConfig, str]:
|
|
40
|
+
id_map: Dict[WorkflowNodeConfig, str] = {}
|
|
41
|
+
name_counts: Dict[str, int] = {}
|
|
42
|
+
sorted_nodes = sorted(list(member_nodes), key=lambda n: n.name)
|
|
43
|
+
for node in sorted_nodes:
|
|
44
|
+
base_name = node.name
|
|
45
|
+
count = name_counts.get(base_name, 0)
|
|
46
|
+
unique_id = f"{base_name}_{count + 1}" if base_name in name_counts else base_name
|
|
47
|
+
id_map[node] = unique_id
|
|
48
|
+
name_counts[base_name] = count + 1
|
|
49
|
+
return id_map
|
|
50
|
+
|
|
51
|
+
def _generate_prompt(self, context: 'WorkflowContext', member_node_ids: Dict[WorkflowNodeConfig, str]) -> str:
|
|
52
|
+
prompt_parts: List[str] = []
|
|
53
|
+
|
|
54
|
+
tools_section = (
|
|
55
|
+
"### Your Tools\n"
|
|
56
|
+
"To accomplish your goal, you have access to the following tools. You should use them as needed.\n"
|
|
57
|
+
"{{tools}}"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
if member_node_ids:
|
|
61
|
+
role_and_goal = (
|
|
62
|
+
"You are the coordinator of a team of specialist agents and sub-workflows. Your primary goal is to "
|
|
63
|
+
"achieve the following objective by delegating tasks to your team members:\n"
|
|
64
|
+
f"### Goal\n{context.config.description}"
|
|
65
|
+
)
|
|
66
|
+
prompt_parts.append(role_and_goal)
|
|
67
|
+
|
|
68
|
+
team_lines = []
|
|
69
|
+
for node, uid in member_node_ids.items():
|
|
70
|
+
node_def = node.node_definition
|
|
71
|
+
if node.is_subworkflow and isinstance(node_def, WorkflowConfig):
|
|
72
|
+
# For sub-workflows, use its role and description
|
|
73
|
+
role = node_def.role or "(Sub-Workflow)"
|
|
74
|
+
team_lines.append(f"- **{uid}** (Role: {role}): {node_def.description}")
|
|
75
|
+
elif isinstance(node_def, AgentConfig):
|
|
76
|
+
# For agents, use its role and description
|
|
77
|
+
team_lines.append(f"- **{uid}** (Role: {node_def.role}): {node_def.description}")
|
|
78
|
+
|
|
79
|
+
team_manifest = "### Your Team\n" + "\n".join(team_lines)
|
|
80
|
+
prompt_parts.append(team_manifest)
|
|
81
|
+
|
|
82
|
+
rules_list: List[str] = []
|
|
83
|
+
for node, uid in member_node_ids.items():
|
|
84
|
+
if node.dependencies:
|
|
85
|
+
dep_names = [member_node_ids.get(dep, dep.name) for dep in node.dependencies]
|
|
86
|
+
rules_list.append(f"To use '{uid}', you must have already successfully used: {', '.join(f'`{name}`' for name in dep_names)}.")
|
|
87
|
+
|
|
88
|
+
if rules_list:
|
|
89
|
+
rules_section = "### Execution Rules\n" + "\n".join(rules_list)
|
|
90
|
+
prompt_parts.append(rules_section)
|
|
91
|
+
|
|
92
|
+
prompt_parts.append(tools_section)
|
|
93
|
+
|
|
94
|
+
final_instruction = "### Your Task\nAnalyze the user's request, formulate a plan, and use the `SendMessageTo` tool to delegate tasks to your team. Address team members by their unique ID as listed under 'Your Team'."
|
|
95
|
+
prompt_parts.append(final_instruction)
|
|
96
|
+
else:
|
|
97
|
+
role_and_goal = (
|
|
98
|
+
"You are working alone. Your primary goal is to achieve the following objective:\n"
|
|
99
|
+
f"### Goal\n{context.config.description}"
|
|
100
|
+
)
|
|
101
|
+
prompt_parts.append(role_and_goal)
|
|
102
|
+
prompt_parts.append("### Your Team\nYou are working alone on this task.")
|
|
103
|
+
prompt_parts.append(tools_section)
|
|
104
|
+
final_instruction = "### Your Task\nAnalyze the user's request, formulate a plan, and use your available tools to achieve the goal."
|
|
105
|
+
prompt_parts.append(final_instruction)
|
|
106
|
+
|
|
107
|
+
return "\n\n".join(prompt_parts)
|
|
108
|
+
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/workflow/bootstrap_steps/workflow_bootstrapper.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING, List, Optional
|
|
4
|
+
|
|
5
|
+
from autobyteus.workflow.bootstrap_steps.base_workflow_bootstrap_step import BaseWorkflowBootstrapStep
|
|
6
|
+
from autobyteus.workflow.bootstrap_steps.workflow_runtime_queue_initialization_step import WorkflowRuntimeQueueInitializationStep
|
|
7
|
+
from autobyteus.workflow.bootstrap_steps.coordinator_prompt_preparation_step import CoordinatorPromptPreparationStep
|
|
8
|
+
from autobyteus.workflow.bootstrap_steps.agent_tool_injection_step import AgentToolInjectionStep
|
|
9
|
+
from autobyteus.workflow.bootstrap_steps.coordinator_initialization_step import CoordinatorInitializationStep
|
|
10
|
+
from autobyteus.workflow.events.workflow_events import WorkflowReadyEvent
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from autobyteus.workflow.context.workflow_context import WorkflowContext
|
|
14
|
+
from autobyteus.workflow.phases.workflow_phase_manager import WorkflowPhaseManager
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
class WorkflowBootstrapper:
|
|
19
|
+
"""Orchestrates the workflow's bootstrapping process."""
|
|
20
|
+
def __init__(self, steps: Optional[List[BaseWorkflowBootstrapStep]] = None):
|
|
21
|
+
self.bootstrap_steps = steps or [
|
|
22
|
+
WorkflowRuntimeQueueInitializationStep(),
|
|
23
|
+
CoordinatorPromptPreparationStep(),
|
|
24
|
+
AgentToolInjectionStep(),
|
|
25
|
+
CoordinatorInitializationStep(),
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
async def run(self, context: 'WorkflowContext', phase_manager: 'WorkflowPhaseManager') -> bool:
|
|
29
|
+
workflow_id = context.workflow_id
|
|
30
|
+
await phase_manager.notify_bootstrapping_started()
|
|
31
|
+
logger.info(f"Workflow '{workflow_id}': Bootstrapper starting.")
|
|
32
|
+
|
|
33
|
+
for step in self.bootstrap_steps:
|
|
34
|
+
step_name = step.__class__.__name__
|
|
35
|
+
logger.debug(f"Workflow '{workflow_id}': Executing bootstrap step: {step_name}")
|
|
36
|
+
if not await step.execute(context, phase_manager):
|
|
37
|
+
error_message = f"Bootstrap step {step_name} failed."
|
|
38
|
+
logger.error(f"Workflow '{workflow_id}': {error_message}")
|
|
39
|
+
await phase_manager.notify_error_occurred(error_message, f"Failed during bootstrap step '{step_name}'.")
|
|
40
|
+
return False
|
|
41
|
+
|
|
42
|
+
logger.info(f"Workflow '{workflow_id}': All bootstrap steps completed successfully.")
|
|
43
|
+
if context.state.input_event_queues:
|
|
44
|
+
await context.state.input_event_queues.enqueue_internal_system_event(WorkflowReadyEvent())
|
|
45
|
+
else:
|
|
46
|
+
logger.critical(f"Workflow '{workflow_id}': Bootstrap succeeded but queues not available.")
|
|
47
|
+
await phase_manager.notify_error_occurred("Queues unavailable after bootstrap.", "")
|
|
48
|
+
return False
|
|
49
|
+
|
|
50
|
+
return True
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/workflow/bootstrap_steps/workflow_runtime_queue_initialization_step.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from autobyteus.workflow.bootstrap_steps.base_workflow_bootstrap_step import BaseWorkflowBootstrapStep
|
|
6
|
+
from autobyteus.workflow.events.workflow_input_event_queue_manager import WorkflowInputEventQueueManager
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from autobyteus.workflow.context.workflow_context import WorkflowContext
|
|
10
|
+
from autobyteus.workflow.phases.workflow_phase_manager import WorkflowPhaseManager
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
class WorkflowRuntimeQueueInitializationStep(BaseWorkflowBootstrapStep):
|
|
15
|
+
"""Bootstrap step for initializing the workflow's runtime event queues."""
|
|
16
|
+
async def execute(self, context: 'WorkflowContext', phase_manager: 'WorkflowPhaseManager') -> bool:
|
|
17
|
+
workflow_id = context.workflow_id
|
|
18
|
+
logger.info(f"Workflow '{workflow_id}': Executing WorkflowRuntimeQueueInitializationStep.")
|
|
19
|
+
try:
|
|
20
|
+
context.state.input_event_queues = WorkflowInputEventQueueManager()
|
|
21
|
+
logger.info(f"Workflow '{workflow_id}': WorkflowInputEventQueueManager initialized.")
|
|
22
|
+
return True
|
|
23
|
+
except Exception as e:
|
|
24
|
+
logger.error(f"Workflow '{workflow_id}': Critical failure during queue initialization: {e}", exc_info=True)
|
|
25
|
+
return False
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/workflow/context/__init__.py
|
|
2
|
+
"""
|
|
3
|
+
Components related to the workflow's runtime context, state, and configuration.
|
|
4
|
+
"""
|
|
5
|
+
from autobyteus.workflow.context.team_manager import TeamManager
|
|
6
|
+
from autobyteus.workflow.context.workflow_config import WorkflowConfig
|
|
7
|
+
from autobyteus.workflow.context.workflow_context import WorkflowContext
|
|
8
|
+
from autobyteus.workflow.context.workflow_node_config import WorkflowNodeConfig
|
|
9
|
+
from autobyteus.workflow.context.workflow_runtime_state import WorkflowRuntimeState
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"TeamManager",
|
|
13
|
+
"WorkflowConfig",
|
|
14
|
+
"WorkflowContext",
|
|
15
|
+
"WorkflowNodeConfig",
|
|
16
|
+
"WorkflowRuntimeState",
|
|
17
|
+
]
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/workflow/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.workflow.utils.wait_for_idle import wait_for_workflow_to_be_idle
|
|
9
|
+
from autobyteus.workflow.exceptions import WorkflowNodeNotFoundException
|
|
10
|
+
from autobyteus.agent.message.send_message_to import SendMessageTo
|
|
11
|
+
from autobyteus.tools.registry import default_tool_registry
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from autobyteus.agent.agent import Agent
|
|
15
|
+
from autobyteus.workflow.agentic_workflow import AgenticWorkflow
|
|
16
|
+
from autobyteus.workflow.events.workflow_events import InterAgentMessageRequestEvent
|
|
17
|
+
from autobyteus.workflow.runtime.workflow_runtime import WorkflowRuntime
|
|
18
|
+
from autobyteus.workflow.streaming.agent_event_multiplexer import AgentEventMultiplexer
|
|
19
|
+
from autobyteus.agent.context.agent_config import AgentConfig
|
|
20
|
+
from autobyteus.workflow.context.workflow_config import WorkflowConfig
|
|
21
|
+
|
|
22
|
+
ManagedNode = Union['Agent', 'AgenticWorkflow']
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
class TeamManager:
|
|
27
|
+
"""
|
|
28
|
+
Manages all nodes (agents and sub-workflows) within a workflow. It handles
|
|
29
|
+
lazy creation, on-demand startup, and provides access to managed instances.
|
|
30
|
+
"""
|
|
31
|
+
def __init__(self, workflow_id: str, runtime: 'WorkflowRuntime', multiplexer: 'AgentEventMultiplexer'):
|
|
32
|
+
self.workflow_id = workflow_id
|
|
33
|
+
self._runtime = runtime
|
|
34
|
+
self._multiplexer = multiplexer
|
|
35
|
+
self._agent_factory = AgentFactory()
|
|
36
|
+
self._nodes_cache: Dict[str, ManagedNode] = {}
|
|
37
|
+
self._coordinator_agent: Optional['Agent'] = None
|
|
38
|
+
logger.info(f"TeamManager created for workflow '{self.workflow_id}'.")
|
|
39
|
+
|
|
40
|
+
async def dispatch_inter_agent_message_request(self, event: 'InterAgentMessageRequestEvent'):
|
|
41
|
+
await self._runtime.submit_event(event)
|
|
42
|
+
|
|
43
|
+
async def ensure_node_is_ready(self, name: str) -> ManagedNode:
|
|
44
|
+
"""
|
|
45
|
+
Retrieves a node (agent or sub-workflow) by its unique friendly name.
|
|
46
|
+
If the node has not been created yet, it is instantiated. If it is not
|
|
47
|
+
running, it is started and awaited until idle.
|
|
48
|
+
Returns a fully ready node instance or raises an exception.
|
|
49
|
+
"""
|
|
50
|
+
node_instance = self._nodes_cache.get(name)
|
|
51
|
+
|
|
52
|
+
was_created = False
|
|
53
|
+
if not node_instance:
|
|
54
|
+
logger.debug(f"Node '{name}' not in cache for workflow '{self.workflow_id}'. Attempting lazy creation.")
|
|
55
|
+
|
|
56
|
+
node_config_wrapper = self._runtime.context.get_node_config_by_name(name)
|
|
57
|
+
if not node_config_wrapper:
|
|
58
|
+
raise WorkflowNodeNotFoundException(node_name=name, workflow_id=self.workflow_id)
|
|
59
|
+
|
|
60
|
+
node_definition = node_config_wrapper.node_definition
|
|
61
|
+
|
|
62
|
+
if node_config_wrapper.is_subworkflow:
|
|
63
|
+
from autobyteus.workflow.factory.workflow_factory import WorkflowFactory
|
|
64
|
+
from autobyteus.workflow.context.workflow_config import WorkflowConfig
|
|
65
|
+
|
|
66
|
+
workflow_factory = WorkflowFactory() # Get singleton instance
|
|
67
|
+
if not isinstance(node_definition, WorkflowConfig):
|
|
68
|
+
raise TypeError(f"Expected WorkflowConfig for node '{name}', but found {type(node_definition)}")
|
|
69
|
+
logger.info(f"Lazily creating sub-workflow node '{name}' in workflow '{self.workflow_id}'.")
|
|
70
|
+
node_instance = workflow_factory.create_workflow(config=node_definition)
|
|
71
|
+
else:
|
|
72
|
+
from autobyteus.agent.context.agent_config import AgentConfig
|
|
73
|
+
if not isinstance(node_definition, AgentConfig):
|
|
74
|
+
raise TypeError(f"Expected AgentConfig for node '{name}', but found {type(node_definition)}")
|
|
75
|
+
|
|
76
|
+
# --- Apply Deferred Logic from Bootstrap Step ---
|
|
77
|
+
final_config = node_definition.copy()
|
|
78
|
+
|
|
79
|
+
# 1. Inject SendMessageTo tool
|
|
80
|
+
send_message_tool = default_tool_registry.create_tool(SendMessageTo.get_name())
|
|
81
|
+
if isinstance(send_message_tool, SendMessageTo):
|
|
82
|
+
send_message_tool.set_team_manager(self)
|
|
83
|
+
final_config.tools = [t for t in final_config.tools if not isinstance(t, SendMessageTo)]
|
|
84
|
+
final_config.tools.append(send_message_tool)
|
|
85
|
+
|
|
86
|
+
# 2. Apply coordinator prompt if this is the coordinator
|
|
87
|
+
coordinator_node_name = self._runtime.context.config.coordinator_node.name
|
|
88
|
+
if name == coordinator_node_name:
|
|
89
|
+
coordinator_prompt = self._runtime.context.state.prepared_coordinator_prompt
|
|
90
|
+
if coordinator_prompt:
|
|
91
|
+
final_config.system_prompt = coordinator_prompt
|
|
92
|
+
logger.info(f"Applied dynamic prompt to coordinator '{name}'.")
|
|
93
|
+
|
|
94
|
+
logger.info(f"Lazily creating agent node '{name}' in workflow '{self.workflow_id}'.")
|
|
95
|
+
node_instance = self._agent_factory.create_agent(config=final_config)
|
|
96
|
+
|
|
97
|
+
self._nodes_cache[name] = node_instance
|
|
98
|
+
was_created = True
|
|
99
|
+
|
|
100
|
+
if was_created and node_instance:
|
|
101
|
+
from autobyteus.workflow.agentic_workflow import AgenticWorkflow
|
|
102
|
+
from autobyteus.agent.agent import Agent
|
|
103
|
+
if isinstance(node_instance, AgenticWorkflow):
|
|
104
|
+
self._multiplexer.start_bridging_workflow_events(node_instance, name)
|
|
105
|
+
elif isinstance(node_instance, Agent):
|
|
106
|
+
self._multiplexer.start_bridging_agent_events(node_instance, name)
|
|
107
|
+
|
|
108
|
+
# On-Demand Startup Logic
|
|
109
|
+
if not node_instance.is_running:
|
|
110
|
+
from autobyteus.workflow.agentic_workflow import AgenticWorkflow
|
|
111
|
+
logger.info(f"Workflow '{self.workflow_id}': Node '{name}' is not running. Starting on-demand.")
|
|
112
|
+
try:
|
|
113
|
+
node_instance.start()
|
|
114
|
+
if isinstance(node_instance, AgenticWorkflow):
|
|
115
|
+
await wait_for_workflow_to_be_idle(node_instance, timeout=120.0)
|
|
116
|
+
else:
|
|
117
|
+
await wait_for_agent_to_be_idle(node_instance, timeout=60.0)
|
|
118
|
+
except Exception as e:
|
|
119
|
+
logger.error(f"Workflow '{self.workflow_id}': Failed to start node '{name}' on-demand: {e}", exc_info=True)
|
|
120
|
+
raise RuntimeError(f"Failed to start node '{name}' on-demand.") from e
|
|
121
|
+
|
|
122
|
+
return node_instance
|
|
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_workflows(self) -> List['AgenticWorkflow']:
|
|
129
|
+
from autobyteus.workflow.agentic_workflow import AgenticWorkflow
|
|
130
|
+
return [node for node in self._nodes_cache.values() if isinstance(node, AgenticWorkflow)]
|
|
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(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,30 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/workflow/context/workflow_config.py
|
|
2
|
+
import logging
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import List, Optional, Tuple
|
|
5
|
+
|
|
6
|
+
from autobyteus.workflow.context.workflow_node_config import WorkflowNodeConfig
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True)
|
|
11
|
+
class WorkflowConfig:
|
|
12
|
+
"""
|
|
13
|
+
Represents the complete, static configuration for an AgenticWorkflow instance.
|
|
14
|
+
This is the user's primary input for defining a workflow.
|
|
15
|
+
"""
|
|
16
|
+
name: str
|
|
17
|
+
description: str
|
|
18
|
+
nodes: Tuple[WorkflowNodeConfig, ...]
|
|
19
|
+
coordinator_node: WorkflowNodeConfig
|
|
20
|
+
role: Optional[str] = None
|
|
21
|
+
|
|
22
|
+
def __post_init__(self):
|
|
23
|
+
if not self.name or not isinstance(self.name, str):
|
|
24
|
+
raise ValueError("The 'name' in WorkflowConfig must be a non-empty string.")
|
|
25
|
+
if not self.nodes:
|
|
26
|
+
raise ValueError("The 'nodes' collection in WorkflowConfig cannot be empty.")
|
|
27
|
+
if self.coordinator_node not in self.nodes:
|
|
28
|
+
raise ValueError("The 'coordinator_node' must be one of the nodes in the 'nodes' collection.")
|
|
29
|
+
logger.debug(f"WorkflowConfig validated for workflow: '{self.name}'.")
|
|
30
|
+
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/workflow/context/workflow_context.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING, List, Optional, Dict
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from autobyteus.workflow.context.workflow_config import WorkflowConfig
|
|
7
|
+
from autobyteus.workflow.context.workflow_runtime_state import WorkflowRuntimeState
|
|
8
|
+
from autobyteus.agent.agent import Agent
|
|
9
|
+
from autobyteus.workflow.phases.workflow_phase_manager import WorkflowPhaseManager
|
|
10
|
+
from autobyteus.workflow.context.team_manager import TeamManager
|
|
11
|
+
from autobyteus.workflow.streaming.agent_event_multiplexer import AgentEventMultiplexer
|
|
12
|
+
from autobyteus.agent.context import AgentConfig
|
|
13
|
+
from autobyteus.workflow.context.workflow_node_config import WorkflowNodeConfig
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
class WorkflowContext:
|
|
18
|
+
"""Represents the complete operational context for a single workflow instance."""
|
|
19
|
+
def __init__(self, workflow_id: str, config: 'WorkflowConfig', state: 'WorkflowRuntimeState'):
|
|
20
|
+
if not workflow_id or not isinstance(workflow_id, str):
|
|
21
|
+
raise ValueError("WorkflowContext requires a non-empty string 'workflow_id'.")
|
|
22
|
+
|
|
23
|
+
self.workflow_id: str = workflow_id
|
|
24
|
+
self.config: 'WorkflowConfig' = config
|
|
25
|
+
self.state: 'WorkflowRuntimeState' = state
|
|
26
|
+
self._node_config_map: Optional[Dict[str, 'WorkflowNodeConfig']] = None
|
|
27
|
+
|
|
28
|
+
logger.info(f"WorkflowContext composed for workflow_id '{self.workflow_id}'.")
|
|
29
|
+
|
|
30
|
+
def get_node_config_by_name(self, name: str) -> Optional['WorkflowNodeConfig']:
|
|
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['WorkflowPhaseManager']:
|
|
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,76 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/workflow/context/workflow_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.workflow.context.workflow_config import WorkflowConfig
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class WorkflowNodeConfig:
|
|
17
|
+
"""
|
|
18
|
+
Represents a node in an agentic workflow graph.
|
|
19
|
+
|
|
20
|
+
This is the core building block for defining workflows. A node can be either
|
|
21
|
+
a single agent (defined by an AgentConfig) or an entire sub-workflow
|
|
22
|
+
(defined by a WorkflowConfig).
|
|
23
|
+
|
|
24
|
+
Attributes:
|
|
25
|
+
node_definition: The configuration for the agent or sub-workflow at this node.
|
|
26
|
+
dependencies: A tuple of other WorkflowNodeConfig 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", "WorkflowConfig"]
|
|
31
|
+
dependencies: Tuple[WorkflowNodeConfig, ...] = 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.workflow.context.workflow_config import WorkflowConfig
|
|
38
|
+
|
|
39
|
+
if not isinstance(self.node_definition, (AgentConfig, WorkflowConfig)):
|
|
40
|
+
raise TypeError("The 'node_definition' attribute must be an instance of AgentConfig or WorkflowConfig.")
|
|
41
|
+
|
|
42
|
+
if not all(isinstance(dep, WorkflowNodeConfig) for dep in self.dependencies):
|
|
43
|
+
raise TypeError("All items in 'dependencies' must be instances of WorkflowNodeConfig.")
|
|
44
|
+
|
|
45
|
+
logger.debug(f"WorkflowNodeConfig 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", "WorkflowConfig"]:
|
|
54
|
+
"""Returns the underlying AgentConfig or WorkflowConfig."""
|
|
55
|
+
return self.node_definition
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def is_subworkflow(self) -> bool:
|
|
59
|
+
"""Returns True if this node represents a sub-workflow."""
|
|
60
|
+
from autobyteus.workflow.context.workflow_config import WorkflowConfig
|
|
61
|
+
return isinstance(self.node_definition, WorkflowConfig)
|
|
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, WorkflowNodeConfig):
|
|
75
|
+
return self.node_id == other.node_id
|
|
76
|
+
return False
|