v0.9.2 — NaN-safe recall
v0.9.2 — patch
Fixes #60: a NaN-valued embedding could panic the process during recall() / procedure(action="surface").
Root cause (two bugs compounding)
hnsw.rs cosine_distance()''s zero-norm guard usednorm == 0.0, which misses NaN (NaN == 0.0isfalse) — so a NaN embedding produced a NaN score with no interception.- Sort comparators used
partial_cmp(..).unwrap_or(Ordering::Equal), which is not a valid total order once NaN mixes with finite scores. Rust ≥ 1.81''s driftsort detects the broken transitivity and panics ("user-provided comparison function does not correctly implement a total order"), killing the call and leaving MCP sessions returning-32602.
Fix
- hnsw norm guard is now
!(norm > 0.0)— catches NaN, zero, and negatives; distance is always finite. - Crate-wide sweep: every float sort comparator (117 sites across 40 files, including the
recall_profiledprofiling-gated duplicates and the hnsw heapOrdimpls) now usesf64::total_cmp— IEEE totalOrder, never panics, order-identical topartial_cmpfor all finite values. - Regression tests: NaN/zero-norm distance guard + end-to-end recall with a NaN embedding mixed into finite candidates.
Credit: reported by @blue-mtn-dog with an exceptional root-cause analysis — traced to exact source lines against the v0.9.1 tag, including the driftsort total-order precondition.
Published to PyPI (pip install -U yantrikdb) and crates.io (yantrikdb).