metrale_bench/benchmarks/ssm_poison/
report.rs1use std::collections::BTreeMap;
11
12use super::compare::TurnDelta;
13use super::score::Score;
14use crate::result::{Cell, CellStyle, Column, ResultTable, Stat};
15
16fn delta_detail(turns: &[TurnDelta]) -> String {
17 turns
18 .iter()
19 .map(|t| {
20 format!(
21 "t{}: {}->{}tok fin {:?}->{:?}",
22 t.turn, t.ref_tokens, t.replay_tokens, t.ref_finish, t.replay_finish
23 )
24 })
25 .collect::<Vec<_>>()
26 .join(", ")
27}
28
29pub(super) fn table(s: &Score) -> ResultTable {
31 let mut t = ResultTable::new(
32 "REPLAY ROUNDS",
33 vec![
34 Column::left("Round", 6),
35 Column::left("Result", 14),
36 Column::left("Detail", 56),
37 ],
38 );
39 let jitter_map: BTreeMap<usize, &Vec<TurnDelta>> =
40 s.jittered_rounds.iter().map(|(n, d)| (*n, d)).collect();
41 let collapse_map: BTreeMap<usize, &Vec<TurnDelta>> =
42 s.collapsed_rounds.iter().map(|(n, d)| (*n, d)).collect();
43 let unmeasured_map: BTreeMap<usize, &str> = s
45 .unmeasured_rounds
46 .iter()
47 .map(|(n, r)| (*n, r.as_str()))
48 .collect();
49 for round in 1..=s.rounds {
50 let (what, style, detail) = if let Some(turns) = collapse_map.get(&round) {
51 ("COLLAPSED".to_string(), CellStyle::Bad, delta_detail(turns))
52 } else if let Some(turns) = jitter_map.get(&round) {
53 ("jittered".to_string(), CellStyle::Warn, delta_detail(turns))
54 } else if let Some(reason) = unmeasured_map.get(&round) {
55 (
56 "unmeasured".into(),
57 CellStyle::Bad,
58 format!("invariant not proven this round — {reason}"),
59 )
60 } else {
61 ("invariant".into(), CellStyle::Good, String::new())
62 };
63 t.push(vec![
64 Cell::new(format!("r{round}")),
65 Cell::styled(what, style),
66 Cell::new(detail),
67 ]);
68 }
69 t
70}
71
72pub(super) fn summary(s: &Score) -> Vec<Stat> {
76 vec![
77 Stat::new("Invariant", format!("{}/{}", s.invariant, s.rounds), "").with_style(
78 if s.rounds > 0 && s.invariant == s.rounds {
79 CellStyle::Good
80 } else {
81 CellStyle::Neutral
82 },
83 ),
84 Stat::new("Jittered", s.jittered.to_string(), "").with_style(if s.jittered == 0 {
85 CellStyle::Good
86 } else {
87 CellStyle::Warn
88 }),
89 Stat::new("Collapsed", s.collapsed.to_string(), "").with_style(if s.collapsed == 0 {
90 CellStyle::Good
91 } else {
92 CellStyle::Bad
93 }),
94 Stat::new("Unmeasured", s.unmeasured.to_string(), "").with_style(if s.unmeasured == 0 {
95 CellStyle::Good
96 } else {
97 CellStyle::Bad
98 }),
99 Stat::new(
101 "Min t1 cache",
102 s.min_turn1_cached.unwrap_or(0).to_string(),
103 "tok",
104 )
105 .with_style(if s.min_turn1_cached.unwrap_or(0) > 0 {
106 CellStyle::Good
107 } else {
108 CellStyle::Bad
109 }),
110 ]
111}
112
113pub(super) fn metrics(s: &Score) -> BTreeMap<String, f64> {
116 let mut m: BTreeMap<String, f64> = [
117 ("rounds", s.rounds),
118 ("invariant", s.invariant),
119 ("jittered", s.jittered),
120 ("collapsed", s.collapsed),
121 ("unmeasured", s.unmeasured),
122 ]
123 .into_iter()
124 .map(|(k, v)| (k.to_string(), v as f64))
125 .collect();
126 if let Some(min) = s.min_turn1_cached {
132 m.insert("min_cached_prompt_tokens".to_string(), min as f64);
133 }
134 m
135}
136
137#[cfg(test)]
138#[path = "report_tests.rs"]
139mod report_tests;