diff --git a/agent/core/agent_loop.py b/agent/core/agent_loop.py index 8c474e09..12542903 100644 --- a/agent/core/agent_loop.py +++ b/agent/core/agent_loop.py @@ -1108,6 +1108,14 @@ async def _exec_tool( ) -> tuple[ToolCall, str, dict, str, bool]: if not valid: return (tc, name, args, err, False) + # Emit "running" immediately so the CLI/frontend shows + # progress instead of going silent (fixes issue #127). + await session.send_event( + Event( + event_type="tool_state_change", + data={"tool_call_id": tc.id, "tool": name, "state": "running"}, + ) + ) out, ok = await session.tool_router.call_tool( name, args, session=session, tool_call_id=tc.id ) @@ -1625,4 +1633,4 @@ async def submission_loop( if local_path: logger.info("Emergency save successful, upload in progress") except Exception as e: - logger.error(f"Emergency save failed: {e}") + logger.error(f"Emergency save failed: {e}") \ No newline at end of file diff --git a/agent/core/tools.py b/agent/core/tools.py index ef2c57bc..8379a498 100644 --- a/agent/core/tools.py +++ b/agent/core/tools.py @@ -3,11 +3,18 @@ Provides ToolSpec and ToolRouter for managing both built-in and MCP tools """ +import asyncio import logging import warnings from dataclasses import dataclass from typing import Any, Awaitable, Callable, Optional +# Timeout (seconds) for a single MCP tool call. +# hub_repo_details fetches model-card + siblings list from huggingface.co/api +# which can stall indefinitely on slow Hub responses. 30 s is generous but +# prevents the agent from silently hanging for 5+ minutes. +MCP_TOOL_TIMEOUT = 30 + logger = logging.getLogger(__name__) from fastmcp import Client @@ -265,9 +272,21 @@ async def call_tool( # Otherwise, use MCP client if self._mcp_initialized: try: - result = await self.mcp_client.call_tool(tool_name, arguments) + result = await asyncio.wait_for( + self.mcp_client.call_tool(tool_name, arguments), + timeout=MCP_TOOL_TIMEOUT, + ) output = convert_mcp_content_to_string(result.content) return output, not result.is_error + except asyncio.TimeoutError: + timeout_msg = ( + f"Tool '{tool_name}' timed out after {MCP_TOOL_TIMEOUT}s " + "with no response from the HF MCP server. " + "The Hub API may be slow or rate-limiting. " + "You can retry, or proceed with the information already available." + ) + logger.warning("MCP tool %s timed out after %ds", tool_name, MCP_TOOL_TIMEOUT) + return timeout_msg, False except ToolError as e: # Catch MCP tool errors and return them to the agent error_msg = f"Tool error: {str(e)}" @@ -387,4 +406,4 @@ def create_builtin_tools(local_mode: bool = False) -> list[ToolSpec]: tool_names = ", ".join([t.name for t in tools]) logger.info(f"Loaded {len(tools)} built-in tools: {tool_names}") - return tools + return tools \ No newline at end of file