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.
Files changed (171) hide show
  1. autobyteus/agent/agent.py +1 -1
  2. autobyteus/agent/bootstrap_steps/__init__.py +2 -0
  3. autobyteus/agent/bootstrap_steps/agent_bootstrapper.py +2 -0
  4. autobyteus/agent/bootstrap_steps/mcp_server_prewarming_step.py +71 -0
  5. autobyteus/agent/bootstrap_steps/system_prompt_processing_step.py +4 -2
  6. autobyteus/agent/context/agent_config.py +36 -5
  7. autobyteus/agent/events/worker_event_dispatcher.py +1 -2
  8. autobyteus/agent/handlers/inter_agent_message_event_handler.py +1 -1
  9. autobyteus/agent/handlers/llm_user_message_ready_event_handler.py +2 -2
  10. autobyteus/agent/handlers/tool_result_event_handler.py +48 -20
  11. autobyteus/agent/handlers/user_input_message_event_handler.py +1 -1
  12. autobyteus/agent/input_processor/__init__.py +1 -7
  13. autobyteus/agent/llm_response_processor/provider_aware_tool_usage_processor.py +41 -12
  14. autobyteus/agent/message/context_file_type.py +6 -0
  15. autobyteus/agent/message/send_message_to.py +68 -99
  16. autobyteus/agent/phases/discover.py +2 -1
  17. autobyteus/agent/runtime/agent_worker.py +25 -34
  18. autobyteus/agent/shutdown_steps/__init__.py +17 -0
  19. autobyteus/agent/shutdown_steps/agent_shutdown_orchestrator.py +63 -0
  20. autobyteus/agent/shutdown_steps/base_shutdown_step.py +33 -0
  21. autobyteus/agent/shutdown_steps/llm_instance_cleanup_step.py +45 -0
  22. autobyteus/agent/shutdown_steps/mcp_server_cleanup_step.py +32 -0
  23. autobyteus/agent/tool_execution_result_processor/__init__.py +9 -0
  24. autobyteus/agent/tool_execution_result_processor/base_processor.py +46 -0
  25. autobyteus/agent/tool_execution_result_processor/processor_definition.py +36 -0
  26. autobyteus/agent/tool_execution_result_processor/processor_meta.py +36 -0
  27. autobyteus/agent/tool_execution_result_processor/processor_registry.py +70 -0
  28. autobyteus/agent/workspace/base_workspace.py +17 -2
  29. autobyteus/cli/__init__.py +1 -1
  30. autobyteus/cli/cli_display.py +1 -1
  31. autobyteus/cli/workflow_tui/__init__.py +4 -0
  32. autobyteus/cli/workflow_tui/app.py +210 -0
  33. autobyteus/cli/workflow_tui/state.py +189 -0
  34. autobyteus/cli/workflow_tui/widgets/__init__.py +6 -0
  35. autobyteus/cli/workflow_tui/widgets/agent_list_sidebar.py +149 -0
  36. autobyteus/cli/workflow_tui/widgets/focus_pane.py +335 -0
  37. autobyteus/cli/workflow_tui/widgets/logo.py +27 -0
  38. autobyteus/cli/workflow_tui/widgets/renderables.py +70 -0
  39. autobyteus/cli/workflow_tui/widgets/shared.py +51 -0
  40. autobyteus/cli/workflow_tui/widgets/status_bar.py +14 -0
  41. autobyteus/events/event_types.py +3 -0
  42. autobyteus/llm/api/lmstudio_llm.py +37 -0
  43. autobyteus/llm/api/openai_compatible_llm.py +20 -3
  44. autobyteus/llm/llm_factory.py +2 -0
  45. autobyteus/llm/lmstudio_provider.py +89 -0
  46. autobyteus/llm/providers.py +1 -0
  47. autobyteus/llm/token_counter/token_counter_factory.py +2 -0
  48. autobyteus/tools/__init__.py +2 -0
  49. autobyteus/tools/ask_user_input.py +2 -1
  50. autobyteus/tools/base_tool.py +2 -0
  51. autobyteus/tools/bash/bash_executor.py +2 -1
  52. autobyteus/tools/browser/session_aware/browser_session_aware_navigate_to.py +2 -0
  53. autobyteus/tools/browser/session_aware/browser_session_aware_web_element_trigger.py +3 -0
  54. autobyteus/tools/browser/session_aware/browser_session_aware_webpage_reader.py +3 -0
  55. autobyteus/tools/browser/session_aware/browser_session_aware_webpage_screenshot_taker.py +3 -0
  56. autobyteus/tools/browser/standalone/google_search_ui.py +2 -0
  57. autobyteus/tools/browser/standalone/navigate_to.py +2 -0
  58. autobyteus/tools/browser/standalone/web_page_pdf_generator.py +3 -0
  59. autobyteus/tools/browser/standalone/webpage_image_downloader.py +3 -0
  60. autobyteus/tools/browser/standalone/webpage_reader.py +2 -0
  61. autobyteus/tools/browser/standalone/webpage_screenshot_taker.py +3 -0
  62. autobyteus/tools/file/file_reader.py +36 -9
  63. autobyteus/tools/file/file_writer.py +37 -9
  64. autobyteus/tools/functional_tool.py +5 -4
  65. autobyteus/tools/image_downloader.py +2 -0
  66. autobyteus/tools/mcp/__init__.py +10 -7
  67. autobyteus/tools/mcp/call_handlers/__init__.py +0 -2
  68. autobyteus/tools/mcp/config_service.py +1 -6
  69. autobyteus/tools/mcp/factory.py +12 -26
  70. autobyteus/tools/mcp/server/__init__.py +16 -0
  71. autobyteus/tools/mcp/server/base_managed_mcp_server.py +139 -0
  72. autobyteus/tools/mcp/server/http_managed_mcp_server.py +29 -0
  73. autobyteus/tools/mcp/server/proxy.py +36 -0
  74. autobyteus/tools/mcp/server/stdio_managed_mcp_server.py +33 -0
  75. autobyteus/tools/mcp/server_instance_manager.py +93 -0
  76. autobyteus/tools/mcp/tool.py +28 -46
  77. autobyteus/tools/mcp/tool_registrar.py +179 -0
  78. autobyteus/tools/mcp/types.py +10 -21
  79. autobyteus/tools/pdf_downloader.py +2 -1
  80. autobyteus/tools/registry/tool_definition.py +20 -7
  81. autobyteus/tools/registry/tool_registry.py +75 -28
  82. autobyteus/tools/timer.py +2 -0
  83. autobyteus/tools/tool_category.py +14 -4
  84. autobyteus/tools/tool_meta.py +6 -1
  85. autobyteus/tools/tool_origin.py +10 -0
  86. autobyteus/workflow/agentic_workflow.py +93 -0
  87. autobyteus/{agent/workflow → workflow}/base_agentic_workflow.py +19 -27
  88. autobyteus/workflow/bootstrap_steps/__init__.py +20 -0
  89. autobyteus/workflow/bootstrap_steps/agent_tool_injection_step.py +34 -0
  90. autobyteus/workflow/bootstrap_steps/base_workflow_bootstrap_step.py +23 -0
  91. autobyteus/workflow/bootstrap_steps/coordinator_initialization_step.py +41 -0
  92. autobyteus/workflow/bootstrap_steps/coordinator_prompt_preparation_step.py +108 -0
  93. autobyteus/workflow/bootstrap_steps/workflow_bootstrapper.py +50 -0
  94. autobyteus/workflow/bootstrap_steps/workflow_runtime_queue_initialization_step.py +25 -0
  95. autobyteus/workflow/context/__init__.py +17 -0
  96. autobyteus/workflow/context/team_manager.py +147 -0
  97. autobyteus/workflow/context/workflow_config.py +30 -0
  98. autobyteus/workflow/context/workflow_context.py +61 -0
  99. autobyteus/workflow/context/workflow_node_config.py +76 -0
  100. autobyteus/workflow/context/workflow_runtime_state.py +53 -0
  101. autobyteus/workflow/events/__init__.py +29 -0
  102. autobyteus/workflow/events/workflow_event_dispatcher.py +39 -0
  103. autobyteus/workflow/events/workflow_events.py +53 -0
  104. autobyteus/workflow/events/workflow_input_event_queue_manager.py +21 -0
  105. autobyteus/workflow/exceptions.py +8 -0
  106. autobyteus/workflow/factory/__init__.py +9 -0
  107. autobyteus/workflow/factory/workflow_factory.py +99 -0
  108. autobyteus/workflow/handlers/__init__.py +19 -0
  109. autobyteus/workflow/handlers/base_workflow_event_handler.py +16 -0
  110. autobyteus/workflow/handlers/inter_agent_message_request_event_handler.py +61 -0
  111. autobyteus/workflow/handlers/lifecycle_workflow_event_handler.py +27 -0
  112. autobyteus/workflow/handlers/process_user_message_event_handler.py +46 -0
  113. autobyteus/workflow/handlers/tool_approval_workflow_event_handler.py +39 -0
  114. autobyteus/workflow/handlers/workflow_event_handler_registry.py +23 -0
  115. autobyteus/workflow/phases/__init__.py +11 -0
  116. autobyteus/workflow/phases/workflow_operational_phase.py +19 -0
  117. autobyteus/workflow/phases/workflow_phase_manager.py +48 -0
  118. autobyteus/workflow/runtime/__init__.py +13 -0
  119. autobyteus/workflow/runtime/workflow_runtime.py +82 -0
  120. autobyteus/workflow/runtime/workflow_worker.py +117 -0
  121. autobyteus/workflow/shutdown_steps/__init__.py +17 -0
  122. autobyteus/workflow/shutdown_steps/agent_team_shutdown_step.py +42 -0
  123. autobyteus/workflow/shutdown_steps/base_workflow_shutdown_step.py +16 -0
  124. autobyteus/workflow/shutdown_steps/bridge_cleanup_step.py +28 -0
  125. autobyteus/workflow/shutdown_steps/sub_workflow_shutdown_step.py +41 -0
  126. autobyteus/workflow/shutdown_steps/workflow_shutdown_orchestrator.py +35 -0
  127. autobyteus/workflow/streaming/__init__.py +26 -0
  128. autobyteus/workflow/streaming/agent_event_bridge.py +48 -0
  129. autobyteus/workflow/streaming/agent_event_multiplexer.py +70 -0
  130. autobyteus/workflow/streaming/workflow_event_bridge.py +50 -0
  131. autobyteus/workflow/streaming/workflow_event_notifier.py +83 -0
  132. autobyteus/workflow/streaming/workflow_event_stream.py +33 -0
  133. autobyteus/workflow/streaming/workflow_stream_event_payloads.py +28 -0
  134. autobyteus/workflow/streaming/workflow_stream_events.py +45 -0
  135. autobyteus/workflow/utils/__init__.py +9 -0
  136. autobyteus/workflow/utils/wait_for_idle.py +46 -0
  137. autobyteus/workflow/workflow_builder.py +151 -0
  138. {autobyteus-1.1.2.dist-info → autobyteus-1.1.4.dist-info}/METADATA +16 -13
  139. {autobyteus-1.1.2.dist-info → autobyteus-1.1.4.dist-info}/RECORD +156 -75
  140. {autobyteus-1.1.2.dist-info → autobyteus-1.1.4.dist-info}/top_level.txt +1 -0
  141. examples/__init__.py +1 -0
  142. examples/discover_phase_transitions.py +104 -0
  143. examples/run_browser_agent.py +260 -0
  144. examples/run_google_slides_agent.py +286 -0
  145. examples/run_mcp_browser_client.py +174 -0
  146. examples/run_mcp_google_slides_client.py +270 -0
  147. examples/run_mcp_list_tools.py +189 -0
  148. examples/run_poem_writer.py +274 -0
  149. examples/run_sqlite_agent.py +293 -0
  150. examples/workflow/__init__.py +1 -0
  151. examples/workflow/run_basic_research_workflow.py +189 -0
  152. examples/workflow/run_code_review_workflow.py +269 -0
  153. examples/workflow/run_debate_workflow.py +212 -0
  154. examples/workflow/run_workflow_with_tui.py +153 -0
  155. autobyteus/agent/context/agent_phase_manager.py +0 -264
  156. autobyteus/agent/context/phases.py +0 -49
  157. autobyteus/agent/group/__init__.py +0 -0
  158. autobyteus/agent/group/agent_group.py +0 -164
  159. autobyteus/agent/group/agent_group_context.py +0 -81
  160. autobyteus/agent/input_processor/content_prefixing_input_processor.py +0 -41
  161. autobyteus/agent/input_processor/metadata_appending_input_processor.py +0 -34
  162. autobyteus/agent/input_processor/passthrough_input_processor.py +0 -33
  163. autobyteus/agent/workflow/__init__.py +0 -11
  164. autobyteus/agent/workflow/agentic_workflow.py +0 -89
  165. autobyteus/tools/mcp/call_handlers/sse_handler.py +0 -22
  166. autobyteus/tools/mcp/registrar.py +0 -323
  167. autobyteus/workflow/simple_task.py +0 -98
  168. autobyteus/workflow/task.py +0 -147
  169. autobyteus/workflow/workflow.py +0 -49
  170. {autobyteus-1.1.2.dist-info → autobyteus-1.1.4.dist-info}/WHEEL +0 -0
  171. {autobyteus-1.1.2.dist-info → autobyteus-1.1.4.dist-info}/licenses/LICENSE +0 -0
