autobyteus 1.1.3__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.
Files changed (148) hide show
  1. autobyteus/agent/agent.py +1 -1
  2. autobyteus/agent/bootstrap_steps/system_prompt_processing_step.py +4 -2
  3. autobyteus/agent/context/agent_config.py +36 -5
  4. autobyteus/agent/events/worker_event_dispatcher.py +1 -2
  5. autobyteus/agent/handlers/inter_agent_message_event_handler.py +1 -1
  6. autobyteus/agent/handlers/llm_user_message_ready_event_handler.py +2 -2
  7. autobyteus/agent/handlers/tool_result_event_handler.py +48 -20
  8. autobyteus/agent/handlers/user_input_message_event_handler.py +1 -1
  9. autobyteus/agent/input_processor/__init__.py +1 -7
  10. autobyteus/agent/message/context_file_type.py +6 -0
  11. autobyteus/agent/message/send_message_to.py +68 -99
  12. autobyteus/agent/phases/discover.py +2 -1
  13. autobyteus/agent/runtime/agent_worker.py +1 -0
  14. autobyteus/agent/tool_execution_result_processor/__init__.py +9 -0
  15. autobyteus/agent/tool_execution_result_processor/base_processor.py +46 -0
  16. autobyteus/agent/tool_execution_result_processor/processor_definition.py +36 -0
  17. autobyteus/agent/tool_execution_result_processor/processor_meta.py +36 -0
  18. autobyteus/agent/tool_execution_result_processor/processor_registry.py +70 -0
  19. autobyteus/agent/workspace/base_workspace.py +17 -2
  20. autobyteus/cli/__init__.py +1 -1
  21. autobyteus/cli/cli_display.py +1 -1
  22. autobyteus/cli/workflow_tui/__init__.py +4 -0
  23. autobyteus/cli/workflow_tui/app.py +210 -0
  24. autobyteus/cli/workflow_tui/state.py +189 -0
  25. autobyteus/cli/workflow_tui/widgets/__init__.py +6 -0
  26. autobyteus/cli/workflow_tui/widgets/agent_list_sidebar.py +149 -0
  27. autobyteus/cli/workflow_tui/widgets/focus_pane.py +335 -0
  28. autobyteus/cli/workflow_tui/widgets/logo.py +27 -0
  29. autobyteus/cli/workflow_tui/widgets/renderables.py +70 -0
  30. autobyteus/cli/workflow_tui/widgets/shared.py +51 -0
  31. autobyteus/cli/workflow_tui/widgets/status_bar.py +14 -0
  32. autobyteus/events/event_types.py +3 -0
  33. autobyteus/llm/api/lmstudio_llm.py +37 -0
  34. autobyteus/llm/api/openai_compatible_llm.py +20 -3
  35. autobyteus/llm/llm_factory.py +2 -0
  36. autobyteus/llm/lmstudio_provider.py +89 -0
  37. autobyteus/llm/providers.py +1 -0
  38. autobyteus/llm/token_counter/token_counter_factory.py +2 -0
  39. autobyteus/tools/__init__.py +2 -0
  40. autobyteus/tools/ask_user_input.py +2 -1
  41. autobyteus/tools/bash/bash_executor.py +2 -1
  42. autobyteus/tools/browser/session_aware/browser_session_aware_navigate_to.py +2 -0
  43. autobyteus/tools/browser/session_aware/browser_session_aware_web_element_trigger.py +3 -0
  44. autobyteus/tools/browser/session_aware/browser_session_aware_webpage_reader.py +3 -0
  45. autobyteus/tools/browser/session_aware/browser_session_aware_webpage_screenshot_taker.py +3 -0
  46. autobyteus/tools/browser/standalone/google_search_ui.py +2 -0
  47. autobyteus/tools/browser/standalone/navigate_to.py +2 -0
  48. autobyteus/tools/browser/standalone/web_page_pdf_generator.py +3 -0
  49. autobyteus/tools/browser/standalone/webpage_image_downloader.py +3 -0
  50. autobyteus/tools/browser/standalone/webpage_reader.py +2 -0
  51. autobyteus/tools/browser/standalone/webpage_screenshot_taker.py +3 -0
  52. autobyteus/tools/file/file_reader.py +36 -9
  53. autobyteus/tools/file/file_writer.py +37 -9
  54. autobyteus/tools/functional_tool.py +5 -4
  55. autobyteus/tools/image_downloader.py +2 -0
  56. autobyteus/tools/mcp/tool_registrar.py +3 -1
  57. autobyteus/tools/pdf_downloader.py +2 -1
  58. autobyteus/tools/registry/tool_definition.py +12 -8
  59. autobyteus/tools/registry/tool_registry.py +50 -2
  60. autobyteus/tools/timer.py +2 -0
  61. autobyteus/tools/tool_category.py +14 -4
  62. autobyteus/tools/tool_meta.py +6 -1
  63. autobyteus/tools/tool_origin.py +10 -0
  64. autobyteus/workflow/agentic_workflow.py +93 -0
  65. autobyteus/{agent/workflow → workflow}/base_agentic_workflow.py +19 -27
  66. autobyteus/workflow/bootstrap_steps/__init__.py +20 -0
  67. autobyteus/workflow/bootstrap_steps/agent_tool_injection_step.py +34 -0
  68. autobyteus/workflow/bootstrap_steps/base_workflow_bootstrap_step.py +23 -0
  69. autobyteus/workflow/bootstrap_steps/coordinator_initialization_step.py +41 -0
  70. autobyteus/workflow/bootstrap_steps/coordinator_prompt_preparation_step.py +108 -0
  71. autobyteus/workflow/bootstrap_steps/workflow_bootstrapper.py +50 -0
  72. autobyteus/workflow/bootstrap_steps/workflow_runtime_queue_initialization_step.py +25 -0
  73. autobyteus/workflow/context/__init__.py +17 -0
  74. autobyteus/workflow/context/team_manager.py +147 -0
  75. autobyteus/workflow/context/workflow_config.py +30 -0
  76. autobyteus/workflow/context/workflow_context.py +61 -0
  77. autobyteus/workflow/context/workflow_node_config.py +76 -0
  78. autobyteus/workflow/context/workflow_runtime_state.py +53 -0
  79. autobyteus/workflow/events/__init__.py +29 -0
  80. autobyteus/workflow/events/workflow_event_dispatcher.py +39 -0
  81. autobyteus/workflow/events/workflow_events.py +53 -0
  82. autobyteus/workflow/events/workflow_input_event_queue_manager.py +21 -0
  83. autobyteus/workflow/exceptions.py +8 -0
  84. autobyteus/workflow/factory/__init__.py +9 -0
  85. autobyteus/workflow/factory/workflow_factory.py +99 -0
  86. autobyteus/workflow/handlers/__init__.py +19 -0
  87. autobyteus/workflow/handlers/base_workflow_event_handler.py +16 -0
  88. autobyteus/workflow/handlers/inter_agent_message_request_event_handler.py +61 -0
  89. autobyteus/workflow/handlers/lifecycle_workflow_event_handler.py +27 -0
  90. autobyteus/workflow/handlers/process_user_message_event_handler.py +46 -0
  91. autobyteus/workflow/handlers/tool_approval_workflow_event_handler.py +39 -0
  92. autobyteus/workflow/handlers/workflow_event_handler_registry.py +23 -0
  93. autobyteus/workflow/phases/__init__.py +11 -0
  94. autobyteus/workflow/phases/workflow_operational_phase.py +19 -0
  95. autobyteus/workflow/phases/workflow_phase_manager.py +48 -0
  96. autobyteus/workflow/runtime/__init__.py +13 -0
  97. autobyteus/workflow/runtime/workflow_runtime.py +82 -0
  98. autobyteus/workflow/runtime/workflow_worker.py +117 -0
  99. autobyteus/workflow/shutdown_steps/__init__.py +17 -0
  100. autobyteus/workflow/shutdown_steps/agent_team_shutdown_step.py +42 -0
  101. autobyteus/workflow/shutdown_steps/base_workflow_shutdown_step.py +16 -0
  102. autobyteus/workflow/shutdown_steps/bridge_cleanup_step.py +28 -0
  103. autobyteus/workflow/shutdown_steps/sub_workflow_shutdown_step.py +41 -0
  104. autobyteus/workflow/shutdown_steps/workflow_shutdown_orchestrator.py +35 -0
  105. autobyteus/workflow/streaming/__init__.py +26 -0
  106. autobyteus/workflow/streaming/agent_event_bridge.py +48 -0
  107. autobyteus/workflow/streaming/agent_event_multiplexer.py +70 -0
  108. autobyteus/workflow/streaming/workflow_event_bridge.py +50 -0
  109. autobyteus/workflow/streaming/workflow_event_notifier.py +83 -0
  110. autobyteus/workflow/streaming/workflow_event_stream.py +33 -0
  111. autobyteus/workflow/streaming/workflow_stream_event_payloads.py +28 -0
  112. autobyteus/workflow/streaming/workflow_stream_events.py +45 -0
  113. autobyteus/workflow/utils/__init__.py +9 -0
  114. autobyteus/workflow/utils/wait_for_idle.py +46 -0
  115. autobyteus/workflow/workflow_builder.py +151 -0
  116. {autobyteus-1.1.3.dist-info → autobyteus-1.1.4.dist-info}/METADATA +16 -14
  117. {autobyteus-1.1.3.dist-info → autobyteus-1.1.4.dist-info}/RECORD +134 -65
  118. {autobyteus-1.1.3.dist-info → autobyteus-1.1.4.dist-info}/top_level.txt +1 -0
  119. examples/__init__.py +1 -0
  120. examples/discover_phase_transitions.py +104 -0
  121. examples/run_browser_agent.py +260 -0
  122. examples/run_google_slides_agent.py +286 -0
  123. examples/run_mcp_browser_client.py +174 -0
  124. examples/run_mcp_google_slides_client.py +270 -0
  125. examples/run_mcp_list_tools.py +189 -0
  126. examples/run_poem_writer.py +274 -0
  127. examples/run_sqlite_agent.py +293 -0
  128. examples/workflow/__init__.py +1 -0
  129. examples/workflow/run_basic_research_workflow.py +189 -0
  130. examples/workflow/run_code_review_workflow.py +269 -0
  131. examples/workflow/run_debate_workflow.py +212 -0
  132. examples/workflow/run_workflow_with_tui.py +153 -0
  133. autobyteus/agent/context/agent_phase_manager.py +0 -264
  134. autobyteus/agent/context/phases.py +0 -49
  135. autobyteus/agent/group/__init__.py +0 -0
  136. autobyteus/agent/group/agent_group.py +0 -164
  137. autobyteus/agent/group/agent_group_context.py +0 -81
  138. autobyteus/agent/input_processor/content_prefixing_input_processor.py +0 -41
  139. autobyteus/agent/input_processor/metadata_appending_input_processor.py +0 -34
  140. autobyteus/agent/input_processor/passthrough_input_processor.py +0 -33
  141. autobyteus/agent/workflow/__init__.py +0 -11
  142. autobyteus/agent/workflow/agentic_workflow.py +0 -89
  143. autobyteus/tools/mcp/registrar.py +0 -202
  144. autobyteus/workflow/simple_task.py +0 -98
  145. autobyteus/workflow/task.py +0 -147
  146. autobyteus/workflow/workflow.py +0 -49
  147. {autobyteus-1.1.3.dist-info → autobyteus-1.1.4.dist-info}/WHEEL +0 -0
  148. {autobyteus-1.1.3.dist-info → autobyteus-1.1.4.dist-info}/licenses/LICENSE +0 -0
@@ -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
@@ -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,9 @@
1
+ # file: autobyteus/autobyteus/workflow/factory/__init__.py
2
+ """
3
+ Workflow factory for creating AgenticWorkflow instances.
4
+ """
5
+ from autobyteus.workflow.factory.workflow_factory import WorkflowFactory
6
+
7
+ __all__ = [
8
+ "WorkflowFactory",
9
+ ]