feat(embedding,notes): implement ADR-0056 Blake3 stable hashing and ADR-0057 priority-aware retention
This commit is contained in:
1 parent
f546579596
commit
3d77e60a02
3 files changed
+38
-10
No files matched your search
+15
-7
@@ -152,7 +152,6 @@ fn fallback_embed(text: &str) -> Vec<f32> {
|
|||||||
return vec;
|
return vec;
|
||||||
}
|
}
|
||||||
|
|
||||||
use std::hash::{Hash, Hasher};
|
|
||||||
for word in words {
|
for word in words {
|
||||||
let clean: String = word
|
let clean: String = word
|
||||||
.chars()
|
.chars()
|
||||||
@@ -163,18 +162,17 @@ fn fallback_embed(text: &str) -> Vec<f32> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
let hash_bytes = blake3::hash(clean.as_bytes());
|
||||||
clean.hash(&mut hasher);
|
let h = u64::from_le_bytes(hash_bytes.as_bytes()[0..8].try_into().unwrap());
|
||||||
let h = hasher.finish();
|
|
||||||
let idx = (h as usize) % DIM;
|
let idx = (h as usize) % DIM;
|
||||||
let sign = if (h >> 32) & 1 == 0 { 1.0f32 } else { -1.0f32 };
|
let sign = if (h >> 32) & 1 == 0 { 1.0f32 } else { -1.0f32 };
|
||||||
vec[idx] += sign;
|
vec[idx] += sign;
|
||||||
|
|
||||||
let chars: Vec<char> = clean.chars().collect();
|
let chars: Vec<char> = clean.chars().collect();
|
||||||
for window in chars.windows(3) {
|
for window in chars.windows(3) {
|
||||||
let mut h2 = std::collections::hash_map::DefaultHasher::new();
|
let window_str: String = window.iter().collect();
|
||||||
window.hash(&mut h2);
|
let h2_bytes = blake3::hash(window_str.as_bytes());
|
||||||
let hv = h2.finish();
|
let hv = u64::from_le_bytes(h2_bytes.as_bytes()[0..8].try_into().unwrap());
|
||||||
let idx2 = (hv as usize) % DIM;
|
let idx2 = (hv as usize) % DIM;
|
||||||
let s2 = if (hv >> 32) & 1 == 0 { 0.5f32 } else { -0.5f32 };
|
let s2 = if (hv >> 32) & 1 == 0 { 0.5f32 } else { -0.5f32 };
|
||||||
vec[idx2] += s2;
|
vec[idx2] += s2;
|
||||||
@@ -378,4 +376,14 @@ mod tests {
|
|||||||
assert_eq!(embedding.len(), 384);
|
assert_eq!(embedding.len(), 384);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fallback_embed_deterministic_stability() {
|
||||||
|
let text = "The quick brown fox jumps over the lazy dog";
|
||||||
|
let emb1 = fallback_embed(text);
|
||||||
|
let emb2 = fallback_embed(text);
|
||||||
|
assert_eq!(emb1.len(), 384);
|
||||||
|
assert_eq!(emb1, emb2);
|
||||||
|
assert!((cosine_similarity(&emb1, &emb2) - 1.0).abs() < 1e-5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -665,7 +665,20 @@ impl McpTool for TechDebtHandler {
|
|||||||
effort_estimate: req.effort_estimate,
|
effort_estimate: req.effort_estimate,
|
||||||
});
|
});
|
||||||
if debts.len() > 300 {
|
if debts.len() > 300 {
|
||||||
debts.remove(0);
|
let severity_rank = |sev: Option<&str>| match sev.unwrap_or("").to_lowercase().as_str() {
|
||||||
|
"critical" => 4,
|
||||||
|
"high" => 3,
|
||||||
|
"medium" => 2,
|
||||||
|
"low" => 1,
|
||||||
|
_ => 1,
|
||||||
|
};
|
||||||
|
if let Some((idx_to_remove, _)) = debts.iter().enumerate().min_by_key(|(_, d)| {
|
||||||
|
let status_score = if d.is_resolved { 0 } else { 10 };
|
||||||
|
let sev_score = severity_rank(d.severity.as_deref());
|
||||||
|
(status_score + sev_score, d.created_at)
|
||||||
|
}) {
|
||||||
|
debts.remove(idx_to_remove);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Ok("Tech debt logged".to_string())
|
Ok("Tech debt logged".to_string())
|
||||||
@@ -2827,4 +2840,4 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(pre_res.contains("active_tasks"));
|
assert!(pre_res.contains("active_tasks"));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
@@ -108,7 +108,14 @@ impl McpTool for AddSessionSummaryHandler {
|
|||||||
next_steps: req.next_steps.unwrap_or_default(),
|
next_steps: req.next_steps.unwrap_or_default(),
|
||||||
});
|
});
|
||||||
if summaries.len() > 200 {
|
if summaries.len() > 200 {
|
||||||
summaries.remove(0);
|
let now = crate::handlers::utils::now_secs();
|
||||||
|
if let Some((idx_to_remove, _)) = summaries.iter().enumerate().min_by_key(|(_, s)| {
|
||||||
|
let is_expired = s.expires_at.map(|exp| exp < now).unwrap_or(false);
|
||||||
|
let status_score = if is_expired { 0 } else { 1 };
|
||||||
|
(status_score, s.timestamp)
|
||||||
|
}) {
|
||||||
|
summaries.remove(idx_to_remove);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Ok("Session summary added".to_string())
|
Ok("Session summary added".to_string())
|
||||||
|
|||||||
Reference in new issue
Block a user