use std::collections::HashMap;
use std::time::Duration;
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::{OrchestrationResult, Task, TaskId};
#[async_trait]
pub trait SimpleAgent: Send + Sync {
async fn execute_task(&self, task: &Task) -> OrchestrationResult<TaskResult>;
fn capabilities(&self) -> &[String];
fn agent_id(&self) -> &str;
fn status(&self) -> AgentStatus {
AgentStatus::Available
}
fn can_handle_task(&self, task: &Task) -> bool {
if task.required_capabilities.is_empty() {
return true; }
let agent_caps: std::collections::HashSet<&String> = self.capabilities().iter().collect();
task.required_capabilities
.iter()
.any(|req| agent_caps.contains(req))
}
fn metadata(&self) -> AgentMetadata {
AgentMetadata {
agent_id: self.agent_id().to_string(),
capabilities: self.capabilities().to_vec(),
status: self.status(),
created_at: Utc::now(),
last_active: Utc::now(),
total_tasks_completed: 0,
average_execution_time: Duration::from_secs(0),
success_rate: 1.0,
custom_fields: HashMap::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TaskResult {
pub task_id: TaskId,
pub agent_id: String,
pub status: TaskExecutionStatus,
pub result_data: Option<serde_json::Value>,
pub error_message: Option<String>,
pub started_at: DateTime<Utc>,
pub completed_at: DateTime<Utc>,
pub duration: Duration,
pub confidence_score: f64,
pub metadata: HashMap<String, serde_json::Value>,
}
impl TaskResult {
pub fn success(
task_id: TaskId,
agent_id: String,
result_data: serde_json::Value,
started_at: DateTime<Utc>,
) -> Self {
let completed_at = Utc::now();
let duration = (completed_at - started_at)
.to_std()
.unwrap_or(Duration::from_secs(0));
Self {
task_id,
agent_id,
status: TaskExecutionStatus::Completed,
result_data: Some(result_data),
error_message: None,
started_at,
completed_at,
duration,
confidence_score: 1.0,
metadata: HashMap::new(),
}
}
pub fn failure(
task_id: TaskId,
agent_id: String,
error_message: String,
started_at: DateTime<Utc>,
) -> Self {
let completed_at = Utc::now();
let duration = (completed_at - started_at)
.to_std()
.unwrap_or(Duration::from_secs(0));
Self {
task_id,
agent_id,
status: TaskExecutionStatus::Failed,
result_data: None,
error_message: Some(error_message),
started_at,
completed_at,
duration,
confidence_score: 0.0,
metadata: HashMap::new(),
}
}
pub fn is_success(&self) -> bool {
matches!(self.status, TaskExecutionStatus::Completed)
}
pub fn is_failure(&self) -> bool {
matches!(self.status, TaskExecutionStatus::Failed)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum TaskExecutionStatus {
Completed,
Failed,
Cancelled,
TimedOut,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum AgentStatus {
Available,
Busy,
Unavailable,
Failed,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentMetadata {
pub agent_id: String,
pub capabilities: Vec<String>,
pub status: AgentStatus,
pub created_at: DateTime<Utc>,
pub last_active: DateTime<Utc>,
pub total_tasks_completed: u64,
pub average_execution_time: Duration,
pub success_rate: f64,
pub custom_fields: HashMap<String, serde_json::Value>,
}
pub struct ExampleAgent {
agent_id: String,
capabilities: Vec<String>,
status: AgentStatus,
}
impl ExampleAgent {
pub fn new(agent_id: String, capabilities: Vec<String>) -> Self {
Self {
agent_id,
capabilities,
status: AgentStatus::Available,
}
}
}
#[async_trait]
impl SimpleAgent for ExampleAgent {
async fn execute_task(&self, task: &Task) -> OrchestrationResult<TaskResult> {
let started_at = Utc::now();
tokio::time::sleep(Duration::from_millis(100)).await;
let result_data = serde_json::json!({
"task_id": task.task_id,
"description": task.description,
"agent_id": self.agent_id,
"message": "Task completed successfully"
});
Ok(TaskResult::success(
task.task_id.clone(),
self.agent_id.clone(),
result_data,
started_at,
))
}
fn capabilities(&self) -> &[String] {
&self.capabilities
}
fn agent_id(&self) -> &str {
&self.agent_id
}
fn status(&self) -> AgentStatus {
self.status.clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::TaskComplexity;
#[tokio::test]
async fn test_example_agent_execution() {
let agent = ExampleAgent::new(
"test_agent".to_string(),
vec!["general".to_string(), "testing".to_string()],
);
let task = Task::new(
"test_task".to_string(),
"Test task description".to_string(),
TaskComplexity::Simple,
1,
);
let result = agent.execute_task(&task).await.unwrap();
assert!(result.is_success());
assert_eq!(result.task_id, "test_task");
assert_eq!(result.agent_id, "test_agent");
}
#[test]
fn test_agent_capabilities() {
let agent = ExampleAgent::new(
"test_agent".to_string(),
vec!["capability1".to_string(), "capability2".to_string()],
);
assert_eq!(agent.capabilities().len(), 2);
assert!(agent.capabilities().contains(&"capability1".to_string()));
assert!(agent.capabilities().contains(&"capability2".to_string()));
}
#[test]
fn test_can_handle_task() {
let agent = ExampleAgent::new(
"test_agent".to_string(),
vec!["analysis".to_string(), "processing".to_string()],
);
let mut task = Task::new(
"test_task".to_string(),
"Test task".to_string(),
TaskComplexity::Simple,
1,
);
assert!(agent.can_handle_task(&task));
task.required_capabilities.push("analysis".to_string());
assert!(agent.can_handle_task(&task));
task.required_capabilities.clear();
task.required_capabilities
.push("unknown_capability".to_string());
assert!(!agent.can_handle_task(&task));
}
#[test]
fn test_task_result_creation() {
let started_at = Utc::now();
let success_result = TaskResult::success(
"task1".to_string(),
"agent1".to_string(),
serde_json::json!({"result": "success"}),
started_at,
);
assert!(success_result.is_success());
assert!(!success_result.is_failure());
let failure_result = TaskResult::failure(
"task2".to_string(),
"agent1".to_string(),
"Task failed".to_string(),
started_at,
);
assert!(!failure_result.is_success());
assert!(failure_result.is_failure());
}
}