Skip to main content

serdes_ai_a2a/
lib.rs

1//! # serdes-ai-a2a
2//!
3//! Agent-to-Agent (A2A) protocol support for serdesAI.
4//!
5//! This crate enables agents to communicate with each other using
6//! a standardized protocol based on the FastA2A specification.
7//!
8//! ## Features
9//!
10//! - Agent cards for capability discovery
11//! - Task submission and tracking
12//! - Streaming results
13//! - Storage and broker abstractions
14//!
15//! ## Example
16//!
17//! ```rust,ignore
18//! use serdes_ai_a2a::{agent_to_a2a, A2AConfig};
19//! use serdes_ai_agent::Agent;
20//!
21//! let agent = Agent::new(model).build();
22//!
23//! let a2a_server = agent_to_a2a(
24//!     agent,
25//!     A2AConfig::new()
26//!         .name("my-agent")
27//!         .url("http://localhost:8000")
28//!         .description("A helpful assistant")
29//! );
30//!
31//! // Start the server
32//! a2a_server.serve("0.0.0.0:8000").await?;
33//! ```
34
35pub mod broker;
36pub mod schema;
37pub mod storage;
38pub mod task;
39pub mod worker;
40
41#[cfg(feature = "server")]
42pub mod server;
43
44pub use broker::{Broker, BrokerError, InMemoryBroker};
45pub use schema::*;
46pub use storage::{InMemoryStorage, Storage, StorageError};
47pub use task::{Task, TaskError, TaskResult, TaskStatus};
48pub use worker::{AgentWorker, WorkerHandle};
49
50// Re-export for convenience
51pub use serdes_ai_agent::Agent;
52
53use std::sync::Arc;
54
55/// Convert an Agent to an A2A server.
56///
57/// This is the main entry point for creating an A2A server from an existing agent.
58///
59/// # Type Parameters
60///
61/// - `Deps`: Dependencies injected into tools and instruction functions.
62/// - `Output`: The output type of the agent.
63///
64/// # Example
65///
66/// ```rust,ignore
67/// let server = agent_to_a2a(
68///     agent,
69///     A2AConfig::new()
70///         .name("my-agent")
71///         .url("http://localhost:8000")
72/// );
73/// ```
74pub fn agent_to_a2a<Deps, Output>(
75    agent: Agent<Deps, Output>,
76    config: A2AConfig,
77) -> A2AServer<Deps, Output>
78where
79    Deps: Send + Sync + 'static,
80    Output: Send + Sync + 'static,
81{
82    A2AServer::new(agent, config)
83}
84
85/// A2A server wrapping an agent.
86///
87/// Provides the A2A protocol interface for an agent, including:
88/// - Agent card endpoint for capability discovery
89/// - Task submission and tracking
90/// - Storage and broker integration
91pub struct A2AServer<Deps, Output> {
92    agent: Arc<Agent<Deps, Output>>,
93    config: A2AConfig,
94    storage: Arc<dyn Storage>,
95    broker: Arc<dyn Broker>,
96}
97
98impl<Deps, Output> A2AServer<Deps, Output>
99where
100    Deps: Send + Sync + 'static,
101    Output: Send + Sync + 'static,
102{
103    /// Create a new A2A server.
104    pub fn new(agent: Agent<Deps, Output>, config: A2AConfig) -> Self {
105        Self {
106            agent: Arc::new(agent),
107            config,
108            storage: Arc::new(InMemoryStorage::new()),
109            broker: Arc::new(InMemoryBroker::new()),
110        }
111    }
112
113    /// Set a custom storage backend.
114    pub fn with_storage(mut self, storage: impl Storage + 'static) -> Self {
115        self.storage = Arc::new(storage);
116        self
117    }
118
119    /// Set a custom broker.
120    pub fn with_broker(mut self, broker: impl Broker + 'static) -> Self {
121        self.broker = Arc::new(broker);
122        self
123    }
124
125    /// Get the agent card describing this agent's capabilities.
126    pub fn agent_card(&self) -> AgentCard {
127        self.config.to_agent_card()
128    }
129
130    /// Get a reference to the underlying agent.
131    pub fn agent(&self) -> &Agent<Deps, Output> {
132        &self.agent
133    }
134
135    /// Get a reference to the storage.
136    pub fn storage(&self) -> &dyn Storage {
137        self.storage.as_ref()
138    }
139
140    /// Get a reference to the broker.
141    pub fn broker(&self) -> &dyn Broker {
142        self.broker.as_ref()
143    }
144
145    /// Get a clone of the storage Arc.
146    pub fn storage_arc(&self) -> Arc<dyn Storage> {
147        Arc::clone(&self.storage)
148    }
149
150    /// Get a clone of the broker Arc.
151    pub fn broker_arc(&self) -> Arc<dyn Broker> {
152        Arc::clone(&self.broker)
153    }
154}
155
156impl<Deps, Output> std::fmt::Debug for A2AServer<Deps, Output> {
157    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158        f.debug_struct("A2AServer")
159            .field("config", &self.config)
160            .finish_non_exhaustive()
161    }
162}
163
164/// Prelude for common imports.
165pub mod prelude {
166    pub use crate::{
167        agent_to_a2a, A2AConfig, A2AServer, AgentCard, Artifact, Broker, InMemoryBroker,
168        InMemoryStorage, Message, MessageRole, Part, Skill, Storage, Task, TaskIdParams,
169        TaskResult, TaskSendParams, TaskStatus,
170    };
171}
172
173#[cfg(test)]
174mod tests {
175    use super::*;
176
177    #[test]
178    fn test_config_builder() {
179        let config = A2AConfig::new()
180            .name("test-agent")
181            .url("http://localhost:8000")
182            .description("A test agent");
183
184        assert_eq!(config.name, "test-agent");
185        assert_eq!(config.url, "http://localhost:8000");
186        assert_eq!(config.description, Some("A test agent".to_string()));
187    }
188
189    #[test]
190    fn test_agent_card_creation() {
191        let config = A2AConfig::new()
192            .name("my-agent")
193            .url("http://localhost:9000")
194            .skill(Skill {
195                id: "chat".to_string(),
196                name: "Chat".to_string(),
197                description: Some("General conversation".to_string()),
198                tags: vec!["general".to_string()],
199            });
200
201        let card = config.to_agent_card();
202        assert_eq!(card.name, "my-agent");
203        assert_eq!(card.url, "http://localhost:9000");
204        assert_eq!(card.skills.len(), 1);
205        assert_eq!(card.skills[0].id, "chat");
206    }
207}