use super::{LogitsContext, LogitsProcessor, ProcessorOutcome};
use crate::scheduler::ActiveSeq;
use std::io::Write;
pub struct AdaDecDiagnostic;
fn masked_entropy(logits: &[f32]) -> f32 {
let mut max = f32::NEG_INFINITY;
for &l in logits {
if l.is_finite() && l > max {
max = l;
}
}
if !max.is_finite() {
return 0.0;
}
let mut z: f64 = 0.0;
let mut weighted_logit_sum: f64 = 0.0;
for &l in logits {
if !l.is_finite() {
continue;
}
let e = ((l - max) as f64).exp();
z += e;
weighted_logit_sum += (l as f64) * e;
}
if z <= 0.0 {
return 0.0;
}
let log_z_plus_max = z.ln() + (max as f64);
let expectation = weighted_logit_sum / z;
(log_z_plus_max - expectation) as f32
}
fn top_k(logits: &[f32], k: usize) -> Vec<(u32, f32)> {
let mut out: Vec<(u32, f32)> = Vec::with_capacity(k);
for (i, &l) in logits.iter().enumerate() {
if !l.is_finite() {
continue;
}
if out.len() < k {
out.push((i as u32, l));
out.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
} else if l > out[k - 1].1 {
out[k - 1] = (i as u32, l);
out.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
}
}
out
}
pub fn log_step(
sink: Option<&std::sync::Mutex<std::fs::File>>,
logits: &[f32],
seq: &ActiveSeq,
path: &'static str,
) {
let Some(mtx) = sink else {
return;
};
let h = masked_entropy(logits);
let tk = top_k(logits, 3);
let topk_ids: Vec<u32> = tk.iter().map(|(i, _)| *i).collect();
let topk_logits: Vec<f32> = tk.iter().map(|(_, l)| *l).collect();
let record = serde_json::json!({
"t": seq.output_tokens.len(),
"h": h,
"topk_ids": topk_ids,
"topk_logits": topk_logits,
"thk": seq.inside_thinking,
"pb": seq.inside_parameter_body,
"pbc": seq.param_body_chars_emitted,
"p": path,
});
if let Ok(mut f) = mtx.lock() {
let mut line = serde_json::to_string(&record).unwrap_or_default();
line.push('\n');
let _ = f.write_all(line.as_bytes());
}
}
impl LogitsProcessor for AdaDecDiagnostic {
fn apply(
&self,
logits: &mut [f32],
seq: &mut ActiveSeq,
ctx: &LogitsContext,
) -> ProcessorOutcome {
log_step(ctx.tel.dumps().adadec.as_ref(), logits, seq, "verify");
ProcessorOutcome::Continue
}
fn name(&self) -> &'static str {
"adadec_diag"
}
fn is_argmax_invariant(&self) -> bool {
true
}
}