reputation-core 0.1.0

Core calculation engine for the KnowThat Reputation System with advanced scoring algorithms
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
//! Generate public test vectors JSON file
//! 
//! Run with: cargo run --example generate_test_vectors

use reputation_core::Calculator;
use reputation_types::{AgentData, AgentDataBuilder};
use chrono::{Duration, Utc, DateTime};
use serde::{Serialize, Deserialize};
use std::collections::HashMap;
use std::fs;
use std::path::Path;

/// Test vector file format
#[derive(Debug, Serialize, Deserialize)]
pub struct TestVectorFile {
    pub version: String,
    pub algorithm: String,
    pub generated: DateTime<Utc>,
    pub calculator_config: CalculatorConfig,
    pub test_categories: HashMap<String, String>,
    pub test_cases: Vec<TestCase>,
}

/// Calculator configuration used for test vectors
#[derive(Debug, Serialize, Deserialize)]
pub struct CalculatorConfig {
    pub confidence_k: f64,
    pub prior_base: f64,
    pub prior_max: f64,
}

/// Individual test case
#[derive(Debug, Serialize, Deserialize)]
pub struct TestCase {
    pub id: String,
    pub category: String,
    pub description: String,
    pub input: TestInput,
    pub expected: ExpectedScore,
}

/// Test input (subset of AgentData fields)
#[derive(Debug, Serialize, Deserialize)]
pub struct TestInput {
    pub did: String,
    pub created_at: DateTime<Utc>,
    pub mcp_level: Option<u8>,
    pub identity_verified: bool,
    pub security_audit_passed: bool,
    pub open_source: bool,
    pub total_interactions: u32,
    pub total_reviews: u32,
    pub average_rating: Option<f64>,
}

/// Expected score details
#[derive(Debug, Serialize, Deserialize)]
pub struct ExpectedScore {
    pub score: f64,
    pub confidence: f64,
    pub level: String,
    pub components: ScoreComponents,
    pub is_provisional: bool,
    pub data_points: u32,
    pub algorithm_version: String,
}

/// Score component breakdown
#[derive(Debug, Serialize, Deserialize)]
pub struct ScoreComponents {
    pub prior_score: f64,
    pub prior_breakdown: PriorBreakdown,
    pub empirical_score: f64,
    pub confidence_value: f64,
    pub confidence_level: String,
    pub prior_weight: f64,
    pub empirical_weight: f64,
}

/// Prior score breakdown
#[derive(Debug, Serialize, Deserialize)]
pub struct PriorBreakdown {
    pub base_score: f64,
    pub mcp_bonus: f64,
    pub identity_bonus: f64,
    pub security_audit_bonus: f64,
    pub open_source_bonus: f64,
    pub age_bonus: f64,
    pub total: f64,
}

