autobyteus 1.1.2__py3-none-any.whl → 1.1.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- autobyteus/agent/agent.py +1 -1
- autobyteus/agent/bootstrap_steps/__init__.py +2 -0
- autobyteus/agent/bootstrap_steps/agent_bootstrapper.py +2 -0
- autobyteus/agent/bootstrap_steps/mcp_server_prewarming_step.py +71 -0
- autobyteus/agent/bootstrap_steps/system_prompt_processing_step.py +4 -2
- autobyteus/agent/context/agent_config.py +36 -5
- autobyteus/agent/events/worker_event_dispatcher.py +1 -2
- autobyteus/agent/handlers/inter_agent_message_event_handler.py +1 -1
- autobyteus/agent/handlers/llm_user_message_ready_event_handler.py +2 -2
- autobyteus/agent/handlers/tool_result_event_handler.py +48 -20
- autobyteus/agent/handlers/user_input_message_event_handler.py +1 -1
- autobyteus/agent/input_processor/__init__.py +1 -7
- autobyteus/agent/llm_response_processor/provider_aware_tool_usage_processor.py +41 -12
- autobyteus/agent/message/context_file_type.py +6 -0
- autobyteus/agent/message/send_message_to.py +68 -99
- autobyteus/agent/phases/discover.py +2 -1
- autobyteus/agent/runtime/agent_worker.py +25 -34
- autobyteus/agent/shutdown_steps/__init__.py +17 -0
- autobyteus/agent/shutdown_steps/agent_shutdown_orchestrator.py +63 -0
- autobyteus/agent/shutdown_steps/base_shutdown_step.py +33 -0
- autobyteus/agent/shutdown_steps/llm_instance_cleanup_step.py +45 -0
- autobyteus/agent/shutdown_steps/mcp_server_cleanup_step.py +32 -0
- autobyteus/agent/tool_execution_result_processor/__init__.py +9 -0
- autobyteus/agent/tool_execution_result_processor/base_processor.py +46 -0
- autobyteus/agent/tool_execution_result_processor/processor_definition.py +36 -0
- autobyteus/agent/tool_execution_result_processor/processor_meta.py +36 -0
- autobyteus/agent/tool_execution_result_processor/processor_registry.py +70 -0
- autobyteus/agent/workspace/base_workspace.py +17 -2
- autobyteus/cli/__init__.py +1 -1
- autobyteus/cli/cli_display.py +1 -1
- autobyteus/cli/workflow_tui/__init__.py +4 -0
- autobyteus/cli/workflow_tui/app.py +210 -0
- autobyteus/cli/workflow_tui/state.py +189 -0
- autobyteus/cli/workflow_tui/widgets/__init__.py +6 -0
- autobyteus/cli/workflow_tui/widgets/agent_list_sidebar.py +149 -0
- autobyteus/cli/workflow_tui/widgets/focus_pane.py +335 -0
- autobyteus/cli/workflow_tui/widgets/logo.py +27 -0
- autobyteus/cli/workflow_tui/widgets/renderables.py +70 -0
- autobyteus/cli/workflow_tui/widgets/shared.py +51 -0
- autobyteus/cli/workflow_tui/widgets/status_bar.py +14 -0
- autobyteus/events/event_types.py +3 -0
- autobyteus/llm/api/lmstudio_llm.py +37 -0
- autobyteus/llm/api/openai_compatible_llm.py +20 -3
- autobyteus/llm/llm_factory.py +2 -0
- autobyteus/llm/lmstudio_provider.py +89 -0
- autobyteus/llm/providers.py +1 -0
- autobyteus/llm/token_counter/token_counter_factory.py +2 -0
- autobyteus/tools/__init__.py +2 -0
- autobyteus/tools/ask_user_input.py +2 -1
- autobyteus/tools/base_tool.py +2 -0
- autobyteus/tools/bash/bash_executor.py +2 -1
- autobyteus/tools/browser/session_aware/browser_session_aware_navigate_to.py +2 -0
- autobyteus/tools/browser/session_aware/browser_session_aware_web_element_trigger.py +3 -0
- autobyteus/tools/browser/session_aware/browser_session_aware_webpage_reader.py +3 -0
- autobyteus/tools/browser/session_aware/browser_session_aware_webpage_screenshot_taker.py +3 -0
- autobyteus/tools/browser/standalone/google_search_ui.py +2 -0
- autobyteus/tools/browser/standalone/navigate_to.py +2 -0
- autobyteus/tools/browser/standalone/web_page_pdf_generator.py +3 -0
- autobyteus/tools/browser/standalone/webpage_image_downloader.py +3 -0
- autobyteus/tools/browser/standalone/webpage_reader.py +2 -0
- autobyteus/tools/browser/standalone/webpage_screenshot_taker.py +3 -0
- autobyteus/tools/file/file_reader.py +36 -9
- autobyteus/tools/file/file_writer.py +37 -9
- autobyteus/tools/functional_tool.py +5 -4
- autobyteus/tools/image_downloader.py +2 -0
- autobyteus/tools/mcp/__init__.py +10 -7
- autobyteus/tools/mcp/call_handlers/__init__.py +0 -2
- autobyteus/tools/mcp/config_service.py +1 -6
- autobyteus/tools/mcp/factory.py +12 -26
- autobyteus/tools/mcp/server/__init__.py +16 -0
- autobyteus/tools/mcp/server/base_managed_mcp_server.py +139 -0
- autobyteus/tools/mcp/server/http_managed_mcp_server.py +29 -0
- autobyteus/tools/mcp/server/proxy.py +36 -0
- autobyteus/tools/mcp/server/stdio_managed_mcp_server.py +33 -0
- autobyteus/tools/mcp/server_instance_manager.py +93 -0
- autobyteus/tools/mcp/tool.py +28 -46
- autobyteus/tools/mcp/tool_registrar.py +179 -0
- autobyteus/tools/mcp/types.py +10 -21
- autobyteus/tools/pdf_downloader.py +2 -1
- autobyteus/tools/registry/tool_definition.py +20 -7
- autobyteus/tools/registry/tool_registry.py +75 -28
- autobyteus/tools/timer.py +2 -0
- autobyteus/tools/tool_category.py +14 -4
- autobyteus/tools/tool_meta.py +6 -1
- autobyteus/tools/tool_origin.py +10 -0
- autobyteus/workflow/agentic_workflow.py +93 -0
- autobyteus/{agent/workflow → workflow}/base_agentic_workflow.py +19 -27
- autobyteus/workflow/bootstrap_steps/__init__.py +20 -0
- autobyteus/workflow/bootstrap_steps/agent_tool_injection_step.py +34 -0
- autobyteus/workflow/bootstrap_steps/base_workflow_bootstrap_step.py +23 -0
- autobyteus/workflow/bootstrap_steps/coordinator_initialization_step.py +41 -0
- autobyteus/workflow/bootstrap_steps/coordinator_prompt_preparation_step.py +108 -0
- autobyteus/workflow/bootstrap_steps/workflow_bootstrapper.py +50 -0
- autobyteus/workflow/bootstrap_steps/workflow_runtime_queue_initialization_step.py +25 -0
- autobyteus/workflow/context/__init__.py +17 -0
- autobyteus/workflow/context/team_manager.py +147 -0
- autobyteus/workflow/context/workflow_config.py +30 -0
- autobyteus/workflow/context/workflow_context.py +61 -0
- autobyteus/workflow/context/workflow_node_config.py +76 -0
- autobyteus/workflow/context/workflow_runtime_state.py +53 -0
- autobyteus/workflow/events/__init__.py +29 -0
- autobyteus/workflow/events/workflow_event_dispatcher.py +39 -0
- autobyteus/workflow/events/workflow_events.py +53 -0
- autobyteus/workflow/events/workflow_input_event_queue_manager.py +21 -0
- autobyteus/workflow/exceptions.py +8 -0
- autobyteus/workflow/factory/__init__.py +9 -0
- autobyteus/workflow/factory/workflow_factory.py +99 -0
- autobyteus/workflow/handlers/__init__.py +19 -0
- autobyteus/workflow/handlers/base_workflow_event_handler.py +16 -0
- autobyteus/workflow/handlers/inter_agent_message_request_event_handler.py +61 -0
- autobyteus/workflow/handlers/lifecycle_workflow_event_handler.py +27 -0
- autobyteus/workflow/handlers/process_user_message_event_handler.py +46 -0
- autobyteus/workflow/handlers/tool_approval_workflow_event_handler.py +39 -0
- autobyteus/workflow/handlers/workflow_event_handler_registry.py +23 -0
- autobyteus/workflow/phases/__init__.py +11 -0
- autobyteus/workflow/phases/workflow_operational_phase.py +19 -0
- autobyteus/workflow/phases/workflow_phase_manager.py +48 -0
- autobyteus/workflow/runtime/__init__.py +13 -0
- autobyteus/workflow/runtime/workflow_runtime.py +82 -0
- autobyteus/workflow/runtime/workflow_worker.py +117 -0
- autobyteus/workflow/shutdown_steps/__init__.py +17 -0
- autobyteus/workflow/shutdown_steps/agent_team_shutdown_step.py +42 -0
- autobyteus/workflow/shutdown_steps/base_workflow_shutdown_step.py +16 -0
- autobyteus/workflow/shutdown_steps/bridge_cleanup_step.py +28 -0
- autobyteus/workflow/shutdown_steps/sub_workflow_shutdown_step.py +41 -0
- autobyteus/workflow/shutdown_steps/workflow_shutdown_orchestrator.py +35 -0
- autobyteus/workflow/streaming/__init__.py +26 -0
- autobyteus/workflow/streaming/agent_event_bridge.py +48 -0
- autobyteus/workflow/streaming/agent_event_multiplexer.py +70 -0
- autobyteus/workflow/streaming/workflow_event_bridge.py +50 -0
- autobyteus/workflow/streaming/workflow_event_notifier.py +83 -0
- autobyteus/workflow/streaming/workflow_event_stream.py +33 -0
- autobyteus/workflow/streaming/workflow_stream_event_payloads.py +28 -0
- autobyteus/workflow/streaming/workflow_stream_events.py +45 -0
- autobyteus/workflow/utils/__init__.py +9 -0
- autobyteus/workflow/utils/wait_for_idle.py +46 -0
- autobyteus/workflow/workflow_builder.py +151 -0
- {autobyteus-1.1.2.dist-info → autobyteus-1.1.4.dist-info}/METADATA +16 -13
- {autobyteus-1.1.2.dist-info → autobyteus-1.1.4.dist-info}/RECORD +156 -75
- {autobyteus-1.1.2.dist-info → autobyteus-1.1.4.dist-info}/top_level.txt +1 -0
- examples/__init__.py +1 -0
- examples/discover_phase_transitions.py +104 -0
- examples/run_browser_agent.py +260 -0
- examples/run_google_slides_agent.py +286 -0
- examples/run_mcp_browser_client.py +174 -0
- examples/run_mcp_google_slides_client.py +270 -0
- examples/run_mcp_list_tools.py +189 -0
- examples/run_poem_writer.py +274 -0
- examples/run_sqlite_agent.py +293 -0
- examples/workflow/__init__.py +1 -0
- examples/workflow/run_basic_research_workflow.py +189 -0
- examples/workflow/run_code_review_workflow.py +269 -0
- examples/workflow/run_debate_workflow.py +212 -0
- examples/workflow/run_workflow_with_tui.py +153 -0
- autobyteus/agent/context/agent_phase_manager.py +0 -264
- autobyteus/agent/context/phases.py +0 -49
- autobyteus/agent/group/__init__.py +0 -0
- autobyteus/agent/group/agent_group.py +0 -164
- autobyteus/agent/group/agent_group_context.py +0 -81
- autobyteus/agent/input_processor/content_prefixing_input_processor.py +0 -41
- autobyteus/agent/input_processor/metadata_appending_input_processor.py +0 -34
- autobyteus/agent/input_processor/passthrough_input_processor.py +0 -33
- autobyteus/agent/workflow/__init__.py +0 -11
- autobyteus/agent/workflow/agentic_workflow.py +0 -89
- autobyteus/tools/mcp/call_handlers/sse_handler.py +0 -22
- autobyteus/tools/mcp/registrar.py +0 -323
- autobyteus/workflow/simple_task.py +0 -98
- autobyteus/workflow/task.py +0 -147
- autobyteus/workflow/workflow.py +0 -49
- {autobyteus-1.1.2.dist-info → autobyteus-1.1.4.dist-info}/WHEEL +0 -0
- {autobyteus-1.1.2.dist-info → autobyteus-1.1.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/tools/mcp/tool_registrar.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Any, Dict, List, Optional, Union
|
|
4
|
+
|
|
5
|
+
# Consolidated imports from the autobyteus.autobyteus.mcp package public API
|
|
6
|
+
from .config_service import McpConfigService
|
|
7
|
+
from .factory import McpToolFactory
|
|
8
|
+
from .schema_mapper import McpSchemaMapper
|
|
9
|
+
from .server_instance_manager import McpServerInstanceManager
|
|
10
|
+
from .types import BaseMcpConfig
|
|
11
|
+
from .server import BaseManagedMcpServer
|
|
12
|
+
|
|
13
|
+
from autobyteus.tools.registry import ToolRegistry, ToolDefinition
|
|
14
|
+
from autobyteus.tools.tool_origin import ToolOrigin
|
|
15
|
+
from autobyteus.tools.tool_category import ToolCategory
|
|
16
|
+
from autobyteus.utils.singleton import SingletonMeta
|
|
17
|
+
from mcp import types as mcp_types
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
class McpToolRegistrar(metaclass=SingletonMeta):
|
|
23
|
+
"""
|
|
24
|
+
Orchestrates the discovery of remote MCP tools and their registration
|
|
25
|
+
with the AutoByteUs ToolRegistry.
|
|
26
|
+
"""
|
|
27
|
+
def __init__(self):
|
|
28
|
+
"""
|
|
29
|
+
Initializes the McpToolRegistrar singleton.
|
|
30
|
+
"""
|
|
31
|
+
self._config_service: McpConfigService = McpConfigService()
|
|
32
|
+
self._tool_registry: ToolRegistry = ToolRegistry()
|
|
33
|
+
self._instance_manager: McpServerInstanceManager = McpServerInstanceManager()
|
|
34
|
+
self._registered_tools_by_server: Dict[str, List[ToolDefinition]] = {}
|
|
35
|
+
logger.info("McpToolRegistrar initialized.")
|
|
36
|
+
|
|
37
|
+
async def _fetch_tools_from_server(self, server_config: BaseMcpConfig) -> List[mcp_types.Tool]:
|
|
38
|
+
"""
|
|
39
|
+
Uses the instance manager to get a temporary, managed session for discovery.
|
|
40
|
+
"""
|
|
41
|
+
async with self._instance_manager.managed_discovery_session(server_config) as discovery_server:
|
|
42
|
+
# The context manager guarantees the server is connected and will be closed.
|
|
43
|
+
remote_tools = await discovery_server.list_remote_tools()
|
|
44
|
+
return remote_tools
|
|
45
|
+
|
|
46
|
+
def _create_tool_definition_from_remote(
|
|
47
|
+
self,
|
|
48
|
+
remote_tool: mcp_types.Tool,
|
|
49
|
+
server_config: BaseMcpConfig,
|
|
50
|
+
schema_mapper: McpSchemaMapper
|
|
51
|
+
) -> ToolDefinition:
|
|
52
|
+
"""
|
|
53
|
+
Maps a single remote tool from an MCP server to an AutoByteUs ToolDefinition.
|
|
54
|
+
"""
|
|
55
|
+
actual_arg_schema = schema_mapper.map_to_autobyteus_schema(remote_tool.inputSchema)
|
|
56
|
+
actual_desc = remote_tool.description
|
|
57
|
+
|
|
58
|
+
registered_name = remote_tool.name
|
|
59
|
+
if server_config.tool_name_prefix:
|
|
60
|
+
registered_name = f"{server_config.tool_name_prefix.rstrip('_')}_{remote_tool.name}"
|
|
61
|
+
|
|
62
|
+
tool_factory = McpToolFactory(
|
|
63
|
+
server_id=server_config.server_id,
|
|
64
|
+
remote_tool_name=remote_tool.name,
|
|
65
|
+
registered_tool_name=registered_name,
|
|
66
|
+
tool_description=actual_desc,
|
|
67
|
+
tool_argument_schema=actual_arg_schema
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
return ToolDefinition(
|
|
71
|
+
name=registered_name,
|
|
72
|
+
description=actual_desc,
|
|
73
|
+
argument_schema=actual_arg_schema,
|
|
74
|
+
origin=ToolOrigin.MCP,
|
|
75
|
+
category=server_config.server_id, # Use server_id as the category
|
|
76
|
+
metadata={"mcp_server_id": server_config.server_id}, # Store origin in generic metadata
|
|
77
|
+
custom_factory=tool_factory.create_tool,
|
|
78
|
+
config_schema=None,
|
|
79
|
+
tool_class=None
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
async def discover_and_register_tools(self, mcp_config: Optional[Union[BaseMcpConfig, Dict[str, Any]]] = None) -> List[ToolDefinition]:
|
|
83
|
+
"""
|
|
84
|
+
Discovers tools from MCP servers and registers them.
|
|
85
|
+
"""
|
|
86
|
+
configs_to_process: List[BaseMcpConfig]
|
|
87
|
+
|
|
88
|
+
if mcp_config:
|
|
89
|
+
validated_config: BaseMcpConfig
|
|
90
|
+
if isinstance(mcp_config, dict):
|
|
91
|
+
try:
|
|
92
|
+
validated_config = self._config_service.load_config(mcp_config)
|
|
93
|
+
except ValueError as e:
|
|
94
|
+
logger.error(f"Failed to parse provided MCP config dictionary: {e}")
|
|
95
|
+
raise
|
|
96
|
+
elif isinstance(mcp_config, BaseMcpConfig):
|
|
97
|
+
validated_config = self._config_service.add_config(mcp_config)
|
|
98
|
+
else:
|
|
99
|
+
raise TypeError(f"mcp_config must be a BaseMcpConfig object or a dictionary, not {type(mcp_config)}.")
|
|
100
|
+
|
|
101
|
+
logger.info(f"Starting targeted MCP tool discovery for server: {validated_config.server_id}")
|
|
102
|
+
self.unregister_tools_from_server(validated_config.server_id)
|
|
103
|
+
configs_to_process = [validated_config]
|
|
104
|
+
else:
|
|
105
|
+
logger.info("Starting full MCP tool discovery. Unregistering all existing MCP tools first.")
|
|
106
|
+
all_server_ids = list(self._registered_tools_by_server.keys())
|
|
107
|
+
for server_id in all_server_ids:
|
|
108
|
+
self.unregister_tools_from_server(server_id)
|
|
109
|
+
self._registered_tools_by_server.clear()
|
|
110
|
+
configs_to_process = self._config_service.get_all_configs()
|
|
111
|
+
|
|
112
|
+
if not configs_to_process:
|
|
113
|
+
logger.info("No MCP server configurations to process. Skipping discovery.")
|
|
114
|
+
return []
|
|
115
|
+
|
|
116
|
+
schema_mapper = McpSchemaMapper()
|
|
117
|
+
registered_tool_definitions: List[ToolDefinition] = []
|
|
118
|
+
for server_config in configs_to_process:
|
|
119
|
+
if not server_config.enabled:
|
|
120
|
+
logger.info(f"MCP server '{server_config.server_id}' is disabled. Skipping.")
|
|
121
|
+
continue
|
|
122
|
+
|
|
123
|
+
logger.info(f"Discovering tools from MCP server: '{server_config.server_id}'")
|
|
124
|
+
|
|
125
|
+
try:
|
|
126
|
+
remote_tools = await self._fetch_tools_from_server(server_config)
|
|
127
|
+
logger.info(f"Discovered {len(remote_tools)} tools from server '{server_config.server_id}'.")
|
|
128
|
+
|
|
129
|
+
for remote_tool in remote_tools:
|
|
130
|
+
try:
|
|
131
|
+
tool_def = self._create_tool_definition_from_remote(remote_tool, server_config, schema_mapper)
|
|
132
|
+
self._tool_registry.register_tool(tool_def)
|
|
133
|
+
self._registered_tools_by_server.setdefault(server_config.server_id, []).append(tool_def)
|
|
134
|
+
registered_tool_definitions.append(tool_def)
|
|
135
|
+
except Exception as e_tool:
|
|
136
|
+
logger.error(f"Failed to process or register remote tool '{remote_tool.name}': {e_tool}", exc_info=True)
|
|
137
|
+
|
|
138
|
+
except Exception as e_server:
|
|
139
|
+
logger.error(f"Failed to discover tools from MCP server '{server_config.server_id}': {e_server}", exc_info=True)
|
|
140
|
+
|
|
141
|
+
logger.info(f"MCP tool discovery and registration process completed. Total tools registered: {len(registered_tool_definitions)}.")
|
|
142
|
+
return registered_tool_definitions
|
|
143
|
+
|
|
144
|
+
async def list_remote_tools(self, mcp_config: Union[BaseMcpConfig, Dict[str, Any]]) -> List[ToolDefinition]:
|
|
145
|
+
validated_config: BaseMcpConfig
|
|
146
|
+
if isinstance(mcp_config, dict):
|
|
147
|
+
validated_config = McpConfigService.parse_mcp_config_dict(mcp_config)
|
|
148
|
+
elif isinstance(mcp_config, BaseMcpConfig):
|
|
149
|
+
validated_config = mcp_config
|
|
150
|
+
else:
|
|
151
|
+
raise TypeError(f"mcp_config must be a BaseMcpConfig object or a dictionary, not {type(mcp_config)}.")
|
|
152
|
+
|
|
153
|
+
logger.info(f"Previewing tools from MCP server: '{validated_config.server_id}'")
|
|
154
|
+
schema_mapper = McpSchemaMapper()
|
|
155
|
+
tool_definitions: List[ToolDefinition] = []
|
|
156
|
+
|
|
157
|
+
try:
|
|
158
|
+
remote_tools = await self._fetch_tools_from_server(validated_config)
|
|
159
|
+
logger.info(f"Discovered {len(remote_tools)} tools from server '{validated_config.server_id}' for preview.")
|
|
160
|
+
for remote_tool in remote_tools:
|
|
161
|
+
tool_def = self._create_tool_definition_from_remote(remote_tool, validated_config, schema_mapper)
|
|
162
|
+
tool_definitions.append(tool_def)
|
|
163
|
+
except Exception as e_server:
|
|
164
|
+
logger.error(f"Failed to discover tools for preview from MCP server '{validated_config.server_id}': {e_server}", exc_info=True)
|
|
165
|
+
raise
|
|
166
|
+
|
|
167
|
+
logger.info(f"MCP tool preview completed. Found {len(tool_definitions)} tools.")
|
|
168
|
+
return tool_definitions
|
|
169
|
+
|
|
170
|
+
def unregister_tools_from_server(self, server_id: str) -> bool:
|
|
171
|
+
if not self.is_server_registered(server_id):
|
|
172
|
+
return False
|
|
173
|
+
tools_to_unregister = self._registered_tools_by_server.pop(server_id, [])
|
|
174
|
+
for tool_def in tools_to_unregister:
|
|
175
|
+
self._tool_registry.unregister_tool(tool_def.name)
|
|
176
|
+
return True
|
|
177
|
+
|
|
178
|
+
def is_server_registered(self, server_id: str) -> bool:
|
|
179
|
+
return server_id in self._registered_tools_by_server
|
autobyteus/tools/mcp/types.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# file: autobyteus/autobyteus/mcp/types.py
|
|
1
|
+
# file: autobyteus/autobyteus/tools/mcp/types.py
|
|
2
2
|
import logging
|
|
3
3
|
from typing import List, Dict, Any, Optional, Type
|
|
4
4
|
from dataclasses import dataclass, field, InitVar
|
|
@@ -9,9 +9,17 @@ logger = logging.getLogger(__name__)
|
|
|
9
9
|
class McpTransportType(str, Enum):
|
|
10
10
|
"""Enumeration of supported MCP transport types."""
|
|
11
11
|
STDIO = "stdio"
|
|
12
|
-
SSE = "sse"
|
|
13
12
|
STREAMABLE_HTTP = "streamable_http"
|
|
14
13
|
|
|
14
|
+
@dataclass(frozen=True)
|
|
15
|
+
class McpServerInstanceKey:
|
|
16
|
+
"""
|
|
17
|
+
A dedicated, hashable key for identifying a unique server instance.
|
|
18
|
+
An instance is unique for a given agent and a specific server configuration.
|
|
19
|
+
"""
|
|
20
|
+
agent_id: str
|
|
21
|
+
server_id: str
|
|
22
|
+
|
|
15
23
|
@dataclass
|
|
16
24
|
class BaseMcpConfig:
|
|
17
25
|
"""
|
|
@@ -59,25 +67,6 @@ class StdioMcpServerConfig(BaseMcpConfig):
|
|
|
59
67
|
if self.cwd is not None and not isinstance(self.cwd, str):
|
|
60
68
|
raise ValueError(f"StdioMcpServerConfig '{self.server_id}' 'cwd' must be a string if provided.")
|
|
61
69
|
|
|
62
|
-
@dataclass
|
|
63
|
-
class SseMcpServerConfig(BaseMcpConfig):
|
|
64
|
-
"""Configuration parameters for an MCP server using SSE transport."""
|
|
65
|
-
url: Optional[str] = None # Changed: Added default None
|
|
66
|
-
token: Optional[str] = None
|
|
67
|
-
headers: Dict[str, str] = field(default_factory=dict)
|
|
68
|
-
|
|
69
|
-
def __post_init__(self):
|
|
70
|
-
super().__post_init__()
|
|
71
|
-
self.transport_type = McpTransportType.SSE
|
|
72
|
-
|
|
73
|
-
if self.url is None or not isinstance(self.url, str) or not self.url.strip():
|
|
74
|
-
raise ValueError(f"SseMcpServerConfig '{self.server_id}' 'url' must be a non-empty string.")
|
|
75
|
-
|
|
76
|
-
if self.token is not None and not isinstance(self.token, str):
|
|
77
|
-
raise ValueError(f"SseMcpServerConfig '{self.server_id}' 'token' must be a string if provided.")
|
|
78
|
-
if not isinstance(self.headers, dict) or not all(isinstance(k, str) and isinstance(v, str) for k, v in self.headers.items()):
|
|
79
|
-
raise ValueError(f"SseMcpServerConfig '{self.server_id}' 'headers' must be a Dict[str, str].")
|
|
80
|
-
|
|
81
70
|
@dataclass
|
|
82
71
|
class StreamableHttpMcpServerConfig(BaseMcpConfig):
|
|
83
72
|
"""Configuration parameters for an MCP server using Streamable HTTP transport."""
|
|
@@ -7,6 +7,7 @@ from datetime import datetime
|
|
|
7
7
|
from typing import TYPE_CHECKING, Optional
|
|
8
8
|
|
|
9
9
|
from autobyteus.tools import tool
|
|
10
|
+
from autobyteus.tools.tool_category import ToolCategory
|
|
10
11
|
from autobyteus.utils.file_utils import get_default_download_folder
|
|
11
12
|
|
|
12
13
|
if TYPE_CHECKING:
|
|
@@ -14,7 +15,7 @@ if TYPE_CHECKING:
|
|
|
14
15
|
|
|
15
16
|
logger = logging.getLogger(__name__)
|
|
16
17
|
|
|
17
|
-
@tool(name="PDFDownloader")
|
|
18
|
+
@tool(name="PDFDownloader", category=ToolCategory.WEB)
|
|
18
19
|
async def pdf_downloader( # function name can be pdf_downloader
|
|
19
20
|
context: 'AgentContext',
|
|
20
21
|
url: str,
|
|
@@ -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.
|
|
9
|
+
from autobyteus.tools.tool_origin import ToolOrigin
|
|
10
10
|
from autobyteus.tools.usage.providers import (
|
|
11
11
|
XmlSchemaProvider,
|
|
12
12
|
JsonSchemaProvider,
|
|
@@ -28,10 +28,12 @@ class ToolDefinition:
|
|
|
28
28
|
name: str,
|
|
29
29
|
description: str,
|
|
30
30
|
argument_schema: Optional['ParameterSchema'],
|
|
31
|
-
|
|
31
|
+
origin: ToolOrigin,
|
|
32
|
+
category: str,
|
|
32
33
|
config_schema: Optional['ParameterSchema'] = None,
|
|
33
34
|
tool_class: Optional[Type['BaseTool']] = None,
|
|
34
|
-
custom_factory: Optional[Callable[['ToolConfig'], 'BaseTool']] = None
|
|
35
|
+
custom_factory: Optional[Callable[['ToolConfig'], 'BaseTool']] = None,
|
|
36
|
+
metadata: Optional[Dict[str, Any]] = None):
|
|
35
37
|
"""
|
|
36
38
|
Initializes the ToolDefinition.
|
|
37
39
|
"""
|
|
@@ -54,8 +56,12 @@ class ToolDefinition:
|
|
|
54
56
|
raise TypeError(f"ToolDefinition '{name}' received an invalid 'argument_schema'. Expected ParameterSchema or None.")
|
|
55
57
|
if config_schema is not None and not isinstance(config_schema, ParameterSchema):
|
|
56
58
|
raise TypeError(f"ToolDefinition '{name}' received an invalid 'config_schema'. Expected ParameterSchema or None.")
|
|
57
|
-
if not isinstance(
|
|
58
|
-
raise TypeError(f"ToolDefinition '{name}' requires a
|
|
59
|
+
if not isinstance(origin, ToolOrigin):
|
|
60
|
+
raise TypeError(f"ToolDefinition '{name}' requires a ToolOrigin for 'origin'. Got {type(origin)}")
|
|
61
|
+
|
|
62
|
+
# Validation for MCP-specific 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.")
|
|
59
65
|
|
|
60
66
|
self._name = name
|
|
61
67
|
self._description = description
|
|
@@ -63,7 +69,9 @@ class ToolDefinition:
|
|
|
63
69
|
self._config_schema: Optional['ParameterSchema'] = config_schema
|
|
64
70
|
self._tool_class = tool_class
|
|
65
71
|
self._custom_factory = custom_factory
|
|
72
|
+
self._origin = origin
|
|
66
73
|
self._category = category
|
|
74
|
+
self._metadata = metadata or {}
|
|
67
75
|
|
|
68
76
|
logger.debug(f"ToolDefinition created for tool '{self.name}'.")
|
|
69
77
|
|
|
@@ -81,7 +89,11 @@ class ToolDefinition:
|
|
|
81
89
|
@property
|
|
82
90
|
def config_schema(self) -> Optional['ParameterSchema']: return self._config_schema
|
|
83
91
|
@property
|
|
84
|
-
def
|
|
92
|
+
def origin(self) -> ToolOrigin: return self._origin
|
|
93
|
+
@property
|
|
94
|
+
def category(self) -> str: return self._category
|
|
95
|
+
@property
|
|
96
|
+
def metadata(self) -> Dict[str, Any]: return self._metadata
|
|
85
97
|
|
|
86
98
|
# --- Schema Generation API ---
|
|
87
99
|
def get_usage_xml(self, provider: Optional[LLMProvider] = None) -> str:
|
|
@@ -136,4 +148,5 @@ class ToolDefinition:
|
|
|
136
148
|
|
|
137
149
|
def __repr__(self) -> str:
|
|
138
150
|
creator_repr = f"class='{self._tool_class.__name__}'" if self._tool_class else "factory=True"
|
|
139
|
-
|
|
151
|
+
metadata_repr = f", metadata={self.metadata}" if self.metadata else ""
|
|
152
|
+
return (f"ToolDefinition(name='{self.name}', origin='{self.origin.value}', category='{self.category}'{metadata_repr}, {creator_repr})")
|
|
@@ -1,10 +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
|
|
9
|
+
from autobyteus.tools.tool_origin import ToolOrigin
|
|
8
10
|
|
|
9
11
|
if TYPE_CHECKING:
|
|
10
12
|
from autobyteus.tools.base_tool import BaseTool
|
|
@@ -78,19 +80,8 @@ class ToolRegistry(metaclass=SingletonMeta):
|
|
|
78
80
|
|
|
79
81
|
def create_tool(self, name: str, config: Optional[ToolConfig] = None) -> 'BaseTool':
|
|
80
82
|
"""
|
|
81
|
-
Creates a tool instance using its definition
|
|
82
|
-
|
|
83
|
-
Args:
|
|
84
|
-
name: The name of the tool to create.
|
|
85
|
-
config: Optional ToolConfig with constructor parameters for class-based tools
|
|
86
|
-
or to be passed to a custom factory.
|
|
87
|
-
|
|
88
|
-
Returns:
|
|
89
|
-
The tool instance if the definition exists.
|
|
90
|
-
|
|
91
|
-
Raises:
|
|
92
|
-
ValueError: If the tool definition is not found or is invalid.
|
|
93
|
-
TypeError: If tool instantiation fails.
|
|
83
|
+
Creates a tool instance using its definition and injects the definition
|
|
84
|
+
back into the instance.
|
|
94
85
|
"""
|
|
95
86
|
definition = self.get_tool_definition(name)
|
|
96
87
|
if not definition:
|
|
@@ -98,24 +89,20 @@ class ToolRegistry(metaclass=SingletonMeta):
|
|
|
98
89
|
raise ValueError(f"No tool definition found for name '{name}'")
|
|
99
90
|
|
|
100
91
|
try:
|
|
101
|
-
|
|
92
|
+
tool_instance: 'BaseTool'
|
|
102
93
|
if definition.custom_factory:
|
|
103
94
|
logger.info(f"Creating tool instance for '{name}' using its custom factory.")
|
|
104
|
-
# Pass the config to the factory. The factory can choose to use it or not.
|
|
105
95
|
tool_instance = definition.custom_factory(config)
|
|
106
|
-
|
|
107
|
-
# Fall back to instantiating the tool_class
|
|
108
96
|
elif definition.tool_class:
|
|
109
|
-
# For class-based tools, the convention is to pass the ToolConfig object
|
|
110
|
-
# itself to the constructor under the 'config' keyword argument.
|
|
111
97
|
logger.info(f"Creating tool instance for '{name}' using class '{definition.tool_class.__name__}' and passing ToolConfig.")
|
|
112
98
|
tool_instance = definition.tool_class(config=config)
|
|
113
|
-
|
|
114
99
|
else:
|
|
115
|
-
# This case should be prevented by ToolDefinition's validation
|
|
116
100
|
raise ValueError(f"ToolDefinition for '{name}' is invalid: missing both tool_class and custom_factory.")
|
|
117
101
|
|
|
118
|
-
|
|
102
|
+
# Inject the definition into the newly created instance.
|
|
103
|
+
tool_instance.definition = definition
|
|
104
|
+
logger.debug(f"Injected ToolDefinition into instance of '{name}'.")
|
|
105
|
+
|
|
119
106
|
return tool_instance
|
|
120
107
|
|
|
121
108
|
except Exception as e:
|
|
@@ -126,23 +113,83 @@ class ToolRegistry(metaclass=SingletonMeta):
|
|
|
126
113
|
def list_tools(self) -> List[ToolDefinition]:
|
|
127
114
|
"""
|
|
128
115
|
Returns a list of all registered tool definitions.
|
|
129
|
-
|
|
130
|
-
Returns:
|
|
131
|
-
A list of ToolDefinition objects.
|
|
132
116
|
"""
|
|
133
117
|
return list(self._definitions.values())
|
|
134
118
|
|
|
135
119
|
def list_tool_names(self) -> List[str]:
|
|
136
120
|
"""
|
|
137
121
|
Returns a list of the names of all registered tools.
|
|
138
|
-
|
|
139
|
-
Returns:
|
|
140
|
-
A list of tool name strings.
|
|
141
122
|
"""
|
|
142
123
|
return list(self._definitions.keys())
|
|
143
124
|
|
|
144
125
|
def get_all_definitions(self) -> Dict[str, ToolDefinition]:
|
|
145
126
|
"""Returns the internal dictionary of definitions."""
|
|
146
127
|
return dict(ToolRegistry._definitions)
|
|
128
|
+
|
|
129
|
+
def get_tools_by_mcp_server(self, server_id: str) -> List[ToolDefinition]:
|
|
130
|
+
"""
|
|
131
|
+
Returns a list of all registered tool definitions that originated
|
|
132
|
+
from a specific MCP server.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
server_id: The unique ID of the MCP server to query for.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
A list of matching ToolDefinition objects.
|
|
139
|
+
"""
|
|
140
|
+
if not server_id:
|
|
141
|
+
return []
|
|
142
|
+
|
|
143
|
+
return [
|
|
144
|
+
td for td in self._definitions.values()
|
|
145
|
+
if td.origin == ToolOrigin.MCP and td.metadata.get("mcp_server_id") == server_id
|
|
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
|
|
147
194
|
|
|
148
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
|
-
"""
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
autobyteus/tools/tool_meta.py
CHANGED
|
@@ -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
|
-
|
|
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
|