mnemara-core 0.3.0

Local-first, explainable AI memory engine for embedded and service-based systems
Documentation
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
pub enum EmbeddingProviderKind {
    #[default]
    Disabled,
    DeterministicLocal,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
pub enum RecallScoringProfile {
    #[default]
    Balanced,
    LexicalFirst,
    ImportanceFirst,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
pub enum RecallScorerKind {
    #[default]
    Profile,
    Curated,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
pub enum RecallPlanningProfile {
    #[default]
    FastPath,
    ContinuityAware,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
pub enum RecallPolicyProfile {
    #[default]
    General,
    Support,
    Research,
    Assistant,
    AutonomousAgent,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct EngineTuningInfo {
    pub recall_scorer_kind: RecallScorerKind,
    pub recall_scoring_profile: RecallScoringProfile,
    pub recall_planning_profile: RecallPlanningProfile,
    pub recall_policy_profile: RecallPolicyProfile,
    pub embedding_provider_kind: EmbeddingProviderKind,
    pub embedding_dimensions: usize,
    pub graph_expansion_max_hops: u8,
    pub compaction_summarize_after_record_count: usize,
    pub compaction_cold_archive_after_days: u32,
    pub compaction_cold_archive_importance_threshold_per_mille: u16,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct EngineConfig {
    pub node_id: String,
    pub default_tenant: String,
    pub max_batch_size: usize,
    pub retention: RetentionPolicy,
    pub compaction: CompactionPolicy,
    pub ingestion: IngestionPolicy,
    pub recall_scorer_kind: RecallScorerKind,
    pub recall_scoring_profile: RecallScoringProfile,
    pub recall_planning_profile: RecallPlanningProfile,
    pub recall_policy_profile: RecallPolicyProfile,
    pub embedding_provider_kind: EmbeddingProviderKind,
    pub embedding_dimensions: usize,
    pub graph_expansion_max_hops: u8,
    pub explain_recall: bool,
}

impl Default for EngineConfig {
    fn default() -> Self {
        Self {
            node_id: "local-node".to_string(),
            default_tenant: "default".to_string(),
            max_batch_size: 256,
            retention: RetentionPolicy::default(),
            compaction: CompactionPolicy::default(),
            ingestion: IngestionPolicy::default(),
            recall_scorer_kind: RecallScorerKind::default(),
            recall_scoring_profile: RecallScoringProfile::default(),
            recall_planning_profile: RecallPlanningProfile::default(),
            recall_policy_profile: RecallPolicyProfile::default(),
            embedding_provider_kind: EmbeddingProviderKind::default(),
            embedding_dimensions: 64,
            graph_expansion_max_hops: 1,
            explain_recall: true,
        }
    }
}

impl EngineConfig {
    pub fn tuning_info(&self) -> EngineTuningInfo {
        EngineTuningInfo {
            recall_scorer_kind: self.recall_scorer_kind,
            recall_scoring_profile: self.recall_scoring_profile,
            recall_planning_profile: self.recall_planning_profile,
            recall_policy_profile: self.recall_policy_profile,
            embedding_provider_kind: self.embedding_provider_kind,
            embedding_dimensions: self.embedding_dimensions,
            graph_expansion_max_hops: self.graph_expansion_max_hops,
            compaction_summarize_after_record_count: self.compaction.summarize_after_record_count,
            compaction_cold_archive_after_days: self.compaction.cold_archive_after_days,
            compaction_cold_archive_importance_threshold_per_mille: self
                .compaction
                .cold_archive_importance_threshold_per_mille,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct RetentionPolicy {
    pub ttl_days: u32,
    pub archive_after_days: u32,
    pub max_records_per_namespace: usize,
    pub pinned_records_exempt: bool,
}

impl Default for RetentionPolicy {
    fn default() -> Self {
        Self {
            ttl_days: 0,
            archive_after_days: 0,
            max_records_per_namespace: 5_000,
            pinned_records_exempt: true,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct CompactionPolicy {
    pub deduplicate_similar_records: bool,
    pub summarize_after_record_count: usize,
    pub prune_stale_graph_edges: bool,
    pub cold_archive_after_days: u32,
    pub cold_archive_importance_threshold_per_mille: u16,
    pub maintenance_interval_operations: u32,
    pub dry_run_supported: bool,
}

impl Default for CompactionPolicy {
    fn default() -> Self {
        Self {
            deduplicate_similar_records: true,
            summarize_after_record_count: 50,
            prune_stale_graph_edges: true,
            cold_archive_after_days: 0,
            cold_archive_importance_threshold_per_mille: 250,
            maintenance_interval_operations: 32,
            dry_run_supported: true,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct IngestionPolicy {
    pub idempotent_writes_required: bool,
    pub deduplication_window_hours: u32,
    pub allow_model_derived_memories: bool,
    pub require_source_labels: bool,
}

impl Default for IngestionPolicy {
    fn default() -> Self {
        Self {
            idempotent_writes_required: true,
            deduplication_window_hours: 24,
            allow_model_derived_memories: true,
            require_source_labels: false,
        }
    }
}