use crate::config::Config;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Stage {
Cosine,
Rerank,
Lexical,
}
pub const LEXICAL_CONF: f32 = 0.90;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Band {
High,
Medium,
Low,
}
pub const HIGH: f32 = 0.80;
pub const LOW: f32 = 0.55;
const COSINE_SPAN: f32 = 0.45;
pub fn of(score: f32, stage: Stage, cfg: &Config) -> f32 {
match stage {
Stage::Rerank => sigmoid(score),
Stage::Cosine => {
let t = ((score - cfg.min_similarity) / COSINE_SPAN).clamp(0.0, 1.0);
(0.5 + 0.47 * t).clamp(0.0, 0.99)
}
Stage::Lexical => LEXICAL_CONF,
}
}
pub fn band(conf: f32) -> Band {
if conf >= HIGH {
Band::High
} else if conf >= LOW {
Band::Medium
} else {
Band::Low
}
}
fn sigmoid(x: f32) -> f32 {
1.0 / (1.0 + (-x).exp())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::Config;
#[test]
fn rerank_sigmoid_anchors() {
let cfg = Config::default();
assert!((of(0.0, Stage::Rerank, &cfg) - 0.5).abs() < 1e-3);
assert!(of(3.0, Stage::Rerank, &cfg) > 0.9); assert!(of(-2.5, Stage::Rerank, &cfg) < 0.1); }
#[test]
fn cosine_climbs_from_floor() {
let cfg = Config::default(); let at_floor = of(0.30, Stage::Cosine, &cfg);
let strong = of(0.80, Stage::Cosine, &cfg);
assert!((at_floor - 0.5).abs() < 1e-3);
assert!(strong > HIGH);
assert!(strong <= 0.99);
}
#[test]
fn cosine_clamps_below_floor() {
let cfg = Config::default();
assert!(of(0.0, Stage::Cosine, &cfg) >= 0.0);
}
#[test]
fn bands_partition_the_axis() {
assert_eq!(band(0.95), Band::High);
assert_eq!(band(HIGH), Band::High);
assert_eq!(band(0.70), Band::Medium);
assert_eq!(band(LOW), Band::Medium);
assert_eq!(band(0.40), Band::Low);
}
}