// Profile adapter: AgentMetadata + KG metrics → Swarm AgentProfile // Phase 5.2: Bridges agent registry with swarm coordination // Phase 5.3: Integrates per-task-type learning profiles from KG use crate::learning_profile::{LearningProfile, TaskTypeExpertise}; use crate::registry::AgentMetadata; use vapora_swarm::messages::AgentProfile; /// Adapter that converts AgentMetadata to SwarmCoordinator AgentProfile pub struct ProfileAdapter; impl ProfileAdapter { /// Create a swarm profile from agent metadata pub fn create_profile(agent: &AgentMetadata) -> AgentProfile { // Extract roles from capabilities (simplistic mapping) let roles = agent .capabilities .iter() .take(1) .cloned() .collect(); AgentProfile { id: agent.id.clone(), roles, capabilities: agent.capabilities.clone(), current_load: agent.current_tasks as f64 / agent.max_concurrent_tasks as f64, success_rate: 0.5, // Default: neutral until KG metrics available availability: agent.status == crate::registry::AgentStatus::Active, } } /// Create profiles for multiple agents pub fn batch_create_profiles(agents: Vec) -> Vec { agents.into_iter().map(|agent| Self::create_profile(&agent)).collect() } /// Update profile from KG success rate (Phase 5.5 integration) pub fn update_with_kg_metrics(mut profile: AgentProfile, success_rate: f64) -> AgentProfile { profile.success_rate = success_rate; profile } /// Create learning profile from agent with task-type expertise. /// Integrates per-task-type learning data from KG for intelligent assignment. pub fn create_learning_profile(agent_id: String) -> LearningProfile { LearningProfile::new(agent_id) } /// Enhance learning profile with task-type expertise from KG data. /// Updates the profile with calculated expertise for specific task type. pub fn add_task_type_expertise( mut profile: LearningProfile, task_type: String, expertise: TaskTypeExpertise, ) -> LearningProfile { profile.set_task_type_expertise(task_type, expertise); profile } /// Update agent profile success rate from learning profile task-type score. /// Uses learned expertise for the specified task type, with fallback to default. pub fn update_profile_with_learning( mut profile: AgentProfile, learning_profile: &LearningProfile, task_type: &str, ) -> AgentProfile { profile.success_rate = learning_profile.get_task_type_score(task_type); profile } } #[cfg(test)] mod tests { use super::*; #[test] fn test_profile_creation_from_metadata() { let agent = AgentMetadata { id: "agent-1".to_string(), role: "developer".to_string(), name: "Dev Agent 1".to_string(), version: "0.1.0".to_string(), status: crate::registry::AgentStatus::Active, capabilities: vec!["coding".to_string(), "review".to_string()], llm_provider: "claude".to_string(), llm_model: "claude-sonnet-4".to_string(), max_concurrent_tasks: 5, current_tasks: 2, created_at: chrono::Utc::now(), last_heartbeat: chrono::Utc::now(), uptime_percentage: 99.5, total_tasks_completed: 10, }; let profile = ProfileAdapter::create_profile(&agent); assert_eq!(profile.id, "agent-1"); assert_eq!(profile.capabilities.len(), 2); assert!((profile.current_load - 0.4).abs() < 0.01); // 2/5 = 0.4 assert_eq!(profile.success_rate, 0.5); // Default assert!(profile.availability); } #[test] fn test_batch_create_profiles() { let agents = vec![ AgentMetadata { id: "agent-1".to_string(), role: "developer".to_string(), name: "Dev 1".to_string(), version: "0.1.0".to_string(), status: crate::registry::AgentStatus::Active, capabilities: vec!["coding".to_string()], llm_provider: "claude".to_string(), llm_model: "claude-sonnet-4".to_string(), max_concurrent_tasks: 5, current_tasks: 1, created_at: chrono::Utc::now(), last_heartbeat: chrono::Utc::now(), uptime_percentage: 99.0, total_tasks_completed: 5, }, AgentMetadata { id: "agent-2".to_string(), role: "reviewer".to_string(), name: "Reviewer 1".to_string(), version: "0.1.0".to_string(), status: crate::registry::AgentStatus::Active, capabilities: vec!["review".to_string()], llm_provider: "gpt4".to_string(), llm_model: "gpt-4".to_string(), max_concurrent_tasks: 3, current_tasks: 0, created_at: chrono::Utc::now(), last_heartbeat: chrono::Utc::now(), uptime_percentage: 98.5, total_tasks_completed: 3, }, ]; let profiles = ProfileAdapter::batch_create_profiles(agents); assert_eq!(profiles.len(), 2); assert_eq!(profiles[0].id, "agent-1"); assert_eq!(profiles[1].id, "agent-2"); } #[test] fn test_update_with_kg_metrics() { let profile = AgentProfile { id: "agent-1".to_string(), roles: vec!["developer".to_string()], capabilities: vec!["coding".to_string()], current_load: 0.4, success_rate: 0.5, availability: true, }; let updated = ProfileAdapter::update_with_kg_metrics(profile, 0.85); assert_eq!(updated.success_rate, 0.85); assert_eq!(updated.id, "agent-1"); // Other fields unchanged } #[test] fn test_create_learning_profile() { let learning = ProfileAdapter::create_learning_profile("agent-1".to_string()); assert_eq!(learning.agent_id, "agent-1"); assert_eq!(learning.task_type_expertise.len(), 0); } #[test] fn test_add_task_type_expertise() { let learning = ProfileAdapter::create_learning_profile("agent-1".to_string()); let expertise = TaskTypeExpertise { success_rate: 0.85, total_executions: 20, recent_success_rate: 0.90, avg_duration_ms: 150.0, learning_curve: Vec::new(), confidence: 1.0, }; let updated = ProfileAdapter::add_task_type_expertise(learning, "coding".to_string(), expertise); assert_eq!(updated.get_task_type_score("coding"), 0.85); assert_eq!(updated.get_confidence("coding"), 1.0); } #[test] fn test_update_profile_with_learning() { let profile = AgentProfile { id: "agent-1".to_string(), roles: vec!["developer".to_string()], capabilities: vec!["coding".to_string()], current_load: 0.4, success_rate: 0.5, availability: true, }; let mut learning = ProfileAdapter::create_learning_profile("agent-1".to_string()); let expertise = TaskTypeExpertise { success_rate: 0.85, total_executions: 20, recent_success_rate: 0.90, avg_duration_ms: 150.0, learning_curve: Vec::new(), confidence: 1.0, }; learning = ProfileAdapter::add_task_type_expertise(learning, "coding".to_string(), expertise); let updated = ProfileAdapter::update_profile_with_learning(profile, &learning, "coding"); assert_eq!(updated.success_rate, 0.85); let unknown_updated = ProfileAdapter::update_profile_with_learning(updated, &learning, "unknown"); assert_eq!(unknown_updated.success_rate, 0.5); // Falls back to default } }