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
autobyteus/agent/agent.py CHANGED
@@ -4,7 +4,7 @@ import logging
4
4
  from typing import AsyncIterator, Optional, List, Any, Dict, TYPE_CHECKING
5
5
 
6
6
  from autobyteus.agent.runtime.agent_runtime import AgentRuntime
7
- from autobyteus.agent.context.phases import AgentOperationalPhase
7
+ from autobyteus.agent.phases.phase_enum import AgentOperationalPhase
8
8
  from autobyteus.agent.message.agent_input_user_message import AgentInputUserMessage
9
9
  from autobyteus.agent.message.inter_agent_message import InterAgentMessage
10
10
  from autobyteus.agent.events import UserMessageReceivedEvent, InterAgentMessageReceivedEvent, ToolExecutionApprovalEvent, BaseEvent
@@ -34,8 +34,10 @@ class SystemPromptProcessingStep(BaseBootstrapStep):
34
34
  if not llm_instance:
35
35
  raise ValueError("LLM instance not found in agent state. It must be provided in AgentConfig.")
36
36
 
37
- current_system_prompt = context.config.system_prompt
38
- logger.debug(f"Agent '{agent_id}': Retrieved base system prompt from agent config.")
37
+ # If a specific system_prompt is not provided in AgentConfig, fall back
38
+ # to the default system_message from the LLM's own configuration.
39
+ current_system_prompt = context.config.system_prompt or llm_instance.config.system_message
40
+ logger.debug(f"Agent '{agent_id}': Retrieved base system prompt.")
39
41
 
40
42
  processor_instances = context.config.system_prompt_processors
41
43
  tool_instances_for_processor = context.tool_instances
@@ -1,5 +1,6 @@
1
1
  # file: autobyteus/autobyteus/agent/context/agent_config.py
2
2
  import logging
3
+ import copy
3
4
  from typing import List, Optional, Union, Tuple, TYPE_CHECKING, Dict, Any
4
5
 
5
6
  # Correctly import the new master processor and the base class
@@ -10,6 +11,7 @@ from autobyteus.agent.llm_response_processor import ProviderAwareToolUsageProces
10
11
  if TYPE_CHECKING:
11
12
  from autobyteus.tools.base_tool import BaseTool
12
13
  from autobyteus.agent.input_processor import BaseAgentUserInputMessageProcessor
14
+ from autobyteus.agent.tool_execution_result_processor import BaseToolExecutionResultProcessor
13
15
  from autobyteus.llm.base_llm import BaseLLM
14
16
  from autobyteus.agent.workspace.base_workspace import BaseAgentWorkspace
15
17
  from autobyteus.agent.hooks.base_phase_hook import BasePhaseHook
@@ -32,13 +34,14 @@ class AgentConfig:
32
34
  role: str,
33
35
  description: str,
34
36
  llm_instance: 'BaseLLM',
35
- system_prompt: str,
36
- tools: List['BaseTool'],
37
+ system_prompt: Optional[str] = None,
38
+ tools: Optional[List['BaseTool']] = None,
37
39
  auto_execute_tools: bool = True,
38
40
  use_xml_tool_format: bool = True,
39
41
  input_processors: Optional[List['BaseAgentUserInputMessageProcessor']] = None,
40
42
  llm_response_processors: Optional[List['BaseLLMResponseProcessor']] = None,
41
43
  system_prompt_processors: Optional[List['BaseSystemPromptProcessor']] = None,
44
+ tool_execution_result_processors: Optional[List['BaseToolExecutionResultProcessor']] = None,
42
45
  workspace: Optional['BaseAgentWorkspace'] = None,
43
46
  phase_hooks: Optional[List['BasePhaseHook']] = None,
44
47
  initial_custom_data: Optional[Dict[str, Any]] = None):
@@ -51,13 +54,15 @@ class AgentConfig:
51
54
  description: A description of the agent.
52
55
  llm_instance: A pre-initialized LLM instance (subclass of BaseLLM).
53
56
  The user is responsible for creating and configuring this instance.
54
- system_prompt: The base system prompt.
55
- tools: A list of pre-initialized tool instances (subclasses of BaseTool).
57
+ system_prompt: The base system prompt. If None, the system_message from the
58
+ llm_instance's config will be used as the base.
59
+ tools: An optional list of pre-initialized tool instances (subclasses of BaseTool).
56
60
  auto_execute_tools: If True, the agent will execute tools without approval.
57
61
  use_xml_tool_format: Whether to use XML for tool descriptions and examples.
58
62
  input_processors: A list of input processor instances.
59
63
  llm_response_processors: A list of LLM response processor instances.
60
64
  system_prompt_processors: A list of system prompt processor instances.
