Implement tool result conversion for anyhow errors (#55001)

Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- N/A
This commit is contained in:
Ben Brandt authored and GitHub committed 2026-04-27 13:03:33 +00:00
1 parent 6e8b8b2fbd
commit 418e5fe615
3 files changed
+34 -22

No files matched your search

+16 -13
View File
@@ -2448,9 +2448,10 @@ impl Thread {
.collect();
(false, output)
} else {
let output = AgentToolOutput::from_error(
let output = anyhow::anyhow!(
"Attempted to read an image, but this model doesn't support it.",
);
)
.into();
(true, output)
}
} else {
@@ -3426,14 +3427,15 @@ pub struct AgentToolOutput {
pub raw_output: serde_json::Value,
}
impl AgentToolOutput {
pub fn from_error(message: impl Into<String>) -> Self {
let message = message.into();
let llm_output = vec![LanguageModelToolResultContent::Text(Arc::from(
message.as_str(),
))];
impl From<anyhow::Error> for AgentToolOutput {
fn from(error: anyhow::Error) -> Self {
let llm_output = vec![error.into()];
let raw_output = serde_json::to_value(&llm_output).unwrap_or_else(|e| {
log::error!("Failed to serialize tool output: {e}");
serde_json::Value::Null
});
Self {
raw_output: serde_json::Value::String(message),
raw_output,
llm_output,
}
}
@@ -3512,12 +3514,13 @@ where
let task = self.0.clone().run(tool_input, event_stream, cx);
cx.spawn(async move |_cx| match task.await {
Ok(output) => {
let raw_output = serde_json::to_value(&output).map_err(|e| {
AgentToolOutput::from_error(format!("Failed to serialize tool output: {e}"))
})?;
let raw_output = serde_json::to_value(&output).unwrap_or_else(|e| {
log::error!("Failed to serialize tool output: {e}");
serde_json::Value::Null
});
Ok(AgentToolOutput {
llm_output: vec![output.into()],
raw_output,
llm_output: vec![output.into()],
})
}
Err(error_output) => {
@@ -337,7 +337,7 @@ impl AnyAgentTool for ContextServerTool {
cx: &mut App,
) -> Task<Result<AgentToolOutput, AgentToolOutput>> {
let Some(server) = self.store.read(cx).get_running_server(&self.server_id) else {
return Task::ready(Err(AgentToolOutput::from_error("Context server not found")));
return Task::ready(Err(anyhow::anyhow!("Context server not found").into()));
};
let tool_name = self.tool.name.clone();
let tool_id = mcp_tool_id(&self.server_id.0, &self.tool.name);
@@ -347,14 +347,17 @@ impl AnyAgentTool for ContextServerTool {
event_stream.authorize_third_party_tool(initial_title, tool_id, display_name, cx);
cx.spawn(async move |_cx| {
let input = input.recv().await.map_err(|e| {
AgentToolOutput::from_error(format!("Failed to receive tool input: {e}"))
})?;
let input = input
.recv()
.await
.map_err(|e| anyhow::anyhow!(format!("Failed to receive tool input: {e}")))?;
authorize.await.map_err(|e| AgentToolOutput::from_error(e.to_string()))?;
authorize
.await
.map_err(|e| anyhow::anyhow!(e.to_string()))?;
let Some(protocol) = server.client() else {
return Err(AgentToolOutput::from_error("Context server not initialized"));
return Err(anyhow::anyhow!("Context server not initialized").into());
};
let arguments = if let serde_json::Value::Object(map) = input {
@@ -378,16 +381,16 @@ impl AnyAgentTool for ContextServerTool {
);
let response = futures::select! {
response = request.fuse() => response.map_err(|e| AgentToolOutput::from_error(e.to_string()))?,
response = request.fuse() => response?,
_ = event_stream.cancelled_by_user().fuse() => {
return Err(AgentToolOutput::from_error("MCP tool cancelled by user"));
return Err(anyhow::anyhow!("MCP tool cancelled by user").into());
}
};
if response.is_error == Some(true) {
let error_message: String =
response.content.iter().filter_map(|c| c.text()).collect();
return Err(AgentToolOutput::from_error(error_message));
return Err(anyhow::anyhow!(error_message).into());
}
let mut llm_output = Vec::new();
@@ -273,6 +273,12 @@ impl From<String> for LanguageModelToolResultContent {
}
}
impl From<anyhow::Error> for LanguageModelToolResultContent {
fn from(error: anyhow::Error) -> Self {
Self::Text(Arc::from(error.to_string()))
}
}
impl From<LanguageModelImage> for LanguageModelToolResultContent {
fn from(image: LanguageModelImage) -> Self {
Self::Image(image)