fn main() {
    println!("🔧 Generating test vectors for KnowThat Reputation Engine...\n");
    
    let mut vectors = TestVectorFile {
        version: "1.0.0".to_string(),
        algorithm: "bayesian_reputation_v1".to_string(),
        generated: Utc::now(),
        calculator_config: CalculatorConfig {
            confidence_k: 15.0,
            prior_base: 50.0,
            prior_max: 80.0,
        },
        test_categories: HashMap::from([
            ("new_agents".to_string(), "Agents with no interaction history".to_string()),
            ("verified_agents".to_string(), "Agents with identity verification and credentials".to_string()),
            ("high_interaction".to_string(), "Agents with significant activity".to_string()),
            ("edge_cases".to_string(), "Boundary conditions and limits".to_string()),
            ("confidence_levels".to_string(), "Different confidence level examples".to_string()),
            ("review_patterns".to_string(), "Various rating distributions".to_string()),
        ]),
        test_cases: Vec::new(),
    };
    
    let calc = Calculator::default();
    let mut case_id = 1;
    
    // Category 1: New Agents
    println!("📝 Generating new agent test cases...");
    
    // TC001: Brand new agent
    add_test_case(&mut vectors, &calc, &mut case_id, "new_agents",
        "Brand new agent with no history",
        AgentDataBuilder::new("did:test:new-agent").build().unwrap());
    
    // TC002: Week-old agent
    add_test_case(&mut vectors, &calc, &mut case_id, "new_agents",
        "Week-old agent with no activity",
        AgentDataBuilder::new("did:test:week-old")
            .created_at(Utc::now() - Duration::days(7))
            .build().unwrap());
    
    // TC003: Month-old agent
    add_test_case(&mut vectors, &calc, &mut case_id, "new_agents",
        "Month-old agent with no activity",
        AgentDataBuilder::new("did:test:month-old")
            .created_at(Utc::now() - Duration::days(30))
            .build().unwrap());
    
    // Category 2: Verified Agents
    println!("📝 Generating verified agent test cases...");
    
    // MCP Levels
    for level in 1..=3 {
        add_test_case(&mut vectors, &calc, &mut case_id, "verified_agents",
            &format!("Agent with MCP Level {}", level),
            AgentDataBuilder::new(&format!("did:test:mcp-{}", level))
                .mcp_level(level)
                .created_at(Utc::now() - Duration::days(90))
                .build().unwrap());
    }
    
    // Identity verified
    add_test_case(&mut vectors, &calc, &mut case_id, "verified_agents",
        "Agent with identity verification",
        AgentDataBuilder::new("did:test:identity-verified")
            .identity_verified(true)
            .created_at(Utc::now() - Duration::days(60))
            .build().unwrap());
    
    // Fully verified
    add_test_case(&mut vectors, &calc, &mut case_id, "verified_agents",
        "Fully verified agent with all credentials",
        AgentDataBuilder::new("did:test:fully-verified")
            .mcp_level(3)
            .identity_verified(true)
            .security_audit_passed(true)
            .open_source(true)
            .created_at(Utc::now() - Duration::days(180))
            .build().unwrap());
    
    // Category 3: Review Patterns
    println!("📝 Generating review pattern test cases...");
    
    // Different ratings
    for rating in 1..=5 {
        add_test_case(&mut vectors, &calc, &mut case_id, "review_patterns",
            &format!("Agent with {} star rating", rating),
            AgentDataBuilder::new(&format!("did:test:rating-{}", rating))
                .total_interactions(100)
                .with_reviews(50, rating as f64)
                .created_at(Utc::now() - Duration::days(30))
                .build().unwrap());
    }
    
    // Mixed reviews
    add_test_case(&mut vectors, &calc, &mut case_id, "review_patterns",
        "Agent with mixed reviews (3.5 stars)",
        AgentDataBuilder::new("did:test:mixed-reviews")
            .total_interactions(200)
            .with_reviews(100, 3.5)
            .created_at(Utc::now() - Duration::days(60))
            .build().unwrap());
    
    // Category 4: Confidence Levels
    println!("📝 Generating confidence level test cases...");
    
    let confidence_cases = [
        (1, "Low confidence (1 interaction)"),
        (10, "Low-Medium confidence (10 interactions)"),
        (50, "Medium confidence (50 interactions)"),
        (200, "High confidence (200 interactions)"),
        (1000, "Very high confidence (1000 interactions)"),
    ];
    
    for (interactions, desc) in confidence_cases {
        let reviews = (interactions / 2).max(1).min(interactions); // Ensure at least 1 review if interactions > 0
        let mut builder = AgentDataBuilder::new(&format!("did:test:conf-{}", interactions))
            .total_interactions(interactions)
            .created_at(Utc::now() - Duration::days(90));
        
        if interactions > 0 {
            builder = builder.with_reviews(reviews, 4.0);
        }
        
        add_test_case(&mut vectors, &calc, &mut case_id, "confidence_levels",
            desc, builder.build().unwrap());
    }
    
    // Category 5: Edge Cases
    println!("📝 Generating edge case test cases...");
    
    // No reviews
    add_test_case(&mut vectors, &calc, &mut case_id, "edge_cases",
        "Many interactions but no reviews",
        AgentDataBuilder::new("did:test:no-reviews")
            .total_interactions(500)
            .created_at(Utc::now() - Duration::days(30))
            .build().unwrap());
    
    // Maximum values
    add_test_case(&mut vectors, &calc, &mut case_id, "edge_cases",
        "Maximum allowed values",
        AgentDataBuilder::new("did:test:max-values")
            .total_interactions(1_000_000)
            .with_reviews(500_000, 5.0)
            .mcp_level(3)
            .identity_verified(true)
            .security_audit_passed(true)
            .open_source(true)
            .created_at(Utc::now() - Duration::days(730))
            .build().unwrap());
    
    // Single review
    add_test_case(&mut vectors, &calc, &mut case_id, "edge_cases",
        "Single perfect review",
        AgentDataBuilder::new("did:test:one-review")
            .total_interactions(1)
            .with_reviews(1, 5.0)
            .created_at(Utc::now() - Duration::days(1))
            .build().unwrap());
    
    // Prior cap
    add_test_case(&mut vectors, &calc, &mut case_id, "edge_cases",
        "Agent reaching prior score cap",
        AgentDataBuilder::new("did:test:prior-cap")
            .mcp_level(3)
            .identity_verified(true)
            .security_audit_passed(true)
            .open_source(true)
            .created_at(Utc::now() - Duration::days(365))
            .build().unwrap());
    
    // Boundary confidence
    add_test_case(&mut vectors, &calc, &mut case_id, "edge_cases",
        "Boundary confidence level (~0.21)",
        AgentDataBuilder::new("did:test:boundary-conf")
            .total_interactions(4) // 4/(4+15) ≈ 0.21
            .created_at(Utc::now() - Duration::days(7))
            .build().unwrap());
    
    // Category 6: High Interaction
    println!("📝 Generating high interaction test cases...");
    
    let interaction_levels = [100, 500, 1000, 5000, 10000];
    for interactions in interaction_levels {
        add_test_case(&mut vectors, &calc, &mut case_id, "high_interaction",
            &format!("Agent with {} interactions", interactions),
            AgentDataBuilder::new(&format!("did:test:high-int-{}", interactions))
                .total_interactions(interactions)
                .with_reviews((interactions as f64 * 0.3) as u32, 4.2)
                .created_at(Utc::now() - Duration::days(180))
                .build().unwrap());
    }
    
    // Additional edge cases
    println!("📝 Generating additional test cases...");
    
    // Various credential combinations
    let credential_combos = [
        (Some(1), false, false, false, "MCP Level 1 only"),
        (None, true, false, false, "Identity verified only"),
        (None, false, true, false, "Security audit only"),
        (None, false, false, true, "Open source only"),
        (Some(2), true, false, false, "MCP Level 2 + Identity"),
        (Some(3), true, true, true, "All credentials maxed"),
    ];
    
    for (mcp, id, sec, os, desc) in credential_combos {
        let mut builder = AgentDataBuilder::new(&format!("did:test:cred-{}", case_id))
            .created_at(Utc::now() - Duration::days(90))
            .total_interactions(50);
        
        // Add reviews properly
        builder = builder.with_reviews(25, 4.0);
        
        if let Some(level) = mcp {
            builder = builder.mcp_level(level);
        }
        builder = builder
            .identity_verified(id)
            .security_audit_passed(sec)
            .open_source(os);
        
        add_test_case(&mut vectors, &calc, &mut case_id, "verified_agents", desc, builder.build().unwrap());
    }
    
    // Age progression
    let age_days = [0, 30, 60, 90, 180, 365];
    for days in age_days {
        add_test_case(&mut vectors, &calc, &mut case_id, "edge_cases",
            &format!("Agent aged {} days", days),
            AgentDataBuilder::new(&format!("did:test:age-{}", days))
                .created_at(Utc::now() - Duration::days(days))
                .build().unwrap());
    }
    
    // Save test vectors
    println!("\n📁 Saving test vectors...");
    
    let json = serde_json::to_string_pretty(&vectors).unwrap();
    
    // Save to multiple locations
    let paths = [
        Path::new("test_vectors.json"),
        Path::new("docs/test_vectors.json"),
    ];
    
    for path in &paths {
        match fs::write(path, &json) {
            Ok(_) => println!("✅ Saved to: {}", path.display()),
            Err(e) => eprintln!("❌ Failed to save to {}: {}", path.display(), e),
        }
    }
    
    // Print summary
    println!("\n📊 Summary:");
    println!("  Total test cases: {}", vectors.test_cases.len());
    
    let mut category_counts = HashMap::new();
    for case in &vectors.test_cases {
        *category_counts.entry(case.category.clone()).or_insert(0) += 1;
    }
    
    println!("  Test cases by category:");
    for (category, desc) in &vectors.test_categories {
        let count = category_counts.get(category).unwrap_or(&0);
        println!("    {} ({}): {}", category, count, desc);
    }
    
    println!("\n✨ Test vector generation complete!");
}

