lean_ctx/core/locomo/
runner.rs1use std::path::Path;
10
11use crate::core::eval_ab::scorers::{qa_contains, qa_exact_match, qa_f1};
12use crate::core::knowledge::ProjectKnowledge;
13use crate::core::memory_policy::MemoryPolicy;
14use crate::core::tokens::count_tokens;
15
16use super::dataset::LocomoSample;
17
18#[derive(Debug, Clone)]
20pub struct QaResult {
21 pub category: u8,
22 pub f1: f64,
23 pub exact_match: bool,
24 pub contained: bool,
26 pub recall_tokens: usize,
27}
28
29#[derive(Debug, Clone)]
31pub struct SampleResult {
32 pub id: String,
33 pub qa: Vec<QaResult>,
34 pub transcript_tokens: usize,
35}
36
37pub fn run_sample(sample: &LocomoSample, project_root: &Path, top_k: usize) -> SampleResult {
43 let root = project_root.to_string_lossy().to_string();
44 let policy = MemoryPolicy::default();
45
46 let _ = ProjectKnowledge::mutate_locked(&root, |k| {
48 for (si, session) in sample.sessions.iter().enumerate() {
49 let session_id = if session.session_id.is_empty() {
50 format!("s{si}")
51 } else {
52 session.session_id.clone()
53 };
54 for (ti, turn) in session.turns.iter().enumerate() {
55 let key = format!("{}-{}-{}", sample.id, session_id, ti);
56 let value = format!("{}: {}", turn.speaker, turn.text);
57 k.remember("conversation", &key, &value, &session_id, 0.9, &policy);
58 }
59 }
60 });
61
62 let transcript_tokens = count_tokens(&sample.transcript());
63
64 let mut knowledge = ProjectKnowledge::load_or_create(&root);
66 let mut qa = Vec::with_capacity(sample.qa.len());
67 for item in &sample.qa {
68 let (hits, _) = knowledge.recall_for_output(&item.question, top_k);
69 let context = hits
70 .iter()
71 .map(|f| f.value.as_str())
72 .collect::<Vec<_>>()
73 .join("\n");
74 let recall_tokens = count_tokens(&context);
75
76 let mut best_f1 = 0.0f64;
81 let mut em = false;
82 let mut contained = false;
83 for gold in &item.answers {
84 contained |= qa_contains(&context, gold);
85 for hit in &hits {
86 best_f1 = best_f1.max(qa_f1(&hit.value, gold));
87 em |= qa_exact_match(&hit.value, gold);
88 }
89 }
90 qa.push(QaResult {
91 category: item.category,
92 f1: best_f1,
93 exact_match: em,
94 contained,
95 recall_tokens,
96 });
97 }
98
99 SampleResult {
100 id: sample.id.clone(),
101 qa,
102 transcript_tokens,
103 }
104}
105
106pub fn run_suite(samples: &[LocomoSample], workspace: &Path, top_k: usize) -> Vec<SampleResult> {
108 samples
109 .iter()
110 .enumerate()
111 .map(|(i, sample)| {
112 let proj = workspace.join(format!("sample-{i}"));
113 let _ = std::fs::create_dir_all(&proj);
114 run_sample(sample, &proj, top_k)
115 })
116 .collect()
117}