//! 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::().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::().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:#}"); } } }); }