use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use anyhow::Result;
use crate::ai::BackendType;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AppConfig {
pub inference: InferenceConfig,
pub model: ModelConfig,
pub performance: PerformanceConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InferenceConfig {
pub backend: BackendType,
pub benchmark_on_startup: bool,
pub max_tokens: usize,
pub temperature: f32,
pub top_p: f32,
pub repeat_penalty: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelConfig {
pub gemma_variant: String,
pub use_8bit: bool,
pub custom_model_path: Option<PathBuf>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceConfig {
pub n_threads: Option<usize>,
pub profile_memory: bool,
pub log_metrics: bool,
}
impl Default for AppConfig {
fn default() -> Self {
Self {
inference: InferenceConfig {
backend: BackendType::Auto, benchmark_on_startup: false,
max_tokens: 512,
temperature: 0.7,
top_p: 0.9,
repeat_penalty: 1.1,
},
model: ModelConfig {
gemma_variant: "E2B".to_string(),
use_8bit: false,
custom_model_path: None,
},
performance: PerformanceConfig {
n_threads: None,
profile_memory: false,
log_metrics: true,
},
}
}
}
impl AppConfig {
pub fn load(path: &PathBuf) -> Result<Self> {
if path.exists() {
let content = std::fs::read_to_string(path)?;
let config: AppConfig = serde_json::from_str(&content)?;
Ok(config)
} else {
Ok(Self::default())
}
}
pub fn save(&self, path: &PathBuf) -> Result<()> {
let content = serde_json::to_string_pretty(self)?;
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)?;
}
std::fs::write(path, content)?;
Ok(())
}
pub fn get_config_path() -> Result<PathBuf> {
let config_dir = dirs::config_dir()
.ok_or_else(|| anyhow::anyhow!("Failed to get config directory"))?
.join("tektra");
Ok(config_dir.join("config.json"))
}
}
impl AppConfig {
pub fn apply_env_overrides(&mut self) {
if let Ok(backend_str) = std::env::var("TEKTRA_BACKEND") {
match backend_str.to_lowercase().as_str() {
"mlx" => self.inference.backend = BackendType::MLX,
"gguf" => self.inference.backend = BackendType::GGUF,
"auto" => self.inference.backend = BackendType::Auto,
_ => tracing::warn!("Unknown backend type in TEKTRA_BACKEND: {}", backend_str),
}
}
if let Ok(benchmark_str) = std::env::var("TEKTRA_BENCHMARK") {
self.inference.benchmark_on_startup = benchmark_str == "1" || benchmark_str.to_lowercase() == "true";
}
if let Ok(threads_str) = std::env::var("TEKTRA_THREADS") {
if let Ok(threads) = threads_str.parse::<usize>() {
self.performance.n_threads = Some(threads);
}
}
}
}