use crate::error::MemoryError;
use crate::ids::{FactId, RunId, ThreadId};
use crate::llm::Message;
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "outcome", rename_all = "snake_case")]
pub enum ToolResult {
Ok {
value: serde_json::Value,
},
Err {
message: String,
},
}
#[async_trait]
pub trait ShortTermMemory: Send + Sync {
async fn append(&self, thread: ThreadId, msg: Message) -> Result<(), MemoryError>;
async fn load(&self, thread: ThreadId, max_tokens: usize) -> Result<Vec<Message>, MemoryError>;
async fn clear(&self, thread: ThreadId) -> Result<(), MemoryError>;
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Scope {
Workspace(String),
Agent(String),
Global,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Fact {
pub text: String,
#[serde(default)]
pub metadata: serde_json::Value,
}
#[async_trait]
pub trait LongTermMemory: Send + Sync {
async fn remember(&self, scope: Scope, fact: Fact) -> Result<FactId, MemoryError>;
async fn recall(&self, scope: Scope, query: &str, k: usize) -> Result<Vec<Fact>, MemoryError>;
async fn forget(&self, id: FactId) -> Result<(), MemoryError>;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Episode {
Started {
agent: String,
},
LlmCall {
tokens: u32,
latency_ms: u32,
},
ToolCall {
name: String,
args: serde_json::Value,
result: ToolResult,
},
BusPublish {
subject: String,
},
BusReceive {
subject: String,
},
Completed,
Failed {
error: String,
},
SummaryCheckpoint {
input_message_count: u32,
summary_chars: u32,
latency_ms: u32,
tokens: u32,
},
Ops(serde_json::Value),
}
#[derive(Debug, Clone, Default)]
pub struct RunFilter {
pub agent: Option<String>,
pub since: Option<DateTime<Utc>>,
pub until: Option<DateTime<Utc>>,
pub limit: Option<usize>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RunSummary {
pub run_id: RunId,
pub agent: String,
pub started_at: DateTime<Utc>,
pub finished_at: Option<DateTime<Utc>>,
pub episode_count: u32,
}
#[async_trait]
pub trait EpisodicMemory: Send + Sync {
async fn record(&self, run: RunId, event: Episode) -> Result<(), MemoryError>;
async fn replay(&self, run: RunId) -> Result<Vec<Episode>, MemoryError>;
async fn list_runs(&self, filter: RunFilter) -> Result<Vec<RunSummary>, MemoryError>;
}
#[cfg(test)]
mod tests {
use super::*;
#[allow(dead_code)]
fn _assert_dyn_short(_: &dyn ShortTermMemory) {}
#[allow(dead_code)]
fn _assert_dyn_long(_: &dyn LongTermMemory) {}
#[allow(dead_code)]
fn _assert_dyn_episodic(_: &dyn EpisodicMemory) {}
}