a2a-agents 0.4.0

Framework for building A2A Protocol agents with declarative configuration
Documentation
use serde_json::{Map, Value, json};
use uuid::Uuid;

use a2a_agents::agents::reimbursement::handler::ReimbursementHandler;
use a2a_rs::InMemoryStreamingHandler;
use a2a_rs::adapter::storage::InMemoryTaskStorage;
use a2a_rs::domain::{Message, Part, Role};
use a2a_rs::port::message_handler::AsyncMessageHandler;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize the handler with in-memory task storage and a dedicated
    // streaming handler (streaming and push are separate ports).
    let task_storage = InMemoryTaskStorage::new();
    let push = task_storage.push_notifier();
    let handler = ReimbursementHandler::new(task_storage, InMemoryStreamingHandler::new(), push);

    // Example 1: Text part with metadata hints
    println!("=== Example 1: Text with metadata ===");
    let mut metadata1 = Map::new();
    metadata1.insert(
        "expense_type".to_string(),
        Value::String("travel".to_string()),
    );
    metadata1.insert("currency".to_string(), Value::String("EUR".to_string()));
    metadata1.insert("priority".to_string(), Value::String("high".to_string()));

    let message1 = Message::builder()
        .role(Role::User)
        .message_id(Uuid::new_v4().to_string())
        .parts(vec![
            Part::text_builder(
                "I need reimbursement for 150 euros spent on hotel in Paris on 2024-01-15"
                    .to_string(),
            )
            .with_metadata(serde_json::from_value(Value::Object(metadata1)).unwrap())
            .build(),
        ])
        .build();

    let task1 = handler.process_message("task1", &message1, None).await?;
    println!("Response: {:?}\n", task1.status.message);

    // Example 2: Data part with metadata
    println!("=== Example 2: Data with metadata ===");
    let mut data2 = Map::new();
    data2.insert("date".to_string(), Value::String("2024-01-20".to_string()));
    data2.insert(
        "amount".to_string(),
        Value::Number(serde_json::Number::from(75)),
    );
    data2.insert(
        "purpose".to_string(),
        Value::String("Client dinner meeting".to_string()),
    );

    let mut metadata2 = Map::new();
    metadata2.insert(
        "category_hint".to_string(),
        Value::String("meals".to_string()),
    );
    metadata2.insert("auto_approve".to_string(), Value::Bool(true));

    let message2 = Message::builder()
        .role(Role::User)
        .message_id(Uuid::new_v4().to_string())
        .parts(vec![
            Part::data_builder(serde_json::from_value(Value::Object(data2)).unwrap())
                .with_metadata(serde_json::from_value(Value::Object(metadata2)).unwrap())
                .build(),
        ])
        .build();

    let task2 = handler.process_message("task2", &message2, None).await?;
    println!("Response: {:?}\n", task2.status.message);

    // Example 3: File part with metadata
    println!("=== Example 3: File with metadata ===");
    let mut file_metadata = Map::new();
    file_metadata.insert(
        "file_name".to_string(),
        Value::String("receipt_hotel.pdf".to_string()),
    );
    file_metadata.insert(
        "size_bytes".to_string(),
        Value::Number(serde_json::Number::from(12345)),
    );
    file_metadata.insert(
        "uploaded_by".to_string(),
        Value::String("john.doe@company.com".to_string()),
    );

    let mut data3 = Map::new();
    data3.insert("date".to_string(), Value::String("2024-01-25".to_string()));
    data3.insert(
        "amount".to_string(),
        json!({"amount": 250.0, "currency": "USD"}),
    );
    data3.insert(
        "purpose".to_string(),
        Value::String("Hotel stay for conference".to_string()),
    );
    data3.insert("category".to_string(), Value::String("travel".to_string()));

    let file_metadata_struct: ::buffa_types::google::protobuf::Struct =
        serde_json::from_value(Value::Object(file_metadata)).unwrap();
    let file_part = Part::file_builder()
        .name("receipt_hotel.pdf".to_string())
        .mime_type("application/pdf".to_string())
        .bytes(b"Hello World!".to_vec())
        .with_metadata(file_metadata_struct)
        .build()
        .unwrap();

    let data_val3: ::buffa_types::google::protobuf::Value =
        serde_json::from_value(Value::Object(data3)).unwrap();
    let data_part3 = Part::data(data_val3);

    let message3 = Message::builder()
        .role(Role::User)
        .message_id(Uuid::new_v4().to_string())
        .parts(vec![data_part3, file_part])
        .build();

    let task3 = handler.process_message("task3", &message3, None).await?;
    println!("Response: {:?}\n", task3.status.message);

    Ok(())
}