Skip to main content

codetether_agent/a2a/
client.rs

1//! A2A Client - for connecting to other A2A agents
2
3use super::types::*;
4use anyhow::Result;
5use reqwest::Client;
6
7/// A2A Client for interacting with A2A agents
8pub struct A2AClient {
9    client: Client,
10    base_url: String,
11    token: Option<String>,
12}
13
14#[allow(dead_code)]
15impl A2AClient {
16    /// Create a new A2A client
17    pub fn new(base_url: impl Into<String>) -> Self {
18        Self {
19            client: Client::new(),
20            base_url: base_url.into().trim_end_matches('/').to_string(),
21            token: None,
22        }
23    }
24
25    /// Set authentication token
26    pub fn with_token(mut self, token: impl Into<String>) -> Self {
27        self.token = Some(token.into());
28        self
29    }
30
31    /// Get the agent card
32    pub async fn get_agent_card(&self) -> Result<AgentCard> {
33        let url = format!("{}/.well-known/agent.json", self.base_url);
34        let res = self.client.get(&url).send().await?;
35        let card: AgentCard = res.json().await?;
36        Ok(card)
37    }
38
39    /// Send a message to the agent
40    pub async fn send_message(&self, params: MessageSendParams) -> Result<Task> {
41        let request = JsonRpcRequest {
42            jsonrpc: "2.0".to_string(),
43            id: serde_json::json!(1),
44            method: "message/send".to_string(),
45            params: serde_json::to_value(&params)?,
46        };
47
48        let response = self.call_rpc(request).await?;
49        
50        if let Some(error) = response.error {
51            anyhow::bail!("RPC error {}: {}", error.code, error.message);
52        }
53
54        let task: Task = serde_json::from_value(
55            response.result.ok_or_else(|| anyhow::anyhow!("No result"))?,
56        )?;
57        Ok(task)
58    }
59
60    /// Get task status
61    #[allow(dead_code)]
62    pub async fn get_task(&self, id: &str, history_length: Option<usize>) -> Result<Task> {
63        let request = JsonRpcRequest {
64            jsonrpc: "2.0".to_string(),
65            id: serde_json::json!(1),
66            method: "tasks/get".to_string(),
67            params: serde_json::to_value(TaskQueryParams {
68                id: id.to_string(),
69                history_length,
70            })?,
71        };
72
73        let response = self.call_rpc(request).await?;
74        
75        if let Some(error) = response.error {
76            anyhow::bail!("RPC error {}: {}", error.code, error.message);
77        }
78
79        let task: Task = serde_json::from_value(
80            response.result.ok_or_else(|| anyhow::anyhow!("No result"))?,
81        )?;
82        Ok(task)
83    }
84
85    /// Cancel a task
86    #[allow(dead_code)]
87    pub async fn cancel_task(&self, id: &str) -> Result<Task> {
88        let request = JsonRpcRequest {
89            jsonrpc: "2.0".to_string(),
90            id: serde_json::json!(1),
91            method: "tasks/cancel".to_string(),
92            params: serde_json::json!({ "id": id }),
93        };
94
95        let response = self.call_rpc(request).await?;
96        
97        if let Some(error) = response.error {
98            anyhow::bail!("RPC error {}: {}", error.code, error.message);
99        }
100
101        let task: Task = serde_json::from_value(
102            response.result.ok_or_else(|| anyhow::anyhow!("No result"))?,
103        )?;
104        Ok(task)
105    }
106
107    /// Make a JSON-RPC call
108    pub async fn call_rpc(&self, request: JsonRpcRequest) -> Result<JsonRpcResponse> {
109        let mut req = self.client.post(&self.base_url);
110        
111        if let Some(ref token) = self.token {
112            req = req.bearer_auth(token);
113        }
114
115        let res = req
116            .header("Content-Type", "application/json")
117            .json(&request)
118            .send()
119            .await?;
120
121        let response: JsonRpcResponse = res.json().await?;
122        Ok(response)
123    }
124}