adk_server/a2a/
agent_card.rs

1use crate::a2a::{AgentCapabilities, AgentCard, AgentSkill};
2use adk_core::Agent;
3
4pub fn build_agent_skills(agent: &dyn Agent) -> Vec<AgentSkill> {
5    let mut skills = build_primary_skills(agent);
6    skills.extend(build_sub_agent_skills(agent));
7    skills
8}
9
10fn build_primary_skills(agent: &dyn Agent) -> Vec<AgentSkill> {
11    vec![AgentSkill::new(
12        agent.name().to_string(),
13        agent.name().to_string(),
14        agent.description().to_string(),
15        vec!["agent".to_string()],
16    )]
17}
18
19fn build_sub_agent_skills(agent: &dyn Agent) -> Vec<AgentSkill> {
20    let sub_agents = agent.sub_agents();
21    if sub_agents.is_empty() {
22        return vec![];
23    }
24
25    let mut skills = vec![];
26
27    // Add orchestration skill
28    let descriptions: Vec<String> = sub_agents
29        .iter()
30        .map(|sub| {
31            if sub.description().is_empty() {
32                "No description".to_string()
33            } else {
34                sub.description().to_string()
35            }
36        })
37        .collect();
38
39    skills.push(AgentSkill::new(
40        format!("{}-sub-agents", agent.name()),
41        "sub-agents".to_string(),
42        format!("Orchestrates: {}", descriptions.join("; ")),
43        vec!["orchestration".to_string()],
44    ));
45
46    // Recursively add sub-agent skills
47    for sub in sub_agents {
48        let sub_skills = build_primary_skills(sub.as_ref());
49        for skill in sub_skills {
50            skills.push(AgentSkill::new(
51                format!("{}_{}", sub.name(), skill.id),
52                format!("{}: {}", sub.name(), skill.name),
53                skill.description,
54                {
55                    let mut tags = vec![format!("sub_agent:{}", sub.name())];
56                    tags.extend(skill.tags);
57                    tags
58                },
59            ));
60        }
61    }
62
63    skills
64}
65
66pub fn build_agent_card(agent: &dyn Agent, base_url: &str) -> AgentCard {
67    AgentCard::builder()
68        .name(agent.name().to_string())
69        .description(agent.description().to_string())
70        .url(base_url.to_string())
71        .version("1.0.0".to_string())
72        .capabilities(AgentCapabilities {
73            streaming: true,
74            push_notifications: false,
75            state_transition_history: true,
76            extensions: None,
77        })
78        .skills(build_agent_skills(agent))
79        .build()
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85    use adk_core::{Agent, EventStream};
86    use async_trait::async_trait;
87    use std::sync::Arc;
88
89    struct TestAgent {
90        name: String,
91        description: String,
92    }
93
94    #[async_trait]
95    impl Agent for TestAgent {
96        fn name(&self) -> &str {
97            &self.name
98        }
99
100        fn description(&self) -> &str {
101            &self.description
102        }
103
104        async fn run(
105            &self,
106            _ctx: Arc<dyn adk_core::InvocationContext>,
107        ) -> adk_core::Result<EventStream> {
108            unimplemented!()
109        }
110
111        fn sub_agents(&self) -> &[Arc<dyn Agent>] {
112            &[]
113        }
114    }
115
116    #[test]
117    fn test_build_agent_skills() {
118        let agent =
119            TestAgent { name: "test_agent".to_string(), description: "A test agent".to_string() };
120
121        let skills = build_agent_skills(&agent);
122        assert_eq!(skills.len(), 1);
123        assert_eq!(skills[0].id, "test_agent");
124        assert_eq!(skills[0].name, "test_agent");
125    }
126
127    #[test]
128    fn test_build_agent_card() {
129        let agent =
130            TestAgent { name: "test_agent".to_string(), description: "A test agent".to_string() };
131
132        let card = build_agent_card(&agent, "https://example.com");
133        assert_eq!(card.name, "test_agent");
134        assert_eq!(card.url, "https://example.com");
135        assert!(card.capabilities.streaming);
136    }
137}