Files
noc/src/nocmem.rs
Fam Zheng 7000ccda0f add nocmem: auto memory recall + ingest via NuoNuo hippocampal network
- nocmem Python service (mem/): FastAPI wrapper around NuoNuo's
  Hopfield-Hebbian memory, with /recall, /ingest, /store, /stats endpoints
- NOC integration: auto recall after user message (injected as system msg),
  async ingest after LLM response (fire-and-forget)
- Recall: cosine pre-filter (threshold 0.35) + Hopfield attention (β=32),
  top_k=3, KV-cache friendly (appended after user msg, not in system prompt)
- Ingest: LLM extraction + paraphrase augmentation, heuristic fallback
- Wired into main.rs, life.rs (agent done), http.rs (api chat)
- Config: optional `nocmem.endpoint` in config.yaml
- Includes benchmarks: LongMemEval (R@5=94.0%), efficiency, noise vs scale
- Design doc: doc/nocmem.md
2026-04-11 12:24:48 +01:00

70 lines
2.3 KiB
Rust

//! nocmem client — auto-recall and async ingest via HTTP.
use tracing::{info, warn};
/// Recall relevant memories for the given text.
/// Returns formatted memory string, or empty if none found / error / not configured.
pub async fn recall(endpoint: &str, text: &str) -> String {
let client = reqwest::Client::builder()
.timeout(std::time::Duration::from_millis(500))
.build()
.unwrap();
let url = format!("{}/recall", endpoint.trim_end_matches('/'));
match client
.post(&url)
.json(&serde_json::json!({"text": text, "top_k": 3, "hops": 2}))
.send()
.await
{
Ok(resp) => {
if let Ok(json) = resp.json::<serde_json::Value>().await {
let count = json["count"].as_i64().unwrap_or(0);
let memories = json["memories"].as_str().unwrap_or("");
if count > 0 && !memories.is_empty() {
let latency = json["latency_ms"].as_f64().unwrap_or(0.0);
info!("nocmem recall: {count} memories, {latency:.1}ms");
return memories.to_string();
}
}
}
Err(e) => {
warn!("nocmem recall failed: {e:#}");
}
}
String::new()
}
/// Fire-and-forget ingest of a conversation turn.
pub fn ingest_spawn(endpoint: String, user_msg: String, assistant_msg: String) {
tokio::spawn(async move {
let client = reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(120))
.build()
.unwrap();
let url = format!("{}/ingest", endpoint.trim_end_matches('/'));
match client
.post(&url)
.json(&serde_json::json!({
"user_msg": user_msg,
"assistant_msg": assistant_msg,
}))
.send()
.await
{
Ok(resp) => {
if let Ok(json) = resp.json::<serde_json::Value>().await {
let stored = json["stored"].as_i64().unwrap_or(0);
if stored > 0 {
info!("nocmem ingest: stored {stored} memories");
}
}
}
Err(e) => {
warn!("nocmem ingest failed: {e:#}");
}
}
});
}