219 lines
8.0 KiB
Rust
219 lines
8.0 KiB
Rust
|
|
// 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<AgentMetadata>) -> Vec<AgentProfile> {
|
||
|
|
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
|
||
|
|
}
|
||
|
|
}
|