@@ -1,31 +1,33 @@
1
- # file: autobyteus/autobyteus/agent/workflow/base_agentic_workflow.py
1
+ # file: autobyteus/autobyteus/workflow/base_agentic_workflow.py
2
2
  import logging
3
3
  from abc import ABC, abstractmethod
4
- from typing import Optional, Any, Union
4
+ from typing import Optional, Any, TYPE_CHECKING
5
5
 
6
- from autobyteus.agent.workflow.agentic_workflow import AgenticWorkflow
7
- from autobyteus.agent.group.agent_group import AgentGroup
6
+ # These are forward declarations that might be used by subclasses.
7
+ # The actual circular dependency is avoided by type-hinting strings.
8
+ if TYPE_CHECKING:
9
+ from autobyteus.workflow.agentic_workflow import AgenticWorkflow
10
+ from autobyteus.agent.group import AgentGroup
8
11
 
9
12
  logger = logging.getLogger(__name__)
10
13
 
11
14
  class BaseAgenticWorkflow(ABC):
12
15
  """
13
- FR28: Optional abstract base class for creating domain-specific, type-safe
16
+ Optional abstract base class for creating domain-specific, type-safe
14
17
  APIs for agentic workflows.
15
18
 
16
- FR29: Users can subclass BaseAgenticWorkflow if they wish to create a more
17
- structured and specific interface for their multi-agent tasks, rather than
18
- using the generic `AgenticWorkflow.process(**kwargs)` method directly.
19
+ Users can subclass BaseAgenticWorkflow to create a more structured and
20
+ specific interface for their multi-agent tasks, rather than using the
21
+ generic `AgenticWorkflow.process(**kwargs)` method directly.
19
22
 
20
- FR30: Subclasses would typically encapsulate an `AgenticWorkflow` instance
21
- or an `AgentGroup` instance directly. They then define methods that map
22
- domain-specific inputs to the underlying workflow's execution, providing
23
- type safety and a clearer API for that particular workflow.
23
+ Subclasses would typically encapsulate an `AgenticWorkflow` or an `AgentGroup`
24
+ instance and define methods that map domain-specific inputs to the
25
+ underlying workflow's execution.
24
26
  """
