use crate::tools::skill::{RiskLevel, SkillResult}; use chrono::Local; /// 内置工具执行器:统一处理参数校验、执行、错误返回 pub struct BuiltinRegistry; impl BuiltinRegistry { pub async fn execute(name: &str, args_json: &str) -> Option { match name { "get_current_datetime" => Some(execute_datetime()), "manage_memos" => Some(handle_memos(args_json).await), "query_weather" => { let params: serde_json::Value = serde_json::from_str(args_json).unwrap_or_default(); Some(super::weather::execute(params).await) } _ => None, } } /// 返回所有内置工具的 spec 列表 pub fn specs() -> Vec { vec![ super::skill::SkillSpec { name: "get_current_datetime".into(), description: "获取当前日期时间(北京时间 UTC+8)".into(), risk_level: RiskLevel::Low, parameters: serde_json::json!({"type":"object","properties":{"format":{"type":"string"}}}), timeout_secs: 5, }, super::skill::SkillSpec { name: "manage_memos".into(), description: "管理备忘录:添加/列出/删除。高风险操作(add/delete)需确认".into(), risk_level: RiskLevel::High, parameters: serde_json::json!({"type":"object","properties":{"action":{"type":"string","enum":["add","list","delete"]},"content":{"type":"string"},"id":{"type":"integer"}}}), timeout_secs: 10, }, super::weather::spec(), ] } pub fn is_high_risk(name: &str) -> bool { Self::specs().iter().any(|s| s.name == name && s.risk_level == RiskLevel::High) } pub fn is_builtin(name: &str) -> bool { Self::specs().iter().any(|s| s.name == name) } } fn execute_datetime() -> SkillResult { let now = Local::now().format("%Y-%m-%d %H:%M:%S").to_string(); SkillResult::ok(serde_json::json!({"datetime": now}).to_string()) } async fn handle_memos(args_json: &str) -> SkillResult { let mut params: serde_json::Value = serde_json::from_str(args_json).unwrap_or_default(); // call_capability 透传完整参数,action/content 在顶层 // 如果顶层没有 action,尝试从 prompt 推断 if params.get("action").is_none() { let prompt = params.get("prompt").and_then(|v| v.as_str()).unwrap_or(""); let action = if prompt.contains("添加") || prompt.contains("新增") || prompt.contains("add") { "add" } else if prompt.contains("删除") || prompt.contains("移除") || prompt.contains("delete") { "delete" } else { "list" }; let content = params.get("prompt").and_then(|v| v.as_str()).unwrap_or(""); let memo_id: i32 = content.chars().filter(|c| c.is_ascii_digit()).collect::().parse().unwrap_or(0); params = serde_json::json!({"action": action, "content": content, "id": memo_id}); } let action = params.get("action").and_then(|v| v.as_str()).unwrap_or("list"); let path = ".data/memos.json"; if let Err(e) = std::fs::create_dir_all(".data") { return SkillResult::error(format!("创建目录失败: {}", e)); } let mut data: Vec = if std::path::Path::new(path).exists() { serde_json::from_str(&std::fs::read_to_string(path).unwrap_or_default()).unwrap_or_default() } else { vec![] }; match action { "add" => { let content = params.get("content").and_then(|v| v.as_str()).unwrap_or(""); if content.is_empty() { return SkillResult::error("请提供 content 参数"); } let next_id = data.iter().filter_map(|m| m.get("id").and_then(|v| v.as_i64())).max().unwrap_or(0) + 1; data.push(serde_json::json!({ "id": next_id, "content": content, "created_at": Local::now().format("%Y-%m-%d %H:%M").to_string() })); if let Err(e) = std::fs::write(path, serde_json::to_string_pretty(&data).unwrap()) { return SkillResult::error(format!("写入备忘录失败: {}", e)); } SkillResult::ok(format!("已添加备忘录 #{}", next_id)) } "list" => { if data.is_empty() { return SkillResult::ok("暂无备忘录".to_string()); } let lines: Vec = data.iter().map(|m| format!( "#{} [{}] {}", m["id"], m.get("created_at").and_then(|v| v.as_str()).unwrap_or("?"), m["content"].as_str().unwrap_or("?") )).collect(); SkillResult::ok(lines.join("\n")) } "delete" => { let id = params.get("id").and_then(|v| v.as_i64()).unwrap_or(0); let before = data.len(); data.retain(|m| m.get("id").and_then(|v| v.as_i64()) != Some(id)); if data.len() == before { return SkillResult::error(format!("未找到备忘录 #{}", id)); } if let Err(e) = std::fs::write(path, serde_json::to_string_pretty(&data).unwrap()) { return SkillResult::error(format!("写入备忘录失败: {}", e)); } SkillResult::ok(format!("已删除备忘录 #{}", id)) } _ => SkillResult::error("未知操作,支持: add, list, delete"), } }