reputation_types/
lib.rs

1//! # Reputation Types
2//! 
3//! Core data types for the MCP agent reputation system.
4//! 
5//! This crate provides the fundamental data structures used throughout
6//! the reputation system, including agent data, reputation scores,
7//! and a builder for constructing agent instances.
8
9// Forbid unsafe code to ensure memory safety
10#![forbid(unsafe_code)]
11//! 
12//! ## Key Types
13//! 
14//! - [`AgentData`]: Represents an MCP agent with all reputation-relevant data
15//! - [`ReputationScore`]: The calculated reputation score with confidence
16//! - [`AgentDataBuilder`]: Fluent builder for constructing `AgentData`
17//! 
18//! ## Examples
19//! 
20//! ### Creating Agent Data
21//! 
22//! ```
23//! use reputation_types::{AgentData, AgentDataBuilder};
24//! 
25//! // Using the builder (recommended)
26//! let agent = AgentDataBuilder::new("did:example:123")
27//!     .total_interactions(150)
28//!     .with_reviews(100, 4.5)
29//!     .mcp_level(2)
30//!     .identity_verified(true)
31//!     .build()
32//!     .unwrap();
33//! 
34//! // Or use the convenience method
35//! let agent = AgentData::builder("did:example:456")
36//!     .total_interactions(60)
37//!     .with_reviews(50, 3.8)
38//!     .build()
39//!     .unwrap();
40//! ```
41
42use serde::{Deserialize, Serialize};
43use chrono::{DateTime, Utc};
44
45mod builder;
46mod display;
47pub use builder::{AgentDataBuilder, BuilderError};
48
49/// Confidence level categorization based on confidence value
50/// 
51/// This enum categorizes the confidence score into three levels
52/// to help users quickly understand how reliable a reputation score is.
53/// 
54/// # Thresholds
55/// 
56/// - `Low`: confidence < 0.2 (limited data)
57/// - `Medium`: 0.2 ≤ confidence < 0.7 (moderate data)
58/// - `High`: confidence ≥ 0.7 (substantial data)
59#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
60pub enum ConfidenceLevel {
61    /// Low confidence - limited interaction data
62    Low,
63    /// Medium confidence - moderate interaction data
64    Medium,
65    /// High confidence - substantial interaction data
66    High,
67}
68
69impl ConfidenceLevel {
70    /// Determine confidence level from a confidence value
71    pub fn from_confidence(confidence: f64) -> Self {
72        if confidence < 0.2 {
73            ConfidenceLevel::Low
74        } else if confidence < 0.7 {
75            ConfidenceLevel::Medium
76        } else {
77            ConfidenceLevel::High
78        }
79    }
80}
81
82/// Detailed breakdown of score components
83/// 
84/// Provides transparency into how the final reputation score
85/// was calculated, showing the contribution of each component.
86#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct ScoreComponents {
88    /// Prior reputation score (based on credentials)
89    pub prior_score: f64,
90    /// Detailed breakdown of prior score calculation
91    pub prior_breakdown: PriorBreakdown,
92    /// Empirical score (based on reviews/ratings)
93    pub empirical_score: f64,
94    /// Raw confidence value (0-1)
95    pub confidence_value: f64,
96    /// Categorized confidence level
97    pub confidence_level: ConfidenceLevel,
98    /// Weight given to prior score (1 - confidence)
99    pub prior_weight: f64,
100    /// Weight given to empirical score (confidence)
101    pub empirical_weight: f64,
102}
103
104/// Breakdown of prior score components
105/// 
106/// Shows how each credential and bonus contributes
107/// to the total prior reputation score.
108#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct PriorBreakdown {
110    /// Base score (default: 50)
111    pub base_score: f64,
112    /// MCP level bonus (0-15 points)
113    pub mcp_bonus: f64,
114    /// Identity verification bonus (5 points)
115    pub identity_bonus: f64,
116    /// Security audit bonus (7 points)
117    pub security_audit_bonus: f64,
118    /// Open source bonus (3 points)
119    pub open_source_bonus: f64,
120    /// Age bonus for established agents (5 points)
121    pub age_bonus: f64,
122    /// Total prior score (capped at maximum)
123    pub total: f64,
124}
125
126/// Represents an MCP agent with all reputation-relevant data
127/// 
128/// This struct contains all the information needed to calculate
129/// a reputation score for an agent, including credentials,
130/// review statistics, and verification status.
131/// 
132/// # Fields
133/// 
134/// - `did`: Decentralized identifier (must follow did:method:identifier format)
135/// - `created_at`: When the agent was first registered
136/// - `mcp_level`: MCP certification level (0-3, if any)
137/// - `identity_verified`: Whether identity has been verified
138/// - `security_audit_passed`: Whether security audit passed
139/// - `open_source`: Whether the agent is open source
140/// - `total_interactions`: Total number of interactions
141/// - `total_reviews`: Total number of reviews received
142/// - `average_rating`: Average rating (1.0-5.0 scale)
143/// - `positive_reviews`: Count of positive reviews
144/// - `negative_reviews`: Count of negative reviews
145/// 
146/// # Example
147/// 
148/// ```
149/// use reputation_types::AgentData;
150/// use chrono::Utc;
151/// 
152/// let agent = AgentData {
153///     did: "did:example:123".to_string(),
154///     created_at: Utc::now(),
155///     mcp_level: Some(2),
156///     identity_verified: true,
157///     security_audit_passed: false,
158///     open_source: true,
159///     total_interactions: 500,
160///     total_reviews: 100,
161///     average_rating: Some(4.2),
162///     positive_reviews: 80,
163///     negative_reviews: 20,
164/// };
165/// ```
166#[derive(Debug, Clone, Serialize, Deserialize)]
167pub struct AgentData {
168    /// Decentralized identifier for the agent
169    pub did: String,
170    
171    /// When the agent was created/registered
172    pub created_at: DateTime<Utc>,
173    
174    /// MCP certification level (0-3)
175    pub mcp_level: Option<u8>,
176    
177    /// Whether the agent's identity has been verified
178    pub identity_verified: bool,
179    
180    /// Whether the agent passed security audit
181    pub security_audit_passed: bool,
182    
183    /// Whether the agent is open source
184    pub open_source: bool,
185    
186    /// Total number of interactions with users
187    pub total_interactions: u32,
188    
189    /// Total number of reviews received
190    pub total_reviews: u32,
191    
192    /// Average rating on 1-5 scale
193    pub average_rating: Option<f64>,
194    
195    /// Number of positive reviews
196    pub positive_reviews: u32,
197    
198    /// Number of negative reviews
199    pub negative_reviews: u32,
200}
201
202impl AgentData {
203    /// Creates a new builder for constructing AgentData instances.
204    /// 
205    /// # Examples
206    /// 
207    /// ```
208    /// use reputation_types::AgentData;
209    /// 
210    /// let agent = AgentData::builder("did:example:123")
211    ///     .total_interactions(150)
212    ///     .mcp_level(2)
213    ///     .identity_verified(true)
214    ///     .with_reviews(100, 4.5)
215    ///     .build()
216    ///     .unwrap();
217    /// ```
218    pub fn builder(did: impl Into<String>) -> AgentDataBuilder {
219        AgentDataBuilder::new(did)
220    }
221}
222
223/// The calculated reputation score for an agent
224/// 
225/// Contains the final reputation score along with detailed metadata
226/// about how it was calculated, component breakdowns, and confidence level.
227/// 
228/// # Fields
229/// 
230/// - `score`: The final reputation score (0-100)
231/// - `confidence`: How confident we are in the score (0-1)
232/// - `level`: Categorized confidence level (Low/Medium/High)
233/// - `components`: Detailed breakdown of score components
234/// - `is_provisional`: Whether score is provisional (confidence < 0.2)
235/// - `data_points`: Total data points used (interactions + reviews)
236/// - `algorithm_version`: Version of algorithm used
237/// - `calculated_at`: When the score was calculated
238/// 
239/// # Interpretation
240/// 
241/// ## Score Ranges
242/// - 0-20: Very poor reputation
243/// - 20-40: Poor reputation
244/// - 40-60: Average reputation
245/// - 60-80: Good reputation
246/// - 80-100: Excellent reputation
247/// 
248/// ## Confidence Levels
249/// - Low (0-0.2): Limited data, provisional score
250/// - Medium (0.2-0.7): Moderate confidence
251/// - High (0.7-1.0): High confidence, substantial data
252/// 
253/// # Example
254/// 
255/// ```
256/// use reputation_types::{ReputationScore, ConfidenceLevel, ScoreComponents, PriorBreakdown};
257/// use chrono::Utc;
258/// 
259/// # let components = ScoreComponents {
260/// #     prior_score: 65.0,
261/// #     prior_breakdown: PriorBreakdown {
262/// #         base_score: 50.0,
263/// #         mcp_bonus: 10.0,
264/// #         identity_bonus: 5.0,
265/// #         security_audit_bonus: 0.0,
266/// #         open_source_bonus: 0.0,
267/// #         age_bonus: 0.0,
268/// #         total: 65.0,
269/// #     },
270/// #     empirical_score: 80.0,
271/// #     confidence_value: 0.85,
272/// #     confidence_level: ConfidenceLevel::High,
273/// #     prior_weight: 0.15,
274/// #     empirical_weight: 0.85,
275/// # };
276/// #
277/// let score = ReputationScore {
278///     score: 75.5,
279///     confidence: 0.85,
280///     level: ConfidenceLevel::High,
281///     components,
282///     is_provisional: false,
283///     data_points: 150,
284///     algorithm_version: "1.0.0".to_string(),
285///     calculated_at: Utc::now(),
286/// };
287/// 
288/// if score.is_provisional {
289///     println!("Provisional score: {:.1} (needs more data)", score.score);
290/// } else {
291///     println!("{:?} confidence score: {:.1}", score.level, score.score);
292/// }
293/// ```
294#[derive(Debug, Clone, Serialize, Deserialize)]
295pub struct ReputationScore {
296    /// The final reputation score (0-100 scale)
297    pub score: f64,
298    
299    /// Confidence in the score based on data availability (0-1)
300    pub confidence: f64,
301    
302    /// Categorized confidence level
303    pub level: ConfidenceLevel,
304    
305    /// Detailed breakdown of score components
306    pub components: ScoreComponents,
307    
308    /// Whether this is a provisional score (confidence < 0.2)
309    pub is_provisional: bool,
310    
311    /// Total data points used in calculation
312    pub data_points: u32,
313    
314    /// Version of the algorithm used for calculation
315    pub algorithm_version: String,
316    
317    /// Timestamp when the score was calculated
318    pub calculated_at: DateTime<Utc>,
319}
320
321#[cfg(test)]
322mod tests {
323    use super::*;
324
325    #[test]
326    fn test_confidence_level_from_confidence() {
327        // Test Low level
328        assert_eq!(ConfidenceLevel::from_confidence(0.0), ConfidenceLevel::Low);
329        assert_eq!(ConfidenceLevel::from_confidence(0.1), ConfidenceLevel::Low);
330        assert_eq!(ConfidenceLevel::from_confidence(0.19), ConfidenceLevel::Low);
331        
332        // Test Medium level
333        assert_eq!(ConfidenceLevel::from_confidence(0.2), ConfidenceLevel::Medium);
334        assert_eq!(ConfidenceLevel::from_confidence(0.5), ConfidenceLevel::Medium);
335        assert_eq!(ConfidenceLevel::from_confidence(0.69), ConfidenceLevel::Medium);
336        
337        // Test High level
338        assert_eq!(ConfidenceLevel::from_confidence(0.7), ConfidenceLevel::High);
339        assert_eq!(ConfidenceLevel::from_confidence(0.9), ConfidenceLevel::High);
340        assert_eq!(ConfidenceLevel::from_confidence(1.0), ConfidenceLevel::High);
341    }
342
343    #[test]
344    fn test_confidence_level_boundaries() {
345        // Test exact boundaries
346        assert_eq!(ConfidenceLevel::from_confidence(0.199999), ConfidenceLevel::Low);
347        assert_eq!(ConfidenceLevel::from_confidence(0.2), ConfidenceLevel::Medium);
348        
349        assert_eq!(ConfidenceLevel::from_confidence(0.699999), ConfidenceLevel::Medium);
350        assert_eq!(ConfidenceLevel::from_confidence(0.7), ConfidenceLevel::High);
351    }
352
353    #[test]
354    fn test_confidence_level_serialization() {
355        // Test that ConfidenceLevel can be serialized and deserialized
356        let levels = vec![
357            ConfidenceLevel::Low,
358            ConfidenceLevel::Medium,
359            ConfidenceLevel::High,
360        ];
361        
362        for level in levels {
363            let serialized = serde_json::to_string(&level).unwrap();
364            let deserialized: ConfidenceLevel = serde_json::from_str(&serialized).unwrap();
365            assert_eq!(level, deserialized);
366        }
367    }
368}