use anyhow::Result;
use async_trait::async_trait;
use std::sync::Arc;
use tokio::task;
use crate::core::adapters::ai::AIProviderTrait;
#[derive(Debug, Clone)]
pub struct Solution {
pub content: String,
pub confidence: f32,
pub reasoning: String,
}
#[derive(Debug, Clone)]
pub struct SolutionWithMetadata {
pub solution: Solution,
pub agent_id: String,
pub timestamp: std::time::SystemTime,
}
#[derive(Debug)]
pub enum VoteResult {
Consensus(Vec<SolutionWithMetadata>),
Split(Vec<SolutionWithMetadata>),
NoAgreement(Vec<SolutionWithMetadata>),
}
#[async_trait]
pub trait SpecializedAgent: Send + Sync {
async fn solve(&self, task: &str) -> Result<Solution>;
fn agent_type(&self) -> &str;
fn confidence(&self) -> f32;
}
pub struct ConsensusOrchestrator {
agents: Vec<Arc<dyn SpecializedAgent>>,
threshold: f32, use_devil_advocate: bool,
}
impl ConsensusOrchestrator {
pub fn new(agents: Vec<Arc<dyn SpecializedAgent>>, threshold: f32) -> Self {
Self {
agents,
threshold,
use_devil_advocate: true,
}
}
pub async fn solve(&self, task: &str) -> Result<Solution> {
let mut results = Vec::new();
let mut tasks = Vec::new();
for agent in &self.agents {
let agent_clone = Arc::clone(agent);
let task_str = task.to_string();
tasks.push(task::spawn(async move {
let solution = agent_clone.solve(&task_str).await;
Ok::<(String, Result<Solution>), anyhow::Error>((
agent_clone.agent_type().to_string(),
solution,
))
}));
}
for task_handle in tasks {
if let Ok(result) = task_handle.await {
if let Ok((agent_type, solution)) = result {
if let Ok(sol) = solution {
results.push(SolutionWithMetadata {
solution: sol,
agent_id: agent_type,
timestamp: std::time::SystemTime::now(),
});
}
}
}
}
results.retain(|r| r.solution.confidence > 0.0);
if results.is_empty() {
return Err(anyhow::anyhow!("No agents provided valid solutions"));
}
let consensus_result = self.vote(&results)?;
match consensus_result {
VoteResult::Consensus(solutions) => {
let best_solution = solutions
.iter()
.max_by(|a, b| {
a.solution
.confidence
.partial_cmp(&b.solution.confidence)
.unwrap()
})
.unwrap()
.solution
.clone();
if self.use_devil_advocate {
let critique = self.devil_advocate_analyze(&best_solution).await?;
Ok(self.apply_critique(best_solution, critique))
} else {
Ok(best_solution)
}
}
VoteResult::Split(solutions) | VoteResult::NoAgreement(solutions) => {
let best_solution = solutions
.iter()
.max_by(|a, b| {
a.solution
.confidence
.partial_cmp(&b.solution.confidence)
.unwrap()
})
.unwrap()
.solution
.clone();
Ok(best_solution)
}
}
}
fn vote(&self, solutions: &[SolutionWithMetadata]) -> Result<VoteResult> {
if solutions.is_empty() {
return Ok(VoteResult::NoAgreement(vec![]));
}
let total_confidence: f32 = solutions.iter().map(|s| s.solution.confidence).sum();
let avg_confidence = total_confidence / solutions.len() as f32;
let high_confidence_solutions: Vec<_> = solutions
.iter()
.filter(|s| s.solution.confidence >= avg_confidence)
.cloned()
.collect();
let agreement_ratio = high_confidence_solutions.len() as f32 / solutions.len() as f32;
if agreement_ratio >= self.threshold {
Ok(VoteResult::Consensus(high_confidence_solutions))
} else if agreement_ratio > 0.3 {
Ok(VoteResult::Split(high_confidence_solutions))
} else {
Ok(VoteResult::NoAgreement(solutions.to_vec()))
}
}
async fn devil_advocate_analyze(&self, solution: &Solution) -> Result<Solution> {
let critique_content = format!(
"Critiquing solution with confidence {}: {}\n\nPotential issues to consider:\n- Edge cases not addressed\n- Performance implications\n- Security vulnerabilities\n- Scalability concerns",
solution.confidence, solution.reasoning
);
Ok(Solution {
content: critique_content,
confidence: 0.9, reasoning: "Critical analysis of proposed solution".to_string(),
})
}
fn apply_critique(&self, mut solution: Solution, critique: Solution) -> Solution {
solution.content = format!(
"{}\n\n[CRITIQUE NOTE]: {}\n\n[IMPROVED SOLUTION]: {}",
solution.content,
critique.content,
solution.content );
solution.confidence = (solution.confidence + critique.confidence) / 2.0;
solution
}
}
pub struct CodeAgent {
pub provider: Arc<dyn AIProviderTrait>,
}
#[async_trait]
impl SpecializedAgent for CodeAgent {
async fn solve(&self, task: &str) -> Result<Solution> {
let response = self
.provider
.chat(&format!("Generate code for: {}", task))
.await?;
Ok(Solution {
content: response,
confidence: 0.85,
reasoning: "Code generation based on task requirements".to_string(),
})
}
fn agent_type(&self) -> &str {
"code"
}
fn confidence(&self) -> f32 {
0.85
}
}
pub struct DesignAgent {
pub provider: Arc<dyn AIProviderTrait>,
}
#[async_trait]
impl SpecializedAgent for DesignAgent {
async fn solve(&self, task: &str) -> Result<Solution> {
let response = self
.provider
.chat(&format!("Design solution for: {}", task))
.await?;
Ok(Solution {
content: response,
confidence: 0.80,
reasoning: "Design approach based on requirements".to_string(),
})
}
fn agent_type(&self) -> &str {
"design"
}
fn confidence(&self) -> f32 {
0.80
}
}
pub struct SecurityAgent {
pub provider: Arc<dyn AIProviderTrait>,
}
#[async_trait]
impl SpecializedAgent for SecurityAgent {
async fn solve(&self, task: &str) -> Result<Solution> {
let response = self
.provider
.chat(&format!("Security implications for: {}", task))
.await?;
Ok(Solution {
content: response,
confidence: 0.90,
reasoning: "Security-focused analysis".to_string(),
})
}
fn agent_type(&self) -> &str {
"security"
}
fn confidence(&self) -> f32 {
0.90
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
struct MockAI;
impl MockAI {
fn new() -> Arc<dyn AIProviderTrait> {
unimplemented!("MockAI not fully implemented for tests")
}
}
#[tokio::test]
async fn test_consensus_orchestrator() {
assert!(true); }
}