65
+ tool_execution_result_processors: A list of tool execution result processor instances.
61
66
  workspace: An optional pre-initialized workspace instance for the agent.
62
67
  phase_hooks: An optional list of phase transition hook instances.
63
68
  initial_custom_data: An optional dictionary of data to pre-populate
@@ -68,17 +73,43 @@ class AgentConfig:
68
73
  self.description = description
69
74
  self.llm_instance = llm_instance
70
75
  self.system_prompt = system_prompt
71
- self.tools = tools
76
+ self.tools = tools or []
72
77
  self.workspace = workspace
73
78
  self.auto_execute_tools = auto_execute_tools
74
79
  self.use_xml_tool_format = use_xml_tool_format
75
80
  self.input_processors = input_processors or []
76
81
  self.llm_response_processors = llm_response_processors if llm_response_processors is not None else list(self.DEFAULT_LLM_RESPONSE_PROCESSORS)
77
82
  self.system_prompt_processors = system_prompt_processors if system_prompt_processors is not None else list(self.DEFAULT_SYSTEM_PROMPT_PROCESSORS)
83
+ self.tool_execution_result_processors = tool_execution_result_processors or []
78
84
  self.phase_hooks = phase_hooks or []
79
85
  self.initial_custom_data = initial_custom_data
80
86
 
81
87
  logger.debug(f"AgentConfig created for name '{self.name}', role '{self.role}'.")
82
88
 
89
+ def copy(self) -> 'AgentConfig':
90
+ """
91
+ Creates a copy of this AgentConfig. It avoids deep-copying complex objects
92
+ like tools, workspaces, and processors that may contain un-pickleable state.
93
+ Instead, it creates shallow copies of the lists, allowing the lists themselves
94
+ to be modified independently while sharing the object instances within them.
95
+ """
96
+ return AgentConfig(
97
+ name=self.name,
98
+ role=self.role,
99
+ description=self.description,
100
+ llm_instance=self.llm_instance, # Keep reference, do not copy
101
+ system_prompt=self.system_prompt,
102
+ tools=self.tools.copy(), # Shallow copy the list, but reference the original tool instances
103
+ auto_execute_tools=self.auto_execute_tools,
104
+ use_xml_tool_format=self.use_xml_tool_format,
105
+ input_processors=self.input_processors.copy(), # Shallow copy the list
106
+ llm_response_processors=self.llm_response_processors.copy(), # Shallow copy the list
107
+ system_prompt_processors=self.system_prompt_processors.copy(), # Shallow copy the list
108
+ tool_execution_result_processors=self.tool_execution_result_processors.copy(), # Shallow copy the list
109
+ workspace=self.workspace, # Pass by reference, do not copy
110
+ phase_hooks=self.phase_hooks.copy(), # Shallow copy the list
111
+ initial_custom_data=copy.deepcopy(self.initial_custom_data) # Deep copy for simple data
112
+ )
113
+
83
114
  def __repr__(self) -> str:
84
115
  return (f"AgentConfig(name='{self.name}', role='{self.role}', llm_instance='{self.llm_instance.__class__.__name__}', workspace_configured={self.workspace is not None})")
