Skip to main content

agent_base/skill/
detail_tool.rs

1use 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}