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
@@ -6,7 +6,7 @@ from typing import Dict, Any, List as TypingList, Type, TYPE_CHECKING, Optional,
6
6
  from autobyteus.llm.providers import LLMProvider
7
7
  from autobyteus.tools.tool_config import ToolConfig
8
8
  from autobyteus.tools.parameter_schema import ParameterSchema
9
- from autobyteus.tools.tool_category import ToolCategory
9
+ from autobyteus.tools.tool_origin import ToolOrigin
10
10
  from autobyteus.tools.usage.providers import (
11
11
  XmlSchemaProvider,
12
12
  JsonSchemaProvider,
@@ -28,7 +28,8 @@ class ToolDefinition:
28
28
  name: str,
29
29
  description: str,
30
30
  argument_schema: Optional['ParameterSchema'],
31
- category: ToolCategory,
31
+ origin: ToolOrigin,
32
+ category: str,
32
33
  config_schema: Optional['ParameterSchema'] = None,
33
34
  tool_class: Optional[Type['BaseTool']] = None,
34
35
  custom_factory: Optional[Callable[['ToolConfig'], 'BaseTool']] = None,
@@ -55,12 +56,12 @@ class ToolDefinition:
55
56
  raise TypeError(f"ToolDefinition '{name}' received an invalid 'argument_schema'. Expected ParameterSchema or None.")
56
57
  if config_schema is not None and not isinstance(config_schema, ParameterSchema):
57
58
  raise TypeError(f"ToolDefinition '{name}' received an invalid 'config_schema'. Expected ParameterSchema or None.")
58
- if not isinstance(category, ToolCategory):
59
- raise TypeError(f"ToolDefinition '{name}' requires a ToolCategory for 'category'. Got {type(category)}")
59
+ if not isinstance(origin, ToolOrigin):
60
+ raise TypeError(f"ToolDefinition '{name}' requires a ToolOrigin for 'origin'. Got {type(origin)}")
60
61
 
61
62
  # Validation for MCP-specific metadata
62
- if category == ToolCategory.MCP and not (metadata and metadata.get("mcp_server_id")):
63
- raise ValueError(f"ToolDefinition '{name}' with category MCP must provide a 'mcp_server_id' in its metadata.")
63
+ if origin == ToolOrigin.MCP and not (metadata and metadata.get("mcp_server_id")):
64
+ raise ValueError(f"ToolDefinition '{name}' with origin MCP must provide a 'mcp_server_id' in its metadata.")
64
65
 
65
66
  self._name = name
66
67
  self._description = description
@@ -68,6 +69,7 @@ class ToolDefinition:
68
69
  self._config_schema: Optional['ParameterSchema'] = config_schema
69
70
  self._tool_class = tool_class
70
71
  self._custom_factory = custom_factory
72
+ self._origin = origin
71
73
  self._category = category
72
74
  self._metadata = metadata or {}
73
75
 
@@ -87,7 +89,9 @@ class ToolDefinition:
87
89
  @property
88
90
  def config_schema(self) -> Optional['ParameterSchema']: return self._config_schema
89
91
  @property
90
- def category(self) -> ToolCategory: return self._category
92
+ def origin(self) -> ToolOrigin: return self._origin
93
+ @property
94
+ def category(self) -> str: return self._category
91
95
  @property
92
96
  def metadata(self) -> Dict[str, Any]: return self._metadata
93
97
 
@@ -145,4 +149,4 @@ class ToolDefinition:
145
149
  def __repr__(self) -> str:
146
150
  creator_repr = f"class='{self._tool_class.__name__}'" if self._tool_class else "factory=True"
147
151
  metadata_repr = f", metadata={self.metadata}" if self.metadata else ""
148
- return (f"ToolDefinition(name='{self.name}', category='{self.category.value}'{metadata_repr}, {creator_repr})")
152
+ return (f"ToolDefinition(name='{self.name}', origin='{self.origin.value}', category='{self.category}'{metadata_repr}, {creator_repr})")
@@ -1,11 +1,12 @@
1
1
  # file: autobyteus/tools/registry/tool_registry.py
2
2
  import logging
3
3
  from typing import Dict, List, Optional, Type, TYPE_CHECKING
4
+ from collections import defaultdict
4
5
 
5
6
  from autobyteus.tools.registry.tool_definition import ToolDefinition
6
7
  from autobyteus.utils.singleton import SingletonMeta
7
8
  from autobyteus.tools.tool_config import ToolConfig
8
- from autobyteus.tools.tool_category import ToolCategory
9
+ from autobyteus.tools.tool_origin import ToolOrigin
9
10
 
10
11
  if TYPE_CHECKING:
11
12
  from autobyteus.tools.base_tool import BaseTool
@@ -141,7 +142,54 @@ class ToolRegistry(metaclass=SingletonMeta):
141
142
 
142
143
  return [
143
144
  td for td in self._definitions.values()
144
- if td.category == ToolCategory.MCP and td.metadata.get("mcp_server_id") == server_id
145
+ if td.origin == ToolOrigin.MCP and td.metadata.get("mcp_server_id") == server_id
145
146
  ]
146
147
 
148
+ def get_tools_by_category(self, category: str) -> List[ToolDefinition]:
149
+ """
150
+ Returns a list of all registered tool definitions that match a specific category.
151
+
152
+ Args:
153
+ category: The category string to filter by.
154
+
155
+ Returns:
156
+ A list of matching ToolDefinition objects, sorted by name.
157
+ """
158
+ if not category:
159
+ return []
160
+
161
+ matching_tools = [
162
+ td for td in self._definitions.values() if td.category == category
163
+ ]
164
+ return sorted(matching_tools, key=lambda td: td.name)
165
+
166
+ def get_tools_grouped_by_category(self, origin: Optional[ToolOrigin] = None) -> Dict[str, List[ToolDefinition]]:
167
+ """
168
+ Returns all registered tool definitions, grouped into a dictionary by their category.
169
+ Can optionally filter by tool origin before grouping.
170
+
171
+ Args:
172
+ origin: If provided, only tools from this origin will be included.
173
+
174
+ Returns:
175
+ A dictionary where keys are category strings and values are lists
176
+ of ToolDefinition objects belonging to that category. Both the categories
177
+ and the tools within each category are sorted alphabetically.
178
+ """
179
+ grouped_tools = defaultdict(list)
180
+
181
+ tools_to_process = self._definitions.values()
182
+ if origin:
183
+ tools_to_process = [td for td in tools_to_process if td.origin == origin]
184
+
185
+ for td in tools_to_process:
186
+ grouped_tools[td.category].append(td)
187
+
188
+ # Sort tools within each category and sort the categories themselves for deterministic output
189
+ sorted_grouped_tools = {}
190
+ for category in sorted(grouped_tools.keys()):
191
+ sorted_grouped_tools[category] = sorted(grouped_tools[category], key=lambda td: td.name)
192
+
193
+ return sorted_grouped_tools
194
+
147
195
  default_tool_registry = ToolRegistry()
autobyteus/tools/timer.py CHANGED
@@ -3,6 +3,7 @@ from typing import Optional, TYPE_CHECKING, Any
3
3
  from autobyteus.tools.base_tool import BaseTool
4
4
  from autobyteus.tools.tool_config import ToolConfig
5
5
  from autobyteus.tools.parameter_schema import ParameterSchema, ParameterDefinition, ParameterType
6
+ from autobyteus.tools.tool_category import ToolCategory
6
7
  from autobyteus.events.event_emitter import EventEmitter
7
8
  from autobyteus.events.event_types import EventType
8
9
  import logging
@@ -17,6 +18,7 @@ class Timer(BaseTool, EventEmitter):
17
18
  A tool that provides timer functionality with configurable duration and event emission.
18
19
  The timer runs independently after being started and emits TIMER_UPDATE events.
19
20
  """
21
+ CATEGORY = ToolCategory.UTILITY
20
22
 
21
23
  def __init__(self, config: Optional[ToolConfig] = None):
22
24
  BaseTool.__init__(self, config=config)
@@ -2,10 +2,20 @@
2
2
  from enum import Enum
3
3
 
4
4
  class ToolCategory(str, Enum):
5
- """Enumeration of tool categories to identify their origin."""
6
- LOCAL = "local"
7
- MCP = "mcp"
8
- # BUILT_IN, USER_DEFINED etc. could be added later.
5
+ """
6
+ Provides standardized string constants for common tool categories.
7
+ While tools can use any string for a category, using these constants
8
+ is recommended to ensure consistency in UI grouping.
9
+ """
10
+ USER_INTERACTION = "User Interaction"
11
+ FILE_SYSTEM = "File System"
12
+ WEB = "Web"
13
+ SYSTEM = "System"
14
+ UTILITY = "Utility"
15
+ AGENT_COMMUNICATION = "Agent Communication"
16
+ PROMPT_MANAGEMENT = "Prompt Management"
17
+ GENERAL = "General"
18
+ MCP = "MCP"
9
19
 
10
20
  def __str__(self) -> str:
11
21
  return self.value
@@ -5,6 +5,7 @@ from typing import Dict, Any
5
5
 
6
6
  from autobyteus.tools.registry import default_tool_registry, ToolDefinition
7
7
  from autobyteus.tools.parameter_schema import ParameterSchema
8
+ from autobyteus.tools.tool_origin import ToolOrigin
8
9
  from autobyteus.tools.tool_category import ToolCategory
9
10
 
10
11
  logger = logging.getLogger(__name__)
@@ -53,6 +54,9 @@ class ToolMeta(ABCMeta):
53
54
  instantiation_config_schema = None
54
55
  except Exception as e:
55
56
  logger.warning(f"Tool class {name} ({tool_name}) has get_config_schema() but it failed: {e}. Assuming no instantiation config.")
57
+
58
+ # Get category from class attribute, defaulting to "General"
59
+ category_str = getattr(cls, 'CATEGORY', ToolCategory.GENERAL)
56
60
 
57
61
  # Create the definition without pre-generating usage strings
58
62
  definition = ToolDefinition(
@@ -62,7 +66,8 @@ class ToolMeta(ABCMeta):
62
66
  custom_factory=None,
63
67
  argument_schema=argument_schema,
64
68
  config_schema=instantiation_config_schema,
65
- category=ToolCategory.LOCAL
69
+ origin=ToolOrigin.LOCAL,
70
+ category=category_str
66
71
  )
67
72
  default_tool_registry.register_tool(definition)
68
73
 
@@ -0,0 +1,10 @@
1
+ # file: autobyteus/autobyteus/tools/tool_origin.py
2
+ from enum import Enum
3
+
4
+ class ToolOrigin(str, Enum):
5
+ """Enumeration of tool origins to identify their execution model."""
6
+ LOCAL = "local"
7
+ MCP = "mcp"
8
+
9
+ def __str__(self) -> str:
10
+ return self.value
@@ -0,0 +1,93 @@
1
+ # file: autobyteus/autobyteus/workflow/agentic_workflow.py
2
+ import logging
3
+ from typing import Optional
4
+
5
+ from autobyteus.workflow.runtime.workflow_runtime import WorkflowRuntime
6
+ from autobyteus.workflow.events.workflow_events import ProcessUserMessageEvent, ToolApprovalWorkflowEvent
7
+ from autobyteus.agent.message.agent_input_user_message import AgentInputUserMessage
8
+ from autobyteus.workflow.phases.workflow_operational_phase import WorkflowOperationalPhase
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ class AgenticWorkflow:
13
+ """
14
+ User-facing facade for interacting with a managed workflow.
15
+ This class is a lightweight wrapper around a WorkflowRuntime instance
16
+ and is typically created by a WorkflowFactory.
17
+ """
18
+ def __init__(self, runtime: WorkflowRuntime):
19
+ """
20
+ Initializes the AgenticWorkflow facade.
21
+
22
+ Args:
23
+ runtime: The pre-configured and ready-to-use runtime for the workflow.
24
+ """
25
+ if not isinstance(runtime, WorkflowRuntime):
26
+ raise TypeError(f"AgenticWorkflow requires a WorkflowRuntime instance, got {type(runtime).__name__}")
27
+
28
+ self._runtime = runtime
29
+ self.workflow_id: str = self._runtime.context.workflow_id
30
+ logger.info(f"AgenticWorkflow facade created for workflow ID '{self.workflow_id}'.")
31
+
32
+ @property
33
+ def name(self) -> str:
34
+ return self._runtime.context.config.name
35
+
36
+ @property
37
+ def role(self) -> Optional[str]:
38
+ """The role of the workflow, for when it's used as a sub-workflow."""
39
+ return self._runtime.context.config.role
40
+
41
+ async def post_message(self, message: AgentInputUserMessage, target_agent_name: Optional[str] = None) -> None:
42
+ """
43
+ Submits a message to the workflow, routing it to a specific node (agent or sub-workflow).
44
+ If `target_agent_name` is not provided, the message is sent to the workflow's coordinator.
45
+ """
46
+ final_target_name = target_agent_name or self._runtime.context.config.coordinator_node.name
47
+ logger.info(f"Workflow '{self.workflow_id}': post_message called. Target: '{final_target_name}'.")
48
+
49
+ if not self._runtime.is_running:
50
+ self.start()
51
+
52
+ event = ProcessUserMessageEvent(
53
+ user_message=message,
54
+ target_agent_name=final_target_name
55
+ )
56
+ await self._runtime.submit_event(event)
57
+
58
+ async def post_tool_execution_approval(
59
+ self,
60
+ agent_name: str,
61
+ tool_invocation_id: str,
62
+ is_approved: bool,
63
+ reason: Optional[str] = None
64
+ ):
65
+ """Submits a tool execution approval/denial to a specific agent in the workflow."""
66
+ logger.info(f"Workflow '{self.workflow_id}': post_tool_execution_approval called for agent '{agent_name}'. Approved: {is_approved}.")
67
+ if not self._runtime.is_running:
68
+ logger.warning(f"Workflow '{self.workflow_id}' is not running. Cannot post approval.")
69
+ return
70
+
71
+ event = ToolApprovalWorkflowEvent(
72
+ agent_name=agent_name,
73
+ tool_invocation_id=tool_invocation_id,
74
+ is_approved=is_approved,
75
+ reason=reason,
76
+ )
77
+ await self._runtime.submit_event(event)
78
+
79
+ def start(self) -> None:
80
+ """Starts the workflow's background worker thread."""
81
+ self._runtime.start()
82
+
83
+ async def stop(self, timeout: float = 10.0) -> None:
84
+ """Stops the workflow and all its agents."""
85
+ await self._runtime.stop(timeout)
86
+
87
+ @property
88
+ def is_running(self) -> bool:
89
+ """Checks if the workflow's worker is running."""
90
+ return self._runtime.is_running
91
+
92
+ def get_current_phase(self) -> WorkflowOperationalPhase:
93
+ return self._runtime.context.state.current_phase
@@ -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
+