use crate::error::CerememoryError;
use crate::types::*;
use std::future::Future;
use uuid::Uuid;
pub trait Store: Send + Sync {
fn store(
&self,
record: MemoryRecord,
) -> impl Future<Output = Result<Uuid, CerememoryError>> + Send;
fn get(
&self,
id: &Uuid,
) -> impl Future<Output = Result<Option<MemoryRecord>, CerememoryError>> + Send;
fn delete(&self, id: &Uuid) -> impl Future<Output = Result<bool, CerememoryError>> + Send;
fn update_fidelity(
&self,
id: &Uuid,
fidelity: FidelityState,
) -> impl Future<Output = Result<(), CerememoryError>> + Send;
fn query_text(
&self,
query: &str,
limit: usize,
) -> impl Future<Output = Result<Vec<MemoryRecord>, CerememoryError>> + Send;
fn list_ids(&self) -> impl Future<Output = Result<Vec<Uuid>, CerememoryError>> + Send;
fn get_all(&self) -> impl Future<Output = Result<Vec<MemoryRecord>, CerememoryError>> + Send;
fn count(&self) -> impl Future<Output = Result<usize, CerememoryError>> + Send;
fn update_record(
&self,
id: &Uuid,
content: Option<MemoryContent>,
emotion: Option<EmotionVector>,
metadata: Option<serde_json::Value>,
) -> impl Future<Output = Result<(), CerememoryError>> + Send;
fn replace_associations(
&self,
id: &Uuid,
associations: Vec<Association>,
) -> impl Future<Output = Result<(), CerememoryError>> + Send;
fn update_access(
&self,
id: &Uuid,
access_count: u32,
last_accessed_at: chrono::DateTime<chrono::Utc>,
) -> impl Future<Output = Result<(), CerememoryError>> + Send;
}
pub trait AssociationGraph: Send + Sync {
fn get_associations(
&self,
record_id: &Uuid,
) -> impl Future<Output = Result<Vec<Association>, CerememoryError>> + Send;
fn get_record_store_type(
&self,
record_id: &Uuid,
) -> impl Future<Output = Result<Option<StoreType>, CerememoryError>> + Send;
}
pub trait DecayEngine: Send + Sync {
fn compute_tick(&self, records: &[DecayInput], tick_duration_secs: f64) -> DecayTickResult;
}
#[derive(Debug, Clone)]
pub struct DecayInput {
pub id: Uuid,
pub fidelity: FidelityState,
pub emotion: EmotionVector,
pub last_accessed_at: chrono::DateTime<chrono::Utc>,
pub access_count: u32,
}
#[derive(Debug, Clone)]
pub struct DecayTickResult {
pub updates: Vec<DecayOutput>,
pub records_updated: u32,
pub records_below_threshold: u32,
pub records_pruned: u32,
}
#[derive(Debug, Clone)]
pub struct DecayOutput {
pub id: Uuid,
pub new_fidelity: FidelityState,
pub should_prune: bool,
}
pub trait AssociationEngine: Send + Sync {
fn activate(
&self,
source_id: &Uuid,
depth: u32,
min_weight: f64,
) -> impl Future<Output = Result<Vec<ActivatedRecord>, CerememoryError>> + Send;
}
#[derive(Debug, Clone)]
pub struct ActivatedRecord {
pub record_id: Uuid,
pub activation_level: f64,
pub path: Vec<Uuid>,
}
#[derive(Debug, Clone)]
pub struct ProviderCapabilities {
pub text_embedding: bool,
pub image_embedding: bool,
pub audio_transcription: bool,
}
impl Default for ProviderCapabilities {
fn default() -> Self {
Self {
text_embedding: true,
image_embedding: false,
audio_transcription: false,
}
}
}
pub trait LLMProvider: Send + Sync {
fn embed(
&self,
text: &str,
) -> std::pin::Pin<Box<dyn Future<Output = Result<Vec<f32>, CerememoryError>> + Send + '_>>;
fn summarize(
&self,
texts: &[String],
max_tokens: usize,
) -> std::pin::Pin<Box<dyn Future<Output = Result<String, CerememoryError>> + Send + '_>>;
fn extract_relations(
&self,
text: &str,
) -> std::pin::Pin<
Box<
dyn Future<Output = Result<Vec<crate::types::ExtractedRelation>, CerememoryError>>
+ Send
+ '_,
>,
>;
fn embed_image(
&self,
_data: &[u8],
_format: &str,
) -> std::pin::Pin<Box<dyn Future<Output = Result<Vec<f32>, CerememoryError>> + Send + '_>>
{
Box::pin(async {
Err(CerememoryError::ModalityUnsupported(
"Image embedding not supported by this provider".to_string(),
))
})
}
fn transcribe_audio(
&self,
_data: &[u8],
_format: &str,
) -> std::pin::Pin<Box<dyn Future<Output = Result<String, CerememoryError>> + Send + '_>> {
Box::pin(async {
Err(CerememoryError::ModalityUnsupported(
"Audio transcription not supported by this provider".to_string(),
))
})
}
fn capabilities(&self) -> ProviderCapabilities {
ProviderCapabilities::default()
}
}
pub fn truncate_str(s: &str, max_len: usize) -> &str {
if s.len() <= max_len {
return s;
}
let mut end = max_len;
while end > 0 && !s.is_char_boundary(end) {
end -= 1;
}
&s[..end]
}
pub struct NoOpProvider;
impl LLMProvider for NoOpProvider {
fn embed(
&self,
_text: &str,
) -> std::pin::Pin<Box<dyn Future<Output = Result<Vec<f32>, CerememoryError>> + Send + '_>>
{
Box::pin(async { Ok(Vec::new()) })
}
fn summarize(
&self,
texts: &[String],
_max_tokens: usize,
) -> std::pin::Pin<Box<dyn Future<Output = Result<String, CerememoryError>> + Send + '_>> {
let joined: String = texts.join(" ");
let result = if joined.len() > 200 {
format!("{}...", truncate_str(&joined, 200))
} else {
joined
};
Box::pin(async move { Ok(result) })
}
fn extract_relations(
&self,
_text: &str,
) -> std::pin::Pin<
Box<
dyn Future<Output = Result<Vec<crate::types::ExtractedRelation>, CerememoryError>>
+ Send
+ '_,
>,
> {
Box::pin(async { Ok(Vec::new()) })
}
fn capabilities(&self) -> ProviderCapabilities {
ProviderCapabilities {
text_embedding: false,
image_embedding: false,
audio_transcription: false,
}
}
}
pub trait LLMAdapter: Send + Sync {
fn serialize_context(&self, memories: &[MemoryRecord], budget_tokens: usize) -> String;
fn estimate_tokens(&self, content: &MemoryContent) -> usize;
fn model_info(&self) -> ModelInfo;
}
#[derive(Debug, Clone)]
pub struct ModelInfo {
pub provider: String,
pub model_name: String,
pub max_context_tokens: usize,
}