25
27
 
26
28
  def __init__(self,
27
29
  name: str,
28
- wrapped_workflow_instance: Optional[Union[AgenticWorkflow, AgentGroup]] = None):
30
+ wrapped_workflow_instance: Optional[Any] = None):
29
31
  """
30
32
  Initializes the BaseAgenticWorkflow.
31
33
 
@@ -37,7 +39,7 @@ class BaseAgenticWorkflow(ABC):
37
39
  workflow/group instance.
38
40
  """
39
41
  self.name: str = name
40
- self._wrapped_workflow: Optional[Union[AgenticWorkflow, AgentGroup]] = wrapped_workflow_instance
42
+ self._wrapped_workflow: Optional[Any] = wrapped_workflow_instance
41
43
 
42
44
  if self._wrapped_workflow:
43
45
  logger.info(f"BaseAgenticWorkflow '{self.name}' initialized, wrapping an instance of "
@@ -47,7 +49,7 @@ class BaseAgenticWorkflow(ABC):
47
49
  "Subclass should handle workflow/group setup.")
48
50
 
49
51
  @property
50
- def wrapped_workflow(self) -> Optional[Union[AgenticWorkflow, AgentGroup]]:
52
+ def wrapped_workflow(self) -> Optional[Any]:
51
53
  """Provides access to the wrapped AgenticWorkflow or AgentGroup instance."""
