lean_ctx/core/triage/
validation.rs1use std::collections::HashMap;
4
5use serde::Deserialize;
6
7use super::{TaskAnalysisInput, TaskAnalyzer, rules::RuleTriageBackend};
8
9const GOLD_SET: &str = include_str!("../../../data/triage_gold_set.jsonl");
10
11#[derive(Debug, Clone, PartialEq)]
12pub struct ValidationResult {
13 pub total: usize,
14 pub correct: usize,
15 pub accuracy: f64,
16 pub per_intent: HashMap<String, (usize, usize)>,
18}
19
20#[derive(Deserialize)]
21struct GoldTask {
22 query: String,
23 labels: GoldLabels,
24}
25
26#[derive(Deserialize)]
27struct GoldLabels {
28 intent: String,
29}
30
31pub fn validate_against_gold_set() -> ValidationResult {
32 let tasks: Vec<GoldTask> = GOLD_SET
33 .lines()
34 .map(|line| serde_json::from_str(line).expect("gold set must be valid JSONL"))
35 .collect();
36 let mut gold_counts = HashMap::<String, usize>::new();
37 let mut predicted_counts = HashMap::<String, usize>::new();
38 let mut true_positives = HashMap::<String, usize>::new();
39 for task in &tasks {
40 *gold_counts.entry(task.labels.intent.clone()).or_default() += 1;
41 let predicted = RuleTriageBackend
42 .analyze(&TaskAnalysisInput {
43 query: task.query.clone(),
44 ..Default::default()
45 })
46 .expect("rule backend must classify gold tasks")
47 .profile
48 .intent;
49 *predicted_counts.entry(predicted.clone()).or_default() += 1;
50 if predicted == task.labels.intent {
51 *true_positives.entry(predicted).or_default() += 1;
52 }
53 }
54 let correct = true_positives.values().sum();
55 let per_intent = gold_counts
56 .iter()
57 .map(|(intent, total)| {
58 (
59 intent.clone(),
60 (*true_positives.get(intent).unwrap_or(&0), *total),
61 )
62 })
63 .collect();
64 for (intent, predicted) in predicted_counts {
65 let tp = *true_positives.get(&intent).unwrap_or(&0) as f64;
66 let precision = tp / predicted as f64;
67 let recall = tp / *gold_counts.get(&intent).unwrap_or(&0) as f64;
68 let _ = (precision, recall);
69 }
70 ValidationResult {
71 total: tasks.len(),
72 correct,
73 accuracy: correct as f64 / tasks.len() as f64,
74 per_intent,
75 }
76}