feat(tools): 添加工具系统框架与 MCP 协议客户端
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
//! 工具系统错误类型。
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
/// 工具调用过程中可能发生的所有错误。
|
||||
#[derive(thiserror::Error, Debug, Clone)]
|
||||
pub enum ToolError {
|
||||
/// 工具未注册。
|
||||
#[error("工具 '{0}' 未注册")]
|
||||
NotFound(String),
|
||||
|
||||
/// 工具执行失败(可恢复——文本回传 LLM)。
|
||||
#[error("工具 '{0}' 执行失败: {1}")]
|
||||
ExecutionFailed(String, String),
|
||||
|
||||
/// 工具参数无效(可恢复——文本回传 LLM)。
|
||||
#[error("工具 '{0}' 参数无效: {1}")]
|
||||
InvalidArguments(String, String),
|
||||
|
||||
/// 权限被拒绝(不可恢复——终止循环)。
|
||||
#[error("权限被拒绝: 工具 '{0}' 需要 {1} 权限")]
|
||||
PermissionDenied(String, String),
|
||||
|
||||
/// MCP 协议错误(不可恢复)。
|
||||
#[error("MCP 协议错误: {0}")]
|
||||
McpError(String),
|
||||
|
||||
/// MCP 未初始化(不可恢复)。
|
||||
#[error("MCP 未初始化: {0}")]
|
||||
McpNotInitialized(String),
|
||||
|
||||
/// MCP 超时(不可恢复)。
|
||||
#[error("MCP 超时: {0}")]
|
||||
McpTimeout(String),
|
||||
|
||||
/// IO 错误(不可恢复)。
|
||||
#[error("IO 错误: {0}")]
|
||||
Io(Arc<std::io::Error>),
|
||||
|
||||
/// 取消。
|
||||
#[error("工具执行已取消: {0}")]
|
||||
Cancelled(String),
|
||||
|
||||
/// 其他未分类错误。
|
||||
#[error("其他错误: {0}")]
|
||||
Other(String),
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for ToolError {
|
||||
fn from(e: std::io::Error) -> Self {
|
||||
ToolError::Io(Arc::new(e))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToolError {
|
||||
/// 判断错误是否可恢复——可恢复的错误回传 LLM 由其自行重试,
|
||||
/// 不可恢复的错误终止自动 tool 循环并返回给调用方。
|
||||
pub fn is_recoverable(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::ExecutionFailed(..) | Self::InvalidArguments(..) | Self::Other(_)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_execution_failed_is_recoverable() {
|
||||
let err = ToolError::ExecutionFailed("foo".into(), "boom".into());
|
||||
assert!(err.is_recoverable());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_arguments_is_recoverable() {
|
||||
let err = ToolError::InvalidArguments("foo".into(), "missing x".into());
|
||||
assert!(err.is_recoverable());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_not_found_is_not_recoverable() {
|
||||
let err = ToolError::NotFound("foo".into());
|
||||
assert!(!err.is_recoverable());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_permission_denied_is_not_recoverable() {
|
||||
let err = ToolError::PermissionDenied("foo".into(), "Shell".into());
|
||||
assert!(!err.is_recoverable());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mcp_error_is_not_recoverable() {
|
||||
let err = ToolError::McpError("protocol".into());
|
||||
assert!(!err.is_recoverable());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mcp_timeout_is_not_recoverable() {
|
||||
let err = ToolError::McpTimeout("foo".into());
|
||||
assert!(!err.is_recoverable());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_io_is_not_recoverable() {
|
||||
let io_err = std::io::Error::new(std::io::ErrorKind::Other, "disk");
|
||||
let err = ToolError::from(io_err);
|
||||
assert!(!err.is_recoverable());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_other_is_recoverable() {
|
||||
let err = ToolError::Other("something".into());
|
||||
assert!(err.is_recoverable());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user