Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion agent/core/agent_loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand Down Expand Up @@ -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}")
23 changes: 21 additions & 2 deletions agent/core/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)}"
Expand Down Expand Up @@ -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
Loading