use cortiq_core::CmfModel;
use cortiq_engine::pipeline::Pipeline;
use std::io::Write;
use std::path::PathBuf;
pub fn ood_dir() -> Option<PathBuf> {
std::env::var("CMF_OOD_DIR").ok().map(PathBuf::from)
}
fn tau() -> f32 {
std::env::var("CMF_OOD_TAU").ok().and_then(|v| v.parse().ok()).unwrap_or(0.30)
}
pub fn touch_last_request() {
let Some(dir) = ood_dir() else { return };
let _ = std::fs::create_dir_all(&dir);
let p = dir.join("last_request");
let _ = std::fs::write(&p, format!("{}\n", unix_now()));
}
fn unix_now() -> u64 {
std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).map(|d| d.as_secs()).unwrap_or(0)
}
pub fn record_if_ood(model: &CmfModel, pipe: &mut Pipeline, prompt_ids: &[u32], prompt_text: &str) -> Option<(String, f32)> {
let dir = ood_dir()?;
let r = cortiq_engine::router::route_full(model, pipe, prompt_ids, tau());
let best = r.scores.first().map(|s| (s.id.clone(), s.error));
let ood = r.is_novel;
if ood {
let _ = std::fs::create_dir_all(&dir);
if let Ok(mut f) = std::fs::OpenOptions::new().create(true).append(true).open(dir.join("buffer.jsonl")) {
let rec = serde_json::json!({
"ts": unix_now(),
"e_min": best.as_ref().map(|(_, e)| *e),
"nearest": best.as_ref().map(|(id, _)| id.clone()),
"novelty": if r.novelty.is_finite() { Some(r.novelty) } else { None },
"confidence": r.confidence,
"calibrated": r.calibrated,
"tokens": prompt_ids.len(),
"text": prompt_text,
});
let _ = writeln!(f, "{}", rec);
}
}
best
}