use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::collections::HashMap;
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryRecord {
pub id: Uuid,
pub content: String,
pub metadata: HashMap<String, serde_json::Value>,
pub user_id: Option<String>,
pub agent_id: Option<String>,
pub run_id: Option<String>,
pub hash: String,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
impl MemoryRecord {
pub fn new(content: impl Into<String>, metadata: serde_json::Value) -> Self {
let content = content.into();
let hash = Self::compute_hash(&content);
let now = Utc::now();
let metadata_map = match metadata {
serde_json::Value::Object(map) => map.into_iter().collect(),
_ => HashMap::new(),
};
Self {
id: Uuid::new_v4(),
content,
metadata: metadata_map,
user_id: None,
agent_id: None,
run_id: None,
hash,
created_at: now,
updated_at: now,
}
}
pub fn with_scoping(
content: impl Into<String>,
metadata: serde_json::Value,
user_id: Option<String>,
agent_id: Option<String>,
run_id: Option<String>,
) -> Self {
let mut record = Self::new(content, metadata);
record.user_id = user_id;
record.agent_id = agent_id;
record.run_id = run_id;
record
}
fn compute_hash(content: &str) -> String {
let mut hasher = Sha256::new();
hasher.update(content.as_bytes());
hex::encode(hasher.finalize())
}
pub fn update_content(&mut self, content: impl Into<String>) {
self.content = content.into();
self.hash = Self::compute_hash(&self.content);
self.updated_at = Utc::now();
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScoredMemory {
pub record: MemoryRecord,
pub score: f32,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum Role {
System,
User,
Assistant,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Message {
pub role: Role,
pub content: String,
pub name: Option<String>,
}
impl Message {
pub fn user(content: impl Into<String>) -> Self {
Self {
role: Role::User,
content: content.into(),
name: None,
}
}
pub fn assistant(content: impl Into<String>) -> Self {
Self {
role: Role::Assistant,
content: content.into(),
name: None,
}
}
pub fn system(content: impl Into<String>) -> Self {
Self {
role: Role::System,
content: content.into(),
name: None,
}
}
pub fn with_name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
}
#[derive(Debug, Clone)]
pub enum Messages {
Text(String),
Single(Message),
Multiple(Vec<Message>),
}
impl From<&str> for Messages {
fn from(s: &str) -> Self {
Messages::Text(s.to_string())
}
}
impl From<String> for Messages {
fn from(s: String) -> Self {
Messages::Text(s)
}
}
impl From<Message> for Messages {
fn from(m: Message) -> Self {
Messages::Single(m)
}
}
impl From<Vec<Message>> for Messages {
fn from(v: Vec<Message>) -> Self {
Messages::Multiple(v)
}
}
impl Messages {
pub fn into_messages(self) -> Vec<Message> {
match self {
Messages::Text(s) => vec![Message::user(s)],
Messages::Single(m) => vec![m],
Messages::Multiple(v) => v,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct AddOptions {
pub user_id: Option<String>,
pub agent_id: Option<String>,
pub run_id: Option<String>,
pub metadata: Option<HashMap<String, serde_json::Value>>,
pub infer: bool,
}
impl AddOptions {
pub fn for_user(user_id: impl Into<String>) -> Self {
Self {
user_id: Some(user_id.into()),
infer: true,
..Default::default()
}
}
pub fn for_agent(agent_id: impl Into<String>) -> Self {
Self {
agent_id: Some(agent_id.into()),
infer: true,
..Default::default()
}
}
pub fn raw(mut self) -> Self {
self.infer = false;
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AddResult {
pub results: Vec<MemoryEvent>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryEvent {
pub id: Uuid,
pub memory: String,
pub event: EventType,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "UPPERCASE")]
pub enum EventType {
Add,
Update,
Delete,
Noop,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SearchOptions {
pub user_id: Option<String>,
pub agent_id: Option<String>,
pub run_id: Option<String>,
pub limit: Option<usize>,
pub threshold: Option<f32>,
pub filters: Option<Filters>,
pub rerank: bool,
}
impl SearchOptions {
pub fn for_user(user_id: impl Into<String>) -> Self {
Self {
user_id: Some(user_id.into()),
limit: Some(10),
..Default::default()
}
}
pub fn with_limit(mut self, limit: usize) -> Self {
self.limit = Some(limit);
self
}
pub fn with_threshold(mut self, threshold: f32) -> Self {
self.threshold = Some(threshold);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchResult {
pub results: Vec<ScoredMemory>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Filters {
pub conditions: Vec<FilterCondition>,
pub logic: FilterLogic,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FilterCondition {
pub field: String,
pub operator: FilterOperator,
pub value: serde_json::Value,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum FilterOperator {
Eq,
Ne,
Gt,
Gte,
Lt,
Lte,
In,
Nin,
Contains,
IContains,
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "UPPERCASE")]
pub enum FilterLogic {
#[default]
And,
Or,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct GetAllOptions {
pub user_id: Option<String>,
pub agent_id: Option<String>,
pub run_id: Option<String>,
pub limit: Option<usize>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HistoryEntry {
pub id: Uuid,
pub memory_id: Uuid,
pub previous_content: Option<String>,
pub new_content: String,
pub event: EventType,
pub timestamp: DateTime<Utc>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ResetOptions {
pub user_id: Option<String>,
pub agent_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Payload {
pub data: String,
pub hash: String,
pub created_at: DateTime<Utc>,
pub user_id: Option<String>,
pub agent_id: Option<String>,
pub run_id: Option<String>,
#[serde(flatten)]
pub metadata: HashMap<String, serde_json::Value>,
}
impl From<&MemoryRecord> for Payload {
fn from(record: &MemoryRecord) -> Self {
Self {
data: record.content.clone(),
hash: record.hash.clone(),
created_at: record.created_at,
user_id: record.user_id.clone(),
agent_id: record.agent_id.clone(),
run_id: record.run_id.clone(),
metadata: record.metadata.clone(),
}
}
}