52
54
  return self._wrapped_workflow
53
55
 
@@ -76,23 +78,13 @@ class BaseAgenticWorkflow(ABC):
76
78
  """
77
79
  pass
78
80
 
79
- # Subclasses will define their own domain-specific process methods, for example:
80
- # @abstractmethod
81
- # async def generate_report(self, topic: str, length_words: int) -> Report:
82
- # pass
83
- #
84
- # @abstractmethod
85
- # async def translate_document(self, document_path: str, target_language: str) -> TranslatedDocument:
86
- # pass
87
-
88
81
  def __repr__(self) -> str:
89
82
  running_status = "N/A (not implemented by subclass)"
90
83
  try:
91
84
  running_status = str(self.is_running)
92
- except NotImplementedError: # pragma: no cover
93
- pass # Keep default N/A
85
+ except NotImplementedError:
86
+ pass
94
87
 
95
88
  return (f"<{self.__class__.__name__} name='{self.name}', "
96
89
  f"wraps='{self._wrapped_workflow.__class__.__name__ if self._wrapped_workflow else 'NoneInternal'}', "
97
90
  f"is_running={running_status}>")
98
-
@@ -0,0 +1,20 @@
1
+ # file: autobyteus/autobyteus/workflow/bootstrap_steps/__init__.py
2
+ """
3
+ Defines individual, self-contained steps for the workflow bootstrapping process.
4
+ """
5
+
6
+ from autobyteus.workflow.bootstrap_steps.base_workflow_bootstrap_step import BaseWorkflowBootstrapStep
7
+ from autobyteus.workflow.bootstrap_steps.workflow_runtime_queue_initialization_step import WorkflowRuntimeQueueInitializationStep
8
+ from autobyteus.workflow.bootstrap_steps.coordinator_prompt_preparation_step import CoordinatorPromptPreparationStep
9
+ from autobyteus.workflow.bootstrap_steps.agent_tool_injection_step import AgentToolInjectionStep
10
+ from autobyteus.workflow.bootstrap_steps.coordinator_initialization_step import CoordinatorInitializationStep
11
+ from autobyteus.workflow.bootstrap_steps.workflow_bootstrapper import WorkflowBootstrapper
12
+
13
+ __all__ = [
14
+ "BaseWorkflowBootstrapStep",
15
+ "WorkflowRuntimeQueueInitializationStep",
16
+ "CoordinatorPromptPreparationStep",
17
+ "AgentToolInjectionStep",
18
+ "CoordinatorInitializationStep",
19
+ "WorkflowBootstrapper",
20
+ ]
@@ -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