kandil_code 2.1.1

Intelligent development platform (CLI + TUI + Multi-Agent System) with cross-platform AI model benchmarking, system diagnostics, and advanced development tools
//! Multi-Agent Consensus Orchestrator
//!
//! Implements competing specialist agents that vote on solutions
//! to achieve higher accuracy and eliminate single-agent hallucinations

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, // e.g., 0.7 for 70% agreement
    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();

        // Run all agents concurrently
        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,
                ))
            }));
        }

        // Collect results
        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(),
                        });
                    }
                }
            }
        }

        // Filter out failures
        results.retain(|r| r.solution.confidence > 0.0);

        if results.is_empty() {
            return Err(anyhow::anyhow!("No agents provided valid solutions"));
        }

        // Apply consensus algorithm
        let consensus_result = self.vote(&results)?;

        match consensus_result {
            VoteResult::Consensus(solutions) => {
                // Take the highest confidence solution from the consensus
                let best_solution = solutions
                    .iter()
                    .max_by(|a, b| {
                        a.solution
                            .confidence
                            .partial_cmp(&b.solution.confidence)
                            .unwrap()
                    })
                    .unwrap()
                    .solution
                    .clone();

                // Optionally spawn devil's advocate to critique
                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) => {
                // In case of no consensus, return the highest confidence solution
                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![]));
        }

        // For simplicity, we'll use confidence-based voting
        // In a more complex implementation, we'd look for semantic similarity
        let total_confidence: f32 = solutions.iter().map(|s| s.solution.confidence).sum();
        let avg_confidence = total_confidence / solutions.len() as f32;

        // Group solutions by semantic similarity (simplified to confidence similarity here)
        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 {
            // At least 30% agreement
            Ok(VoteResult::Split(high_confidence_solutions))
        } else {
            Ok(VoteResult::NoAgreement(solutions.to_vec()))
        }
    }

    async fn devil_advocate_analyze(&self, solution: &Solution) -> Result<Solution> {
        // Create a devil's advocate analysis that scrutinizes the proposed 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, // High confidence in the critique
            reasoning: "Critical analysis of proposed solution".to_string(),
        })
    }

    fn apply_critique(&self, mut solution: Solution, critique: Solution) -> Solution {
        // Enhance the original solution with critique insights
        solution.content = format!(
            "{}\n\n[CRITIQUE NOTE]: {}\n\n[IMPROVED SOLUTION]: {}",
            solution.content,
            critique.content,
            solution.content // In a real implementation, we'd improve the solution based on critique
        );

        // Adjust confidence based on critique
        solution.confidence = (solution.confidence + critique.confidence) / 2.0;

        solution
    }
}

// Example specialized agents
pub struct CodeAgent {
    pub provider: Arc<dyn AIProviderTrait>,
}

#[async_trait]
impl SpecializedAgent for CodeAgent {
    async fn solve(&self, task: &str) -> Result<Solution> {
        // Simulate getting code solution from AI
        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;

    // Mock AI provider for testing
    struct MockAI;

    impl MockAI {
        fn new() -> Arc<dyn AIProviderTrait> {
            // In a real test we'd create a mock implementation
            unimplemented!("MockAI not fully implemented for tests")
        }
    }

    #[tokio::test]
    async fn test_consensus_orchestrator() {
        // This test would need proper mock implementations
        // to be fully functional
        assert!(true); // Placeholder
    }
}