gemini_adk_fluent_rs/
a2a.rs

1//! A2A — Agent-to-Agent protocol builders.
2//!
3//! Fluent builders for remote agent discovery, delegation, and server publishing.
4
5use std::time::Duration;
6
7/// Builder for a remote agent reference (client-side).
8///
9/// ```ignore
10/// let remote = RemoteAgent::new("verifier")
11///     .endpoint("https://agent.example.com")
12///     .timeout(Duration::from_secs(30))
13///     .describe("Verifies caller identity");
14/// ```
15#[derive(Clone, Debug)]
16pub struct RemoteAgent {
17    name: String,
18    endpoint: Option<String>,
19    timeout: Option<Duration>,
20    description: Option<String>,
21    streaming: bool,
22}
23
24impl RemoteAgent {
25    /// Create a new remote agent reference with the given name.
26    pub fn new(name: impl Into<String>) -> Self {
27        Self {
28            name: name.into(),
29            endpoint: None,
30            timeout: None,
31            description: None,
32            streaming: false,
33        }
34    }
35
36    /// Set the remote endpoint URL.
37    pub fn endpoint(mut self, url: impl Into<String>) -> Self {
38        self.endpoint = Some(url.into());
39        self
40    }
41
42    /// Set the request timeout.
43    pub fn timeout(mut self, duration: Duration) -> Self {
44        self.timeout = Some(duration);
45        self
46    }
47
48    /// Set a description for this remote agent.
49    pub fn describe(mut self, desc: impl Into<String>) -> Self {
50        self.description = Some(desc.into());
51        self
52    }
53
54    /// Enable streaming responses from the remote agent.
55    pub fn streaming(mut self, enabled: bool) -> Self {
56        self.streaming = enabled;
57        self
58    }
59
60    /// The agent name.
61    pub fn name(&self) -> &str {
62        &self.name
63    }
64
65    /// The configured endpoint.
66    pub fn get_endpoint(&self) -> Option<&str> {
67        self.endpoint.as_deref()
68    }
69
70    /// The configured timeout.
71    pub fn get_timeout(&self) -> Option<Duration> {
72        self.timeout
73    }
74}
75
76/// Builder for an A2A server that exposes a local agent.
77///
78/// ```ignore
79/// let server = A2AServer::new(my_agent)
80///     .host("0.0.0.0")
81///     .port(8080)
82///     .health_check("/health");
83/// ```
84#[derive(Clone, Debug)]
85pub struct A2AServer {
86    agent_name: String,
87    host: String,
88    port: u16,
89    health_check: String,
90    streaming: bool,
91}
92
93impl A2AServer {
94    /// Create a new A2A server for the given agent name.
95    pub fn new(agent_name: impl Into<String>) -> Self {
96        Self {
97            agent_name: agent_name.into(),
98            host: "0.0.0.0".to_string(),
99            port: 8080,
100            health_check: "/health".to_string(),
101            streaming: false,
102        }
103    }
104
105    /// Set the host to bind to.
106    pub fn host(mut self, host: impl Into<String>) -> Self {
107        self.host = host.into();
108        self
109    }
110
111    /// Set the port to listen on.
112    pub fn port(mut self, port: u16) -> Self {
113        self.port = port;
114        self
115    }
116
117    /// Set the health check endpoint path.
118    pub fn health_check(mut self, path: impl Into<String>) -> Self {
119        self.health_check = path.into();
120        self
121    }
122
123    /// Enable streaming support.
124    pub fn streaming(mut self, enabled: bool) -> Self {
125        self.streaming = enabled;
126        self
127    }
128
129    /// The agent name this server exposes.
130    pub fn agent_name(&self) -> &str {
131        &self.agent_name
132    }
133
134    /// The configured host.
135    pub fn get_host(&self) -> &str {
136        &self.host
137    }
138
139    /// The configured port.
140    pub fn get_port(&self) -> u16 {
141        self.port
142    }
143}
144
145/// Registry for discovering remote agents.
146#[derive(Clone, Debug)]
147pub struct AgentRegistry {
148    base_url: String,
149}
150
151impl AgentRegistry {
152    /// Create a registry pointing at the given base URL.
153    pub fn new(base_url: impl Into<String>) -> Self {
154        Self {
155            base_url: base_url.into(),
156        }
157    }
158
159    /// The base URL of this registry.
160    pub fn base_url(&self) -> &str {
161        &self.base_url
162    }
163}
164
165/// A2A skill declaration metadata.
166#[derive(Clone, Debug)]
167pub struct SkillDeclaration {
168    /// Skill identifier.
169    pub id: String,
170    /// Human-readable skill name.
171    pub name: String,
172    /// Description of what the skill does.
173    pub description: Option<String>,
174}
175
176impl SkillDeclaration {
177    /// Create a new skill declaration.
178    pub fn new(id: impl Into<String>, name: impl Into<String>) -> Self {
179        Self {
180            id: id.into(),
181            name: name.into(),
182            description: None,
183        }
184    }
185
186    /// Set a description.
187    pub fn describe(mut self, desc: impl Into<String>) -> Self {
188        self.description = Some(desc.into());
189        self
190    }
191}
192
193#[cfg(test)]
194mod tests {
195    use super::*;
196
197    #[test]
198    fn remote_agent_builder() {
199        let agent = RemoteAgent::new("verifier")
200            .endpoint("https://agent.example.com")
201            .timeout(Duration::from_secs(30))
202            .describe("Verifies identity")
203            .streaming(true);
204
205        assert_eq!(agent.name(), "verifier");
206        assert_eq!(agent.get_endpoint(), Some("https://agent.example.com"));
207        assert_eq!(agent.get_timeout(), Some(Duration::from_secs(30)));
208    }
209
210    #[test]
211    fn a2a_server_builder() {
212        let server = A2AServer::new("my-agent")
213            .host("127.0.0.1")
214            .port(9090)
215            .health_check("/ping")
216            .streaming(true);
217
218        assert_eq!(server.agent_name(), "my-agent");
219        assert_eq!(server.get_host(), "127.0.0.1");
220        assert_eq!(server.get_port(), 9090);
221    }
222
223    #[test]
224    fn agent_registry() {
225        let registry = AgentRegistry::new("https://registry.example.com");
226        assert_eq!(registry.base_url(), "https://registry.example.com");
227    }
228
229    #[test]
230    fn skill_declaration() {
231        let skill = SkillDeclaration::new("verify", "Identity Verification")
232            .describe("Verifies caller identity");
233        assert_eq!(skill.id, "verify");
234        assert_eq!(skill.name, "Identity Verification");
235        assert!(skill.description.is_some());
236    }
237}