diff --git a/Cargo.lock b/Cargo.lock index 92eeb7d..7539718 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -590,6 +590,7 @@ dependencies = [ "futures", "indicatif", "mockall", + "once_cell", "regex", "reqwest", "rustyline", diff --git a/Cargo.toml b/Cargo.toml index 6285965..286c624 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ regex = "1.0" futures = "0.3" tokio-stream = "0.1" signal-hook = "0.3" +once_cell = "1.21" [dev-dependencies] tempfile = "3.0" diff --git a/src/core/session.rs b/src/core/session.rs index 08e14ca..2887232 100644 --- a/src/core/session.rs +++ b/src/core/session.rs @@ -1,5 +1,6 @@ use anyhow::{Context, Result}; use chrono::{DateTime, Utc}; +use once_cell::sync::OnceCell; use serde::{Deserialize, Serialize}; use std::fs; use std::path::PathBuf; @@ -12,6 +13,12 @@ ASCII art or concise bullet lists over heavy markup, and wrap code \ snippets in fenced blocks when helpful. Do not emit trailing spaces or \ control characters."; +static CONFIG: OnceCell = OnceCell::new(); + +fn get_config() -> &'static Config { + CONFIG.get_or_init(|| Config::load().unwrap_or_default()) +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Message { pub role: String, @@ -98,7 +105,7 @@ impl Session { } pub fn sessions_dir() -> Result { - let config = Config::load().unwrap_or_default(); + let config = get_config(); let home = dirs::home_dir().context("Could not find home directory")?; let sessions_dir = home.join(&config.session.sessions_dir_name); @@ -111,11 +118,12 @@ impl Session { } pub fn session_path(name: &str) -> Result { - let config = Config::load().unwrap_or_default(); + let config = get_config(); Ok(Self::sessions_dir()?.join(format!("{}.{}", name, config.session.file_extension))) } pub fn save(&self) -> Result<()> { + let _config = get_config(); let data = SessionData { model: self.model.clone(), messages: self.messages.clone(), @@ -143,6 +151,7 @@ impl Session { } pub fn load(name: &str) -> Result { + let _config = get_config(); let path = Self::session_path(name)?; if !path.exists() { @@ -195,7 +204,7 @@ impl Session { /// Truncates conversation history to stay within configured limits fn truncate_history_if_needed(&mut self) { - let config = Config::load().unwrap_or_default(); + let config = get_config(); let max_history = config.limits.max_conversation_history; // Always preserve the system prompt (first message) @@ -252,13 +261,14 @@ impl Session { } pub fn list_sessions() -> Result)>> { + let _config = get_config(); let sessions = Self::list_sessions_lazy(false)?; Ok(sessions.into_iter().map(|s| (s.name, s.last_modified)).collect()) } /// Lists sessions with lazy loading - only loads full data if detailed=true pub fn list_sessions_lazy(detailed: bool) -> Result> { - let config = Config::load().unwrap_or_default(); + let config = get_config(); let sessions_dir = Self::sessions_dir()?; if !sessions_dir.exists() { @@ -351,7 +361,7 @@ impl Session { /// Checks if session needs cleanup based on size and age pub fn needs_cleanup(&self) -> bool { let stats = self.get_stats(); - let config = Config::load().unwrap_or_default(); + let config = get_config(); // Check if conversation is too long if stats.total_messages > config.limits.max_conversation_history * 2 { @@ -368,7 +378,7 @@ impl Session { /// Performs aggressive cleanup for memory optimization pub fn cleanup_for_memory(&mut self) { - let config = Config::load().unwrap_or_default(); + let config = get_config(); let target_messages = config.limits.max_conversation_history / 2; if self.messages.len() > target_messages + 1 {