fn add_test_case(
    vectors: &mut TestVectorFile,
    calc: &Calculator,
    case_id: &mut usize,
    category: &str,
    description: &str,
    agent: AgentData,
) {
    let score = match calc.calculate(&agent) {
        Ok(s) => s,
        Err(e) => {
            eprintln!("Error calculating score for {}: {:?}", description, e);
            eprintln!("Agent: {:?}", agent);
            panic!("Test vector generation failed");
        }
    };
    
    vectors.test_cases.push(TestCase {
        id: format!("TC{:03}", case_id),
        category: category.to_string(),
        description: description.to_string(),
        input: TestInput {
            did: agent.did.clone(),
            created_at: agent.created_at,
            mcp_level: agent.mcp_level,
            identity_verified: agent.identity_verified,
            security_audit_passed: agent.security_audit_passed,
            open_source: agent.open_source,
            total_interactions: agent.total_interactions,
            total_reviews: agent.total_reviews,
            average_rating: agent.average_rating,
        },
        expected: ExpectedScore {
            score: score.score,
            confidence: score.confidence,
            level: format!("{:?}", score.level),
            components: ScoreComponents {
                prior_score: score.components.prior_score,
                prior_breakdown: PriorBreakdown {
                    base_score: score.components.prior_breakdown.base_score,
                    mcp_bonus: score.components.prior_breakdown.mcp_bonus,
                    identity_bonus: score.components.prior_breakdown.identity_bonus,
                    security_audit_bonus: score.components.prior_breakdown.security_audit_bonus,
                    open_source_bonus: score.components.prior_breakdown.open_source_bonus,
                    age_bonus: score.components.prior_breakdown.age_bonus,
                    total: score.components.prior_breakdown.total,
                },
                empirical_score: score.components.empirical_score,
                confidence_value: score.components.confidence_value,
                confidence_level: format!("{:?}", score.components.confidence_level),
                prior_weight: score.components.prior_weight,
                empirical_weight: score.components.empirical_weight,
            },
            is_provisional: score.is_provisional,
            data_points: score.data_points,
            algorithm_version: score.algorithm_version.clone(),
        },
    });
    
    *case_id += 1;
}