1use crate::{Agent, AgentId, AgentState};
7use std::collections::HashMap;
8use std::sync::{Arc, RwLock};
9use uuid::Uuid;
10
11pub type BehaviorId = Uuid;
13
14#[derive(Debug, Clone)]
16pub struct BehaviorContext {
17 pub agent_id: AgentId,
19 pub agent_state: AgentState,
21 pub timestamp: u64,
23 pub data: HashMap<String, String>,
25}
26
27impl BehaviorContext {
28 pub fn new(agent: &Agent) -> Self {
30 Self {
31 agent_id: agent.id(),
32 agent_state: agent.state().clone(),
33 timestamp: std::time::SystemTime::now()
34 .duration_since(std::time::UNIX_EPOCH)
35 .unwrap_or_default()
36 .as_millis() as u64,
37 data: HashMap::new(),
38 }
39 }
40
41 pub fn with_data(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
43 self.data.insert(key.into(), value.into());
44 self
45 }
46
47 pub fn get_data(&self, key: &str) -> Option<&String> {
49 self.data.get(key)
50 }
51}
52
53#[derive(Debug, Clone)]
55pub enum BehaviorResult {
56 Success,
58 Failed(String),
60 Skipped(String),
62}
63
64impl BehaviorResult {
65 pub fn is_success(&self) -> bool {
66 matches!(self, BehaviorResult::Success)
67 }
68
69 pub fn is_failed(&self) -> bool {
70 matches!(self, BehaviorResult::Failed(_))
71 }
72}
73
74pub trait Behavior: Send + Sync {
76 fn name(&self) -> &str;
78
79 fn description(&self) -> &str {
81 ""
82 }
83
84 fn can_execute(&self, _context: &BehaviorContext) -> bool {
86 true
87 }
88
89 fn execute(&self, context: &BehaviorContext) -> BehaviorResult;
91
92 fn on_attach(&self, _agent_id: AgentId) -> BehaviorResult {
94 BehaviorResult::Success
95 }
96
97 fn on_detach(&self, _agent_id: AgentId) -> BehaviorResult {
99 BehaviorResult::Success
100 }
101}
102
103pub struct ClosureBehavior {
105 name: String,
106 description: String,
107 executor: Arc<dyn Fn(&BehaviorContext) -> BehaviorResult + Send + Sync>,
108}
109
110impl ClosureBehavior {
111 pub fn new<F>(name: impl Into<String>, executor: F) -> Self
113 where
114 F: Fn(&BehaviorContext) -> BehaviorResult + Send + Sync + 'static,
115 {
116 Self {
117 name: name.into(),
118 description: String::new(),
119 executor: Arc::new(executor),
120 }
121 }
122
123 pub fn with_description(mut self, description: impl Into<String>) -> Self {
125 self.description = description.into();
126 self
127 }
128}
129
130impl Behavior for ClosureBehavior {
131 fn name(&self) -> &str {
132 &self.name
133 }
134
135 fn description(&self) -> &str {
136 &self.description
137 }
138
139 fn execute(&self, context: &BehaviorContext) -> BehaviorResult {
140 (self.executor)(context)
141 }
142}
143
144pub struct LoggingBehavior {
146 name: String,
147}
148
149impl LoggingBehavior {
150 pub fn new() -> Self {
151 Self {
152 name: "logging".to_string(),
153 }
154 }
155}
156
157impl Default for LoggingBehavior {
158 fn default() -> Self {
159 Self::new()
160 }
161}
162
163impl Behavior for LoggingBehavior {
164 fn name(&self) -> &str {
165 &self.name
166 }
167
168 fn description(&self) -> &str {
169 "Logs agent state and activity"
170 }
171
172 fn execute(&self, context: &BehaviorContext) -> BehaviorResult {
173 println!(
174 "[Log] Agent {} in state {:?} at {}",
175 context.agent_id, context.agent_state, context.timestamp
176 );
177 BehaviorResult::Success
178 }
179}
180
181pub struct MetricsBehavior {
183 name: String,
184 metrics: Arc<RwLock<HashMap<String, u64>>>,
185}
186
187impl MetricsBehavior {
188 pub fn new() -> Self {
189 Self {
190 name: "metrics".to_string(),
191 metrics: Arc::new(RwLock::new(HashMap::new())),
192 }
193 }
194
195 pub fn get_metric(&self, key: &str) -> Option<u64> {
197 self.metrics
198 .read()
199 .expect("Lock poisoned: metrics")
200 .get(key)
201 .copied()
202 }
203
204 pub fn all_metrics(&self) -> HashMap<String, u64> {
206 self.metrics.read().expect("Lock poisoned: metrics").clone()
207 }
208}
209
210impl Default for MetricsBehavior {
211 fn default() -> Self {
212 Self::new()
213 }
214}
215
216impl Behavior for MetricsBehavior {
217 fn name(&self) -> &str {
218 &self.name
219 }
220
221 fn description(&self) -> &str {
222 "Collects agent execution metrics"
223 }
224
225 fn execute(&self, context: &BehaviorContext) -> BehaviorResult {
226 let mut metrics = self.metrics.write().expect("Lock poisoned: metrics");
227 let key = format!("state_{:?}", context.agent_state);
228 *metrics.entry(key).or_insert(0) += 1;
229 *metrics.entry("total_executions".to_string()).or_insert(0) += 1;
230 BehaviorResult::Success
231 }
232}
233
234pub struct HealthCheckBehavior {
236 name: String,
237 healthy_states: Vec<AgentState>,
238}
239
240impl HealthCheckBehavior {
241 pub fn new() -> Self {
242 Self {
243 name: "health_check".to_string(),
244 healthy_states: vec![AgentState::Running, AgentState::Created],
245 }
246 }
247
248 pub fn with_healthy_states(mut self, states: Vec<AgentState>) -> Self {
249 self.healthy_states = states;
250 self
251 }
252}
253
254impl Default for HealthCheckBehavior {
255 fn default() -> Self {
256 Self::new()
257 }
258}
259
260impl Behavior for HealthCheckBehavior {
261 fn name(&self) -> &str {
262 &self.name
263 }
264
265 fn description(&self) -> &str {
266 "Checks agent health based on state"
267 }
268
269 fn execute(&self, context: &BehaviorContext) -> BehaviorResult {
270 if self.healthy_states.contains(&context.agent_state) {
271 BehaviorResult::Success
272 } else {
273 BehaviorResult::Failed(format!("Unhealthy state: {:?}", context.agent_state))
274 }
275 }
276}
277
278pub struct BehaviorComposite {
280 agent_id: AgentId,
281 behaviors: RwLock<HashMap<BehaviorId, Arc<dyn Behavior>>>,
282 execution_order: RwLock<Vec<BehaviorId>>,
283}
284
285impl BehaviorComposite {
286 pub fn new(agent_id: AgentId) -> Self {
288 Self {
289 agent_id,
290 behaviors: RwLock::new(HashMap::new()),
291 execution_order: RwLock::new(Vec::new()),
292 }
293 }
294
295 pub fn attach(&self, behavior: Arc<dyn Behavior>) -> BehaviorId {
297 let id = Uuid::new_v4();
298
299 behavior.on_attach(self.agent_id);
301
302 self.behaviors
304 .write()
305 .expect("Lock poisoned: behaviors")
306 .insert(id, behavior);
307 self.execution_order
308 .write()
309 .expect("Lock poisoned: execution_order")
310 .push(id);
311
312 id
313 }
314
315 pub fn detach(&self, behavior_id: &BehaviorId) -> Option<Arc<dyn Behavior>> {
317 let behavior = self
318 .behaviors
319 .write()
320 .expect("Lock poisoned: behaviors")
321 .remove(behavior_id)?;
322
323 behavior.on_detach(self.agent_id);
325
326 self.execution_order
328 .write()
329 .expect("Lock poisoned: execution_order")
330 .retain(|id| id != behavior_id);
331
332 Some(behavior)
333 }
334
335 pub fn get(&self, behavior_id: &BehaviorId) -> Option<Arc<dyn Behavior>> {
337 self.behaviors
338 .read()
339 .expect("Lock poisoned: behaviors")
340 .get(behavior_id)
341 .cloned()
342 }
343
344 pub fn execute_all(&self, context: &BehaviorContext) -> Vec<(BehaviorId, BehaviorResult)> {
346 let order = self
347 .execution_order
348 .read()
349 .expect("Lock poisoned: execution_order");
350 let behaviors = self.behaviors.read().expect("Lock poisoned: behaviors");
351
352 order
353 .iter()
354 .filter_map(|id| {
355 let behavior = behaviors.get(id)?;
356 if behavior.can_execute(context) {
357 let result = behavior.execute(context);
358 Some((*id, result))
359 } else {
360 Some((
361 *id,
362 BehaviorResult::Skipped("Preconditions not met".to_string()),
363 ))
364 }
365 })
366 .collect()
367 }
368
369 pub fn execute(
371 &self,
372 behavior_id: &BehaviorId,
373 context: &BehaviorContext,
374 ) -> Option<BehaviorResult> {
375 let behaviors = self.behaviors.read().expect("Lock poisoned: behaviors");
376 let behavior = behaviors.get(behavior_id)?;
377
378 if behavior.can_execute(context) {
379 Some(behavior.execute(context))
380 } else {
381 Some(BehaviorResult::Skipped("Preconditions not met".to_string()))
382 }
383 }
384
385 pub fn count(&self) -> usize {
387 self.behaviors
388 .read()
389 .expect("Lock poisoned: behaviors")
390 .len()
391 }
392
393 pub fn behavior_ids(&self) -> Vec<BehaviorId> {
395 self.execution_order
396 .read()
397 .expect("Lock poisoned: execution_order")
398 .clone()
399 }
400
401 pub fn behavior_names(&self) -> Vec<String> {
403 let order = self
404 .execution_order
405 .read()
406 .expect("Lock poisoned: execution_order");
407 let behaviors = self.behaviors.read().expect("Lock poisoned: behaviors");
408
409 order
410 .iter()
411 .filter_map(|id| behaviors.get(id).map(|b| b.name().to_string()))
412 .collect()
413 }
414}
415
416pub struct BehaviorRegistry {
418 composites: RwLock<HashMap<AgentId, Arc<BehaviorComposite>>>,
420}
421
422impl BehaviorRegistry {
423 pub fn new() -> Self {
425 Self {
426 composites: RwLock::new(HashMap::new()),
427 }
428 }
429
430 pub fn register_agent(&self, agent_id: AgentId) -> Arc<BehaviorComposite> {
432 let composite = Arc::new(BehaviorComposite::new(agent_id));
433 self.composites
434 .write()
435 .expect("Lock poisoned: composites")
436 .insert(agent_id, Arc::clone(&composite));
437 composite
438 }
439
440 pub fn get_composite(&self, agent_id: &AgentId) -> Option<Arc<BehaviorComposite>> {
442 self.composites
443 .read()
444 .expect("Lock poisoned: composites")
445 .get(agent_id)
446 .cloned()
447 }
448
449 pub fn unregister_agent(&self, agent_id: &AgentId) -> Option<Arc<BehaviorComposite>> {
451 self.composites
452 .write()
453 .expect("Lock poisoned: composites")
454 .remove(agent_id)
455 }
456
457 pub fn execute_for_agent(
459 &self,
460 agent_id: &AgentId,
461 context: &BehaviorContext,
462 ) -> Option<Vec<(BehaviorId, BehaviorResult)>> {
463 let composite = self.get_composite(agent_id)?;
464 Some(composite.execute_all(context))
465 }
466
467 pub fn agent_count(&self) -> usize {
469 self.composites
470 .read()
471 .expect("Lock poisoned: composites")
472 .len()
473 }
474}
475
476impl Default for BehaviorRegistry {
477 fn default() -> Self {
478 Self::new()
479 }
480}
481
482#[cfg(test)]
483mod tests {
484 use super::*;
485
486 #[test]
487 fn test_behavior_context() {
488 let agent = Agent::new(vec![0x00, 0x61, 0x73, 0x6d]);
489 let context = BehaviorContext::new(&agent);
490
491 assert_eq!(context.agent_id, agent.id());
492 assert_eq!(context.agent_state, AgentState::Created);
493 }
494
495 #[test]
496 fn test_closure_behavior() {
497 let behavior = ClosureBehavior::new("test", |_| BehaviorResult::Success);
498
499 let agent = Agent::new(vec![0x00, 0x61, 0x73, 0x6d]);
500 let context = BehaviorContext::new(&agent);
501
502 let result = behavior.execute(&context);
503 assert!(result.is_success());
504 }
505
506 #[test]
507 fn test_logging_behavior() {
508 let behavior = LoggingBehavior::new();
509 let agent = Agent::new(vec![0x00, 0x61, 0x73, 0x6d]);
510 let context = BehaviorContext::new(&agent);
511
512 let result = behavior.execute(&context);
513 assert!(result.is_success());
514 }
515
516 #[test]
517 fn test_metrics_behavior() {
518 let behavior = MetricsBehavior::new();
519 let agent = Agent::new(vec![0x00, 0x61, 0x73, 0x6d]);
520 let context = BehaviorContext::new(&agent);
521
522 behavior.execute(&context);
523 behavior.execute(&context);
524
525 assert_eq!(behavior.get_metric("total_executions"), Some(2));
526 }
527
528 #[test]
529 fn test_health_check_behavior() {
530 let behavior = HealthCheckBehavior::new();
531 let mut agent = Agent::new(vec![0x00, 0x61, 0x73, 0x6d]);
532
533 let context = BehaviorContext::new(&agent);
535 let result = behavior.execute(&context);
536 assert!(result.is_success());
537
538 agent.set_state(AgentState::Error);
540 let context = BehaviorContext::new(&agent);
541 let result = behavior.execute(&context);
542 assert!(result.is_failed());
543 }
544
545 #[test]
546 fn test_behavior_composite() {
547 let agent = Agent::new(vec![0x00, 0x61, 0x73, 0x6d]);
548 let composite = BehaviorComposite::new(agent.id());
549
550 let behavior1 = Arc::new(LoggingBehavior::new());
552 let behavior2 = Arc::new(MetricsBehavior::new());
553
554 let id1 = composite.attach(behavior1);
555 let _id2 = composite.attach(behavior2);
556
557 assert_eq!(composite.count(), 2);
558
559 let context = BehaviorContext::new(&agent);
561 let results = composite.execute_all(&context);
562 assert_eq!(results.len(), 2);
563
564 composite.detach(&id1);
566 assert_eq!(composite.count(), 1);
567 }
568
569 #[test]
570 fn test_behavior_registry() {
571 let registry = BehaviorRegistry::new();
572 let agent = Agent::new(vec![0x00, 0x61, 0x73, 0x6d]);
573
574 let composite = registry.register_agent(agent.id());
575 assert_eq!(registry.agent_count(), 1);
576
577 let behavior = Arc::new(LoggingBehavior::new());
579 composite.attach(behavior);
580
581 let context = BehaviorContext::new(&agent);
583 let results = registry.execute_for_agent(&agent.id(), &context);
584 assert!(results.is_some());
585 assert_eq!(results.unwrap().len(), 1);
586
587 registry.unregister_agent(&agent.id());
589 assert_eq!(registry.agent_count(), 0);
590 }
591
592 #[test]
593 fn test_behavior_names() {
594 let agent = Agent::new(vec![0x00, 0x61, 0x73, 0x6d]);
595 let composite = BehaviorComposite::new(agent.id());
596
597 composite.attach(Arc::new(LoggingBehavior::new()));
598 composite.attach(Arc::new(MetricsBehavior::new()));
599 composite.attach(Arc::new(HealthCheckBehavior::new()));
600
601 let names = composite.behavior_names();
602 assert_eq!(names.len(), 3);
603 assert!(names.contains(&"logging".to_string()));
604 assert!(names.contains(&"metrics".to_string()));
605 assert!(names.contains(&"health_check".to_string()));
606 }
607}