use super::{Memory, MemoryHook};
use schemars::JsonSchema;
use serde::Deserialize;
use wcore::{
ToolDispatch,
model::{HistoryEntry, Role},
};
#[derive(Deserialize, JsonSchema)]
pub struct Recall {
pub query: String,
pub limit: Option<usize>,
}
impl Memory {
pub fn recall(&self, query: &str, limit: usize) -> String {
let store = self.store_read();
let hits = store.search(query, limit);
if hits.is_empty() {
return "no memories found".to_owned();
}
hits.iter()
.map(|h| format!("## {}\n{}", h.entry.name, h.entry.content))
.collect::<Vec<_>>()
.join("\n---\n")
}
pub fn before_run(&self, history: &[HistoryEntry], limit: usize) -> Vec<HistoryEntry> {
let last_user = history
.iter()
.rev()
.find(|e| *e.role() == Role::User && !e.text().is_empty());
let Some(entry) = last_user else {
return Vec::new();
};
let query: String = entry
.text()
.split_whitespace()
.take(8)
.collect::<Vec<_>>()
.join(" ");
if query.is_empty() {
return Vec::new();
}
let result = self.recall(&query, limit);
if result == "no memories found" {
return Vec::new();
}
vec![HistoryEntry::user(format!("<recall>\n{result}\n</recall>")).auto_injected()]
}
}
impl MemoryHook {
pub(super) async fn handle_recall(&self, call: ToolDispatch) -> Result<String, String> {
let input: Recall =
serde_json::from_str(&call.args).map_err(|e| format!("invalid arguments: {e}"))?;
let limit = input
.limit
.unwrap_or_else(|| self.recall_limit(&call.agent));
Ok(self.memory.recall(&input.query, limit))
}
}