use std::sync::Arc;
use std::time::Instant;
use ruvector_sona::{SonaConfig, SonaEngine};
use super::rerank::{RerankCandidate, SkillReranker};
pub const DEFAULT_SONA_DIM: usize = 64;
const COSINE_WEIGHT: f32 = 0.35;
const PATTERN_WEIGHT: f32 = 0.5;
const LORA_DELTA_EPS: f32 = 1e-6;
pub const RERANK_TRACING_TARGET: &str = "context_router.hybrid_rerank";
pub struct SonaSkillReranker {
engine: Arc<SonaEngine>,
dim: usize,
pattern_k: usize,
}
impl SonaSkillReranker {
pub fn new() -> Self {
Self::with_config(SonaConfig {
hidden_dim: DEFAULT_SONA_DIM,
embedding_dim: DEFAULT_SONA_DIM,
pattern_clusters: 16,
trajectory_capacity: 1024,
quality_threshold: 0.15,
..SonaConfig::default()
})
}
pub fn with_config(config: SonaConfig) -> Self {
let dim = config.hidden_dim.max(1);
Self {
engine: Arc::new(SonaEngine::with_config(config)),
dim,
pattern_k: 3,
}
}
pub fn from_engine(engine: Arc<SonaEngine>) -> Self {
let dim = engine.config().hidden_dim.max(1);
Self {
engine,
dim,
pattern_k: 3,
}
}
#[must_use]
pub fn with_pattern_k(mut self, k: usize) -> Self {
self.pattern_k = k.max(1);
self
}
pub fn engine(&self) -> &Arc<SonaEngine> {
&self.engine
}
pub fn observe(&self, query: &str, selected_skill: &str, quality: f32) {
let q = hash_embed(query, self.dim);
let skill_emb = hash_embed(selected_skill, self.dim);
let mut builder = self.engine.begin_trajectory(q);
builder.add_step(skill_emb, vec![], quality.clamp(0.0, 1.0));
builder.add_context(selected_skill);
builder.set_model_route(selected_skill);
self.engine
.end_trajectory(builder, quality.clamp(0.0, 1.0));
}
pub fn force_learn(&self) -> String {
self.engine.force_learn()
}
pub fn flush(&self) {
self.engine.flush();
}
}
#[cfg(feature = "vector-memory")]
impl crate::agent::memory_recall::MemoryReranker for SonaSkillReranker {
fn rerank(
&self,
query: &str,
candidates: Vec<crate::agent::memory_recall::MemorySnippet>,
) -> Vec<crate::agent::memory_recall::MemorySnippet> {
let primary: Vec<RerankCandidate> = candidates
.iter()
.enumerate()
.map(|(i, s)| RerankCandidate::new(s.text.clone(), 1.0 / (i as f32 + 1.0)))
.collect();
let order = SkillReranker::rerank(self, query, &primary);
let mut pool = candidates;
order
.into_iter()
.filter_map(|c| pool.iter().position(|s| s.text == c.id).map(|i| pool.remove(i)))
.collect()
}
fn observe(&self, query: &str, rewards: &[(crate::agent::memory_recall::MemorySnippet, i8)]) {
for (snip, r) in rewards {
SonaSkillReranker::observe(self, query, &snip.text, if *r > 0 { 1.0 } else { 0.0 });
}
}
}
impl Default for SonaSkillReranker {
fn default() -> Self {
Self::new()
}
}
impl SkillReranker for SonaSkillReranker {
fn rerank(&self, query: &str, candidates: &[RerankCandidate]) -> Vec<RerankCandidate> {
if candidates.len() <= 1 {
return candidates.to_vec();
}
let started = Instant::now();
let q = hash_embed(query, self.dim);
let mut q_opt = q.clone();
self.engine.apply_micro_lora(&q, &mut q_opt);
let lora_delta: f32 = q
.iter()
.zip(q_opt.iter())
.map(|(a, b)| (a - b).abs())
.sum();
let patterns = self.engine.find_patterns(&q_opt, self.pattern_k);
if patterns.is_empty() && lora_delta < LORA_DELTA_EPS {
tracing::debug!(
target: RERANK_TRACING_TARGET,
latency_us = started.elapsed().as_micros() as u64,
"SonaSkillReranker: no learned signal; preserving primary order"
);
return candidates.to_vec();
}
let mut scored: Vec<(usize, RerankCandidate)> = candidates
.iter()
.cloned()
.enumerate()
.map(|(idx, mut c)| {
let skill_emb = hash_embed(&c.id, self.dim);
let cos = cosine(&q_opt, &skill_emb);
let mut pattern_boost = 0.0f32;
for p in &patterns {
let sim = p.similarity(&skill_emb);
if sim > 0.0 {
pattern_boost += p.avg_quality * sim;
}
}
c.score = c.score + COSINE_WEIGHT * cos + PATTERN_WEIGHT * pattern_boost;
(idx, c)
})
.collect();
scored.sort_by(|a, b| {
b.1.score
.partial_cmp(&a.1.score)
.unwrap_or(std::cmp::Ordering::Equal)
.then_with(|| a.0.cmp(&b.0))
});
let out: Vec<RerankCandidate> = scored.into_iter().map(|(_, c)| c).collect();
tracing::debug!(
target: RERANK_TRACING_TARGET,
latency_us = started.elapsed().as_micros() as u64,
patterns = patterns.len(),
lora_delta,
top = %out.first().map(|c| c.id.as_str()).unwrap_or(""),
"SonaSkillReranker: reranked primary top-K"
);
out
}
}
pub fn hash_embed(text: &str, dim: usize) -> Vec<f32> {
let dim = dim.max(1);
let mut v = vec![0.0f32; dim];
if text.is_empty() {
return v;
}
let mut tokens: Vec<&str> = text.split_whitespace().collect();
tokens.push(text);
for (ti, token) in tokens.iter().enumerate() {
let mut h: u64 = 0xcbf2_9ce4_8422_2325; for b in token.bytes() {
h ^= u64::from(b);
h = h.wrapping_mul(0x0100_0000_01b3);
}
for k in 0u64..4 {
let salt = k.wrapping_mul(0x9e37_79b9_7f4a_7c15);
let idx = (h.wrapping_add(salt) as usize) % dim;
let sign = if (h >> (k * 8)) & 1 == 0 { 1.0 } else { -1.0 };
let w = 1.0 / (1.0 + ti as f32 * 0.25);
v[idx] += sign * w;
}
}
let norm: f32 = v.iter().map(|x| x * x).sum::<f32>().sqrt();
if norm > f32::EPSILON {
for x in &mut v {
*x /= norm;
}
}
v
}
fn cosine(a: &[f32], b: &[f32]) -> f32 {
if a.len() != b.len() || a.is_empty() {
return 0.0;
}
let mut dot = 0.0f32;
let mut na = 0.0f32;
let mut nb = 0.0f32;
for (x, y) in a.iter().zip(b.iter()) {
dot += x * y;
na += x * x;
nb += y * y;
}
let denom = na.sqrt() * nb.sqrt();
if denom > f32::EPSILON {
(dot / denom).clamp(-1.0, 1.0)
} else {
0.0
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Instant;
#[test]
fn hash_embed_is_deterministic_and_unit() {
let a = hash_embed("code-review", DEFAULT_SONA_DIM);
let b = hash_embed("code-review", DEFAULT_SONA_DIM);
assert_eq!(a, b);
let n: f32 = a.iter().map(|x| x * x).sum::<f32>().sqrt();
assert!((n - 1.0).abs() < 1e-4, "norm={n}");
}
#[test]
fn day0_preserves_primary_order() {
let rr = SonaSkillReranker::new();
let cands = vec![
RerankCandidate::new("alpha", 1.0),
RerankCandidate::new("beta", 0.5),
RerankCandidate::new("gamma", 0.33),
];
let out = rr.rerank("please help with X", &cands);
assert_eq!(
out.iter().map(|c| c.id.as_str()).collect::<Vec<_>>(),
vec!["alpha", "beta", "gamma"]
);
}
#[test]
fn observe_and_force_learn_do_not_panic() {
let rr = SonaSkillReranker::new();
for _ in 0..20 {
rr.observe("refactor this rust module", "code-review", 0.9);
}
let msg = rr.force_learn();
assert!(msg.contains("trajectories"), "got: {msg}");
rr.flush();
let cands = vec![
RerankCandidate::new("chat", 1.0),
RerankCandidate::new("code-review", 0.5),
];
let out = rr.rerank("refactor this rust module", &cands);
assert_eq!(out.len(), 2);
let mut ids: Vec<_> = out.iter().map(|c| c.id.as_str()).collect();
ids.sort_unstable();
assert_eq!(ids, vec!["chat", "code-review"]);
}
#[test]
fn bench_rerank_on_vs_off_latency_and_accuracy() {
const N: usize = 200;
const K: usize = 8;
let skills: Vec<String> = (0..K).map(|i| format!("skill-{i}")).collect();
let cands: Vec<RerankCandidate> = skills
.iter()
.enumerate()
.map(|(i, s)| RerankCandidate::new(s.clone(), 1.0 / (i as f32 + 1.0)))
.collect();
let query = "how do I wire HybridRouter sona rerank";
let identity = super::super::rerank::IdentityReranker;
let t0 = Instant::now();
let mut off_top1_hits = 0usize;
for _ in 0..N {
let out = identity.rerank(query, &cands);
if out.first().map(|c| c.id.as_str()) == Some("skill-0") {
off_top1_hits += 1;
}
}
let off_us = t0.elapsed().as_micros() as f64 / N as f64;
let sona = SonaSkillReranker::new();
let t1 = Instant::now();
let mut on_top1_hits = 0usize;
for _ in 0..N {
let out = sona.rerank(query, &cands);
if out.first().map(|c| c.id.as_str()) == Some("skill-0") {
on_top1_hits += 1;
}
}
let on_us = t1.elapsed().as_micros() as f64 / N as f64;
assert_eq!(off_top1_hits, N, "identity must always keep primary top-1");
assert_eq!(
on_top1_hits, N,
"untrained SONA must preserve primary top-1 (day-0 fail-open)"
);
assert!(
on_us < 3000.0,
"sona rerank mean latency {on_us:.1} µs exceeds 3 ms budget"
);
eprintln!(
"WEFT-46 bench: off={off_us:.2} µs/call hit@1={off_top1_hits}/{N}; \
on={on_us:.2} µs/call hit@1={on_top1_hits}/{N} (day-0)"
);
for i in 0..40 {
let q = format!("{query} variant {i}");
sona.observe(&q, "skill-3", 0.95);
}
sona.force_learn();
sona.flush();
let trained = sona.rerank(query, &cands);
assert_eq!(trained.len(), K, "trained rerank must keep full candidate set");
let mut ids: Vec<_> = trained.iter().map(|c| c.id.clone()).collect();
ids.sort();
let mut expected = skills.clone();
expected.sort();
assert_eq!(ids, expected);
}
#[cfg(feature = "vector-memory")]
#[test]
fn untrained_memory_rerank_keeps_retriever_order_and_observe_accepts_rewards() {
use crate::agent::memory_recall::{split_snippets, MemoryReranker};
let r = SonaSkillReranker::new();
let snips = split_snippets("alpha fact\n\nbeta fact\n\ngamma fact");
let out = MemoryReranker::rerank(&r, "beta", snips.clone());
assert_eq!(out, snips, "day-0 fail-open: retriever order preserved");
MemoryReranker::observe(&r, "beta", &[(snips[1].clone(), 1), (snips[0].clone(), -1)]);
let after = MemoryReranker::rerank(&r, "beta", snips.clone());
assert_eq!(after.len(), 3);
for s in &snips {
assert_eq!(after.iter().filter(|x| *x == s).count(), 1);
}
}
}