fix(runtime): add 30s timeout to tool execution in AgentLoop
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled

Tool execution (ShellExec, WebFetch, etc.) had no timeout, causing the
entire streaming response to hang indefinitely when a tool fails or stalls.
Now wraps execute_tool calls in tokio::time::timeout(30s) with a graceful
error message on timeout.
This commit is contained in:
iven
2026-04-08 20:44:52 +08:00
parent 6e0c1e55a9
commit de2d3e3a11

View File

@@ -429,10 +429,17 @@ impl AgentLoop {
continue; continue;
} }
middleware::ToolCallDecision::ReplaceInput(new_input) => { middleware::ToolCallDecision::ReplaceInput(new_input) => {
// Execute with replaced input // Execute with replaced input (with timeout)
let tool_result = match self.execute_tool(&name, new_input, &tool_context).await { let tool_result = match tokio::time::timeout(
Ok(result) => result, std::time::Duration::from_secs(30),
Err(e) => serde_json::json!({ "error": e.to_string() }), self.execute_tool(&name, new_input, &tool_context),
).await {
Ok(Ok(result)) => result,
Ok(Err(e)) => serde_json::json!({ "error": e.to_string() }),
Err(_) => {
tracing::warn!("[AgentLoop] Tool '{}' (replaced input) timed out after 30s", name);
serde_json::json!({ "error": format!("工具 '{}' 执行超时30秒请重试", name) })
}
}; };
messages.push(Message::tool_result(id, zclaw_types::ToolId::new(&name), tool_result, false)); messages.push(Message::tool_result(id, zclaw_types::ToolId::new(&name), tool_result, false));
continue; continue;
@@ -471,9 +478,16 @@ impl AgentLoop {
} }
} }
let tool_result = match self.execute_tool(&name, input, &tool_context).await { let tool_result = match tokio::time::timeout(
Ok(result) => result, std::time::Duration::from_secs(30),
Err(e) => serde_json::json!({ "error": e.to_string() }), self.execute_tool(&name, input, &tool_context),
).await {
Ok(Ok(result)) => result,
Ok(Err(e)) => serde_json::json!({ "error": e.to_string() }),
Err(_) => {
tracing::warn!("[AgentLoop] Tool '{}' timed out after 30s", name);
serde_json::json!({ "error": format!("工具 '{}' 执行超时30秒请重试", name) })
}
}; };
// Check if this is a clarification response — terminate loop immediately // Check if this is a clarification response — terminate loop immediately