feat: Improved tool and MCP invocation error handling by returning stderr to the model when it is available
This commit is contained in:
+42
-10
@@ -23,6 +23,7 @@ use std::{
|
|||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
env, fs, io,
|
env, fs, io,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
process::{Command, Stdio},
|
||||||
};
|
};
|
||||||
use strum_macros::AsRefStr;
|
use strum_macros::AsRefStr;
|
||||||
|
|
||||||
@@ -847,13 +848,29 @@ impl ToolCall {
|
|||||||
|
|
||||||
let output = match cmd_name.as_str() {
|
let output = match cmd_name.as_str() {
|
||||||
_ if cmd_name.starts_with(MCP_SEARCH_META_FUNCTION_NAME_PREFIX) => {
|
_ if cmd_name.starts_with(MCP_SEARCH_META_FUNCTION_NAME_PREFIX) => {
|
||||||
Self::search_mcp_tools(config, &cmd_name, &json_data)?
|
Self::search_mcp_tools(config, &cmd_name, &json_data).unwrap_or_else(|e| {
|
||||||
|
let error_msg = format!("MCP search failed: {e}");
|
||||||
|
println!("{}", warning_text(&format!("⚠️ {error_msg} ⚠️")));
|
||||||
|
json!({"tool_call_error": error_msg})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
_ if cmd_name.starts_with(MCP_DESCRIBE_META_FUNCTION_NAME_PREFIX) => {
|
_ if cmd_name.starts_with(MCP_DESCRIBE_META_FUNCTION_NAME_PREFIX) => {
|
||||||
Self::describe_mcp_tool(config, &cmd_name, json_data).await?
|
Self::describe_mcp_tool(config, &cmd_name, json_data)
|
||||||
|
.await
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
let error_msg = format!("MCP describe failed: {e}");
|
||||||
|
println!("{}", warning_text(&format!("⚠️ {error_msg} ⚠️")));
|
||||||
|
json!({"tool_call_error": error_msg})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
_ if cmd_name.starts_with(MCP_INVOKE_META_FUNCTION_NAME_PREFIX) => {
|
_ if cmd_name.starts_with(MCP_INVOKE_META_FUNCTION_NAME_PREFIX) => {
|
||||||
Self::invoke_mcp_tool(config, &cmd_name, &json_data).await?
|
Self::invoke_mcp_tool(config, &cmd_name, &json_data)
|
||||||
|
.await
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
let error_msg = format!("MCP tool invocation failed: {e}");
|
||||||
|
println!("{}", warning_text(&format!("⚠️ {error_msg} ⚠️")));
|
||||||
|
json!({"tool_call_error": error_msg})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
_ => match run_llm_function(cmd_name, cmd_args, envs, agent_name) {
|
_ => match run_llm_function(cmd_name, cmd_args, envs, agent_name) {
|
||||||
Ok(Some(contents)) => serde_json::from_str(&contents)
|
Ok(Some(contents)) => serde_json::from_str(&contents)
|
||||||
@@ -1019,21 +1036,36 @@ pub fn run_llm_function(
|
|||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let cmd_name = polyfill_cmd_name(&cmd_name, &bin_dirs);
|
let cmd_name = polyfill_cmd_name(&cmd_name, &bin_dirs);
|
||||||
|
|
||||||
let exit_code = run_command(&cmd_name, &cmd_args, Some(envs))
|
let output = Command::new(&cmd_name)
|
||||||
|
.args(&cmd_args)
|
||||||
|
.envs(envs)
|
||||||
|
.stdout(Stdio::inherit())
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.and_then(|child| child.wait_with_output())
|
||||||
.map_err(|err| anyhow!("Unable to run {command_name}, {err}"))?;
|
.map_err(|err| anyhow!("Unable to run {command_name}, {err}"))?;
|
||||||
|
|
||||||
|
let exit_code = output.status.code().unwrap_or_default();
|
||||||
if exit_code != 0 {
|
if exit_code != 0 {
|
||||||
let tool_error_message =
|
let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string();
|
||||||
format!("⚠️ Tool call '{command_name}' threw exit code {exit_code} ⚠️");
|
if !stderr.is_empty() {
|
||||||
println!("{}", warning_text(&tool_error_message));
|
eprintln!("{stderr}");
|
||||||
let tool_error_json = format!("{{\"tool_call_error\":\"{}\"}}", &tool_error_message);
|
}
|
||||||
return Ok(Some(tool_error_json));
|
let tool_error_message = format!("Tool call '{command_name}' exited with code {exit_code}");
|
||||||
|
println!("{}", warning_text(&format!("⚠️ {tool_error_message} ⚠️")));
|
||||||
|
let mut error_json = json!({"tool_call_error": tool_error_message});
|
||||||
|
if !stderr.is_empty() {
|
||||||
|
error_json["stderr"] = json!(stderr);
|
||||||
|
}
|
||||||
|
debug!("Tool call error: {error_json:?}");
|
||||||
|
return Ok(Some(error_json.to_string()));
|
||||||
}
|
}
|
||||||
let mut output = None;
|
let mut output = None;
|
||||||
if temp_file.exists() {
|
if temp_file.exists() {
|
||||||
let contents =
|
let contents =
|
||||||
fs::read_to_string(temp_file).context("Failed to retrieve tool call output")?;
|
fs::read_to_string(temp_file).context("Failed to retrieve tool call output")?;
|
||||||
if !contents.is_empty() {
|
if !contents.is_empty() {
|
||||||
debug!("Tool {cmd_name} output: {}", contents);
|
debug!("Tool {command_name} output: {}", contents);
|
||||||
output = Some(contents);
|
output = Some(contents);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user