agentic_payments/agents/
mod.rs1use crate::error::{Error, Result};
4use chrono::{DateTime, Utc};
5use serde::{Deserialize, Serialize};
6use std::time::Duration;
7use uuid::Uuid;
8
9pub mod recovery;
11pub mod verification;
12
13pub use recovery::*;
15pub use verification::*;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
19pub enum AgentStatus {
20 Healthy,
22 Busy,
24 Error,
26 Recovering,
28 Quarantined,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct AgentHealth {
35 pub agent_id: Uuid,
37 pub status: AgentStatus,
39 pub total_verifications: usize,
41 pub successful_verifications: usize,
43 pub failed_verifications: usize,
45 pub avg_response_time_ms: f64,
47 pub last_heartbeat: DateTime<Utc>,
49 pub uptime_secs: u64,
51}
52
53impl AgentHealth {
54 pub fn new(agent_id: Uuid) -> Self {
56 Self {
57 agent_id,
58 status: AgentStatus::Healthy,
59 total_verifications: 0,
60 successful_verifications: 0,
61 failed_verifications: 0,
62 avg_response_time_ms: 0.0,
63 last_heartbeat: Utc::now(),
64 uptime_secs: 0,
65 }
66 }
67
68 pub fn heartbeat(&mut self) {
70 self.last_heartbeat = Utc::now();
71 }
72
73 pub fn record_verification(&mut self, success: bool, duration: Duration) {
75 self.total_verifications += 1;
76 if success {
77 self.successful_verifications += 1;
78 } else {
79 self.failed_verifications += 1;
80 }
81
82 let new_time = duration.as_millis() as f64;
84 if self.total_verifications == 1 {
85 self.avg_response_time_ms = new_time;
86 } else {
87 self.avg_response_time_ms =
88 (self.avg_response_time_ms * (self.total_verifications - 1) as f64 + new_time)
89 / self.total_verifications as f64;
90 }
91 }
92
93 pub fn success_rate(&self) -> f64 {
95 if self.total_verifications == 0 {
96 1.0
97 } else {
98 self.successful_verifications as f64 / self.total_verifications as f64
99 }
100 }
101
102 pub fn is_healthy(&self) -> bool {
104 self.status == AgentStatus::Healthy && self.success_rate() >= 0.95
105 }
106}
107
108#[async_trait::async_trait]
110pub trait VerificationAgent: Send + Sync {
111 fn id(&self) -> Uuid;
113
114 fn is_healthy(&self) -> bool;
116
117 async fn health_check(&self) -> Result<()> {
119 if self.is_healthy() {
120 Ok(())
121 } else {
122 Err(Error::agent_pool(format!(
123 "Agent {} is unhealthy",
124 self.id()
125 )))
126 }
127 }
128
129 async fn verify(
131 &self,
132 message: &[u8],
133 signature: &[u8],
134 public_key: &ed25519_dalek::VerifyingKey,
135 ) -> Result<bool>;
136}
137
138#[derive(Debug)]
140pub struct BasicVerificationAgent {
141 pub id: Uuid,
143 pub health: std::sync::Arc<std::sync::Mutex<AgentHealth>>,
145}
146
147impl BasicVerificationAgent {
148 pub fn new() -> Result<Self> {
150 let id = Uuid::new_v4();
151 Ok(Self {
152 id,
153 health: std::sync::Arc::new(std::sync::Mutex::new(AgentHealth::new(id))),
154 })
155 }
156}
157
158#[async_trait::async_trait]
159impl VerificationAgent for BasicVerificationAgent {
160 fn id(&self) -> Uuid {
161 self.id
162 }
163
164 fn is_healthy(&self) -> bool {
165 self.health.lock().unwrap().is_healthy()
166 }
167
168 async fn verify(
169 &self,
170 message: &[u8],
171 signature: &[u8],
172 public_key: &ed25519_dalek::VerifyingKey,
173 ) -> Result<bool> {
174 use ed25519_dalek::{Signature, Verifier};
175
176 let sig = Signature::from_bytes(signature.try_into().map_err(|_| {
177 Error::verification("Invalid signature length")
178 })?);
179
180 let start = std::time::Instant::now();
181 let result = public_key.verify(message, &sig).is_ok();
182
183 let mut health = self.health.lock().unwrap();
185 health.record_verification(result, start.elapsed());
186 health.heartbeat();
187
188 Ok(result)
189 }
190}
191
192impl Default for BasicVerificationAgent {
193 fn default() -> Self {
194 Self::new().unwrap()
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201
202 #[test]
203 fn test_agent_creation() {
204 let agent = BasicVerificationAgent::new().unwrap();
205 assert!(agent.is_healthy());
206 let health = agent.health.lock().unwrap();
208 assert_eq!(health.status, AgentStatus::Healthy);
209 assert_eq!(health.total_verifications, 0);
210 }
211
212 #[test]
213 fn test_health_metrics() {
214 let mut health = AgentHealth::new(Uuid::new_v4());
215
216 health.record_verification(true, Duration::from_millis(10));
217 assert_eq!(health.total_verifications, 1);
218 assert_eq!(health.successful_verifications, 1);
219 assert_eq!(health.success_rate(), 1.0);
220
221 health.record_verification(false, Duration::from_millis(20));
222 assert_eq!(health.total_verifications, 2);
223 assert_eq!(health.successful_verifications, 1);
224 assert_eq!(health.success_rate(), 0.5);
225 }
226}