use async_trait::async_trait;
use deepstrike_core::memory::curator::CurationResult;
use deepstrike_core::memory::durable::SessionData;
use deepstrike_core::memory::semantic::MemoryEntry;
#[async_trait]
pub trait DreamStore: Send + Sync {
async fn load_sessions(&self, agent_id: &str) -> crate::Result<Vec<SessionData>>;
async fn load_memories(&self, agent_id: &str) -> crate::Result<Vec<MemoryEntry>>;
async fn commit(
&self,
agent_id: &str,
result: CurationResult,
existing: &[MemoryEntry],
) -> crate::Result<()>;
async fn search(
&self,
agent_id: &str,
query: &str,
top_k: usize,
) -> crate::Result<Vec<MemoryEntry>>;
async fn save_session(
&self,
data: deepstrike_core::memory::durable::SessionData,
) -> crate::Result<()>;
}
#[derive(Debug, Default, Clone)]
pub struct DreamResult {
pub sessions_processed: usize,
pub insights_extracted: usize,
pub entries_added: usize,
pub entries_removed: usize,
}
#[derive(Default)]
pub struct WorkingMemory {
store: std::collections::HashMap<String, serde_json::Value>,
}
impl WorkingMemory {
pub fn set(&mut self, key: impl Into<String>, value: impl Into<serde_json::Value>) {
self.store.insert(key.into(), value.into());
}
pub fn get(&self, key: &str) -> Option<&serde_json::Value> {
self.store.get(key)
}
pub fn clear(&mut self) {
self.store.clear();
}
}
pub struct InMemoryDreamStore {
sessions: std::sync::Mutex<std::collections::HashMap<String, Vec<SessionData>>>,
memories: std::sync::Mutex<std::collections::HashMap<String, Vec<MemoryEntry>>>,
initial_memories: Vec<MemoryEntry>,
saved_sessions: std::sync::Mutex<Vec<SessionData>>,
}
impl InMemoryDreamStore {
pub fn new() -> Self {
Self::with_initial_memories(Vec::new())
}
pub fn with_initial_memories(initial: Vec<MemoryEntry>) -> Self {
Self {
sessions: std::sync::Mutex::new(std::collections::HashMap::new()),
memories: std::sync::Mutex::new(std::collections::HashMap::new()),
initial_memories: initial,
saved_sessions: std::sync::Mutex::new(Vec::new()),
}
}
pub fn add_session(&self, agent_id: impl Into<String>, session: SessionData) {
self.sessions
.lock()
.unwrap()
.entry(agent_id.into())
.or_default()
.push(session);
}
pub fn add_memories(&self, agent_id: impl Into<String>, entries: Vec<MemoryEntry>) {
self.memories
.lock()
.unwrap()
.entry(agent_id.into())
.or_default()
.extend(entries);
}
pub fn saved_sessions(&self) -> Vec<SessionData> {
self.saved_sessions.lock().unwrap().clone()
}
}
impl Default for InMemoryDreamStore {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl DreamStore for InMemoryDreamStore {
async fn load_sessions(&self, agent_id: &str) -> crate::Result<Vec<SessionData>> {
Ok(self.sessions.lock().unwrap().get(agent_id).cloned().unwrap_or_default())
}
async fn load_memories(&self, agent_id: &str) -> crate::Result<Vec<MemoryEntry>> {
let mut memories = self.memories.lock().unwrap();
if let Some(existing) = memories.get(agent_id) {
return Ok(existing.clone());
}
if !self.initial_memories.is_empty() {
memories.insert(agent_id.to_string(), self.initial_memories.clone());
return Ok(self.initial_memories.clone());
}
Ok(Vec::new())
}
async fn commit(
&self,
agent_id: &str,
result: CurationResult,
existing: &[MemoryEntry],
) -> crate::Result<()> {
let remove: std::collections::HashSet<usize> = result.to_remove_indices.iter().copied().collect();
let mut kept: Vec<MemoryEntry> = existing
.iter()
.enumerate()
.filter_map(|(i, m)| if remove.contains(&i) { None } else { Some(m.clone()) })
.collect();
kept.extend(result.to_add);
self.memories
.lock()
.unwrap()
.insert(agent_id.to_string(), kept);
Ok(())
}
async fn search(
&self,
agent_id: &str,
_query: &str,
top_k: usize,
) -> crate::Result<Vec<MemoryEntry>> {
let all = self.load_memories(agent_id).await?;
Ok(all.into_iter().take(top_k).collect())
}
async fn save_session(&self, data: SessionData) -> crate::Result<()> {
self.saved_sessions.lock().unwrap().push(data.clone());
self.sessions
.lock()
.unwrap()
.entry(data.agent_id.clone())
.or_default()
.push(data);
Ok(())
}
}