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