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}