coalescent 0.1.0

High-level AI coordination patterns enabling intelligent agent coalescence
Documentation
//! Trust and reputation management for AI agents
//!
//! This module provides trust scoring and reputation tracking to enable
//! reliable coordination between AI agents.

use crate::agent::AgentId;
use crate::error::Result;
use std::collections::HashMap;
use tokio::sync::RwLock;

/// Trust score type (0.0 to 1.0)
pub type TrustScore = f64;

/// Reputation metrics for an agent
#[derive(Debug, Clone)]
pub struct ReputationMetrics {
    /// Overall trust score (0.0 to 1.0)
    pub trust_score: TrustScore,
    
    /// Number of successful collaborations
    pub successful_collaborations: u32,
    
    /// Number of failed collaborations
    pub failed_collaborations: u32,
    
    /// Average response time (seconds)
    pub avg_response_time: f64,
    
    /// Reliability score (0.0 to 1.0)
    pub reliability: f64,
}

impl Default for ReputationMetrics {
    fn default() -> Self {
        Self {
            trust_score: 0.5, // Start with neutral trust
            successful_collaborations: 0,
            failed_collaborations: 0,
            avg_response_time: 0.0,
            reliability: 0.5,
        }
    }
}

/// Manages trust scores and reputation for agents
pub struct TrustManager {
    /// Agent reputation data
    reputation_data: RwLock<HashMap<AgentId, ReputationMetrics>>,
    
    /// Trust calculation parameters
    config: TrustConfig,
}

/// Configuration for trust calculations
#[derive(Debug, Clone)]
pub struct TrustConfig {
    /// Weight given to successful collaborations
    pub success_weight: f64,
    
    /// Weight given to failure penalty
    pub failure_weight: f64,
    
    /// Weight given to response time
    pub response_time_weight: f64,
    
    /// Decay factor for old reputation data
    pub decay_factor: f64,
    
    /// Minimum trust score
    pub min_trust_score: f64,
    
    /// Maximum trust score
    pub max_trust_score: f64,
}

impl Default for TrustConfig {
    fn default() -> Self {
        Self {
            success_weight: 0.4,
            failure_weight: 0.3,
            response_time_weight: 0.3,
            decay_factor: 0.95,
            min_trust_score: 0.0,
            max_trust_score: 1.0,
        }
    }
}

impl TrustManager {
    /// Create a new trust manager with default configuration
    pub fn new() -> Self {
        Self::with_config(TrustConfig::default())
    }

    /// Create a new trust manager with custom configuration
    pub fn with_config(config: TrustConfig) -> Self {
        Self {
            reputation_data: RwLock::new(HashMap::new()),
            config,
        }
    }

    /// Get the trust score for an agent
    pub async fn get_trust_score(&self, agent_id: &AgentId) -> Result<TrustScore> {
        let reputation_data = self.reputation_data.read().await;
        
        if let Some(metrics) = reputation_data.get(agent_id) {
            Ok(metrics.trust_score)
        } else {
            // Return default trust score for unknown agents
            Ok(self.config.min_trust_score + 
               (self.config.max_trust_score - self.config.min_trust_score) / 2.0)
        }
    }

    /// Get reputation metrics for an agent
    pub async fn get_reputation_metrics(&self, agent_id: &AgentId) -> Option<ReputationMetrics> {
        let reputation_data = self.reputation_data.read().await;
        reputation_data.get(agent_id).cloned()
    }

    /// Record a successful collaboration
    pub async fn record_success(&self, agent_id: &AgentId, response_time: f64) -> Result<()> {
        let mut reputation_data = self.reputation_data.write().await;
        let metrics = reputation_data.entry(*agent_id).or_insert_with(ReputationMetrics::default);
        
        metrics.successful_collaborations += 1;
        self.update_response_time(metrics, response_time);
        self.recalculate_trust_score(metrics);
        
        Ok(())
    }

