use std::path::PathBuf;
use serde::{Deserialize, Serialize};
pub const INPUT_TOKEN_BUDGET: u32 = 50_000;
pub const OUTPUT_TOKEN_BUDGET: u32 = 5_000;
pub const SUMMARY_FANOUT: u32 = 10;
pub const DEFAULT_FLUSH_AGE_SECS: u64 = 7 * 24 * 60 * 60;
pub const DEFAULT_EMBEDDING_DIM: usize = 768;
pub const FOLDER_FILE_SIZE_CAP_BYTES: u64 = 10 * 1024 * 1024;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryConfig {
pub workspace: PathBuf,
#[serde(default)]
pub embedding: EmbeddingConfig,
#[serde(default)]
pub tree: TreeConfig,
#[serde(default)]
pub retrieval: RetrievalConfig,
#[serde(default)]
pub sync_budget: SyncBudgetConfig,
}
impl MemoryConfig {
pub fn new(workspace: impl Into<PathBuf>) -> Self {
Self {
workspace: workspace.into(),
embedding: EmbeddingConfig::default(),
tree: TreeConfig::default(),
retrieval: RetrievalConfig::default(),
sync_budget: SyncBudgetConfig::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmbeddingConfig {
pub dim: usize,
pub model: String,
pub strict: bool,
}
impl Default for EmbeddingConfig {
fn default() -> Self {
Self {
dim: DEFAULT_EMBEDDING_DIM,
model: "nomic-embed-text".to_string(),
strict: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TreeConfig {
pub input_token_budget: u32,
pub output_token_budget: u32,
pub summary_fanout: u32,
pub flush_age_secs: u64,
}
impl Default for TreeConfig {
fn default() -> Self {
Self {
input_token_budget: INPUT_TOKEN_BUDGET,
output_token_budget: OUTPUT_TOKEN_BUDGET,
summary_fanout: SUMMARY_FANOUT,
flush_age_secs: DEFAULT_FLUSH_AGE_SECS,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct WeightProfile {
pub graph: f64,
pub vector: f64,
pub keyword: f64,
pub freshness: f64,
}
impl WeightProfile {
pub const BALANCED: Self = Self {
graph: 0.35,
vector: 0.35,
keyword: 0.15,
freshness: 0.15,
};
pub const SEMANTIC: Self = Self {
graph: 0.15,
vector: 0.65,
keyword: 0.20,
freshness: 0.0,
};
pub const LEXICAL: Self = Self {
graph: 0.25,
vector: 0.15,
keyword: 0.60,
freshness: 0.0,
};
pub const GRAPH_FIRST: Self = Self {
graph: 0.55,
vector: 0.30,
keyword: 0.15,
freshness: 0.0,
};
pub fn by_name(name: &str) -> Self {
match name {
"semantic" => Self::SEMANTIC,
"lexical" => Self::LEXICAL,
"graph_first" => Self::GRAPH_FIRST,
_ => Self::BALANCED,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RetrievalConfig {
pub default_profile: WeightProfile,
}
impl Default for RetrievalConfig {
fn default() -> Self {
Self {
default_profile: WeightProfile::BALANCED,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SyncBudgetConfig {
pub max_tokens_per_sync: Option<u64>,
pub max_cost_per_sync_usd: Option<f64>,
pub sync_depth_days: Option<u32>,
}
#[cfg(test)]
#[path = "config_tests.rs"]
mod tests;