|
55 | 55 | IValidatorManager, |
56 | 56 | ) |
57 | 57 | from dare_framework.tool._internal.managers.tool_manager import ToolManager |
| 58 | +from dare_framework.tool._internal.tools.ask_user import AskUserTool, IUserInputHandler |
58 | 59 | from dare_framework.tool.interfaces import ITool, IToolProvider, RunContext |
59 | 60 | from dare_framework.tool.kernel import IExecutionControl, IToolGateway, IToolManager |
60 | 61 |
|
@@ -102,6 +103,9 @@ def __init__(self, name: str) -> None: |
102 | 103 | # Initial skill path (None = not set; single path for one skill). |
103 | 104 | self._initial_skill_path: str | None = None |
104 | 105 |
|
| 106 | + # User input handler for the built-in ask_user tool. |
| 107 | + self._user_input_handler: IUserInputHandler | None = None |
| 108 | + |
105 | 109 | def _manager_config(self) -> Config | None: |
106 | 110 | """Return the Config passed to managers.""" |
107 | 111 |
|
@@ -209,6 +213,15 @@ def with_skill(self: TBuilder, path: str | Path) -> TBuilder: |
209 | 213 | self._initial_skill_path = str(path) |
210 | 214 | return self |
211 | 215 |
|
| 216 | + def with_user_input_handler(self: TBuilder, handler: IUserInputHandler) -> TBuilder: |
| 217 | + """Inject a custom user-input handler for the built-in ``ask_user`` tool. |
| 218 | +
|
| 219 | + If not called, the default ``CLIUserInputHandler`` (stdin/stdout) is |
| 220 | + used. Pass a custom implementation for web UIs, Slack bots, etc. |
| 221 | + """ |
| 222 | + self._user_input_handler = handler |
| 223 | + return self |
| 224 | + |
212 | 225 | async def build(self) -> Any: |
213 | 226 | """Build the agent. When with_config() was used and config.mcp_paths is set, loads MCP inside build().""" |
214 | 227 | if self._config is not None and getattr(self._config, "mcp_paths", None): |
@@ -300,6 +313,8 @@ def _ensure_tool_provider( |
300 | 313 | # Include knowledge tool names so they are always listed when knowledge is set. |
301 | 314 | if self._resolved_knowledge() is not None: |
302 | 315 | explicit_names = explicit_names | {"knowledge_get", "knowledge_add"} |
| 316 | + # Built-in ask_user is always available. |
| 317 | + explicit_names = explicit_names | {"ask_user"} |
303 | 318 | return _ConfiguredToolProvider( |
304 | 319 | manager=manager, |
305 | 320 | config=self._config, |
@@ -329,6 +344,9 @@ def _register_tools_with_manager(self, manager: IToolManager | None, tools: list |
329 | 344 | if knowledge is not None: |
330 | 345 | manager.register_tool(KnowledgeGetTool(knowledge)) |
331 | 346 | manager.register_tool(KnowledgeAddTool(knowledge)) |
| 347 | + # Built-in ask_user tool — always available so the LLM can request user input. |
| 348 | + ask_user_tool = AskUserTool(handler=self._user_input_handler) |
| 349 | + manager.register_tool(ask_user_tool) |
332 | 350 |
|
333 | 351 | def _resolved_model(self) -> IModelAdapter: |
334 | 352 | if self._model is not None: |
|
0 commit comments