minimal_agent/
minimal_agent.rs1use a2a_agents::core::{AgentBuilder, BuildError};
7use a2a_rs::{
8 InMemoryTaskStorage,
9 domain::{A2AError, Message, Part, Role, Task, TaskState},
10 port::AsyncMessageHandler,
11};
12use async_trait::async_trait;
13use uuid::Uuid;
14
15#[derive(Clone)]
17struct EchoHandler;
18
19#[async_trait]
20impl AsyncMessageHandler for EchoHandler {
21 async fn process_message(
22 &self,
23 task_id: &str,
24 message: &Message,
25 _session_id: Option<&str>,
26 ) -> Result<Task, A2AError> {
27 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()
36 .role(Role::Agent)
37 .parts(vec![Part::text(format!("Echo: {}", text))])
38 .message_id(Uuid::new_v4().to_string())
39 .context_id(message.context_id.clone())
40 .build();
41
42 Ok(Task::builder()
44 .id(task_id.to_string())
45 .context_id(message.context_id.clone())
46 .status(a2a_rs::domain::TaskStatus::new(
47 TaskState::Completed,
48 Some(response.clone()),
49 ))
50 .history(vec![message.clone(), response])
51 .build())
52 }
53
54 async fn validate_message(&self, message: &Message) -> Result<(), A2AError> {
55 if message.parts.is_empty() {
56 return Err(A2AError::ValidationError {
57 field: "parts".to_string(),
58 message: "Message must contain at least one part".to_string(),
59 });
60 }
61 Ok(())
62 }
63}
64
65#[tokio::main]
66async fn main() -> Result<(), BuildError> {
67 tracing_subscriber::fmt()
69 .with_env_filter(
70 tracing_subscriber::EnvFilter::from_default_env()
71 .add_directive(tracing::Level::INFO.into()),
72 )
73 .init();
74
75 println!("🚀 Starting Echo Agent with declarative configuration");
76 println!();
77
78 AgentBuilder::from_file("examples/minimal_agent.toml")?
81 .with_handler(EchoHandler)
82 .with_storage(InMemoryTaskStorage::new())
83 .build()?
84 .run()
85 .await
86 .map_err(|e| BuildError::RuntimeError(e.to_string()))?;
87
88 Ok(())
89}