feat: ToolError migration to ErrorData (#4051)
This commit is contained in:
@@ -3,8 +3,8 @@ use crate::model::ModelConfig;
|
||||
use crate::providers::base::Usage;
|
||||
use crate::providers::errors::ProviderError;
|
||||
use anyhow::{anyhow, Result};
|
||||
use mcp_core::tool::ToolCall;
|
||||
use rmcp::model::{Role, Tool};
|
||||
use mcp_core::ToolCall;
|
||||
use rmcp::model::{ErrorCode, ErrorData, Role, Tool};
|
||||
use serde_json::{json, Value};
|
||||
use std::collections::HashSet;
|
||||
|
||||
@@ -572,8 +572,10 @@ where
|
||||
Ok(parsed) => parsed,
|
||||
Err(_) => {
|
||||
// If parsing fails, create an error tool request
|
||||
let error = mcp_core::handler::ToolError::InvalidParameters(
|
||||
format!("Could not parse tool arguments: {}", args)
|
||||
let error = ErrorData::new(
|
||||
ErrorCode::INVALID_PARAMS,
|
||||
format!("Could not parse tool arguments: {}", args),
|
||||
None,
|
||||
);
|
||||
let mut message = Message::new(
|
||||
Role::Assistant,
|
||||
@@ -985,7 +987,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_tool_error_handling_maintains_pairing() {
|
||||
use crate::conversation::message::Message;
|
||||
use mcp_core::handler::ToolError;
|
||||
use rmcp::model::{ErrorCode, ErrorData};
|
||||
|
||||
let messages = vec![
|
||||
Message::assistant().with_tool_request(
|
||||
@@ -994,7 +996,11 @@ mod tests {
|
||||
),
|
||||
Message::user().with_tool_response(
|
||||
"tool_1",
|
||||
Err(ToolError::ExecutionError("Tool failed".to_string())),
|
||||
Err(ErrorData::new(
|
||||
ErrorCode::INTERNAL_ERROR,
|
||||
"Tool failed".to_string(),
|
||||
None,
|
||||
)),
|
||||
),
|
||||
];
|
||||
|
||||
@@ -1012,7 +1018,7 @@ mod tests {
|
||||
assert_eq!(spec[1]["content"][0]["tool_use_id"], "tool_1");
|
||||
assert_eq!(
|
||||
spec[1]["content"][0]["content"],
|
||||
"Error: Execution failed: Tool failed"
|
||||
"Error: -32603: Tool failed"
|
||||
);
|
||||
assert_eq!(spec[1]["content"][0]["is_error"], true);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
|
||||
@@ -6,8 +7,8 @@ use aws_sdk_bedrockruntime::types as bedrock;
|
||||
use aws_smithy_types::{Document, Number};
|
||||
use base64::Engine;
|
||||
use chrono::Utc;
|
||||
use mcp_core::{ToolCall, ToolError, ToolResult};
|
||||
use rmcp::model::{Content, RawContent, ResourceContents, Role, Tool};
|
||||
use mcp_core::{ToolCall, ToolResult};
|
||||
use rmcp::model::{Content, ErrorCode, ErrorData, RawContent, ResourceContents, Role, Tool};
|
||||
use serde_json::Value;
|
||||
|
||||
use super::super::base::Usage;
|
||||
@@ -286,9 +287,11 @@ pub fn from_bedrock_content_block(block: &bedrock::ContentBlock) -> Result<Messa
|
||||
bedrock::ContentBlock::ToolResult(tool_res) => MessageContent::tool_response(
|
||||
tool_res.tool_use_id.to_string(),
|
||||
if tool_res.content.is_empty() {
|
||||
Err(ToolError::ExecutionError(
|
||||
"Empty content for tool use from Bedrock".to_string(),
|
||||
))
|
||||
Err(ErrorData {
|
||||
code: ErrorCode::INTERNAL_ERROR,
|
||||
message: Cow::from("Empty content for tool use from Bedrock".to_string()),
|
||||
data: None,
|
||||
})
|
||||
} else {
|
||||
tool_res
|
||||
.content
|
||||
@@ -307,9 +310,11 @@ pub fn from_bedrock_tool_result_content_block(
|
||||
Ok(match content {
|
||||
bedrock::ToolResultContentBlock::Text(text) => Content::text(text.to_string()),
|
||||
_ => {
|
||||
return Err(ToolError::ExecutionError(
|
||||
"Unsupported tool result from Bedrock".to_string(),
|
||||
))
|
||||
return Err(ErrorData {
|
||||
code: ErrorCode::INTERNAL_ERROR,
|
||||
message: Cow::from("Unsupported tool result from Bedrock".to_string()),
|
||||
data: None,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,10 +5,13 @@ use crate::providers::utils::{
|
||||
sanitize_function_name, ImageFormat,
|
||||
};
|
||||
use anyhow::{anyhow, Error};
|
||||
use mcp_core::{ToolCall, ToolError};
|
||||
use rmcp::model::{AnnotateAble, Content, RawContent, ResourceContents, Role, Tool};
|
||||
use mcp_core::ToolCall;
|
||||
use rmcp::model::{
|
||||
AnnotateAble, Content, ErrorCode, ErrorData, RawContent, ResourceContents, Role, Tool,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{json, Value};
|
||||
use std::borrow::Cow;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct DatabricksMessage {
|
||||
@@ -356,10 +359,14 @@ pub fn response_to_message(response: &Value) -> anyhow::Result<Message> {
|
||||
};
|
||||
|
||||
if !is_valid_function_name(&function_name) {
|
||||
let error = ToolError::NotFound(format!(
|
||||
"The provided function name '{}' had invalid characters, it must match this regex [a-zA-Z0-9_-]+",
|
||||
function_name
|
||||
));
|
||||
let error = ErrorData {
|
||||
code: ErrorCode::INVALID_REQUEST,
|
||||
message: Cow::from(format!(
|
||||
"The provided function name '{}' had invalid characters, it must match this regex [a-zA-Z0-9_-]+",
|
||||
function_name
|
||||
)),
|
||||
data: None,
|
||||
};
|
||||
content.push(MessageContent::tool_request(id, Err(error)));
|
||||
} else {
|
||||
match safely_parse_json(&arguments_str) {
|
||||
@@ -370,10 +377,14 @@ pub fn response_to_message(response: &Value) -> anyhow::Result<Message> {
|
||||
));
|
||||
}
|
||||
Err(e) => {
|
||||
let error = ToolError::InvalidParameters(format!(
|
||||
"Could not interpret tool use parameters for id {}: {}. Raw arguments: '{}'",
|
||||
id, e, arguments_str
|
||||
));
|
||||
let error = ErrorData {
|
||||
code: ErrorCode::INVALID_PARAMS,
|
||||
message: Cow::from(format!(
|
||||
"Could not interpret tool use parameters for id {}: {}. Raw arguments: '{}'",
|
||||
id, e, arguments_str
|
||||
)),
|
||||
data: None,
|
||||
};
|
||||
content.push(MessageContent::tool_request(id, Err(error)));
|
||||
}
|
||||
}
|
||||
@@ -963,7 +974,11 @@ mod tests {
|
||||
|
||||
if let MessageContent::ToolRequest(request) = &message.content[0] {
|
||||
match &request.tool_call {
|
||||
Err(ToolError::NotFound(msg)) => {
|
||||
Err(ErrorData {
|
||||
code: ErrorCode::INVALID_REQUEST,
|
||||
message: msg,
|
||||
data: None,
|
||||
}) => {
|
||||
assert!(msg.starts_with("The provided function name"));
|
||||
}
|
||||
_ => panic!("Expected ToolNotFound error"),
|
||||
@@ -985,7 +1000,11 @@ mod tests {
|
||||
|
||||
if let MessageContent::ToolRequest(request) = &message.content[0] {
|
||||
match &request.tool_call {
|
||||
Err(ToolError::InvalidParameters(msg)) => {
|
||||
Err(ErrorData {
|
||||
code: ErrorCode::INVALID_PARAMS,
|
||||
message: msg,
|
||||
data: None,
|
||||
}) => {
|
||||
assert!(msg.starts_with("Could not interpret tool use parameters"));
|
||||
}
|
||||
_ => panic!("Expected InvalidParameters error"),
|
||||
|
||||
@@ -3,9 +3,10 @@ use crate::providers::base::Usage;
|
||||
use crate::providers::errors::ProviderError;
|
||||
use crate::providers::utils::{is_valid_function_name, sanitize_function_name};
|
||||
use anyhow::Result;
|
||||
use mcp_core::tool::ToolCall;
|
||||
use mcp_core::ToolCall;
|
||||
use rand::{distributions::Alphanumeric, Rng};
|
||||
use rmcp::model::{AnnotateAble, RawContent, Role, Tool};
|
||||
use rmcp::model::{AnnotateAble, ErrorCode, ErrorData, RawContent, Role, Tool};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::conversation::message::{Message, MessageContent};
|
||||
use serde_json::{json, Map, Value};
|
||||
@@ -254,10 +255,14 @@ pub fn response_to_message(response: Value) -> Result<Message> {
|
||||
.unwrap_or_default()
|
||||
.to_string();
|
||||
if !is_valid_function_name(&name) {
|
||||
let error = mcp_core::ToolError::NotFound(format!(
|
||||
"The provided function name '{}' had invalid characters, it must match this regex [a-zA-Z0-9_-]+",
|
||||
name
|
||||
));
|
||||
let error = ErrorData {
|
||||
code: ErrorCode::INVALID_REQUEST,
|
||||
message: Cow::from(format!(
|
||||
"The provided function name '{}' had invalid characters, it must match this regex [a-zA-Z0-9_-]+",
|
||||
name
|
||||
)),
|
||||
data: None,
|
||||
};
|
||||
content.push(MessageContent::tool_request(id, Err(error)));
|
||||
} else {
|
||||
let parameters = function_call.get("args");
|
||||
@@ -741,7 +746,14 @@ mod tests {
|
||||
assert_eq!(message.role, Role::Assistant);
|
||||
assert_eq!(message.content.len(), 1);
|
||||
if let Err(error) = &message.content[0].as_tool_request().unwrap().tool_call {
|
||||
assert!(matches!(error, mcp_core::ToolError::NotFound(_)));
|
||||
assert!(matches!(
|
||||
error,
|
||||
ErrorData {
|
||||
code: ErrorCode::INVALID_REQUEST,
|
||||
message: _,
|
||||
data: None,
|
||||
}
|
||||
));
|
||||
} else {
|
||||
panic!("Expected tool request error");
|
||||
}
|
||||
|
||||
@@ -8,10 +8,13 @@ use crate::providers::utils::{
|
||||
use anyhow::{anyhow, Error};
|
||||
use async_stream::try_stream;
|
||||
use futures::Stream;
|
||||
use mcp_core::{ToolCall, ToolError};
|
||||
use rmcp::model::{AnnotateAble, Content, RawContent, ResourceContents, Role, Tool};
|
||||
use mcp_core::ToolCall;
|
||||
use rmcp::model::{
|
||||
AnnotateAble, Content, ErrorCode, ErrorData, RawContent, ResourceContents, Role, Tool,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{json, Value};
|
||||
use std::borrow::Cow;
|
||||
use std::ops::Deref;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
@@ -299,10 +302,14 @@ pub fn response_to_message(response: &Value) -> anyhow::Result<Message> {
|
||||
};
|
||||
|
||||
if !is_valid_function_name(&function_name) {
|
||||
let error = ToolError::NotFound(format!(
|
||||
"The provided function name '{}' had invalid characters, it must match this regex [a-zA-Z0-9_-]+",
|
||||
function_name
|
||||
));
|
||||
let error = ErrorData {
|
||||
code: ErrorCode::INVALID_REQUEST,
|
||||
message: Cow::from(format!(
|
||||
"The provided function name '{}' had invalid characters, it must match this regex [a-zA-Z0-9_-]+",
|
||||
function_name
|
||||
)),
|
||||
data: None,
|
||||
};
|
||||
content.push(MessageContent::tool_request(id, Err(error)));
|
||||
} else {
|
||||
match safely_parse_json(&arguments_str) {
|
||||
@@ -313,10 +320,14 @@ pub fn response_to_message(response: &Value) -> anyhow::Result<Message> {
|
||||
));
|
||||
}
|
||||
Err(e) => {
|
||||
let error = ToolError::InvalidParameters(format!(
|
||||
"Could not interpret tool use parameters for id {}: {}. Raw arguments: '{}'",
|
||||
id, e, arguments_str
|
||||
));
|
||||
let error = ErrorData {
|
||||
code: ErrorCode::INVALID_PARAMS,
|
||||
message: Cow::from(format!(
|
||||
"Could not interpret tool use parameters for id {}: {}. Raw arguments: '{}'",
|
||||
id, e, arguments_str
|
||||
)),
|
||||
data: None,
|
||||
};
|
||||
content.push(MessageContent::tool_request(id, Err(error)));
|
||||
}
|
||||
}
|
||||
@@ -508,10 +519,14 @@ where
|
||||
)
|
||||
},
|
||||
Err(e) => {
|
||||
let error = ToolError::InvalidParameters(format!(
|
||||
"Could not interpret tool use parameters for id {}: {}",
|
||||
id, e
|
||||
));
|
||||
let error = ErrorData {
|
||||
code: ErrorCode::INVALID_PARAMS,
|
||||
message: Cow::from(format!(
|
||||
"Could not interpret tool use parameters for id {}: {}",
|
||||
id, e
|
||||
)),
|
||||
data: None,
|
||||
};
|
||||
MessageContent::tool_request(id.clone(), Err(error))
|
||||
}
|
||||
};
|
||||
@@ -991,7 +1006,11 @@ mod tests {
|
||||
|
||||
if let MessageContent::ToolRequest(request) = &message.content[0] {
|
||||
match &request.tool_call {
|
||||
Err(ToolError::NotFound(msg)) => {
|
||||
Err(ErrorData {
|
||||
code: ErrorCode::INVALID_REQUEST,
|
||||
message: msg,
|
||||
data: None,
|
||||
}) => {
|
||||
assert!(msg.starts_with("The provided function name"));
|
||||
}
|
||||
_ => panic!("Expected ToolNotFound error"),
|
||||
@@ -1013,7 +1032,11 @@ mod tests {
|
||||
|
||||
if let MessageContent::ToolRequest(request) = &message.content[0] {
|
||||
match &request.tool_call {
|
||||
Err(ToolError::InvalidParameters(msg)) => {
|
||||
Err(ErrorData {
|
||||
code: ErrorCode::INVALID_PARAMS,
|
||||
message: msg,
|
||||
data: None,
|
||||
}) => {
|
||||
assert!(msg.starts_with("Could not interpret tool use parameters"));
|
||||
}
|
||||
_ => panic!("Expected InvalidParameters error"),
|
||||
|
||||
Reference in New Issue
Block a user