    /// Record a failed collaboration
    pub async fn record_failure(&self, agent_id: &AgentId, response_time: f64) -> Result<()> {
        let mut reputation_data = self.reputation_data.write().await;
        let metrics = reputation_data.entry(*agent_id).or_insert_with(ReputationMetrics::default);
        
        metrics.failed_collaborations += 1;
        self.update_response_time(metrics, response_time);
        self.recalculate_trust_score(metrics);
        
        Ok(())
    }

    /// Update average response time
    fn update_response_time(&self, metrics: &mut ReputationMetrics, new_response_time: f64) {
        let total_collaborations = metrics.successful_collaborations + metrics.failed_collaborations;
        if total_collaborations > 1 {
            metrics.avg_response_time = (metrics.avg_response_time * (total_collaborations - 1) as f64 
                                       + new_response_time) / total_collaborations as f64;
        } else {
            metrics.avg_response_time = new_response_time;
        }
    }

    /// Recalculate trust score based on current metrics
    fn recalculate_trust_score(&self, metrics: &mut ReputationMetrics) {
        let total_collaborations = metrics.successful_collaborations + metrics.failed_collaborations;
        
        if total_collaborations == 0 {
            return; // Keep default trust score
        }

        // Calculate success ratio
        let success_ratio = metrics.successful_collaborations as f64 / total_collaborations as f64;
        
        // Calculate reliability based on response time (faster = more reliable)
        // Assuming 10 seconds is "perfect" response time
        let response_reliability = if metrics.avg_response_time > 0.0 {
            (10.0 / (metrics.avg_response_time + 10.0)).min(1.0)
        } else {
            1.0
        };
        
        metrics.reliability = response_reliability;

        // Weighted trust score calculation
        let trust_score = 
            success_ratio * self.config.success_weight +
            response_reliability * self.config.response_time_weight +
            (1.0 - (metrics.failed_collaborations as f64 / (total_collaborations as f64 + 1.0))) * self.config.failure_weight;

        // Apply bounds
        metrics.trust_score = trust_score
            .max(self.config.min_trust_score)
            .min(self.config.max_trust_score);
    }

    /// Apply decay to all trust scores (call periodically)
    pub async fn apply_decay(&self) -> Result<()> {
        let mut reputation_data = self.reputation_data.write().await;
        
        for metrics in reputation_data.values_mut() {
            metrics.trust_score *= self.config.decay_factor;
            metrics.trust_score = metrics.trust_score.max(self.config.min_trust_score);
        }
        
        Ok(())
    }

    /// Get trust rankings (sorted by trust score, highest first)
    pub async fn get_trust_rankings(&self) -> Vec<(AgentId, TrustScore)> {
        let reputation_data = self.reputation_data.read().await;
        let mut rankings: Vec<(AgentId, TrustScore)> = reputation_data
            .iter()
            .map(|(agent_id, metrics)| (*agent_id, metrics.trust_score))
            .collect();
        
        rankings.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
        rankings
    }
}

impl Default for TrustManager {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn test_trust_score_calculation() {
        let trust_manager = TrustManager::new();
        let agent_id = AgentId::new();
        
        // Initially should have default trust score
        let initial_score = trust_manager.get_trust_score(&agent_id).await.unwrap();
        assert!((initial_score - 0.5).abs() < 0.01);
        
        // Record a success
        trust_manager.record_success(&agent_id, 5.0).await.unwrap();
        
        let updated_score = trust_manager.get_trust_score(&agent_id).await.unwrap();
        assert!(updated_score > initial_score);
    }

    #[tokio::test]
    async fn test_trust_rankings() {
        let trust_manager = TrustManager::new();
        let agent1 = AgentId::new();
        let agent2 = AgentId::new();
        
        // Agent1 has more successes
        trust_manager.record_success(&agent1, 5.0).await.unwrap();
        trust_manager.record_success(&agent1, 6.0).await.unwrap();
        
        // Agent2 has one success
        trust_manager.record_success(&agent2, 8.0).await.unwrap();
        
        let rankings = trust_manager.get_trust_rankings().await;
        assert_eq!(rankings.len(), 2);
        assert_eq!(rankings[0].0, agent1); // agent1 should be ranked higher
    }
}