use async_trait::async_trait;
use crate::{
AcceptedExtraction, DiaryEntry, Embedding, Memory, MemoryId, NamespaceId, QueuedExtraction,
RepoDirectory, Result, Session, SessionId, SessionTurn, TokenRevocation,
harness::Harness,
namespace::NamespaceMembership,
palace::{PalaceGraph, ProjectTaxon, Room, TunnelTraversal},
};
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SearchHit {
pub memory: Memory,
pub similarity: f32,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct StoreStats {
pub total_memories: usize,
pub namespaces: Vec<NamespaceCount>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NamespaceCount {
pub namespace: String,
pub memories: usize,
}
#[async_trait]
pub trait Store: Send + Sync {
async fn store_memory(&self, ns: &NamespaceId, memory: Memory) -> Result<MemoryId>;
async fn recall_memory(&self, ns: &NamespaceId, id: &MemoryId) -> Result<Option<Memory>>;
async fn delete_memory(&self, ns: &NamespaceId, id: &MemoryId) -> Result<()>;
async fn list_memories(&self, ns: &NamespaceId, limit: usize) -> Result<Vec<Memory>>;
async fn list_memories_filtered(
&self,
ns: &NamespaceId,
project: Option<&str>,
topic: Option<&str>,
limit: usize,
) -> Result<Vec<Memory>> {
let cap = if project.is_some() || topic.is_some() {
500
} else {
limit
};
let mut mems = self.list_memories(ns, cap).await?;
if let Some(p) = project {
mems.retain(|m| m.project == p);
}
if let Some(t) = topic {
mems.retain(|m| m.topic == t);
}
mems.truncate(limit);
Ok(mems)
}
async fn store_stats(&self) -> Result<StoreStats>;
async fn check_duplicate(&self, ns: &NamespaceId, content: &str) -> Result<Option<MemoryId>>;
async fn search_memories(
&self,
ns: &NamespaceId,
embedding: &Embedding,
limit: usize,
) -> Result<Vec<SearchHit>>;
async fn list_rooms(
&self,
ns: &NamespaceId,
project: Option<&str>,
limit: usize,
) -> Result<Vec<Room>>;
async fn taxonomy(&self, ns: &NamespaceId) -> Result<Vec<ProjectTaxon>>;
async fn palace_graph(&self, ns: &NamespaceId) -> Result<PalaceGraph>;
async fn traverse_tunnel(
&self,
ns: &NamespaceId,
topic: &str,
project_a: &str,
project_b: &str,
limit: usize,
) -> Result<TunnelTraversal>;
async fn ingest_turn(&self, ns: &NamespaceId, turn: SessionTurn) -> Result<()>;
async fn session_turns(
&self,
ns: &NamespaceId,
session: &SessionId,
limit: usize,
) -> Result<Vec<SessionTurn>>;
async fn create_session(&self, ns: &NamespaceId, session: Session) -> Result<SessionId>;
async fn list_sessions(
&self,
ns: &NamespaceId,
harness: Option<&Harness>,
limit: usize,
) -> Result<Vec<Session>>;
async fn end_session(
&self,
ns: &NamespaceId,
session: &SessionId,
ended_at: String,
) -> Result<()>;
async fn write_diary(&self, ns: &NamespaceId, entry: DiaryEntry) -> Result<()>;
async fn read_diary(
&self,
ns: &NamespaceId,
agent: &str,
limit: usize,
) -> Result<Vec<DiaryEntry>>;
async fn register_repo(&self, repo: RepoDirectory) -> Result<()>;
async fn list_repos(&self) -> Result<Vec<RepoDirectory>>;
async fn resolve_repo(&self, cwd: &str) -> Result<Option<RepoDirectory>> {
let target = crate::repo::normalize_path(cwd);
let repos = self.list_repos().await?;
Ok(repos
.into_iter()
.filter(|r| target == r.path || target.starts_with(&format!("{}/", r.path)))
.max_by_key(|r| r.path.len()))
}
async fn revoke_token(&self, revocation: TokenRevocation) -> Result<()>;
async fn list_revocations(&self) -> Result<Vec<TokenRevocation>>;
async fn grant_namespace_membership(&self, membership: NamespaceMembership) -> Result<()>;
async fn revoke_namespace_membership(&self, ns: &NamespaceId, principal: &str) -> Result<()>;
async fn list_namespace_members(&self, ns: &NamespaceId) -> Result<Vec<NamespaceMembership>>;
async fn is_namespace_member(&self, ns: &NamespaceId, principal: &str) -> Result<bool>;
async fn enqueue_extraction(
&self,
ns: &NamespaceId,
memory: Memory,
confidence: f32,
) -> Result<String>;
async fn list_pending(&self, ns: &NamespaceId, limit: usize) -> Result<Vec<QueuedExtraction>>;
async fn accept_extraction(
&self,
ns: &NamespaceId,
queue_id: &str,
) -> Result<AcceptedExtraction>;
async fn reject_extraction(&self, ns: &NamespaceId, queue_id: &str) -> Result<()>;
}