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| part.get_text().map(|s| s.to_string()))
32            .unwrap_or_else(|| "No text provided".to_string());
33
34        let response = Message::builder()
35            .role(Role::Agent)
36            .parts(vec![Part::text(format!("Echo: {}", text))])
37            .message_id(Uuid::new_v4().to_string())
38            .context_id(message.context_id.clone())
39            .build();
40
41        Ok(Task::builder()
42            .id(task_id.to_string())
43            .context_id(message.context_id.clone())
44            .status(a2a_rs::domain::TaskStatus::new(
45                TaskState::Completed,
46                Some(response.clone()),
47            ))
48            .history(vec![message.clone(), response])
49            .build())
50    }
51
52    async fn validate_message(&self, message: &Message) -> Result<(), A2AError> {
53        if message.parts.is_empty() {
54            return Err(A2AError::ValidationError {
55                field: "parts".to_string(),
56                message: "Message must contain at least one part".to_string(),
57            });
58        }
59        Ok(())
60    }
61}
62
63#[tokio::main]
64async fn main() -> Result<(), BuildError> {
65    // Initialize logging
66    tracing_subscriber::fmt()
67        .with_env_filter(
68            tracing_subscriber::EnvFilter::from_default_env()
69                .add_directive(tracing::Level::INFO.into()),
70        )
71        .init();
72
73    println!("🚀 Starting agent with automatic storage creation");
74    println!("   Storage type is configured in auto_storage.toml");
75    println!();
76
77    // This is ALL you need! The builder:
78    // 1. Reads the TOML config
79    // 2. Creates the appropriate storage (in-memory or SQLx)
80    // 3. Wires everything together
81    // 4. Starts the servers
82    //
83    // No manual storage creation, no boilerplate!
84    AgentBuilder::from_file("examples/auto_storage.toml")?
85        .with_handler(EchoHandler)
86        .build_with_auto_storage() // <- Magic happens here!
87        .await?
88        .run()
89        .await
90        .map_err(|e| BuildError::RuntimeError(e.to_string()))?;
91
92    Ok(())
93}