@@ -21,8 +21,7 @@ from autobyteus.agent.events.agent_events import ( # Updated relative import pat
21
21
  if TYPE_CHECKING:
22
22
  from autobyteus.agent.context import AgentContext
23
23
  from autobyteus.agent.handlers import EventHandlerRegistry
24
- from autobyteus.agent.context.agent_phase_manager import AgentPhaseManager
25
-
24
+ from autobyteus.agent.phases import AgentPhaseManager
26
25
  logger = logging.getLogger(__name__)
27
26
 
28
27
  class WorkerEventDispatcher:
@@ -43,7 +43,7 @@ class InterAgentMessageReceivedEventHandler(AgentEventHandler):
43
43
  logger.info(
44
44
  f"Agent '{context.agent_id}' handling InterAgentMessageReceivedEvent from sender "
45
45
  f"'{inter_agent_msg.sender_agent_id}', type '{inter_agent_msg.message_type.value}'. "
46
- f"Content: '{inter_agent_msg.content[:100]}...'"
46
+ f"Content: '{inter_agent_msg.content}'"
47
47
  )
48
48
 
49
49
  content_for_llm = (
@@ -45,7 +45,7 @@ class LLMUserMessageReadyEventHandler(AgentEventHandler):
45
45
  raise RuntimeError(error_msg)
46
46
 
47
47
  llm_user_message: LLMUserMessage = event.llm_user_message
48
- logger.info(f"Agent '{agent_id}' handling LLMUserMessageReadyEvent: '{llm_user_message.content[:100]}...'")
48
+ logger.info(f"Agent '{agent_id}' handling LLMUserMessageReadyEvent: '{llm_user_message.content}'")
49
49
  logger.debug(f"Agent '{agent_id}' preparing to send full message to LLM:\n---\n{llm_user_message.content}\n---")
50
50
 
51
51
  context.state.add_message_to_history({"role": "user", "content": llm_user_message.content})
@@ -137,4 +137,4 @@ class LLMUserMessageReadyEventHandler(AgentEventHandler):
137
137
  complete_response=complete_response_obj
138
138
  )
139
139
  await context.input_event_queues.enqueue_internal_system_event(llm_complete_event)
140
- logger.info(f"Agent '{agent_id}' enqueued LLMCompleteResponseReceivedEvent from LLMUserMessageReadyEventHandler.")
140
+ logger.info(f"Agent '{agent_id}' enqueued LLMCompleteResponseReceivedEvent from LLMUserMessageReadyEventHandler.")
@@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, Optional
6
6
  from autobyteus.agent.handlers.base_event_handler import AgentEventHandler
7
7
  from autobyteus.agent.events import ToolResultEvent, LLMUserMessageReadyEvent
8
8
  from autobyteus.llm.user_message import LLMUserMessage
9
+ from autobyteus.agent.tool_execution_result_processor import BaseToolExecutionResultProcessor
9
10
 
10
11
  if TYPE_CHECKING:
11
12
  from autobyteus.agent.context import AgentContext
@@ -29,10 +30,37 @@ class ToolResultEventHandler(AgentEventHandler):
29
30
  logger.warning(f"ToolResultEventHandler received non-ToolResultEvent: {type(event)}. Skipping.")
30
31
  return
31
32
 
32
- agent_id = context.agent_id
33
- tool_invocation_id = event.tool_invocation_id if event.tool_invocation_id else 'N/A'
33
+ agent_id = context.agent_id
34
+ processed_event = event
34
35
 
35
- logger.info(f"Agent '{agent_id}' handling ToolResultEvent from tool: '{event.tool_name}' (Invocation ID: {tool_invocation_id}). Error: {event.error is not None}")
36
+ # --- New: Apply Tool Execution Result Processors ---
37
+ processor_instances = context.config.tool_execution_result_processors
38
+ if processor_instances:
39
+ processor_names = [p.get_name() for p in processor_instances]
40
+ logger.debug(f"Agent '{agent_id}': Applying tool execution result processors: {processor_names}")
41
+ for processor_instance in processor_instances:
42
+ processor_name_for_log = "unknown"
43
+ try:
44
+ if not isinstance(processor_instance, BaseToolExecutionResultProcessor):
45
+ logger.error(f"Agent '{agent_id}': Invalid tool result processor type: {type(processor_instance)}. Skipping.")
46
+ continue
47
+
48
+ processor_name_for_log = processor_instance.get_name()
49
+ logger.debug(f"Agent '{agent_id}': Applying tool result processor '{processor_name_for_log}'.")
50
+
51
+ event_before_proc = processed_event
52
+ processed_event = await processor_instance.process(event_before_proc, context)
53
+ logger.info(f"Agent '{agent_id}': Tool result processor '{processor_name_for_log}' applied successfully.")
54
+
55
+ except Exception as e:
56
+ logger.error(f"Agent '{agent_id}': Error applying tool result processor '{processor_name_for_log}': {e}. "
57
+ f"Skipping and continuing with result from before this processor.", exc_info=True)
58
+ processed_event = event_before_proc
59
+ # --- End New ---
60
+
61
+ tool_invocation_id = processed_event.tool_invocation_id if processed_event.tool_invocation_id else 'N/A'
62
+
63
+ logger.info(f"Agent '{agent_id}' handling processed ToolResultEvent from tool: '{processed_event.tool_name}' (Invocation ID: {tool_invocation_id}). Error: {processed_event.error is not None}")
36
64
 
37
65
  notifier: Optional['AgentExternalEventNotifier'] = None
38
66
  if context.phase_manager:
@@ -41,61 +69,61 @@ class ToolResultEventHandler(AgentEventHandler):
41
69
  if not notifier: # pragma: no cover
42
70
  logger.error(f"Agent '{agent_id}': Notifier not available in ToolResultEventHandler. Tool result processing logs will not be emitted.")
43
71
 
44
- if event.error:
45
- logger.debug(f"Agent '{agent_id}' tool '{event.tool_name}' (ID: {tool_invocation_id}) raw error details: {event.error}")
72
+ if processed_event.error:
73
+ logger.debug(f"Agent '{agent_id}' tool '{processed_event.tool_name}' (ID: {tool_invocation_id}) raw error details: {processed_event.error}")
46
74
  else:
47
75
  try:
48
- raw_result_str_for_debug_log = json.dumps(event.result, indent=2)
76
+ raw_result_str_for_debug_log = json.dumps(processed_event.result, indent=2)
49
77
  except TypeError: # pragma: no cover
50
- raw_result_str_for_debug_log = str(event.result)
51
- logger.debug(f"Agent '{agent_id}' tool '{event.tool_name}' (ID: {tool_invocation_id}) raw result:\n---\n{raw_result_str_for_debug_log}\n---")
78
+ raw_result_str_for_debug_log = str(processed_event.result)
79
+ logger.debug(f"Agent '{agent_id}' tool '{processed_event.tool_name}' (ID: {tool_invocation_id}) raw result:\n---\n{raw_result_str_for_debug_log}\n---")
52
80
 
53
81
 
54
82
  content_for_llm: str
55
- if event.error:
83
+ if processed_event.error:
56
84
  content_for_llm = (
57
- f"The tool '{event.tool_name}' (invocation ID: {tool_invocation_id}) encountered an error.\n"
58
- f"Error details: {event.error}\n"
85
+ f"The tool '{processed_event.tool_name}' (invocation ID: {tool_invocation_id}) encountered an error.\n"
86
+ f"Error details: {processed_event.error}\n"
59
87
  f"Please analyze this error and decide the next course of action."
60
88
  )
61
- log_msg_error_processed = f"[TOOL_RESULT_ERROR_PROCESSED] Agent_ID: {agent_id}, Tool: {event.tool_name}, Invocation_ID: {tool_invocation_id}, Error: {event.error}"
89
+ log_msg_error_processed = f"[TOOL_RESULT_ERROR_PROCESSED] Agent_ID: {agent_id}, Tool: {processed_event.tool_name}, Invocation_ID: {tool_invocation_id}, Error: {processed_event.error}"
62
90
  if notifier:
63
91
  try:
64
92
  log_data = {
65
93
  "log_entry": log_msg_error_processed,
66
94
  "tool_invocation_id": tool_invocation_id,
67
- "tool_name": event.tool_name,
95
+ "tool_name": processed_event.tool_name,
68
96
  }
69
97
  notifier.notify_agent_data_tool_log(log_data)
70
98
  except Exception as e_notify:
71
99
  logger.error(f"Agent '{agent_id}': Error notifying tool result error log: {e_notify}", exc_info=True)
72
100
  else:
73
101
  try:
74
- result_str_for_llm = json.dumps(event.result, indent=2) if not isinstance(event.result, str) else event.result
102
+ result_str_for_llm = json.dumps(processed_event.result, indent=2) if not isinstance(processed_event.result, str) else processed_event.result
75
103
  except TypeError: # pragma: no cover
76
- result_str_for_llm = str(event.result)
104
+ result_str_for_llm = str(processed_event.result)
77
105
 
78
106
  content_for_llm = (
79
- f"The tool '{event.tool_name}' (invocation ID: {tool_invocation_id}) has executed.\n"
107
+ f"The tool '{processed_event.tool_name}' (invocation ID: {tool_invocation_id}) has executed.\n"
80
108
  f"Result:\n{result_str_for_llm}\n"
81
109
  f"Based on this result, what is the next step or final answer?"
82
110
  )
83
- log_msg_success_processed = f"[TOOL_RESULT_SUCCESS_PROCESSED] Agent_ID: {agent_id}, Tool: {event.tool_name}, Invocation_ID: {tool_invocation_id}, Result (first 200 chars of stringified): {str(event.result)[:200]}"
111
+ log_msg_success_processed = f"[TOOL_RESULT_SUCCESS_PROCESSED] Agent_ID: {agent_id}, Tool: {processed_event.tool_name}, Invocation_ID: {tool_invocation_id}, Result: {str(processed_event.result)}"
84
112
  if notifier:
85
113
  try:
86
114
  log_data = {
87
115
  "log_entry": log_msg_success_processed,
88
116
  "tool_invocation_id": tool_invocation_id,
89
- "tool_name": event.tool_name,
117
+ "tool_name": processed_event.tool_name,
90
118
  }
91
119
  notifier.notify_agent_data_tool_log(log_data)
92
120
  except Exception as e_notify:
93
121
  logger.error(f"Agent '{agent_id}': Error notifying tool result success log: {e_notify}", exc_info=True)
94
122
 
95
- logger.debug(f"Agent '{agent_id}' preparing message for LLM based on tool '{event.tool_name}' (ID: {tool_invocation_id}) result:\n---\n{content_for_llm}\n---")
123
+ logger.debug(f"Agent '{agent_id}' preparing message for LLM based on tool '{processed_event.tool_name}' (ID: {tool_invocation_id}) result:\n---\n{content_for_llm}\n---")
96
124
  llm_user_message = LLMUserMessage(content=content_for_llm)
97
125
 
98
126
  next_event = LLMUserMessageReadyEvent(llm_user_message=llm_user_message)
99
127
  await context.input_event_queues.enqueue_internal_system_event(next_event)
100
128
 
101
- logger.info(f"Agent '{agent_id}' enqueued LLMUserMessageReadyEvent for LLM based on tool '{event.tool_name}' (ID: {tool_invocation_id}) result summary.")
129
+ logger.info(f"Agent '{agent_id}' enqueued LLMUserMessageReadyEvent for LLM based on tool '{processed_event.tool_name}' (ID: {tool_invocation_id}) result summary.")
@@ -35,7 +35,7 @@ class UserInputMessageEventHandler(AgentEventHandler):
35
35
  original_agent_input_user_msg: AgentInputUserMessage = event.agent_input_user_message
36
36
  processed_agent_input_user_msg: AgentInputUserMessage = original_agent_input_user_msg
37
37
 
38
- logger.info(f"Agent '{context.agent_id}' handling UserMessageReceivedEvent: '{original_agent_input_user_msg.content[:100]}...'")
38
+ logger.info(f"Agent '{context.agent_id}' handling UserMessageReceivedEvent: '{original_agent_input_user_msg.content}'")
39
39
 
40
40
  processor_instances = context.config.input_processors
41
41
  if processor_instances:
@@ -4,15 +4,9 @@ Components for pre-processing AgentUserMessage objects.
4
4
  """
5
5
  from .base_user_input_processor import BaseAgentUserInputMessageProcessor
6
6
 
7
- # Import concrete processors to make them easily accessible for instantiation
8
- from .passthrough_input_processor import PassthroughInputProcessor
9
- from .metadata_appending_input_processor import MetadataAppendingInputProcessor
10
- from .content_prefixing_input_processor import ContentPrefixingInputProcessor
7
+ # Concrete processors have been removed. Users can define their own.
11
8
 
12
9
 
13
10
  __all__ = [
14
11
  "BaseAgentUserInputMessageProcessor",
15
- "PassthroughInputProcessor",
16
- "MetadataAppendingInputProcessor",
17
- "ContentPrefixingInputProcessor",
18
12
  ]
@@ -17,6 +17,8 @@ class ContextFileType(str, Enum):
17
17
  HTML = "html" # .html, .htm
18
18
  PYTHON = "python" # .py
19
19
  JAVASCRIPT = "javascript" # .js
20
+ AUDIO = "audio" # .mp3, .wav, .m4a, .flac, .ogg
21
+ VIDEO = "video" # .mp4, .mov, .avi, .mkv, .webm
20
22
  IMAGE = "image" # .png, .jpg, .jpeg, .gif, .webp (when image is for contextual analysis, not direct LLM vision input)
21
23
  UNKNOWN = "unknown" # Fallback for unrecognized types
22
24
 
@@ -54,6 +56,10 @@ class ContextFileType(str, Enum):
54
56
  return cls.PYTHON
55
57
  elif extension == ".js":
56
58
  return cls.JAVASCRIPT
59
+ elif extension in [".mp3", ".wav", ".m4a", ".flac", ".ogg"]:
60
+ return cls.AUDIO
61
+ elif extension in [".mp4", ".mov", ".avi", ".mkv", ".webm"]:
62
+ return cls.VIDEO
57
63
  elif extension in [".png", ".jpg", ".jpeg", ".gif", ".webp"]:
58
64
  return cls.IMAGE
59
65
  else:
@@ -2,149 +2,118 @@
2
2
  import logging
3
3
  from typing import TYPE_CHECKING, Any, Optional
4
4
 
5
- from autobyteus.agent.message.inter_agent_message import InterAgentMessage
6
5
  from autobyteus.tools.base_tool import BaseTool
7
- from autobyteus.agent.group.agent_group_context import AgentGroupContext
8
- # Updated imports for schema
6
+ from autobyteus.tools.tool_category import ToolCategory
9
7
  from autobyteus.tools.parameter_schema import ParameterSchema, ParameterDefinition, ParameterType
10
-
8
+ from autobyteus.tools.tool_config import ToolConfig
9
+ # This import is for type hinting only and avoids circular dependencies at runtime
11
10
  if TYPE_CHECKING:
12
11
  from autobyteus.agent.context import AgentContext
13
- from autobyteus.agent.agent import Agent
12
+ from autobyteus.workflow.context.team_manager import TeamManager
13
+ from autobyteus.workflow.events.workflow_events import InterAgentMessageRequestEvent
14
14
 
15
15
  logger = logging.getLogger(__name__)
16
16
 
17
17
  class SendMessageTo(BaseTool):
18
18
  """
19
- A tool for sending messages to other agents within the same AgentGroup.
20
- It utilizes AgentGroupContext injected into the calling agent's AgentContext
21
- to resolve recipient agents.
19
+ A tool for sending messages to other agents within the same workflow team.
20
+ This tool requires a TeamManager to be injected at runtime by the
21
+ workflow framework to enable communication with the parent orchestrator.
22
22
  """
23
- TOOL_NAME = "SendMessageTo" # Class attribute for the name
23
+ TOOL_NAME = "SendMessageTo"
24
+ CATEGORY = ToolCategory.AGENT_COMMUNICATION
25
+
26
+ def __init__(self, config: Optional[ToolConfig] = None):
27
+ """
28
+ Initializes the SendMessageTo tool. The TeamManager is injected separately
29
+ after instantiation.
30
+ """
31
+ super().__init__(config=config)
32
+ self._team_manager: Optional['TeamManager'] = None
33
+ logger.debug("SendMessageTo tool initialized. TeamManager is not yet injected.")
24
34
 
25
- def __init__(self):
26
- super().__init__()
27
- logger.debug(f"{self.get_name()} tool initialized.") # Use get_name()
35
+ def set_team_manager(self, team_manager: 'TeamManager'):
36
+ """Sets the TeamManager instance after the tool has been created."""
37
+ self._team_manager = team_manager
38
+ logger.debug(f"TeamManager was set on SendMessageTo instance post-creation.")
28
39
 
29
40
  @classmethod
30
- def get_name(cls) -> str: # Implemented as per BaseTool requirement
41
+ def get_name(cls) -> str:
31
42
  return cls.TOOL_NAME
32
43
 
33
44
  @classmethod
34
45
  def get_description(cls) -> str:
35
- return ("Sends a message to another agent within the same group. "
36
- "Can target by role or specific agent ID.")
46
+ return ("Sends a message to another agent within the same team, starting them if necessary. "
47
+ "You must specify the recipient by their unique name as provided in your team manifest.")
37
48
 
38
49
  @classmethod
39
50
  def get_argument_schema(cls) -> Optional[ParameterSchema]:
40
51
  schema = ParameterSchema()
41
52
  schema.add_parameter(ParameterDefinition(
42
- name="recipient_role_name",
53
+ name="recipient_name",
43
54
  param_type=ParameterType.STRING,
44
- description="The general role name of the recipient agent (e.g., 'worker', 'reviewer').",
55
+ description='The unique name of the recipient agent (e.g., "Researcher", "Writer_1"). This MUST match a name from your team manifest.',
45
56
  required=True
46
57
  ))
47
58
  schema.add_parameter(ParameterDefinition(
48
59
  name="content",
49
60
  param_type=ParameterType.STRING,
50
- description="The actual message text.",
61
+ description="The actual message content or task instruction.",
51
62
  required=True
52
63
  ))
53
64
  schema.add_parameter(ParameterDefinition(
54
65
  name="message_type",
55
- param_type=ParameterType.STRING, # Or ENUM if InterAgentMessageType values are known and fixed for schema
66
+ param_type=ParameterType.STRING,
56
67
  description="Type of the message (e.g., TASK_ASSIGNMENT, CLARIFICATION). Custom types allowed.",
57
68
  required=True
58
69
  ))
59
- schema.add_parameter(ParameterDefinition(
60
- name="recipient_agent_id",
61
- param_type=ParameterType.STRING,
62
- description='Optional. Specific ID of the recipient agent. If "unknown" or omitted, resolves by role.',
63
- required=False,
64
- default_value=None # Explicitly no default, truly optional
65
- ))
66
70
  return schema
67
71
 
68
- # tool_usage_xml() and tool_usage_json() are inherited from BaseTool
69
- # get_config_schema() for instantiation defaults to None
70
-
71
72
  async def _execute(self,
72
73
  context: 'AgentContext',
73
- recipient_role_name: str,
74
+ recipient_name: str,
74
75
  content: str,
75
- message_type: str,
76
- recipient_agent_id: Optional[str] = None) -> str: # Named parameters
76
+ message_type: str) -> str:
77
77
  """
78
- Sends a message to another agent in the group.
79
- Arguments are validated by BaseTool.execute().
78
+ Creates and dispatches a InterAgentMessageRequestEvent to the parent workflow
79
+ using the injected team_manager.
80
80
  """
81
- sender_agent_id = context.agent_id
82
- logger.info(f"Tool '{self.get_name()}': Sender '{sender_agent_id}' attempting to send message. "
83
- f"Recipient Role: '{recipient_role_name}', Recipient ID: '{recipient_agent_id}', Type: '{message_type}'.")
81
+ # Local import to break circular dependency at module load time.
82
+ from autobyteus.workflow.events.workflow_events import InterAgentMessageRequestEvent
84
83
 
85
- group_context_any = context.custom_data.get('agent_group_context')
86
-
87
- if not isinstance(group_context_any, AgentGroupContext):
88
- error_msg = f"Tool '{self.get_name()}' critical error: AgentGroupContext not found or invalid in AgentContext.custom_data for agent '{sender_agent_id}'. Cannot send message."
89
- logger.error(error_msg)
84
+ if self._team_manager is None:
85
+ error_msg = "Critical error: SendMessageTo tool is not configured for workflow communication. It can only be used within a managed AgenticWorkflow."
86
+ logger.error(f"Agent '{context.agent_id}': {error_msg}")
90
87
  return f"Error: {error_msg}"
91
-
92
- group_context: AgentGroupContext = group_context_any # Type cast after check
93
88
 
94
- target_agent: Optional['Agent'] = None
95
-
96
- # Use recipient_agent_id if provided and not explicitly "unknown" (case-insensitive)
97
- if recipient_agent_id and recipient_agent_id.lower() != "unknown":
98
- target_agent = group_context.get_agent(recipient_agent_id)
99
- if not target_agent:
100
- logger.warning(f"Tool '{self.get_name()}': Agent with ID '{recipient_agent_id}' not found in group '{group_context.group_id}'. "
101
- f"Attempting to find by role '{recipient_role_name}'.")
102
-
103
- if not target_agent:
104
- agents_with_role = group_context.get_agents_by_role(recipient_role_name)
105
- if not agents_with_role:
106
- error_msg = f"No agent found with role '{recipient_role_name}' (and specific ID '{recipient_agent_id}' if provided was not found) in group '{group_context.group_id}'."
107
- logger.error(f"Tool '{self.get_name()}': {error_msg}")
108
- return f"Error: {error_msg}"
89
+ # --- Input Validation ---
90
+ if not isinstance(recipient_name, str) or not recipient_name.strip():
91
+ error_msg = "Error: `recipient_name` must be a non-empty string."
92
+ logger.error(f"Tool '{self.get_name()}' validation failed: {error_msg}")
93
+ return error_msg
94
+ if not isinstance(content, str) or not content.strip():
95
+ error_msg = "Error: `content` must be a non-empty string."
96
+ logger.error(f"Tool '{self.get_name()}' validation failed: {error_msg}")
97
+ return error_msg
98
+ if not isinstance(message_type, str) or not message_type.strip():
99
+ error_msg = "Error: `message_type` must be a non-empty string."
100
+ logger.error(f"Tool '{self.get_name()}' validation failed: {error_msg}")
101
+ return error_msg
109
102
 
110
- if len(agents_with_role) > 1:
111
- logger.warning(f"Tool '{self.get_name()}': Multiple agents ({len(agents_with_role)}) found for role '{recipient_role_name}'. "
112
- f"Sending to the first one: {agents_with_role[0].agent_id}. "
113
- "Consider using specific recipient_agent_id for clarity.")
114
- target_agent = agents_with_role[0]
115
- # Update recipient_agent_id to the one resolved by role if it was initially None or "unknown"
116
- recipient_agent_id = target_agent.agent_id
117
-
118
-
119
- if not target_agent:
120
- error_msg = f"Could not resolve recipient agent with role '{recipient_role_name}' or ID '{recipient_agent_id}'." # recipient_agent_id would be updated here
121
- logger.error(f"Tool '{self.get_name()}': {error_msg}")
122
- return f"Error: {error_msg}"
123
-
124
- try:
125
- message_to_send = InterAgentMessage.create_with_dynamic_message_type(
126
- recipient_role_name=target_agent.context.config.role,
127
- recipient_agent_id=target_agent.agent_id, # Use the definitively resolved agent ID
128
- content=content,
129
- message_type=message_type,
130
- sender_agent_id=sender_agent_id
131
- )
132
-
133
- await target_agent.post_inter_agent_message(message_to_send)
134
- success_msg = (f"Message successfully sent from '{sender_agent_id}' to agent "
135
- f"'{target_agent.agent_id}' (Role: '{target_agent.context.config.role}').")
136
- logger.info(f"Tool '{self.get_name()}': {success_msg}")
137
- return success_msg
138
- except ValueError as ve:
139
- error_msg = f"Error creating message: {str(ve)}"
140
- logger.error(f"Tool '{self.get_name()}': {error_msg}", exc_info=True)
141
- return f"Error: {error_msg}"
142
- except Exception as e:
143
- error_msg = f"An unexpected error occurred while sending message: {str(e)}"
144
- logger.error(f"Tool '{self.get_name()}': {error_msg}", exc_info=True)
145
- return f"Error: {error_msg}"
103
+ sender_agent_id = context.agent_id
104
+ logger.info(f"Tool '{self.get_name()}': Agent '{sender_agent_id}' requesting to send message to '{recipient_name}'.")
105
+
106
+ # Create the event for the workflow to handle
107
+ event = InterAgentMessageRequestEvent(
108
+ sender_agent_id=sender_agent_id,
109
+ recipient_name=recipient_name,
110
+ content=content,
111
+ message_type=message_type
112
+ )
113
+
114
+ # Dispatch the event "up" to the workflow's event loop via the team manager
115
+ await self._team_manager.dispatch_inter_agent_message_request(event)
146
116
 
147
- # tool_usage_xml was defined directly here, this is now inherited from BaseTool
148
- # BaseTool.tool_usage_xml() will use get_argument_schema() to generate it.
149
- # If a custom XML format was desired that differs from the auto-generated one,
150
- # then this method could be overridden. For now, assume auto-generated is fine.
117
+ success_msg = f"Message dispatch for recipient '{recipient_name}' has been successfully requested."
118
+ logger.info(f"Tool '{self.get_name()}': {success_msg}")
119
+ return success_msg
@@ -3,7 +3,8 @@ import inspect
3
3
  import logging
4
4
  from typing import List, Optional
5
5
 
6
- from autobyteus.agent.context.agent_phase_manager import AgentPhaseManager
6
+ from autobyteus.agent.phases.manager import AgentPhaseManager
7
+
7
8
  from .transition_info import PhaseTransitionInfo
8
9
 
9
10
  logger = logging.getLogger(__name__)
@@ -221,6 +221,7 @@ class AgentWorker:
221
221
  # Wait for the main thread future to complete.
222
222
  if self._thread_future:
223
223
  try:
224
+ # FIX: Use asyncio.wait_for() to handle the timeout correctly.
224
225
  await asyncio.wait_for(asyncio.wrap_future(self._thread_future), timeout=timeout)
225
226
  logger.info(f"AgentWorker '{agent_id}': Worker thread has terminated.")
226
227
  except asyncio.TimeoutError:
@@ -0,0 +1,9 @@
1
+ # file: autobyteus/autobyteus/agent/tool_execution_result_processor/__init__.py
2
+ """
3
+ Components for processing tool execution results before they are sent to the LLM.
4
+ """
5
+ from .base_processor import BaseToolExecutionResultProcessor
6
+
7
+ __all__ = [
8
+ "BaseToolExecutionResultProcessor",
9
+ ]
@@ -0,0 +1,46 @@
1
+ # file: autobyteus/autobyteus/agent/tool_execution_result_processor/base_processor.py
2
+ import logging
3
+ from abc import ABC, abstractmethod
4
+ from typing import TYPE_CHECKING
5
+
6
+ from .processor_meta import ToolExecutionResultProcessorMeta
7
+
8
+ if TYPE_CHECKING:
9
+ from autobyteus.agent.context import AgentContext
10
+ from autobyteus.agent.events import ToolResultEvent
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+ class BaseToolExecutionResultProcessor(ABC, metaclass=ToolExecutionResultProcessorMeta):
15
+ """
16
+ Abstract base class for processors that can modify a tool's execution result.
17
+ These processors are applied after a tool runs but before its result is formatted
18
+ for the LLM.
19
+ """
20
+
21
+ @classmethod
22
+ def get_name(cls) -> str:
23
+ """
24
+ Returns the unique registration name for this processor.
25
+ Defaults to the class name.
26
+ """
27
+ return cls.__name__
28
+
29
+ @abstractmethod
30
+ async def process(self,
31
+ event: 'ToolResultEvent',
32
+ context: 'AgentContext') -> 'ToolResultEvent':
33
+ """
34
+ Processes the given ToolResultEvent.
35
+
36
+ Args:
37
+ event: The ToolResultEvent containing the tool's output or error.
38
+ context: The agent's context, providing access to config and state.
39
+
40
+ Returns:
41
+ The processed (potentially modified) ToolResultEvent.
42
+ """
43
+ raise NotImplementedError("Subclasses must implement the 'process' method.")
44
+
45
+ def __repr__(self) -> str:
46
+ return f"<{self.__class__.__name__}>"