autobyteus 1.1.4__py3-none-any.whl → 1.1.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. autobyteus/agent/context/__init__.py +4 -2
  2. autobyteus/agent/context/agent_config.py +0 -4
  3. autobyteus/agent/context/agent_context_registry.py +73 -0
  4. autobyteus/agent/events/notifiers.py +4 -0
  5. autobyteus/agent/handlers/inter_agent_message_event_handler.py +7 -2
  6. autobyteus/agent/handlers/llm_complete_response_received_event_handler.py +19 -19
  7. autobyteus/agent/handlers/user_input_message_event_handler.py +15 -0
  8. autobyteus/agent/message/send_message_to.py +29 -23
  9. autobyteus/agent/runtime/agent_runtime.py +10 -2
  10. autobyteus/agent/sender_type.py +15 -0
  11. autobyteus/agent/streaming/agent_event_stream.py +6 -0
  12. autobyteus/agent/streaming/stream_event_payloads.py +12 -0
  13. autobyteus/agent/streaming/stream_events.py +3 -0
  14. autobyteus/agent/system_prompt_processor/tool_manifest_injector_processor.py +7 -4
  15. autobyteus/agent_team/__init__.py +1 -0
  16. autobyteus/agent_team/agent_team.py +93 -0
  17. autobyteus/agent_team/agent_team_builder.py +184 -0
  18. autobyteus/agent_team/base_agent_team.py +86 -0
  19. autobyteus/agent_team/bootstrap_steps/__init__.py +24 -0
  20. autobyteus/agent_team/bootstrap_steps/agent_configuration_preparation_step.py +73 -0
  21. autobyteus/agent_team/bootstrap_steps/agent_team_bootstrapper.py +54 -0
  22. autobyteus/agent_team/bootstrap_steps/agent_team_runtime_queue_initialization_step.py +25 -0
  23. autobyteus/agent_team/bootstrap_steps/base_agent_team_bootstrap_step.py +23 -0
  24. autobyteus/agent_team/bootstrap_steps/coordinator_initialization_step.py +41 -0
  25. autobyteus/agent_team/bootstrap_steps/coordinator_prompt_preparation_step.py +85 -0
  26. autobyteus/agent_team/bootstrap_steps/task_notifier_initialization_step.py +51 -0
  27. autobyteus/agent_team/bootstrap_steps/team_context_initialization_step.py +45 -0
  28. autobyteus/agent_team/context/__init__.py +17 -0
  29. autobyteus/agent_team/context/agent_team_config.py +33 -0
  30. autobyteus/agent_team/context/agent_team_context.py +61 -0
  31. autobyteus/agent_team/context/agent_team_runtime_state.py +56 -0
  32. autobyteus/agent_team/context/team_manager.py +147 -0
  33. autobyteus/agent_team/context/team_node_config.py +76 -0
  34. autobyteus/agent_team/events/__init__.py +29 -0
  35. autobyteus/agent_team/events/agent_team_event_dispatcher.py +39 -0
  36. autobyteus/agent_team/events/agent_team_events.py +53 -0
  37. autobyteus/agent_team/events/agent_team_input_event_queue_manager.py +21 -0
  38. autobyteus/agent_team/exceptions.py +8 -0
  39. autobyteus/agent_team/factory/__init__.py +9 -0
  40. autobyteus/agent_team/factory/agent_team_factory.py +99 -0
  41. autobyteus/agent_team/handlers/__init__.py +19 -0
  42. autobyteus/agent_team/handlers/agent_team_event_handler_registry.py +23 -0
  43. autobyteus/agent_team/handlers/base_agent_team_event_handler.py +16 -0
  44. autobyteus/agent_team/handlers/inter_agent_message_request_event_handler.py +61 -0
  45. autobyteus/agent_team/handlers/lifecycle_agent_team_event_handler.py +27 -0
  46. autobyteus/agent_team/handlers/process_user_message_event_handler.py +46 -0
  47. autobyteus/agent_team/handlers/tool_approval_team_event_handler.py +48 -0
  48. autobyteus/agent_team/phases/__init__.py +11 -0
  49. autobyteus/agent_team/phases/agent_team_operational_phase.py +19 -0
  50. autobyteus/agent_team/phases/agent_team_phase_manager.py +48 -0
  51. autobyteus/agent_team/runtime/__init__.py +13 -0
  52. autobyteus/agent_team/runtime/agent_team_runtime.py +82 -0
  53. autobyteus/agent_team/runtime/agent_team_worker.py +117 -0
  54. autobyteus/agent_team/shutdown_steps/__init__.py +17 -0
  55. autobyteus/agent_team/shutdown_steps/agent_team_shutdown_orchestrator.py +35 -0
  56. autobyteus/agent_team/shutdown_steps/agent_team_shutdown_step.py +42 -0
  57. autobyteus/agent_team/shutdown_steps/base_agent_team_shutdown_step.py +16 -0
  58. autobyteus/agent_team/shutdown_steps/bridge_cleanup_step.py +28 -0
  59. autobyteus/agent_team/shutdown_steps/sub_team_shutdown_step.py +41 -0
  60. autobyteus/agent_team/streaming/__init__.py +26 -0
  61. autobyteus/agent_team/streaming/agent_event_bridge.py +48 -0
  62. autobyteus/agent_team/streaming/agent_event_multiplexer.py +70 -0
  63. autobyteus/agent_team/streaming/agent_team_event_notifier.py +64 -0
  64. autobyteus/agent_team/streaming/agent_team_event_stream.py +33 -0
  65. autobyteus/agent_team/streaming/agent_team_stream_event_payloads.py +32 -0
  66. autobyteus/agent_team/streaming/agent_team_stream_events.py +56 -0
  67. autobyteus/agent_team/streaming/team_event_bridge.py +50 -0
  68. autobyteus/agent_team/task_notification/__init__.py +11 -0
  69. autobyteus/agent_team/task_notification/system_event_driven_agent_task_notifier.py +164 -0
  70. autobyteus/agent_team/task_notification/task_notification_mode.py +24 -0
  71. autobyteus/agent_team/utils/__init__.py +9 -0
  72. autobyteus/agent_team/utils/wait_for_idle.py +46 -0
  73. autobyteus/cli/agent_team_tui/__init__.py +4 -0
  74. autobyteus/cli/agent_team_tui/app.py +210 -0
  75. autobyteus/cli/agent_team_tui/state.py +180 -0
  76. autobyteus/cli/agent_team_tui/widgets/__init__.py +6 -0
  77. autobyteus/cli/agent_team_tui/widgets/agent_list_sidebar.py +149 -0
  78. autobyteus/cli/agent_team_tui/widgets/focus_pane.py +320 -0
  79. autobyteus/cli/agent_team_tui/widgets/logo.py +20 -0
  80. autobyteus/cli/agent_team_tui/widgets/renderables.py +77 -0
  81. autobyteus/cli/agent_team_tui/widgets/shared.py +60 -0
  82. autobyteus/cli/agent_team_tui/widgets/status_bar.py +14 -0
  83. autobyteus/cli/agent_team_tui/widgets/task_board_panel.py +82 -0
  84. autobyteus/events/event_types.py +7 -2
  85. autobyteus/llm/api/autobyteus_llm.py +11 -12
  86. autobyteus/llm/api/lmstudio_llm.py +10 -13
  87. autobyteus/llm/api/ollama_llm.py +8 -13
  88. autobyteus/llm/autobyteus_provider.py +73 -46
  89. autobyteus/llm/llm_factory.py +102 -140
  90. autobyteus/llm/lmstudio_provider.py +63 -48
  91. autobyteus/llm/models.py +83 -53
  92. autobyteus/llm/ollama_provider.py +69 -61
  93. autobyteus/llm/ollama_provider_resolver.py +1 -0
  94. autobyteus/llm/providers.py +13 -13
  95. autobyteus/llm/runtimes.py +11 -0
  96. autobyteus/task_management/__init__.py +43 -0
  97. autobyteus/task_management/base_task_board.py +68 -0
  98. autobyteus/task_management/converters/__init__.py +11 -0
  99. autobyteus/task_management/converters/task_board_converter.py +64 -0
  100. autobyteus/task_management/converters/task_plan_converter.py +48 -0
  101. autobyteus/task_management/deliverable.py +16 -0
  102. autobyteus/task_management/deliverables/__init__.py +8 -0
  103. autobyteus/task_management/deliverables/file_deliverable.py +15 -0
  104. autobyteus/task_management/events.py +27 -0
  105. autobyteus/task_management/in_memory_task_board.py +126 -0
  106. autobyteus/task_management/schemas/__init__.py +15 -0
  107. autobyteus/task_management/schemas/deliverable_schema.py +13 -0
  108. autobyteus/task_management/schemas/plan_definition.py +35 -0
  109. autobyteus/task_management/schemas/task_status_report.py +27 -0
  110. autobyteus/task_management/task_plan.py +110 -0
  111. autobyteus/task_management/tools/__init__.py +14 -0
  112. autobyteus/task_management/tools/get_task_board_status.py +68 -0
  113. autobyteus/task_management/tools/publish_task_plan.py +113 -0
  114. autobyteus/task_management/tools/update_task_status.py +135 -0
  115. autobyteus/tools/bash/bash_executor.py +59 -14
  116. autobyteus/tools/mcp/config_service.py +63 -58
  117. autobyteus/tools/mcp/server/http_managed_mcp_server.py +14 -2
  118. autobyteus/tools/mcp/server/stdio_managed_mcp_server.py +14 -2
  119. autobyteus/tools/mcp/server_instance_manager.py +30 -4
  120. autobyteus/tools/mcp/tool_registrar.py +103 -50
  121. autobyteus/tools/parameter_schema.py +17 -11
  122. autobyteus/tools/registry/tool_definition.py +24 -29
  123. autobyteus/tools/tool_category.py +1 -0
  124. autobyteus/tools/usage/formatters/default_json_example_formatter.py +78 -3
  125. autobyteus/tools/usage/formatters/default_xml_example_formatter.py +23 -3
  126. autobyteus/tools/usage/formatters/gemini_json_example_formatter.py +6 -0
  127. autobyteus/tools/usage/formatters/google_json_example_formatter.py +7 -0
  128. autobyteus/tools/usage/formatters/openai_json_example_formatter.py +6 -4
  129. autobyteus/tools/usage/parsers/gemini_json_tool_usage_parser.py +23 -7
  130. autobyteus/tools/usage/parsers/provider_aware_tool_usage_parser.py +14 -25
  131. autobyteus/tools/usage/providers/__init__.py +2 -12
  132. autobyteus/tools/usage/providers/tool_manifest_provider.py +36 -29
  133. autobyteus/tools/usage/registries/__init__.py +7 -12
  134. autobyteus/tools/usage/registries/tool_formatter_pair.py +15 -0
  135. autobyteus/tools/usage/registries/tool_formatting_registry.py +58 -0
  136. autobyteus/tools/usage/registries/tool_usage_parser_registry.py +55 -0
  137. {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/METADATA +3 -3
  138. {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/RECORD +146 -72
  139. examples/agent_team/__init__.py +1 -0
  140. examples/run_browser_agent.py +17 -15
  141. examples/run_google_slides_agent.py +17 -16
  142. examples/run_poem_writer.py +22 -12
  143. examples/run_sqlite_agent.py +17 -15
  144. autobyteus/tools/mcp/call_handlers/__init__.py +0 -16
  145. autobyteus/tools/mcp/call_handlers/base_handler.py +0 -40
  146. autobyteus/tools/mcp/call_handlers/stdio_handler.py +0 -76
  147. autobyteus/tools/mcp/call_handlers/streamable_http_handler.py +0 -55
  148. autobyteus/tools/usage/providers/json_example_provider.py +0 -32
  149. autobyteus/tools/usage/providers/json_schema_provider.py +0 -35
  150. autobyteus/tools/usage/providers/json_tool_usage_parser_provider.py +0 -28
  151. autobyteus/tools/usage/providers/xml_example_provider.py +0 -28
  152. autobyteus/tools/usage/providers/xml_schema_provider.py +0 -29
  153. autobyteus/tools/usage/providers/xml_tool_usage_parser_provider.py +0 -26
  154. autobyteus/tools/usage/registries/json_example_formatter_registry.py +0 -51
  155. autobyteus/tools/usage/registries/json_schema_formatter_registry.py +0 -51
  156. autobyteus/tools/usage/registries/json_tool_usage_parser_registry.py +0 -42
  157. autobyteus/tools/usage/registries/xml_example_formatter_registry.py +0 -30
  158. autobyteus/tools/usage/registries/xml_schema_formatter_registry.py +0 -33
  159. autobyteus/tools/usage/registries/xml_tool_usage_parser_registry.py +0 -30
  160. examples/workflow/__init__.py +0 -1
  161. examples/workflow/run_basic_research_workflow.py +0 -189
  162. examples/workflow/run_code_review_workflow.py +0 -269
  163. examples/workflow/run_debate_workflow.py +0 -212
  164. examples/workflow/run_workflow_with_tui.py +0 -153
  165. {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/WHEEL +0 -0
  166. {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/licenses/LICENSE +0 -0
  167. {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,17 @@
1
+ # file: autobyteus/autobyteus/agent_team/shutdown_steps/__init__.py
2
+ """
3
+ Defines individual, self-contained steps for the agent team shutdown process.
4
+ """
5
+ from autobyteus.agent_team.shutdown_steps.base_agent_team_shutdown_step import BaseAgentTeamShutdownStep
6
+ from autobyteus.agent_team.shutdown_steps.agent_team_shutdown_step import AgentTeamShutdownStep
7
+ from autobyteus.agent_team.shutdown_steps.sub_team_shutdown_step import SubTeamShutdownStep
8
+ from autobyteus.agent_team.shutdown_steps.bridge_cleanup_step import BridgeCleanupStep
9
+ from autobyteus.agent_team.shutdown_steps.agent_team_shutdown_orchestrator import AgentTeamShutdownOrchestrator
10
+
11
+ __all__ = [
12
+ "BaseAgentTeamShutdownStep",
13
+ "AgentTeamShutdownStep",
14
+ "SubTeamShutdownStep",
15
+ "BridgeCleanupStep",
16
+ "AgentTeamShutdownOrchestrator",
17
+ ]
@@ -0,0 +1,35 @@
1
+ # file: autobyteus/autobyteus/agent_team/shutdown_steps/agent_team_shutdown_orchestrator.py
2
+ import logging
3
+ from typing import TYPE_CHECKING, List, Optional
4
+
5
+ from autobyteus.agent_team.shutdown_steps.base_agent_team_shutdown_step import BaseAgentTeamShutdownStep
6
+ from autobyteus.agent_team.shutdown_steps.bridge_cleanup_step import BridgeCleanupStep
7
+ from autobyteus.agent_team.shutdown_steps.sub_team_shutdown_step import SubTeamShutdownStep
8
+ from autobyteus.agent_team.shutdown_steps.agent_team_shutdown_step import AgentTeamShutdownStep
9
+
10
+ if TYPE_CHECKING:
11
+ from autobyteus.agent_team.context.agent_team_context import AgentTeamContext
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+ class AgentTeamShutdownOrchestrator:
16
+ """Orchestrates the agent team's shutdown process."""
17
+ def __init__(self, steps: Optional[List[BaseAgentTeamShutdownStep]] = None):
18
+ self.shutdown_steps = steps or [
19
+ BridgeCleanupStep(),
20
+ SubTeamShutdownStep(),
21
+ AgentTeamShutdownStep(),
22
+ ]
23
+
24
+ async def run(self, context: 'AgentTeamContext') -> bool:
25
+ team_id = context.team_id
26
+ logger.info(f"Team '{team_id}': Shutdown orchestrator starting.")
27
+
28
+ all_successful = True
29
+ for step in self.shutdown_steps:
30
+ if not await step.execute(context):
31
+ logger.error(f"Team '{team_id}': Shutdown step {step.__class__.__name__} failed.")
32
+ all_successful = False
33
+
34
+ logger.info(f"Team '{team_id}': Shutdown orchestration completed.")
35
+ return all_successful
@@ -0,0 +1,42 @@
1
+ # file: autobyteus/autobyteus/agent_team/shutdown_steps/agent_team_shutdown_step.py
2
+ import asyncio
3
+ import logging
4
+ from typing import TYPE_CHECKING
5
+
6
+ from autobyteus.agent_team.shutdown_steps.base_agent_team_shutdown_step import BaseAgentTeamShutdownStep
7
+
8
+ if TYPE_CHECKING:
9
+ from autobyteus.agent_team.context.agent_team_context import AgentTeamContext
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ class AgentTeamShutdownStep(BaseAgentTeamShutdownStep):
14
+ """Shutdown step to gracefully stop all running agents in the team."""
15
+ async def execute(self, context: 'AgentTeamContext') -> bool:
16
+ team_id = context.team_id
17
+ logger.info(f"Team '{team_id}': Executing AgentTeamShutdownStep.")
18
+
19
+ team_manager = context.team_manager
20
+ if not team_manager:
21
+ logger.warning(f"Team '{team_id}': No TeamManager found, cannot shut down agents.")
22
+ return True
23
+
24
+ # Get the list of all created agents from the single source of truth.
25
+ all_agents = team_manager.get_all_agents()
26
+ running_agents = [agent for agent in all_agents if agent.is_running]
27
+
28
+ if not running_agents:
29
+ logger.info(f"Team '{team_id}': No running agents to shut down.")
30
+ return True
31
+
32
+ logger.info(f"Team '{team_id}': Shutting down {len(running_agents)} running agents.")
33
+ stop_tasks = [agent.stop(timeout=10.0) for agent in running_agents]
34
+ results = await asyncio.gather(*stop_tasks, return_exceptions=True)
35
+
36
+ all_successful = True
37
+ for agent, result in zip(running_agents, results):
38
+ if isinstance(result, Exception):
39
+ logger.error(f"Team '{team_id}': Error stopping agent '{agent.agent_id}': {result}", exc_info=result)
40
+ all_successful = False
41
+
42
+ return all_successful
@@ -0,0 +1,16 @@
1
+ # file: autobyteus/autobyteus/agent_team/shutdown_steps/base_agent_team_shutdown_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.agent_team.context.agent_team_context import AgentTeamContext
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+ class BaseAgentTeamShutdownStep(ABC):
12
+ """Abstract base class for individual steps in the agent team shutdown process."""
13
+ @abstractmethod
14
+ async def execute(self, context: 'AgentTeamContext') -> bool:
15
+ """Executes the shutdown step."""
16
+ raise NotImplementedError("Subclasses must implement the 'execute' method.")
@@ -0,0 +1,28 @@
1
+ # file: autobyteus/autobyteus/agent_team/shutdown_steps/bridge_cleanup_step.py
2
+ import logging
3
+ from typing import TYPE_CHECKING
4
+
5
+ from autobyteus.agent_team.shutdown_steps.base_agent_team_shutdown_step import BaseAgentTeamShutdownStep
6
+
7
+ if TYPE_CHECKING:
8
+ from autobyteus.agent_team.context.agent_team_context import AgentTeamContext
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ class BridgeCleanupStep(BaseAgentTeamShutdownStep):
13
+ """Shutdown step to gracefully stop all AgentEventBridge instances via the multiplexer."""
14
+ async def execute(self, context: 'AgentTeamContext') -> bool:
15
+ team_id = context.team_id
16
+ logger.info(f"Team '{team_id}': Executing BridgeCleanupStep.")
17
+
18
+ multiplexer = context.multiplexer
19
+ if not multiplexer:
20
+ logger.warning(f"Team '{team_id}': No AgentEventMultiplexer found, cannot shut down event bridges.")
21
+ return True
22
+
23
+ try:
24
+ await multiplexer.shutdown()
25
+ return True
26
+ except Exception as e:
27
+ logger.error(f"Team '{team_id}': Error shutting down agent event bridges via multiplexer: {e}", exc_info=True)
28
+ return False
@@ -0,0 +1,41 @@
1
+ # file: autobyteus/autobyteus/agent_team/shutdown_steps/sub_team_shutdown_step.py
2
+ import asyncio
3
+ import logging
4
+ from typing import TYPE_CHECKING
5
+
6
+ from autobyteus.agent_team.shutdown_steps.base_agent_team_shutdown_step import BaseAgentTeamShutdownStep
7
+
8
+ if TYPE_CHECKING:
9
+ from autobyteus.agent_team.context.agent_team_context import AgentTeamContext
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ class SubTeamShutdownStep(BaseAgentTeamShutdownStep):
14
+ """Shutdown step to gracefully stop all running sub-teams."""
15
+ async def execute(self, context: 'AgentTeamContext') -> bool:
16
+ team_id = context.team_id
17
+ logger.info(f"Team '{team_id}': Executing SubTeamShutdownStep.")
18
+
19
+ team_manager = context.team_manager
20
+ if not team_manager:
21
+ logger.warning(f"Team '{team_id}': No TeamManager found, cannot shut down sub-teams.")
22
+ return True
23
+
24
+ all_sub_teams = team_manager.get_all_sub_teams()
25
+ running_sub_teams = [wf for wf in all_sub_teams if wf.is_running]
26
+
27
+ if not running_sub_teams:
28
+ logger.info(f"Team '{team_id}': No running sub-teams to shut down.")
29
+ return True
30
+
31
+ logger.info(f"Team '{team_id}': Shutting down {len(running_sub_teams)} running sub-teams.")
32
+ stop_tasks = [team.stop(timeout=20.0) for team in running_sub_teams]
33
+ results = await asyncio.gather(*stop_tasks, return_exceptions=True)
34
+
35
+ all_successful = True
36
+ for team, result in zip(running_sub_teams, results):
37
+ if isinstance(result, Exception):
38
+ logger.error(f"Team '{team_id}': Error stopping sub-team '{team.name}': {result}", exc_info=result)
39
+ all_successful = False
40
+
41
+ return all_successful
@@ -0,0 +1,26 @@
1
+ # file: autobyteus/autobyteus/agent_team/streaming/__init__.py
2
+ """
3
+ Components related to agent team output streaming.
4
+ """
5
+ from .agent_team_event_notifier import AgentTeamExternalEventNotifier
6
+ from .agent_team_event_stream import AgentTeamEventStream
7
+ from .agent_team_stream_events import AgentTeamStreamEvent, AgentTeamStreamDataPayload
8
+ from .agent_team_stream_event_payloads import (
9
+ BaseTeamSpecificPayload,
10
+ AgentTeamPhaseTransitionData,
11
+ AgentEventRebroadcastPayload,
12
+ )
13
+ from .agent_event_bridge import AgentEventBridge
14
+ from .agent_event_multiplexer import AgentEventMultiplexer
15
+
16
+ __all__ = [
17
+ "AgentTeamExternalEventNotifier",
18
+ "AgentTeamEventStream",
19
+ "AgentTeamStreamEvent",
20
+ "AgentTeamStreamDataPayload",
21
+ "BaseTeamSpecificPayload",
22
+ "AgentTeamPhaseTransitionData",
23
+ "AgentEventRebroadcastPayload",
24
+ "AgentEventBridge",
25
+ "AgentEventMultiplexer",
26
+ ]
@@ -0,0 +1,48 @@
1
+ # file: autobyteus/autobyteus/agent_team/streaming/agent_event_bridge.py
2
+ import asyncio
3
+ import logging
4
+ from typing import TYPE_CHECKING
5
+
6
+ from autobyteus.agent.streaming.agent_event_stream import AgentEventStream
7
+
8
+ if TYPE_CHECKING:
9
+ from autobyteus.agent.agent import Agent
10
+ from autobyteus.agent_team.streaming.agent_team_event_notifier import AgentTeamExternalEventNotifier
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+ class AgentEventBridge:
15
+ """
16
+ A dedicated component that bridges events from a single agent's event stream
17
+ to the main team notifier. This is the core of the multiplexing pattern.
18
+ """
19
+ def __init__(self, agent: 'Agent', agent_name: str, notifier: 'AgentTeamExternalEventNotifier', loop: asyncio.AbstractEventLoop):
20
+ self._agent_name = agent_name
21
+ self._notifier = notifier
22
+ self._stream = AgentEventStream(agent)
23
+ self._task: asyncio.Task = loop.create_task(self._run())
24
+ logger.info(f"AgentEventBridge created and task started for agent '{agent_name}'.")
25
+
26
+ async def _run(self):
27
+ """The background task that consumes from the stream and publishes to the notifier."""
28
+ try:
29
+ async for event in self._stream.all_events():
30
+ self._notifier.publish_agent_event(self._agent_name, event)
31
+ except asyncio.CancelledError:
32
+ logger.info(f"AgentEventBridge task for '{self._agent_name}' was cancelled.")
33
+ except Exception as e:
34
+ logger.error(f"Error in AgentEventBridge for '{self._agent_name}': {e}", exc_info=True)
35
+ finally:
36
+ logger.debug(f"AgentEventBridge task for '{self._agent_name}' is finishing.")
37
+
38
+ async def cancel(self):
39
+ """Gracefully stops the bridge."""
40
+ logger.info(f"Cancelling AgentEventBridge for '{self._agent_name}'.")
41
+ if not self._task.done():
42
+ self._task.cancel()
43
+ try:
44
+ await self._task
45
+ except asyncio.CancelledError:
46
+ pass # Expected
47
+ await self._stream.close()
48
+ logger.info(f"AgentEventBridge for '{self._agent_name}' cancelled successfully.")
@@ -0,0 +1,70 @@
1
+ # file: autobyteus/autobyteus/agent_team/streaming/agent_event_multiplexer.py
2
+ import asyncio
3
+ import logging
4
+ from typing import TYPE_CHECKING, Dict, Optional
5
+
6
+ from autobyteus.agent_team.streaming.agent_event_bridge import AgentEventBridge
7
+ from autobyteus.agent_team.streaming.team_event_bridge import TeamEventBridge
8
+
9
+ if TYPE_CHECKING:
10
+ from autobyteus.agent.agent import Agent
11
+ from autobyteus.agent_team.agent_team import AgentTeam
12
+ from autobyteus.agent_team.streaming.agent_team_event_notifier import AgentTeamExternalEventNotifier
13
+ from autobyteus.agent_team.runtime.agent_team_worker import AgentTeamWorker
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+ class AgentEventMultiplexer:
18
+ """
19
+ Manages the lifecycle of event bridges for all nodes (agents and sub-teams).
20
+ It creates, tracks, and shuts down the bridges that forward node events
21
+ to the agent team's main event stream.
22
+ """
23
+ def __init__(self, team_id: str, notifier: 'AgentTeamExternalEventNotifier', worker_ref: 'AgentTeamWorker'):
24
+ self._team_id = team_id
25
+ self._notifier = notifier
26
+ self._worker = worker_ref
27
+ self._loop: Optional[asyncio.AbstractEventLoop] = None
28
+ self._agent_bridges: Dict[str, AgentEventBridge] = {}
29
+ self._team_bridges: Dict[str, TeamEventBridge] = {}
30
+ logger.info(f"AgentEventMultiplexer initialized for team '{self._team_id}'.")
31
+
32
+ def _get_loop(self) -> asyncio.AbstractEventLoop:
33
+ """Retrieves the event loop from the worker on-demand."""
34
+ if self._loop is None or self._loop.is_closed():
35
+ self._loop = self._worker.get_worker_loop()
36
+ if self._loop is None:
37
+ raise RuntimeError(f"Agent team worker loop for team '{self._team_id}' is not available or not running.")
38
+ return self._loop
39
+
40
+ def start_bridging_agent_events(self, agent: 'Agent', agent_name: str):
41
+ """Creates and starts an AgentEventBridge for a direct agent node."""
42
+ if agent_name in self._agent_bridges:
43
+ logger.warning(f"Event bridge for agent '{agent_name}' already exists. Skipping creation.")
44
+ return
45
+
46
+ bridge = AgentEventBridge(agent=agent, agent_name=agent_name, notifier=self._notifier, loop=self._get_loop())
47
+ self._agent_bridges[agent_name] = bridge
48
+ logger.info(f"AgentEventMultiplexer started agent event bridge for '{agent_name}'.")
49
+
50
+ def start_bridging_team_events(self, sub_team: 'AgentTeam', node_name: str):
51
+ """Creates and starts a TeamEventBridge for a sub-team node."""
52
+ if node_name in self._team_bridges:
53
+ logger.warning(f"Event bridge for sub-team '{node_name}' already exists. Skipping creation.")
54
+ return
55
+
56
+ bridge = TeamEventBridge(sub_team=sub_team, sub_team_node_name=node_name, parent_notifier=self._notifier, loop=self._get_loop())
57
+ self._team_bridges[node_name] = bridge
58
+ logger.info(f"AgentEventMultiplexer started team event bridge for '{node_name}'.")
59
+
60
+ async def shutdown(self):
61
+ """Gracefully shuts down all active event bridges."""
62
+ logger.info(f"AgentEventMultiplexer for '{self._team_id}' shutting down all event bridges.")
63
+ agent_bridge_tasks = [b.cancel() for b in self._agent_bridges.values()]
64
+ team_bridge_tasks = [b.cancel() for b in self._team_bridges.values()]
65
+
66
+ await asyncio.gather(*(agent_bridge_tasks + team_bridge_tasks), return_exceptions=True)
67
+
68
+ self._agent_bridges.clear()
69
+ self._team_bridges.clear()
70
+ logger.info(f"All event bridges for team '{self._team_id}' have been shut down by multiplexer.")
@@ -0,0 +1,64 @@
1
+ # file: autobyteus/autobyteus/agent_team/streaming/agent_team_event_notifier.py
2
+ import logging
3
+ from typing import Optional, Dict, Any, TYPE_CHECKING
4
+
5
+ from autobyteus.events.event_emitter import EventEmitter
6
+ from autobyteus.events.event_types import EventType
7
+ from autobyteus.agent_team.phases.agent_team_operational_phase import AgentTeamOperationalPhase
8
+ from autobyteus.agent.streaming.stream_events import StreamEvent as AgentStreamEvent
9
+ from .agent_team_stream_events import AgentTeamStreamEvent, AgentEventRebroadcastPayload, AgentTeamPhaseTransitionData, SubTeamEventRebroadcastPayload
10
+ from autobyteus.task_management.events import BaseTaskBoardEvent
11
+
12
+ if TYPE_CHECKING:
13
+ from autobyteus.agent_team.runtime.agent_team_runtime import AgentTeamRuntime
14
+ from autobyteus.agent_team.streaming.agent_team_stream_events import AgentTeamStreamEvent as AgentTeamStreamEventTypeHint
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+ class AgentTeamExternalEventNotifier(EventEmitter):
19
+ """
20
+ Responsible for emitting unified AgentTeamStreamEvents for consumption by
21
+ external listeners (like a UI or the AgentTeamEventStream).
22
+ """
23
+ def __init__(self, team_id: str, runtime_ref: 'AgentTeamRuntime'):
24
+ super().__init__()
25
+ self.team_id = team_id
26
+ self.runtime_ref = runtime_ref
27
+ logger.debug(f"AgentTeamExternalEventNotifier initialized for team '{self.team_id}'.")
28
+
29
+ def _emit_event(self, event: 'AgentTeamStreamEventTypeHint'):
30
+ """
31
+ Emits a fully-formed AgentTeamStreamEvent.
32
+ """
33
+ self.emit(EventType.TEAM_STREAM_EVENT, payload=event)
34
+
35
+ def notify_phase_change(self, new_phase: AgentTeamOperationalPhase, old_phase: Optional[AgentTeamOperationalPhase], extra_data: Optional[Dict[str, Any]] = None):
36
+ """
37
+ Notifies of an agent team phase transition by creating and emitting a
38
+ 'TEAM' sourced event.
39
+ """
40
+ payload_dict = { "new_phase": new_phase, "old_phase": old_phase, "error_message": extra_data.get("error_message") if extra_data else None }
41
+ filtered_payload_dict = {k: v for k, v in payload_dict.items() if v is not None}
42
+ event = AgentTeamStreamEvent(team_id=self.team_id, event_source_type="TEAM", data=AgentTeamPhaseTransitionData(**filtered_payload_dict))
43
+ self._emit_event(event)
44
+
45
+ def publish_agent_event(self, agent_name: str, agent_event: AgentStreamEvent):
46
+ """
47
+ Wraps an event from a direct member agent and publishes it on the main team stream.
48
+ """
49
+ event = AgentTeamStreamEvent(team_id=self.team_id, event_source_type="AGENT", data=AgentEventRebroadcastPayload(agent_name=agent_name, agent_event=agent_event))
50
+ self._emit_event(event)
51
+
52
+ def publish_sub_team_event(self, sub_team_node_name: str, sub_team_event: 'AgentTeamStreamEventTypeHint'):
53
+ """
54
+ Wraps an event from a sub-team and publishes it on the parent team stream.
55
+ """
56
+ event = AgentTeamStreamEvent(team_id=self.team_id, event_source_type="SUB_TEAM", data=SubTeamEventRebroadcastPayload(sub_team_node_name=sub_team_node_name, sub_team_event=sub_team_event))
57
+ self._emit_event(event)
58
+
59
+ def handle_and_publish_task_board_event(self, payload: BaseTaskBoardEvent, **kwargs):
60
+ """
61
+ Listener for TaskBoard events. It wraps the event and publishes it on the main team stream.
62
+ """
63
+ event = AgentTeamStreamEvent(team_id=self.team_id, event_source_type="TASK_BOARD", data=payload)
64
+ self._emit_event(event)
@@ -0,0 +1,33 @@
1
+ # file: autobyteus/autobyteus/agent_team/streaming/agent_team_event_stream.py
2
+ import asyncio
3
+ import queue
4
+ from typing import AsyncIterator, TYPE_CHECKING
5
+
6
+ from autobyteus.events.event_types import EventType
7
+ from autobyteus.agent_team.streaming.agent_team_stream_events import AgentTeamStreamEvent
8
+ from autobyteus.agent.streaming.queue_streamer import stream_queue_items
9
+
10
+ if TYPE_CHECKING:
11
+ from autobyteus.agent_team.agent_team import AgentTeam
12
+
13
+ _SENTINEL = object()
14
+
15
+ class AgentTeamEventStream:
16
+ """Consumes events from an AgentTeamExternalEventNotifier for a specific team."""
17
+ def __init__(self, team: 'AgentTeam'):
18
+ self.team_id = team.team_id
19
+ self._internal_q = queue.Queue()
20
+ self._notifier = team._runtime.notifier
21
+ self._notifier.subscribe(EventType.TEAM_STREAM_EVENT, self._handle_event)
22
+
23
+ def _handle_event(self, payload: AgentTeamStreamEvent, **kwargs):
24
+ if isinstance(payload, AgentTeamStreamEvent) and payload.team_id == self.team_id:
25
+ self._internal_q.put(payload)
26
+
27
+ async def close(self):
28
+ self._notifier.unsubscribe(EventType.TEAM_STREAM_EVENT, self._handle_event)
29
+ await asyncio.get_running_loop().run_in_executor(None, self._internal_q.put, _SENTINEL)
30
+
31
+ def all_events(self) -> AsyncIterator[AgentTeamStreamEvent]:
32
+ """The primary method to consume all structured events from the agent team."""
33
+ return stream_queue_items(self._internal_q, _SENTINEL, f"team_{self.team_id}_stream")
@@ -0,0 +1,32 @@
1
+ # file: autobyteus/autobyteus/agent_team/streaming/agent_team_stream_event_payloads.py
2
+ from typing import Optional, Any
3
+ from pydantic import BaseModel, Field
4
+ from autobyteus.agent_team.phases.agent_team_operational_phase import AgentTeamOperationalPhase
5
+ from autobyteus.agent.streaming.stream_events import StreamEvent as AgentStreamEvent
6
+ from autobyteus.task_management.events import TaskPlanPublishedEvent, TaskStatusUpdatedEvent
7
+ # Need to use a forward reference string to avoid circular import at runtime
8
+ from typing import TYPE_CHECKING, Union
9
+ if TYPE_CHECKING:
10
+ from autobyteus.agent_team.streaming.agent_team_stream_events import AgentTeamStreamEvent
11
+
12
+ # --- Payloads for events originating from the "TEAM" source ---
13
+ class BaseTeamSpecificPayload(BaseModel):
14
+ pass
15
+
16
+ class AgentTeamPhaseTransitionData(BaseTeamSpecificPayload):
17
+ new_phase: AgentTeamOperationalPhase
18
+ old_phase: Optional[AgentTeamOperationalPhase] = None
19
+ error_message: Optional[str] = None
20
+
21
+ # --- Payload for events originating from the "AGENT" source ---
22
+ class AgentEventRebroadcastPayload(BaseModel):
23
+ agent_name: str # The friendly name, e.g., "Researcher_1"
24
+ agent_event: AgentStreamEvent # The original, unmodified event from the agent
25
+
26
+ # --- Payload for events originating from the "SUB_TEAM" source ---
27
+ class SubTeamEventRebroadcastPayload(BaseModel):
28
+ sub_team_node_name: str # The friendly name of the sub-team node
29
+ sub_team_event: "AgentTeamStreamEvent" = Field(..., description="The original, unmodified event from the sub-team's stream")
30
+
31
+ # --- Payload for events originating from the "TASK_BOARD" source ---
32
+ TaskBoardEventPayload = Union[TaskPlanPublishedEvent, TaskStatusUpdatedEvent]
@@ -0,0 +1,56 @@
1
+ # file: autobyteus/autobyteus/agent_team/streaming/agent_team_stream_events.py
2
+ import datetime
3
+ import uuid
4
+ from typing import Literal, Union
5
+ from pydantic import BaseModel, Field, model_validator
6
+
7
+ from .agent_team_stream_event_payloads import (
8
+ AgentTeamPhaseTransitionData, AgentEventRebroadcastPayload,
9
+ SubTeamEventRebroadcastPayload, TaskBoardEventPayload
10
+ )
11
+ from autobyteus.task_management.events import BaseTaskBoardEvent
12
+
13
+ # A union of all possible payloads for a "TEAM" sourced event.
14
+ TeamSpecificPayload = Union[AgentTeamPhaseTransitionData]
15
+
16
+ # The top-level discriminated union for the main event stream's payload.
17
+ AgentTeamStreamDataPayload = Union[TeamSpecificPayload, AgentEventRebroadcastPayload, SubTeamEventRebroadcastPayload, TaskBoardEventPayload]
18
+
19
+ class AgentTeamStreamEvent(BaseModel):
20
+ event_id: str = Field(default_factory=lambda: str(uuid.uuid4()))
21
+ timestamp: datetime.datetime = Field(default_factory=datetime.datetime.utcnow)
22
+ team_id: str
23
+ event_source_type: Literal["TEAM", "AGENT", "SUB_TEAM", "TASK_BOARD"]
24
+ data: AgentTeamStreamDataPayload
25
+
26
+ @model_validator(mode='after')
27
+ def check_data_matches_source_type(self) -> 'AgentTeamStreamEvent':
28
+ is_agent_event = self.event_source_type == "AGENT"
29
+ is_agent_payload = isinstance(self.data, AgentEventRebroadcastPayload)
30
+
31
+ is_sub_team_event = self.event_source_type == "SUB_TEAM"
32
+ is_sub_team_payload = isinstance(self.data, SubTeamEventRebroadcastPayload)
33
+
34
+ is_team_event = self.event_source_type == "TEAM"
35
+ is_team_payload = isinstance(self.data, AgentTeamPhaseTransitionData)
36
+
37
+ is_task_board_event = self.event_source_type == "TASK_BOARD"
38
+ is_task_board_payload = isinstance(self.data, BaseTaskBoardEvent)
39
+
40
+ if is_agent_event and not is_agent_payload:
41
+ raise ValueError("event_source_type is 'AGENT' but data is not an AgentEventRebroadcastPayload")
42
+
43
+ if is_sub_team_event and not is_sub_team_payload:
44
+ raise ValueError("event_source_type is 'SUB_TEAM' but data is not a SubTeamEventRebroadcastPayload")
45
+
46
+ if is_team_event and not is_team_payload:
47
+ raise ValueError("event_source_type is 'TEAM' but data is not a valid team-specific payload")
48
+
49
+ if is_task_board_event and not is_task_board_payload:
50
+ raise ValueError("event_source_type is 'TASK_BOARD' but data is not a TaskBoardEventPayload")
51
+
52
+ return self
53
+
54
+ # This is necessary for Pydantic v2 to correctly handle the recursive model
55
+ from .agent_team_stream_event_payloads import SubTeamEventRebroadcastPayload
56
+ SubTeamEventRebroadcastPayload.model_rebuild()
@@ -0,0 +1,50 @@
1
+ # file: autobyteus/autobyteus/agent_team/streaming/team_event_bridge.py
2
+ import asyncio
3
+ import logging
4
+ from typing import TYPE_CHECKING
5
+
6
+ from autobyteus.agent_team.streaming.agent_team_event_stream import AgentTeamEventStream
7
+
8
+ if TYPE_CHECKING:
9
+ from autobyteus.agent_team.agent_team import AgentTeam
10
+ from autobyteus.agent_team.streaming.agent_team_event_notifier import AgentTeamExternalEventNotifier
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+ class TeamEventBridge:
15
+ """
16
+ A dedicated component that bridges events from a sub-team's event stream
17
+ to the parent team's notifier.
18
+ """
19
+ def __init__(self, sub_team: 'AgentTeam', sub_team_node_name: str, parent_notifier: 'AgentTeamExternalEventNotifier', loop: asyncio.AbstractEventLoop):
20
+ self._sub_team = sub_team
21
+ self._sub_team_node_name = sub_team_node_name
22
+ self._parent_notifier = parent_notifier
23
+ self._stream = AgentTeamEventStream(sub_team)
24
+ self._task: asyncio.Task = loop.create_task(self._run())
25
+ logger.info(f"TeamEventBridge created and task started for sub-team '{sub_team_node_name}'.")
26
+
27
+ async def _run(self):
28
+ """The background task that consumes from the sub-team stream and re-publishes."""
29
+ try:
30
+ async for event in self._stream.all_events():
31
+ # Re-broadcast the event to the parent, adding the sub-team context.
32
+ self._parent_notifier.publish_sub_team_event(self._sub_team_node_name, event)
33
+ except asyncio.CancelledError:
34
+ logger.info(f"TeamEventBridge task for '{self._sub_team_node_name}' was cancelled.")
35
+ except Exception as e:
36
+ logger.error(f"Error in TeamEventBridge for '{self._sub_team_node_name}': {e}", exc_info=True)
37
+ finally:
38
+ logger.debug(f"TeamEventBridge task for '{self._sub_team_node_name}' is finishing.")
39
+
40
+ async def cancel(self):
41
+ """Gracefully stops the bridge."""
42
+ logger.info(f"Cancelling TeamEventBridge for '{self._sub_team_node_name}'.")
43
+ if not self._task.done():
44
+ self._task.cancel()
45
+ try:
46
+ await self._task
47
+ except asyncio.CancelledError:
48
+ pass # Expected
49
+ await self._stream.close()
50
+ logger.info(f"TeamEventBridge for '{self._sub_team_node_name}' cancelled successfully.")
@@ -0,0 +1,11 @@
1
+ # file: autobyteus/autobyteus/agent_team/task_notification/__init__.py
2
+ """
3
+ This package contains components for automatically notifying agents of runnable tasks.
4
+ """
5
+ from .system_event_driven_agent_task_notifier import SystemEventDrivenAgentTaskNotifier
6
+ from .task_notification_mode import TaskNotificationMode
7
+
8
+ __all__ = [
9
+ "SystemEventDrivenAgentTaskNotifier",
10
+ "TaskNotificationMode",
11
+ ]