agent_base/skill/
detail_tool.rs1use std::sync::Arc;
2
3use async_trait::async_trait;
4use serde_json::{json, Value};
5
6use crate::tool::{Tool, ToolContext, ToolControlFlow, ToolOutput};
7use crate::types::{AgentEvent, AgentResult};
8
9use super::Skill;
10
11pub(crate) struct SkillDetailTool {
12 pub(crate) skills: Vec<Arc<dyn Skill>>,
13 pub(crate) name: &'static str,
14}
15
16impl SkillDetailTool {
17 pub(crate) fn new(skills: Vec<Arc<dyn Skill>>, tool_name: String) -> Self {
18 let name: &'static str = Box::leak(tool_name.into_boxed_str());
19 Self { skills, name }
20 }
21}
22
23#[async_trait]
24impl Tool for SkillDetailTool {
25 fn name(&self) -> &'static str {
26 self.name
27 }
28
29 fn definition(&self) -> Value {
30 json!({
31 "type": "function",
32 "function": {
33 "name": self.name,
34 "description": "Get detailed instructions for a Skill. Call this when you need the complete usage guide for a Skill.",
35 "parameters": {
36 "type": "object",
37 "properties": {
38 "name": {
39 "type": "string",
40 "description": "Skill name"
41 }
42 },
43 "required": ["name"]
44 }
45 }
46 })
47 }
48
49 async fn call(&self, args: &Value, ctx: &ToolContext) -> AgentResult<ToolOutput> {
50 let name = args
51 .get("name")
52 .and_then(Value::as_str)
53 .unwrap_or("");
54
55 if name.is_empty() {
56 return Ok(ToolOutput {
57 summary: format!(
58 "Please provide a Skill name. Available Skills: {}",
59 self.skills
60 .iter()
61 .map(|s| s.name())
62 .collect::<Vec<_>>()
63 .join(", ")
64 ),
65 raw: None,
66 control_flow: ToolControlFlow::Break,
67 truncated: false,
68 });
69 }
70
71 let detail = self
72 .skills
73 .iter()
74 .find(|s| s.name() == name)
75 .map(|s| s.detailed_description());
76
77 let _ = ctx.event_bus.send(AgentEvent::Custom {
78 session_id: ctx.session_id.clone(),
79 payload: json!({
80 "type": "skill_detail_loaded",
81 "skill": name,
82 }),
83 });
84
85 match detail {
86 Some(desc) => Ok(ToolOutput {
87 summary: desc.to_string(),
88 raw: None,
89 control_flow: ToolControlFlow::Break,
90 truncated: false,
91 }),
92 None => {
93 let available: Vec<&str> = self.skills.iter().map(|s| s.name()).collect();
94 Ok(ToolOutput {
95 summary: format!(
96 "Skill '{}' not found. Available Skills: {}",
97 name,
98 available.join(", ")
99 ),
100 raw: None,
101 control_flow: ToolControlFlow::Break,
102 truncated: false,
103 })
104 }
105 }
106 }
107}