use crate::agent::{Agent, AgentId};
use crate::error::{CoalescentError, Result};
use crate::task::{Task, TaskStatus};
use crate::trust::TrustManager;
use std::collections::HashMap;
use tokio::sync::RwLock;
use uuid::Uuid;
#[derive(Debug, Clone)]
pub struct Coalition {
pub id: Uuid,
pub task: Task,
pub agents: Vec<AgentId>,
pub leader: Option<AgentId>,
pub state: CoalitionState,
}
#[derive(Debug, Clone, PartialEq)]
pub enum CoalitionState {
Forming,
Active,
Completed,
Failed(String),
Disbanded,
}
#[derive(Debug)]
pub struct CoordinationResult {
pub success: bool,
pub coalition: Option<Coalition>,
pub messages: Vec<String>,
}
pub struct CoordinationEngine {
agents: RwLock<HashMap<AgentId, Agent>>,
coalitions: RwLock<HashMap<Uuid, Coalition>>,
trust_manager: TrustManager,
config: CoordinationConfig,
}
#[derive(Debug, Clone)]
pub struct CoordinationConfig {
pub formation_timeout: u64,
pub min_trust_score: f64,
pub auto_assign_leaders: bool,
pub max_coalition_size: usize,
}
impl Default for CoordinationConfig {
fn default() -> Self {
Self {
formation_timeout: 300, min_trust_score: 0.5,
auto_assign_leaders: true,
max_coalition_size: 10,
}
}
}
impl CoordinationEngine {
pub fn new() -> Self {
Self::with_config(CoordinationConfig::default())
}
pub fn with_config(config: CoordinationConfig) -> Self {
Self {
agents: RwLock::new(HashMap::new()),
coalitions: RwLock::new(HashMap::new()),
trust_manager: TrustManager::new(),
config,
}
}
pub async fn register_agent(&self, agent: Agent) -> Result<()> {
let agent_id = agent.id;
let mut agents = self.agents.write().await;
if agents.contains_key(&agent_id) {
return Err(CoalescentError::agent(format!(
"Agent {} is already registered", agent_id
)));
}
agents.insert(agent_id, agent);
tracing::info!("Registered agent: {}", agent_id);
Ok(())
}
pub async fn unregister_agent(&self, agent_id: &AgentId) -> Result<()> {
let mut agents = self.agents.write().await;
if agents.remove(agent_id).is_none() {
return Err(CoalescentError::agent(format!(
"Agent {} not found", agent_id
)));
}
tracing::info!("Unregistered agent: {}", agent_id);
Ok(())
}
pub async fn find_capable_agents(&self, task: &Task) -> Vec<AgentId> {
let agents = self.agents.read().await;
let mut capable_agents = Vec::new();
for (agent_id, agent) in agents.iter() {
let has_required = task.required_capabilities.iter()
.all(|cap| agent.capabilities.iter().any(|agent_cap| &agent_cap.name == cap));
if has_required {
if let Ok(trust_score) = self.trust_manager.get_trust_score(agent_id).await {
if trust_score >= self.config.min_trust_score {
capable_agents.push(*agent_id);
}
}
}
}
capable_agents
}
pub async fn coalesce_around_task(&self, mut task: Task) -> Result<CoordinationResult> {
tracing::info!("Starting coalescence for task: {}", task.title);
let capable_agents = self.find_capable_agents(&task).await;
if capable_agents.len() < task.min_agents {
return Ok(CoordinationResult {
success: false,
coalition: None,
messages: vec![format!(
"Not enough capable agents found. Required: {}, Found: {}",
task.min_agents, capable_agents.len()
)],
});
}
let coalition_size = std::cmp::min(
capable_agents.len(),
task.max_agents.unwrap_or(self.config.max_coalition_size)
);
let selected_agents: Vec<AgentId> = capable_agents.into_iter().take(coalition_size).collect();
let coalition_id = Uuid::new_v4();
let leader = if self.config.auto_assign_leaders && !selected_agents.is_empty() {
Some(selected_agents[0])
} else {
None
};
task.update_status(TaskStatus::InProgress);
let coalition = Coalition {
id: coalition_id,
task,
agents: selected_agents,
leader,
state: CoalitionState::Active,
};
let mut coalitions = self.coalitions.write().await;
let agent_count = coalition.agents.len();
coalitions.insert(coalition_id, coalition.clone());
tracing::info!("Coalition {} formed with {} agents", coalition_id, agent_count);
Ok(CoordinationResult {
success: true,
coalition: Some(coalition),
messages: vec![format!("Coalition formed successfully with {} agents", agent_count)],
})
}
pub async fn get_coalition(&self, coalition_id: &Uuid) -> Option<Coalition> {
let coalitions = self.coalitions.read().await;
coalitions.get(coalition_id).cloned()
}
pub async fn list_coalitions(&self) -> Vec<Coalition> {
let coalitions = self.coalitions.read().await;
coalitions.values().cloned().collect()
}
pub fn config(&self) -> &CoordinationConfig {
&self.config
}
}
impl Default for CoordinationEngine {
fn default() -> Self {
Self::new()
}
}
impl Coalition {
pub async fn execute(&self) -> Result<String> {
tracing::info!("Coalition {} executing task: {}", self.id, self.task.title);
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
Ok(format!("Task '{}' completed by {} agents", self.task.title, self.agents.len()))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_agent_registration() {
let engine = CoordinationEngine::new();
let agent = Agent::new("test-agent", "Test Agent").unwrap();
let result = engine.register_agent(agent).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_coalition_formation() {
let engine = CoordinationEngine::new();
let agent = Agent::with_capabilities("test-agent", "Test Agent", vec!["analysis"]).unwrap();
engine.register_agent(agent).await.unwrap();
let task = Task::new("Test analysis task")
.require_capabilities(vec!["analysis".to_string()]);
let result = engine.coalesce_around_task(task).await.unwrap();
assert!(result.success);
assert!(result.coalition.is_some());
}
}