Skip to main content

auto_storage_agent/
auto_storage_agent.rs

1//! Agent with automatic storage creation
2//!
3//! This example shows the SIMPLEST possible agent setup - just define your handler
4//! and the builder automatically creates storage from the TOML config.
5//!
6//! No manual storage creation needed!
7
8use a2a_agents::core::{AgentBuilder, BuildError};
9use a2a_rs::{
10    domain::{A2AError, Message, Part, Role, Task, TaskState},
11    port::AsyncMessageHandler,
12};
13use async_trait::async_trait;
14use uuid::Uuid;
15
16/// Simple echo handler
17#[derive(Clone)]
18struct EchoHandler;
19
20#[async_trait]
21impl AsyncMessageHandler for EchoHandler {
22    async fn process_message(
23        &self,
24        task_id: &str,
25        message: &Message,
26        _session_id: Option<&str>,
27    ) -> Result<Task, A2AError> {
28        let text = message
29            .parts
30            .iter()
31            .find_map(|part| match part {
32                Part::Text { text, .. } => Some(text.clone()),
33                _ => None,
34            })
35            .unwrap_or_else(|| "No text provided".to_string());
36
37        let response = Message::builder()
38            .role(Role::Agent)
39            .parts(vec![Part::text(format!("Echo: {}", text))])
40            .message_id(Uuid::new_v4().to_string())
41            .context_id(message.context_id.clone().unwrap_or_default())
42            .build();
43
44        Ok(Task::builder()
45            .id(task_id.to_string())
46            .context_id(message.context_id.clone().unwrap_or_default())
47            .status(a2a_rs::domain::TaskStatus {
48                state: TaskState::Completed,
49                message: Some(response.clone()),
50                timestamp: Some(chrono::Utc::now()),
51            })
52            .history(vec![message.clone(), response])
53            .build())
54    }
55
56    async fn validate_message(&self, message: &Message) -> Result<(), A2AError> {
57        if message.parts.is_empty() {
58            return Err(A2AError::ValidationError {
59                field: "parts".to_string(),
60                message: "Message must contain at least one part".to_string(),
61            });
62        }
63        Ok(())
64    }
65}
66
67#[tokio::main]
68async fn main() -> Result<(), BuildError> {
69    // Initialize logging
70    tracing_subscriber::fmt()
71        .with_env_filter(
72            tracing_subscriber::EnvFilter::from_default_env()
73                .add_directive(tracing::Level::INFO.into()),
74        )
75        .init();
76
77    println!("🚀 Starting agent with automatic storage creation");
78    println!("   Storage type is configured in auto_storage.toml");
79    println!();
80
81    // This is ALL you need! The builder:
82    // 1. Reads the TOML config
83    // 2. Creates the appropriate storage (in-memory or SQLx)
84    // 3. Wires everything together
85    // 4. Starts the servers
86    //
87    // No manual storage creation, no boilerplate!
88    AgentBuilder::from_file("examples/auto_storage.toml")?
89        .with_handler(EchoHandler)
90        .build_with_auto_storage() // <- Magic happens here!
91        .await?
92        .run()
93        .await
94        .map_err(|e| BuildError::RuntimeError(e.to_string()))?;
95
96    Ok(())
97}