lean_ctx/core/quality_lab/
fidelity.rs1#[derive(
4 Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
5)]
6pub enum FidelityClassV1 {
7 Exact,
9 Structural,
12 Lossy,
15 Unknown,
17}
18
19#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
21pub struct FidelityAssessment {
22 pub class: FidelityClassV1,
23 pub preservation_score: f64,
24 pub identifier_score: f64,
25 pub density: f64,
26 pub passed_quality_gate: bool,
27}
28
29impl FidelityClassV1 {
30 pub fn is_lossless(&self) -> bool {
31 matches!(self, Self::Exact | Self::Structural)
32 }
33
34 pub fn savings_eligible(&self) -> bool {
35 matches!(self, Self::Exact | Self::Structural)
36 }
37
38 pub fn requires_policy_opt_in(&self) -> bool {
39 matches!(self, Self::Lossy)
40 }
41}
42
43impl FidelityAssessment {
44 pub fn token_savings_pct(&self, original_tokens: usize, compressed_tokens: usize) -> f64 {
45 if original_tokens == 0 {
46 return 0.0;
47 }
48
49 (1.0 - compressed_tokens as f64 / original_tokens as f64) * 100.0
50 }
51}
52
53pub fn assess_fidelity(original: &str, compressed: &str, ext: &str) -> FidelityAssessment {
54 if original == compressed {
55 return FidelityAssessment {
56 class: FidelityClassV1::Exact,
57 preservation_score: 1.0,
58 identifier_score: 1.0,
59 density: 1.0,
60 passed_quality_gate: true,
61 };
62 }
63
64 if original.is_empty() || compressed.is_empty() {
65 return unknown_assessment();
66 }
67
68 let quality_score = crate::core::quality::score(original, compressed, ext);
69 let preservation = crate::core::preservation::measure(original, compressed, ext);
70 let preservation_score = preservation.overall();
71
72 let class = classify(&quality_score, preservation_score);
73
74 FidelityAssessment {
75 class,
76 preservation_score,
77 identifier_score: quality_score.identifier_score,
78 density: quality_score.density,
79 passed_quality_gate: matches!(class, FidelityClassV1::Exact | FidelityClassV1::Structural),
80 }
81}
82
83fn classify(qs: &crate::core::quality::QualityScore, preservation: f64) -> FidelityClassV1 {
84 let has_ast = qs.ast_score > 0.0;
85
86 let structural = if has_ast {
87 qs.composite >= 0.75 && preservation >= 0.90
88 } else {
89 qs.identifier_score >= 0.6 && preservation >= 0.90
90 };
91
92 if structural {
93 return FidelityClassV1::Structural;
94 }
95
96 let signal = if has_ast {
97 qs.composite
98 } else {
99 qs.identifier_score
100 };
101
102 if signal >= 0.4 || preservation >= 0.7 {
103 return FidelityClassV1::Lossy;
104 }
105 if signal < 0.3 && preservation < 0.5 {
106 return FidelityClassV1::Unknown;
107 }
108 FidelityClassV1::Lossy
109}
110
111pub fn assess_fidelity_from_bytes(
112 original: &[u8],
113 compressed: &[u8],
114 ext: &str,
115) -> FidelityAssessment {
116 let Ok(original) = std::str::from_utf8(original) else {
117 return unknown_assessment();
118 };
119 let Ok(compressed) = std::str::from_utf8(compressed) else {
120 return unknown_assessment();
121 };
122
123 assess_fidelity(original, compressed, ext)
124}
125
126fn unknown_assessment() -> FidelityAssessment {
127 FidelityAssessment {
128 class: FidelityClassV1::Unknown,
129 preservation_score: 0.0,
130 identifier_score: 0.0,
131 density: 0.0,
132 passed_quality_gate: false,
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::{FidelityAssessment, FidelityClassV1, assess_fidelity, assess_fidelity_from_bytes};
139
140 #[test]
141 fn test_exact_fidelity_for_identical_content() {
142 let source = "pub fn calculate_total() -> f64 { 42.0 }";
143
144 let assessment = assess_fidelity(source, source, "rs");
145
146 assert_eq!(assessment.class, FidelityClassV1::Exact);
147 assert_eq!(assessment.preservation_score, 1.0);
148 assert!(assessment.passed_quality_gate);
149 }
150
151 #[test]
152 fn test_structural_fidelity_for_good_compression() {
153 let original = r#"
154pub fn calculate_total(items: &[Item]) -> f64 {
155 items.iter().map(|i| i.price * i.quantity as f64).sum()
156}
157
158pub fn format_receipt(total: f64) -> String {
159 format!("Total: ${:.2}", total)
160}
161"#;
162 let compressed = r#"pub fn calculate_total(items: &[Item]) -> f64 {
163 items.iter()
164 .map(|i| i.price * i.quantity as f64)
165 .sum()
166}
167pub fn format_receipt(total: f64) -> String {
168 format!("Total: ${total:.2}")
169}"#;
170
171 let assessment = assess_fidelity(original, compressed, "rs");
172
173 assert_eq!(assessment.class, FidelityClassV1::Structural);
174 assert!(assessment.preservation_score >= 0.95);
175 assert!(assessment.passed_quality_gate);
176 }
177
178 #[test]
179 fn test_lossy_fidelity_for_aggressive_compression() {
180 let original = r#"
181pub fn calculate_total(items: &[Item]) -> f64 {
182 items.iter().map(|i| i.price * i.quantity as f64).sum()
183}
184
185pub fn format_receipt(total: f64) -> String {
186 format!("Total: ${:.2}", total)
187}
188"#;
189 let compressed = "pub fn calculate_total(items: &[Item]) -> f64 { ... }";
190
191 let assessment = assess_fidelity(original, compressed, "rs");
192
193 assert_eq!(assessment.class, FidelityClassV1::Lossy);
194 assert!(!assessment.passed_quality_gate);
195 }
196
197 #[test]
198 fn test_unknown_fidelity_for_empty_input() {
199 let assessment = assess_fidelity("", "summary", "txt");
200
201 assert_eq!(assessment.class, FidelityClassV1::Unknown);
202 assert!(!assessment.passed_quality_gate);
203 }
204
205 #[test]
206 fn test_exact_is_lossless() {
207 assert!(FidelityClassV1::Exact.is_lossless());
208 }
209
210 #[test]
211 fn test_lossy_requires_policy() {
212 assert!(FidelityClassV1::Lossy.requires_policy_opt_in());
213 assert!(!FidelityClassV1::Structural.requires_policy_opt_in());
214 }
215
216 #[test]
217 fn test_savings_pct_calculation() {
218 let assessment = FidelityAssessment {
219 class: FidelityClassV1::Structural,
220 preservation_score: 1.0,
221 identifier_score: 1.0,
222 density: 1.0,
223 passed_quality_gate: true,
224 };
225
226 assert_eq!(assessment.token_savings_pct(100, 40), 60.0);
227 assert_eq!(assessment.token_savings_pct(0, 40), 0.0);
228 }
229
230 #[test]
231 fn test_bytes_assessment_with_valid_utf8() {
232 let source = b"pub fn total() -> usize { 42 }";
233
234 let assessment = assess_fidelity_from_bytes(source, source, "rs");
235
236 assert_eq!(assessment.class, FidelityClassV1::Exact);
237 }
238
239 #[test]
240 fn test_bytes_assessment_with_invalid_utf8() {
241 let assessment = assess_fidelity_from_bytes(&[0xff], b"summary", "txt");
242
243 assert_eq!(assessment.class, FidelityClassV1::Unknown);
244 assert!(!assessment.passed_quality_gate);
245 }
246
247 #[test]
248 fn test_fidelity_ordering() {
249 assert!(FidelityClassV1::Exact < FidelityClassV1::Structural);
250 assert!(FidelityClassV1::Structural < FidelityClassV1::Lossy);
251 assert!(FidelityClassV1::Lossy < FidelityClassV1::Unknown);
252 }
253
254 #[test]
255 fn test_savings_eligibility() {
256 assert!(FidelityClassV1::Exact.savings_eligible());
257 assert!(FidelityClassV1::Structural.savings_eligible());
258 assert!(!FidelityClassV1::Lossy.savings_eligible());
259 assert!(!FidelityClassV1::Unknown.savings_eligible());
260 }
261}