Skip to main content

rs_adk/a2a/
remote_agent.rs

1//! Remote A2A agent — communicates with remote agents via A2A protocol.
2//!
3//! Mirrors ADK-Python's `RemoteA2aAgent`. Represents a remote agent
4//! that can be used as a sub-agent via the Agent-to-Agent protocol.
5
6use serde::{Deserialize, Serialize};
7
8/// Agent card describing a remote A2A agent's capabilities.
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct AgentCard {
11    /// The agent's name.
12    pub name: String,
13    /// The agent's description.
14    pub description: String,
15    /// URL of the remote agent's A2A endpoint.
16    pub url: String,
17    /// Supported input content types.
18    #[serde(default)]
19    pub input_content_types: Vec<String>,
20    /// Supported output content types.
21    #[serde(default)]
22    pub output_content_types: Vec<String>,
23    /// Skills/capabilities advertised by the agent.
24    #[serde(default)]
25    pub skills: Vec<AgentSkill>,
26}
27
28/// A skill advertised by a remote agent.
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct AgentSkill {
31    /// Name of the skill.
32    pub name: String,
33    /// Description of what the skill does.
34    pub description: String,
35    /// Example inputs that trigger this skill.
36    #[serde(default)]
37    pub examples: Vec<String>,
38}
39
40/// Configuration for the remote A2A agent.
41#[derive(Debug, Clone)]
42pub struct RemoteA2aAgentConfig {
43    /// Connection timeout in seconds.
44    pub timeout_secs: u64,
45    /// Whether to send full conversation history for stateless remote agents.
46    pub full_history_when_stateless: bool,
47}
48
49impl Default for RemoteA2aAgentConfig {
50    fn default() -> Self {
51        Self {
52            timeout_secs: 30,
53            full_history_when_stateless: false,
54        }
55    }
56}
57
58/// A remote agent accessible via the A2A protocol.
59///
60/// This agent delegates execution to a remote service via HTTP,
61/// converting between the local agent framework's events and the
62/// A2A protocol wire format.
63#[derive(Debug, Clone)]
64pub struct RemoteA2aAgent {
65    /// The agent's local name.
66    name: String,
67    /// The remote agent's card (or URL to fetch it from).
68    agent_card: AgentCard,
69    /// Configuration.
70    config: RemoteA2aAgentConfig,
71}
72
73impl RemoteA2aAgent {
74    /// Create a new remote A2A agent from an agent card.
75    pub fn new(name: impl Into<String>, agent_card: AgentCard) -> Self {
76        Self {
77            name: name.into(),
78            agent_card,
79            config: RemoteA2aAgentConfig::default(),
80        }
81    }
82
83    /// Set the configuration.
84    pub fn with_config(mut self, config: RemoteA2aAgentConfig) -> Self {
85        self.config = config;
86        self
87    }
88
89    /// Returns the agent's local name.
90    pub fn name(&self) -> &str {
91        &self.name
92    }
93
94    /// Returns the remote agent's card.
95    pub fn agent_card(&self) -> &AgentCard {
96        &self.agent_card
97    }
98
99    /// Returns the remote agent's A2A endpoint URL.
100    pub fn url(&self) -> &str {
101        &self.agent_card.url
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108
109    fn test_card() -> AgentCard {
110        AgentCard {
111            name: "remote-helper".into(),
112            description: "A helpful remote agent".into(),
113            url: "https://example.com/a2a".into(),
114            input_content_types: vec!["text/plain".into()],
115            output_content_types: vec!["text/plain".into()],
116            skills: vec![AgentSkill {
117                name: "search".into(),
118                description: "Search the web".into(),
119                examples: vec!["Find information about...".into()],
120            }],
121        }
122    }
123
124    #[test]
125    fn create_remote_agent() {
126        let agent = RemoteA2aAgent::new("helper", test_card());
127        assert_eq!(agent.name(), "helper");
128        assert_eq!(agent.url(), "https://example.com/a2a");
129    }
130
131    #[test]
132    fn agent_card_serde() {
133        let card = test_card();
134        let json = serde_json::to_string(&card).unwrap();
135        let deserialized: AgentCard = serde_json::from_str(&json).unwrap();
136        assert_eq!(deserialized.name, "remote-helper");
137        assert_eq!(deserialized.skills.len(), 1);
138    }
139
140    #[test]
141    fn default_config() {
142        let config = RemoteA2aAgentConfig::default();
143        assert_eq!(config.timeout_secs, 30);
144        assert!(!config.full_history_when_stateless);
145    }
146}