#[derive(Debug, Clone, PartialEq)]
pub struct EmbedderManifest {
pub model_id: String,
pub dim: u32,
pub noise_floor: f32,
}
impl EmbedderManifest {
#[must_use]
pub fn new(model_id: impl Into<String>, dim: u32, noise_floor: f32) -> Self {
assert!(
noise_floor.is_finite() && (0.0..=1.0).contains(&noise_floor),
"noise_floor must be a finite f32 in [0.0, 1.0]; got {noise_floor}"
);
Self {
model_id: model_id.into(),
dim,
noise_floor,
}
}
}
pub const DEFAULT_LATENCY_BUDGET_MS: u32 = 200;
#[must_use]
pub fn derive_max_cooccurrence_ms(latency_budget: Option<u32>) -> u32 {
let budget = latency_budget.unwrap_or(DEFAULT_LATENCY_BUDGET_MS);
(budget / 2).max(20)
}
#[must_use]
pub fn derive_max_knn_ingest_per_node_ms(coocc_ms: u32, batch: u32) -> u32 {
let divisor = batch.max(1);
(coocc_ms / divisor).max(5)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn manifest_round_trip() {
let m = EmbedderManifest::new("mock:test", 16, 0.0);
assert_eq!(m.model_id, "mock:test");
assert_eq!(m.dim, 16);
assert!((m.noise_floor - 0.0).abs() < f32::EPSILON);
}
#[test]
#[should_panic(expected = "noise_floor")]
fn manifest_rejects_nan() {
let _ = EmbedderManifest::new("bad:model", 16, f32::NAN);
}
#[test]
#[should_panic(expected = "noise_floor")]
fn manifest_rejects_negative() {
let _ = EmbedderManifest::new("bad:model", 16, -0.01);
}
#[test]
#[should_panic(expected = "noise_floor")]
fn manifest_rejects_above_one() {
let _ = EmbedderManifest::new("bad:model", 16, 1.01);
}
#[test]
fn derive_coocc_uses_default_when_none() {
assert_eq!(
derive_max_cooccurrence_ms(None),
DEFAULT_LATENCY_BUDGET_MS / 2
);
}
#[test]
fn derive_coocc_is_half_of_budget() {
assert_eq!(derive_max_cooccurrence_ms(Some(400)), 200);
}
#[test]
fn derive_coocc_has_floor() {
assert_eq!(derive_max_cooccurrence_ms(Some(10)), 20);
}
#[test]
fn derive_knn_divides_by_batch() {
assert_eq!(derive_max_knn_ingest_per_node_ms(100, 10), 10);
}
#[test]
fn derive_knn_has_floor() {
assert_eq!(derive_max_knn_ingest_per_node_ms(10, 1000), 5);
}
#[test]
fn derive_knn_handles_zero_batch() {
assert_eq!(derive_max_knn_ingest_per_node_ms(100, 0), 100);
}
}