autobyteus 1.1.2__py3-none-any.whl → 1.1.4__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/__init__.py +2 -0
- autobyteus/agent/bootstrap_steps/agent_bootstrapper.py +2 -0
- autobyteus/agent/bootstrap_steps/mcp_server_prewarming_step.py +71 -0
- autobyteus/agent/bootstrap_steps/system_prompt_processing_step.py +4 -2
- autobyteus/agent/context/agent_config.py +36 -5
- autobyteus/agent/events/worker_event_dispatcher.py +1 -2
- autobyteus/agent/handlers/inter_agent_message_event_handler.py +1 -1
- 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 +1 -1
- autobyteus/agent/input_processor/__init__.py +1 -7
- autobyteus/agent/llm_response_processor/provider_aware_tool_usage_processor.py +41 -12
- autobyteus/agent/message/context_file_type.py +6 -0
- autobyteus/agent/message/send_message_to.py +68 -99
- autobyteus/agent/phases/discover.py +2 -1
- autobyteus/agent/runtime/agent_worker.py +25 -34
- autobyteus/agent/shutdown_steps/__init__.py +17 -0
- autobyteus/agent/shutdown_steps/agent_shutdown_orchestrator.py +63 -0
- autobyteus/agent/shutdown_steps/base_shutdown_step.py +33 -0
- autobyteus/agent/shutdown_steps/llm_instance_cleanup_step.py +45 -0
- autobyteus/agent/shutdown_steps/mcp_server_cleanup_step.py +32 -0
- 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/cli/__init__.py +1 -1
- 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 +3 -0
- autobyteus/llm/api/lmstudio_llm.py +37 -0
- autobyteus/llm/api/openai_compatible_llm.py +20 -3
- autobyteus/llm/llm_factory.py +2 -0
- autobyteus/llm/lmstudio_provider.py +89 -0
- autobyteus/llm/providers.py +1 -0
- autobyteus/llm/token_counter/token_counter_factory.py +2 -0
- autobyteus/tools/__init__.py +2 -0
- autobyteus/tools/ask_user_input.py +2 -1
- autobyteus/tools/base_tool.py +2 -0
- autobyteus/tools/bash/bash_executor.py +2 -1
- 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/__init__.py +10 -7
- autobyteus/tools/mcp/call_handlers/__init__.py +0 -2
- autobyteus/tools/mcp/config_service.py +1 -6
- autobyteus/tools/mcp/factory.py +12 -26
- autobyteus/tools/mcp/server/__init__.py +16 -0
- autobyteus/tools/mcp/server/base_managed_mcp_server.py +139 -0
- autobyteus/tools/mcp/server/http_managed_mcp_server.py +29 -0
- autobyteus/tools/mcp/server/proxy.py +36 -0
- autobyteus/tools/mcp/server/stdio_managed_mcp_server.py +33 -0
- autobyteus/tools/mcp/server_instance_manager.py +93 -0
- autobyteus/tools/mcp/tool.py +28 -46
- autobyteus/tools/mcp/tool_registrar.py +179 -0
- autobyteus/tools/mcp/types.py +10 -21
- autobyteus/tools/pdf_downloader.py +2 -1
- autobyteus/tools/registry/tool_definition.py +20 -7
- autobyteus/tools/registry/tool_registry.py +75 -28
- autobyteus/tools/timer.py +2 -0
- autobyteus/tools/tool_category.py +14 -4
- autobyteus/tools/tool_meta.py +6 -1
- autobyteus/tools/tool_origin.py +10 -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.2.dist-info → autobyteus-1.1.4.dist-info}/METADATA +16 -13
- {autobyteus-1.1.2.dist-info → autobyteus-1.1.4.dist-info}/RECORD +156 -75
- {autobyteus-1.1.2.dist-info → autobyteus-1.1.4.dist-info}/top_level.txt +1 -0
- examples/__init__.py +1 -0
- examples/discover_phase_transitions.py +104 -0
- examples/run_browser_agent.py +260 -0
- examples/run_google_slides_agent.py +286 -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 +274 -0
- examples/run_sqlite_agent.py +293 -0
- examples/workflow/__init__.py +1 -0
- examples/workflow/run_basic_research_workflow.py +189 -0
- examples/workflow/run_code_review_workflow.py +269 -0
- examples/workflow/run_debate_workflow.py +212 -0
- examples/workflow/run_workflow_with_tui.py +153 -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/sse_handler.py +0 -22
- autobyteus/tools/mcp/registrar.py +0 -323
- autobyteus/workflow/simple_task.py +0 -98
- autobyteus/workflow/task.py +0 -147
- autobyteus/workflow/workflow.py +0 -49
- {autobyteus-1.1.2.dist-info → autobyteus-1.1.4.dist-info}/WHEEL +0 -0
- {autobyteus-1.1.2.dist-info → autobyteus-1.1.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -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
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/workflow/context/workflow_runtime_state.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import List, Optional, TYPE_CHECKING, Dict
|
|
4
|
+
|
|
5
|
+
from autobyteus.workflow.phases.workflow_operational_phase import WorkflowOperationalPhase
|
|
6
|
+
from autobyteus.agent.context import AgentConfig
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from autobyteus.agent.agent import Agent
|
|
10
|
+
from autobyteus.workflow.events.workflow_input_event_queue_manager import WorkflowInputEventQueueManager
|
|
11
|
+
from autobyteus.workflow.phases.workflow_phase_manager import WorkflowPhaseManager
|
|
12
|
+
from autobyteus.workflow.context.workflow_node_config import WorkflowNodeConfig
|
|
13
|
+
from autobyteus.workflow.context.team_manager import TeamManager
|
|
14
|
+
from autobyteus.workflow.streaming.agent_event_multiplexer import AgentEventMultiplexer
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
class WorkflowRuntimeState:
|
|
19
|
+
"""Encapsulates the dynamic, stateful data of a running workflow instance."""
|
|
20
|
+
def __init__(self, workflow_id: str):
|
|
21
|
+
if not workflow_id or not isinstance(workflow_id, str):
|
|
22
|
+
raise ValueError("WorkflowRuntimeState requires a non-empty string 'workflow_id'.")
|
|
23
|
+
|
|
24
|
+
self.workflow_id: str = workflow_id
|
|
25
|
+
self.current_phase: WorkflowOperationalPhase = WorkflowOperationalPhase.UNINITIALIZED
|
|
26
|
+
|
|
27
|
+
# State populated by bootstrap steps
|
|
28
|
+
self.prepared_coordinator_prompt: Optional[str] = None
|
|
29
|
+
# This is now deprecated in favor of just-in-time resolution by TeamManager
|
|
30
|
+
# self.resolved_agent_configs: Optional[Dict[str, 'AgentConfig']] = None
|
|
31
|
+
|
|
32
|
+
# Core services
|
|
33
|
+
self.team_manager: Optional['TeamManager'] = None
|
|
34
|
+
|
|
35
|
+
# Runtime components and references
|
|
36
|
+
self.input_event_queues: Optional['WorkflowInputEventQueueManager'] = None
|
|
37
|
+
self.phase_manager_ref: Optional['WorkflowPhaseManager'] = None
|
|
38
|
+
self.multiplexer_ref: Optional['AgentEventMultiplexer'] = None
|
|
39
|
+
|
|
40
|
+
logger.info(f"WorkflowRuntimeState initialized for workflow_id '{self.workflow_id}'.")
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def resolved_agent_configs(self) -> Optional[Dict[str, 'AgentConfig']]:
|
|
44
|
+
"""This property is now DEPRECATED as configs are resolved just-in-time."""
|
|
45
|
+
logger.warning("'resolved_agent_configs' is deprecated. Node configs are resolved by TeamManager as needed.")
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
def __repr__(self) -> str:
|
|
49
|
+
agents_count = len(self.team_manager.get_all_agents()) if self.team_manager else 0
|
|
50
|
+
coordinator_set = self.team_manager.coordinator_agent is not None if self.team_manager else False
|
|
51
|
+
return (f"<WorkflowRuntimeState id='{self.workflow_id}', phase='{self.current_phase.value}', "
|
|
52
|
+
f"agents_count={agents_count}, coordinator_set={coordinator_set}, "
|
|
53
|
+
f"team_manager_set={self.team_manager is not None}>")
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/workflow/events/__init__.py
|
|
2
|
+
"""
|
|
3
|
+
This package contains event definitions and dispatchers for the workflow runtime.
|
|
4
|
+
"""
|
|
5
|
+
from autobyteus.workflow.events.workflow_events import (
|
|
6
|
+
BaseWorkflowEvent,
|
|
7
|
+
LifecycleWorkflowEvent,
|
|
8
|
+
OperationalWorkflowEvent,
|
|
9
|
+
WorkflowReadyEvent,
|
|
10
|
+
WorkflowErrorEvent,
|
|
11
|
+
ProcessUserMessageEvent,
|
|
12
|
+
InterAgentMessageRequestEvent,
|
|
13
|
+
ToolApprovalWorkflowEvent,
|
|
14
|
+
)
|
|
15
|
+
from autobyteus.workflow.events.workflow_event_dispatcher import WorkflowEventDispatcher
|
|
16
|
+
from autobyteus.workflow.events.workflow_input_event_queue_manager import WorkflowInputEventQueueManager
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"BaseWorkflowEvent",
|
|
20
|
+
"LifecycleWorkflowEvent",
|
|
21
|
+
"OperationalWorkflowEvent",
|
|
22
|
+
"WorkflowReadyEvent",
|
|
23
|
+
"WorkflowErrorEvent",
|
|
24
|
+
"ProcessUserMessageEvent",
|
|
25
|
+
"InterAgentMessageRequestEvent",
|
|
26
|
+
"ToolApprovalWorkflowEvent",
|
|
27
|
+
"WorkflowEventDispatcher",
|
|
28
|
+
"WorkflowInputEventQueueManager",
|
|
29
|
+
]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/workflow/events/workflow_event_dispatcher.py
|
|
2
|
+
import logging
|
|
3
|
+
import traceback
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from autobyteus.workflow.events.workflow_events import BaseWorkflowEvent, WorkflowReadyEvent, ProcessUserMessageEvent
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from autobyteus.workflow.context.workflow_context import WorkflowContext
|
|
10
|
+
from autobyteus.workflow.handlers.workflow_event_handler_registry import WorkflowEventHandlerRegistry
|
|
11
|
+
from autobyteus.workflow.phases.workflow_phase_manager import WorkflowPhaseManager
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
class WorkflowEventDispatcher:
|
|
16
|
+
"""Dispatches workflow events to their appropriate handlers."""
|
|
17
|
+
|
|
18
|
+
def __init__(self,
|
|
19
|
+
event_handler_registry: 'WorkflowEventHandlerRegistry',
|
|
20
|
+
phase_manager: 'WorkflowPhaseManager'):
|
|
21
|
+
self.registry = event_handler_registry
|
|
22
|
+
self.phase_manager = phase_manager
|
|
23
|
+
|
|
24
|
+
async def dispatch(self, event: BaseWorkflowEvent, context: 'WorkflowContext'):
|
|
25
|
+
handler = self.registry.get_handler(type(event))
|
|
26
|
+
workflow_id = context.workflow_id
|
|
27
|
+
|
|
28
|
+
if not handler:
|
|
29
|
+
logger.warning(f"Workflow '{workflow_id}': No handler for event '{type(event).__name__}'.")
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
await handler.handle(event, context)
|
|
34
|
+
if isinstance(event, WorkflowReadyEvent):
|
|
35
|
+
await self.phase_manager.notify_initialization_complete()
|
|
36
|
+
except Exception as e:
|
|
37
|
+
error_msg = f"Error handling '{type(event).__name__}' in workflow '{workflow_id}': {e}"
|
|
38
|
+
logger.error(error_msg, exc_info=True)
|
|
39
|
+
await self.phase_manager.notify_error_occurred(error_msg, traceback.format_exc())
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/workflow/events/workflow_events.py
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Dict, Any, Optional
|
|
4
|
+
|
|
5
|
+
from autobyteus.agent.message.agent_input_user_message import AgentInputUserMessage
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class BaseWorkflowEvent:
|
|
9
|
+
"""Base class for all workflow events."""
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class LifecycleWorkflowEvent(BaseWorkflowEvent):
|
|
13
|
+
"""Base class for events related to the workflow's lifecycle."""
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class OperationalWorkflowEvent(BaseWorkflowEvent):
|
|
17
|
+
"""Base class for events related to the workflow's operational logic."""
|
|
18
|
+
|
|
19
|
+
# Specific Events
|
|
20
|
+
@dataclass
|
|
21
|
+
class WorkflowReadyEvent(LifecycleWorkflowEvent):
|
|
22
|
+
"""Indicates the workflow has completed bootstrapping and is ready for tasks."""
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class WorkflowErrorEvent(LifecycleWorkflowEvent):
|
|
26
|
+
"""Indicates a significant error occurred within the workflow."""
|
|
27
|
+
error_message: str
|
|
28
|
+
exception_details: Optional[str] = None
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class ProcessUserMessageEvent(OperationalWorkflowEvent):
|
|
32
|
+
"""Carries a user's message to be processed by a specific agent in the workflow."""
|
|
33
|
+
user_message: AgentInputUserMessage
|
|
34
|
+
target_agent_name: str
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class InterAgentMessageRequestEvent(OperationalWorkflowEvent):
|
|
38
|
+
"""
|
|
39
|
+
An internal request within the workflow to post a message from one agent to another.
|
|
40
|
+
This triggers on-demand startup logic if needed.
|
|
41
|
+
"""
|
|
42
|
+
sender_agent_id: str
|
|
43
|
+
recipient_name: str
|
|
44
|
+
content: str
|
|
45
|
+
message_type: str
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class ToolApprovalWorkflowEvent(OperationalWorkflowEvent):
|
|
49
|
+
"""Carries a user's approval/denial for a tool execution to a specific agent."""
|
|
50
|
+
agent_name: str
|
|
51
|
+
tool_invocation_id: str
|
|
52
|
+
is_approved: bool
|
|
53
|
+
reason: Optional[str] = None
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/workflow/events/workflow_input_event_queue_manager.py
|
|
2
|
+
import asyncio
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from autobyteus.workflow.events.workflow_events import ProcessUserMessageEvent
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
class WorkflowInputEventQueueManager:
|
|
11
|
+
"""Manages asyncio.Queue instances for events consumed by the WorkflowWorker."""
|
|
12
|
+
def __init__(self, queue_size: int = 0):
|
|
13
|
+
self.user_message_queue: asyncio.Queue[ProcessUserMessageEvent] = asyncio.Queue(maxsize=queue_size)
|
|
14
|
+
self.internal_system_event_queue: asyncio.Queue[Any] = asyncio.Queue(maxsize=queue_size)
|
|
15
|
+
logger.info("WorkflowInputEventQueueManager initialized.")
|
|
16
|
+
|
|
17
|
+
async def enqueue_user_message(self, event: ProcessUserMessageEvent):
|
|
18
|
+
await self.user_message_queue.put(event)
|
|
19
|
+
|
|
20
|
+
async def enqueue_internal_system_event(self, event: Any):
|
|
21
|
+
await self.internal_system_event_queue.put(event)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/workflow/exceptions.py
|
|
2
|
+
|
|
3
|
+
class WorkflowNodeNotFoundException(Exception):
|
|
4
|
+
"""Raised when a node (agent or sub-workflow) cannot be found in the workflow."""
|
|
5
|
+
def __init__(self, node_name: str, workflow_id: str):
|
|
6
|
+
super().__init__(f"Node '{node_name}' not found in workflow '{workflow_id}'.")
|
|
7
|
+
self.node_name = node_name
|
|
8
|
+
self.workflow_id = workflow_id
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/workflow/factory/workflow_factory.py
|
|
2
|
+
import logging
|
|
3
|
+
import uuid
|
|
4
|
+
from typing import Optional, Dict, List
|
|
5
|
+
|
|
6
|
+
from autobyteus.utils.singleton import SingletonMeta
|
|
7
|
+
from autobyteus.workflow.agentic_workflow import AgenticWorkflow
|
|
8
|
+
from autobyteus.workflow.context.workflow_config import WorkflowConfig
|
|
9
|
+
from autobyteus.workflow.context.workflow_context import WorkflowContext
|
|
10
|
+
from autobyteus.workflow.context.workflow_runtime_state import WorkflowRuntimeState
|
|
11
|
+
from autobyteus.workflow.context.team_manager import TeamManager
|
|
12
|
+
from autobyteus.workflow.runtime.workflow_runtime import WorkflowRuntime
|
|
13
|
+
from autobyteus.workflow.handlers.workflow_event_handler_registry import WorkflowEventHandlerRegistry
|
|
14
|
+
from autobyteus.workflow.handlers.process_user_message_event_handler import ProcessUserMessageEventHandler
|
|
15
|
+
from autobyteus.workflow.handlers.lifecycle_workflow_event_handler import LifecycleWorkflowEventHandler
|
|
16
|
+
from autobyteus.workflow.handlers.inter_agent_message_request_event_handler import InterAgentMessageRequestEventHandler
|
|
17
|
+
from autobyteus.workflow.handlers.tool_approval_workflow_event_handler import ToolApprovalWorkflowEventHandler
|
|
18
|
+
from autobyteus.workflow.events.workflow_events import (
|
|
19
|
+
ProcessUserMessageEvent,
|
|
20
|
+
WorkflowReadyEvent,
|
|
21
|
+
WorkflowErrorEvent,
|
|
22
|
+
InterAgentMessageRequestEvent,
|
|
23
|
+
ToolApprovalWorkflowEvent
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
class WorkflowFactory(metaclass=SingletonMeta):
|
|
29
|
+
"""
|
|
30
|
+
A singleton factory for creating and managing AgenticWorkflow instances.
|
|
31
|
+
It orchestrates the assembly of all core workflow components.
|
|
32
|
+
"""
|
|
33
|
+
def __init__(self):
|
|
34
|
+
self._active_workflows: Dict[str, AgenticWorkflow] = {}
|
|
35
|
+
logger.info("WorkflowFactory (Singleton) initialized.")
|
|
36
|
+
|
|
37
|
+
def _get_default_event_handler_registry(self) -> WorkflowEventHandlerRegistry:
|
|
38
|
+
"""Returns a registry with default handlers for a new workflow."""
|
|
39
|
+
registry = WorkflowEventHandlerRegistry()
|
|
40
|
+
registry.register(ProcessUserMessageEvent, ProcessUserMessageEventHandler())
|
|
41
|
+
registry.register(InterAgentMessageRequestEvent, InterAgentMessageRequestEventHandler())
|
|
42
|
+
registry.register(ToolApprovalWorkflowEvent, ToolApprovalWorkflowEventHandler())
|
|
43
|
+
lifecycle_handler = LifecycleWorkflowEventHandler()
|
|
44
|
+
registry.register(WorkflowReadyEvent, lifecycle_handler)
|
|
45
|
+
registry.register(WorkflowErrorEvent, lifecycle_handler)
|
|
46
|
+
return registry
|
|
47
|
+
|
|
48
|
+
def create_workflow(self, config: WorkflowConfig) -> AgenticWorkflow:
|
|
49
|
+
"""
|
|
50
|
+
Creates a new workflow based on the provided WorkflowConfig, stores it,
|
|
51
|
+
and returns its facade (AgenticWorkflow).
|
|
52
|
+
"""
|
|
53
|
+
if not isinstance(config, WorkflowConfig):
|
|
54
|
+
raise TypeError(f"Expected WorkflowConfig instance, got {type(config).__name__}.")
|
|
55
|
+
|
|
56
|
+
workflow_id = f"workflow_{uuid.uuid4().hex[:8]}"
|
|
57
|
+
while workflow_id in self._active_workflows:
|
|
58
|
+
workflow_id = f"workflow_{uuid.uuid4().hex[:8]}"
|
|
59
|
+
|
|
60
|
+
# --- Component Assembly as per new architecture ---
|
|
61
|
+
state = WorkflowRuntimeState(workflow_id=workflow_id)
|
|
62
|
+
context = WorkflowContext(workflow_id=workflow_id, config=config, state=state)
|
|
63
|
+
|
|
64
|
+
handler_registry = self._get_default_event_handler_registry()
|
|
65
|
+
runtime = WorkflowRuntime(context=context, event_handler_registry=handler_registry)
|
|
66
|
+
|
|
67
|
+
team_manager = TeamManager(
|
|
68
|
+
workflow_id=workflow_id,
|
|
69
|
+
runtime=runtime,
|
|
70
|
+
multiplexer=runtime.multiplexer # Pass multiplexer created in runtime
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
context.state.team_manager = team_manager
|
|
74
|
+
|
|
75
|
+
workflow = AgenticWorkflow(runtime=runtime)
|
|
76
|
+
|
|
77
|
+
self._active_workflows[workflow_id] = workflow
|
|
78
|
+
logger.info(f"Workflow '{workflow_id}' created and stored successfully.")
|
|
79
|
+
return workflow
|
|
80
|
+
|
|
81
|
+
def get_workflow(self, workflow_id: str) -> Optional[AgenticWorkflow]:
|
|
82
|
+
"""Retrieves an active workflow instance by its ID."""
|
|
83
|
+
return self._active_workflows.get(workflow_id)
|
|
84
|
+
|
|
85
|
+
async def remove_workflow(self, workflow_id: str, shutdown_timeout: float = 10.0) -> bool:
|
|
86
|
+
"""
|
|
87
|
+
Removes a workflow from the factory's management and gracefully stops it.
|
|
88
|
+
"""
|
|
89
|
+
workflow = self._active_workflows.pop(workflow_id, None)
|
|
90
|
+
if workflow:
|
|
91
|
+
logger.info(f"Removing workflow '{workflow_id}'. Attempting graceful shutdown.")
|
|
92
|
+
await workflow.stop(timeout=shutdown_timeout)
|
|
93
|
+
return True
|
|
94
|
+
logger.warning(f"Workflow with ID '{workflow_id}' not found for removal.")
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
def list_active_workflow_ids(self) -> List[str]:
|
|
98
|
+
"""Returns a list of IDs of all active workflows managed by this factory."""
|
|
99
|
+
return list(self._active_workflows.keys())
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/workflow/handlers/__init__.py
|
|
2
|
+
"""
|
|
3
|
+
Event handlers for the workflow runtime.
|
|
4
|
+
"""
|
|
5
|
+
from autobyteus.workflow.handlers.base_workflow_event_handler import BaseWorkflowEventHandler
|
|
6
|
+
from autobyteus.workflow.handlers.lifecycle_workflow_event_handler import LifecycleWorkflowEventHandler
|
|
7
|
+
from autobyteus.workflow.handlers.inter_agent_message_request_event_handler import InterAgentMessageRequestEventHandler
|
|
8
|
+
from autobyteus.workflow.handlers.process_user_message_event_handler import ProcessUserMessageEventHandler
|
|
9
|
+
from autobyteus.workflow.handlers.tool_approval_workflow_event_handler import ToolApprovalWorkflowEventHandler
|
|
10
|
+
from autobyteus.workflow.handlers.workflow_event_handler_registry import WorkflowEventHandlerRegistry
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"BaseWorkflowEventHandler",
|
|
14
|
+
"LifecycleWorkflowEventHandler",
|
|
15
|
+
"InterAgentMessageRequestEventHandler",
|
|
16
|
+
"ProcessUserMessageEventHandler",
|
|
17
|
+
"ToolApprovalWorkflowEventHandler",
|
|
18
|
+
"WorkflowEventHandlerRegistry",
|
|
19
|
+
]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/workflow/handlers/base_workflow_event_handler.py
|
|
2
|
+
import logging
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import Any, TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from autobyteus.workflow.context.workflow_context import WorkflowContext
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
class BaseWorkflowEventHandler(ABC):
|
|
12
|
+
"""Abstract base class for workflow event handlers."""
|
|
13
|
+
|
|
14
|
+
@abstractmethod
|
|
15
|
+
async def handle(self, event: Any, context: 'WorkflowContext') -> None:
|
|
16
|
+
raise NotImplementedError("Subclasses must implement the 'handle' method.")
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/workflow/handlers/inter_agent_message_request_event_handler.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from autobyteus.workflow.handlers.base_workflow_event_handler import BaseWorkflowEventHandler
|
|
6
|
+
from autobyteus.workflow.events.workflow_events import InterAgentMessageRequestEvent
|
|
7
|
+
from autobyteus.agent.message.inter_agent_message import InterAgentMessage
|
|
8
|
+
from autobyteus.agent.message.agent_input_user_message import AgentInputUserMessage
|
|
9
|
+
from autobyteus.workflow.agentic_workflow import AgenticWorkflow
|
|
10
|
+
from autobyteus.agent.agent import Agent
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from autobyteus.workflow.context.workflow_context import WorkflowContext
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
class InterAgentMessageRequestEventHandler(BaseWorkflowEventHandler):
|
|
18
|
+
"""
|
|
19
|
+
Handles requests to send messages between nodes (agents or sub-workflows).
|
|
20
|
+
It relies on the TeamManager to handle on-demand startup of the recipient.
|
|
21
|
+
"""
|
|
22
|
+
async def handle(self, event: InterAgentMessageRequestEvent, context: 'WorkflowContext') -> None:
|
|
23
|
+
workflow_id = context.workflow_id
|
|
24
|
+
team_manager = context.team_manager
|
|
25
|
+
|
|
26
|
+
if not team_manager:
|
|
27
|
+
logger.error(f"Workflow '{workflow_id}': TeamManager not found. Cannot route message from '{event.sender_agent_id}' to '{event.recipient_name}'.")
|
|
28
|
+
return
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
target_node = await team_manager.ensure_node_is_ready(event.recipient_name)
|
|
32
|
+
except Exception as e:
|
|
33
|
+
msg = f"Recipient node '{event.recipient_name}' not found or failed to start for message from '{event.sender_agent_id}'. Error: {e}"
|
|
34
|
+
logger.error(f"Workflow '{workflow_id}': {msg}", exc_info=True)
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
if isinstance(target_node, AgenticWorkflow):
|
|
39
|
+
# If target is a sub-workflow, post a user message to it.
|
|
40
|
+
# The sub-workflow will route it to its own coordinator.
|
|
41
|
+
message_for_workflow = AgentInputUserMessage(content=event.content)
|
|
42
|
+
await target_node.post_message(message_for_workflow)
|
|
43
|
+
logger.info(f"Workflow '{workflow_id}': Successfully posted message from '{event.sender_agent_id}' to sub-workflow '{event.recipient_name}'.")
|
|
44
|
+
|
|
45
|
+
elif isinstance(target_node, Agent):
|
|
46
|
+
# If target is a regular agent, create and post an InterAgentMessage.
|
|
47
|
+
message_for_agent = InterAgentMessage.create_with_dynamic_message_type(
|
|
48
|
+
recipient_role_name=target_node.context.config.role,
|
|
49
|
+
recipient_agent_id=target_node.agent_id,
|
|
50
|
+
content=event.content,
|
|
51
|
+
message_type=event.message_type,
|
|
52
|
+
sender_agent_id=event.sender_agent_id
|
|
53
|
+
)
|
|
54
|
+
await target_node.post_inter_agent_message(message_for_agent)
|
|
55
|
+
logger.info(f"Workflow '{workflow_id}': Successfully posted message from '{event.sender_agent_id}' to agent '{event.recipient_name}'.")
|
|
56
|
+
else:
|
|
57
|
+
raise TypeError(f"Target node '{event.recipient_name}' is of an unsupported type: {type(target_node).__name__}")
|
|
58
|
+
|
|
59
|
+
except Exception as e:
|
|
60
|
+
msg = f"Error posting message to node '{event.recipient_name}': {e}"
|
|
61
|
+
logger.error(f"Workflow '{workflow_id}': {msg}", exc_info=True)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/workflow/handlers/lifecycle_workflow_event_handler.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from autobyteus.workflow.handlers.base_workflow_event_handler import BaseWorkflowEventHandler
|
|
6
|
+
from autobyteus.workflow.events.workflow_events import BaseWorkflowEvent, WorkflowReadyEvent, WorkflowErrorEvent
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from autobyteus.workflow.context.workflow_context import WorkflowContext
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
class LifecycleWorkflowEventHandler(BaseWorkflowEventHandler):
|
|
14
|
+
"""Logs various lifecycle events for a workflow."""
|
|
15
|
+
async def handle(self, event: BaseWorkflowEvent, context: 'WorkflowContext') -> None:
|
|
16
|
+
workflow_id = context.workflow_id
|
|
17
|
+
current_phase = context.state.current_phase.value
|
|
18
|
+
|
|
19
|
+
if isinstance(event, WorkflowReadyEvent):
|
|
20
|
+
logger.info(f"Workflow '{workflow_id}' Logged WorkflowReadyEvent. Current phase: {current_phase}")
|
|
21
|
+
elif isinstance(event, WorkflowErrorEvent):
|
|
22
|
+
logger.error(
|
|
23
|
+
f"Workflow '{workflow_id}' Logged WorkflowErrorEvent: {event.error_message}. "
|
|
24
|
+
f"Details: {event.exception_details}. Current phase: {current_phase}"
|
|
25
|
+
)
|
|
26
|
+
else:
|
|
27
|
+
logger.warning(f"LifecycleWorkflowEventHandler received unhandled event type: {type(event).__name__}")
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/workflow/handlers/process_user_message_event_handler.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
from autobyteus.workflow.handlers.base_workflow_event_handler import BaseWorkflowEventHandler
|
|
5
|
+
from autobyteus.workflow.events.workflow_events import ProcessUserMessageEvent
|
|
6
|
+
from autobyteus.agent.agent import Agent
|
|
7
|
+
from autobyteus.workflow.agentic_workflow import AgenticWorkflow
|
|
8
|
+
from autobyteus.agent.message.agent_input_user_message import AgentInputUserMessage
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from autobyteus.workflow.context.workflow_context import WorkflowContext
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
class ProcessUserMessageEventHandler(BaseWorkflowEventHandler):
|
|
16
|
+
"""Handles user messages by routing them to the specified target agent or sub-workflow."""
|
|
17
|
+
async def handle(self, event: ProcessUserMessageEvent, context: 'WorkflowContext') -> None:
|
|
18
|
+
await context.phase_manager.notify_processing_started()
|
|
19
|
+
|
|
20
|
+
team_manager = context.team_manager
|
|
21
|
+
if not team_manager:
|
|
22
|
+
msg = f"Workflow '{context.workflow_id}': TeamManager not found. Cannot route message."
|
|
23
|
+
logger.error(msg)
|
|
24
|
+
await context.phase_manager.notify_error_occurred(msg, "TeamManager is not initialized.")
|
|
25
|
+
return
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
target_node = await team_manager.ensure_node_is_ready(event.target_agent_name)
|
|
29
|
+
except Exception as e:
|
|
30
|
+
msg = f"Workflow '{context.workflow_id}': Node '{event.target_agent_name}' not found or failed to start. Cannot route message. Error: {e}"
|
|
31
|
+
logger.error(msg, exc_info=True)
|
|
32
|
+
await context.phase_manager.notify_error_occurred(msg, f"Node '{event.target_agent_name}' not found or failed to start.")
|
|
33
|
+
return
|
|
34
|
+
|
|
35
|
+
if isinstance(target_node, Agent):
|
|
36
|
+
await target_node.post_user_message(event.user_message)
|
|
37
|
+
logger.info(f"Workflow '{context.workflow_id}': Routed user message to agent node '{event.target_agent_name}'.")
|
|
38
|
+
elif isinstance(target_node, AgenticWorkflow):
|
|
39
|
+
await target_node.post_message(event.user_message)
|
|
40
|
+
logger.info(f"Workflow '{context.workflow_id}': Routed user message to sub-workflow node '{event.target_agent_name}'.")
|
|
41
|
+
else:
|
|
42
|
+
msg = f"Target node '{event.target_agent_name}' is of an unsupported type: {type(target_node).__name__}"
|
|
43
|
+
logger.error(f"Workflow '{context.workflow_id}': {msg}")
|
|
44
|
+
await context.phase_manager.notify_error_occurred(msg, "")
|
|
45
|
+
|
|
46
|
+
await context.phase_manager.notify_processing_complete_and_idle()
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/workflow/handlers/tool_approval_workflow_event_handler.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from autobyteus.workflow.handlers.base_workflow_event_handler import BaseWorkflowEventHandler
|
|
6
|
+
from autobyteus.workflow.events.workflow_events import ToolApprovalWorkflowEvent
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from autobyteus.workflow.context.workflow_context import WorkflowContext
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
class ToolApprovalWorkflowEventHandler(BaseWorkflowEventHandler):
|
|
14
|
+
"""
|
|
15
|
+
Handles tool approval events by routing them to the correct agent.
|
|
16
|
+
"""
|
|
17
|
+
async def handle(self, event: ToolApprovalWorkflowEvent, context: 'WorkflowContext') -> None:
|
|
18
|
+
workflow_id = context.workflow_id
|
|
19
|
+
team_manager = context.team_manager
|
|
20
|
+
|
|
21
|
+
if not team_manager:
|
|
22
|
+
msg = f"Workflow '{workflow_id}': TeamManager not found. Cannot route approval for agent '{event.agent_name}'."
|
|
23
|
+
logger.error(msg)
|
|
24
|
+
await context.phase_manager.notify_error_occurred(msg, "TeamManager is not initialized.")
|
|
25
|
+
return
|
|
26
|
+
|
|
27
|
+
target_agent = await team_manager.ensure_agent_is_ready(event.agent_name)
|
|
28
|
+
if not target_agent:
|
|
29
|
+
msg = f"Workflow '{workflow_id}': Target agent '{event.agent_name}' for approval not found or failed to start."
|
|
30
|
+
logger.error(msg)
|
|
31
|
+
await context.phase_manager.notify_error_occurred(msg, f"Agent '{event.agent_name}' not found or failed to start.")
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
logger.info(f"Workflow '{workflow_id}': Posting tool approval (Approved: {event.is_approved}) to agent '{event.agent_name}' for invocation '{event.tool_invocation_id}'.")
|
|
35
|
+
await target_agent.post_tool_execution_approval(
|
|
36
|
+
tool_invocation_id=event.tool_invocation_id,
|
|
37
|
+
is_approved=event.is_approved,
|
|
38
|
+
reason=event.reason
|
|
39
|
+
)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/workflow/handlers/workflow_event_handler_registry.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Dict, Type, Optional
|
|
4
|
+
|
|
5
|
+
from autobyteus.workflow.handlers.base_workflow_event_handler import BaseWorkflowEventHandler
|
|
6
|
+
from autobyteus.workflow.events.workflow_events import BaseWorkflowEvent
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
class WorkflowEventHandlerRegistry:
|
|
11
|
+
"""Manages registration and retrieval of workflow event handlers."""
|
|
12
|
+
def __init__(self):
|
|
13
|
+
self._handlers: Dict[Type[BaseWorkflowEvent], BaseWorkflowEventHandler] = {}
|
|
14
|
+
logger.info("WorkflowEventHandlerRegistry initialized.")
|
|
15
|
+
|
|
16
|
+
def register(self, event_class: Type[BaseWorkflowEvent], handler_instance: BaseWorkflowEventHandler):
|
|
17
|
+
if not issubclass(event_class, BaseWorkflowEvent):
|
|
18
|
+
raise TypeError("Can only register handlers for BaseWorkflowEvent subclasses.")
|
|
19
|
+
self._handlers[event_class] = handler_instance
|
|
20
|
+
logger.info(f"Handler '{type(handler_instance).__name__}' registered for event '{event_class.__name__}'.")
|
|
21
|
+
|
|
22
|
+
def get_handler(self, event_class: Type[BaseWorkflowEvent]) -> Optional[BaseWorkflowEventHandler]:
|
|
23
|
+
return self._handlers.get(event_class)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/workflow/phases/__init__.py
|
|
2
|
+
"""
|
|
3
|
+
This package contains components for defining and managing workflow operational phases.
|
|
4
|
+
"""
|
|
5
|
+
from autobyteus.workflow.phases.workflow_operational_phase import WorkflowOperationalPhase
|
|
6
|
+
from autobyteus.workflow.phases.workflow_phase_manager import WorkflowPhaseManager
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"WorkflowOperationalPhase",
|
|
10
|
+
"WorkflowPhaseManager",
|
|
11
|
+
]
|