package config import ( "fmt" "os" "strconv" "time" ) type Config struct { // llama.cpp server LlamaCppURL string Model string SystemPrompt string Temperature float64 TopP float64 MaxTokens int ContextSize int // SQLite DBPath string CompanionDB string // Matrix MatrixEnabled bool MatrixHomeserver string MatrixUser string MatrixToken string // Scheduler SchedulerInterval time.Duration AutoDiary bool DiaryHour int // Memory MemoryTopK int MemoryMinScore float64 // Chat template ChatTemplate string // Embedding EmbeddingModel string // Personality TraitChangeMaxDaily float64 TraitChangeMaxMonthly float64 // Logging LogLevel string // API APIEnabled bool APIPort string } func Load() *Config { return &Config{ LlamaCppURL: getEnv("LLAMACPP_URL", "http://localhost:8080"), Model: getEnv("MODEL_NAME", ""), SystemPrompt: getEnv("SYSTEM_PROMPT", "You are Diva, a curious and thoughtful AI companion."), Temperature: getEnvFloat("TEMPERATURE", 0.85), TopP: getEnvFloat("TOP_P", 0.9), MaxTokens: getEnvInt("MAX_TOKENS", 2048), ContextSize: getEnvInt("CONTEXT_SIZE", 8192), DBPath: getEnv("DB_PATH", "./data/divacode.db"), CompanionDB: getEnv("COMPANION_DB_PATH", "./data/companion.db"), MatrixEnabled: getEnvBool("MATRIX_ENABLED", false), MatrixHomeserver: getEnv("MATRIX_HOMESERVER", ""), MatrixUser: getEnv("MATRIX_USER", ""), MatrixToken: getEnv("MATRIX_TOKEN", ""), SchedulerInterval: getEnvDuration("SCHEDULER_INTERVAL", 15*time.Minute), AutoDiary: getEnvBool("AUTO_DIARY", true), DiaryHour: getEnvInt("DIARY_HOUR", 23), MemoryTopK: getEnvInt("MEMORY_TOP_K", 5), MemoryMinScore: getEnvFloat("MEMORY_MIN_SCORE", 0.6), ChatTemplate: getEnv("CHAT_TEMPLATE", "chatml"), EmbeddingModel: getEnv("EMBEDDING_MODEL", ""), TraitChangeMaxDaily: getEnvFloat("TRAIT_CHANGE_MAX_DAILY", 0.005), TraitChangeMaxMonthly: getEnvFloat("TRAIT_CHANGE_MAX_MONTHLY", 0.05), LogLevel: getEnv("LOG_LEVEL", "INFO"), APIEnabled: getEnvBool("API_ENABLED", false), APIPort: getEnv("API_PORT", "8080"), } } func (c *Config) Validate() error { if c.LlamaCppURL == "" { return fmt.Errorf("LLAMACPP_URL is required") } if c.DBPath == "" { return fmt.Errorf("DB_PATH is required") } if c.CompanionDB == "" { return fmt.Errorf("COMPANION_DB_PATH is required") } if c.SystemPrompt == "" { return fmt.Errorf("SYSTEM_PROMPT is required") } if c.DiaryHour < 0 || c.DiaryHour > 23 { return fmt.Errorf("DIARY_HOUR must be 0-23") } switch c.ChatTemplate { case "chatml", "llama3", "none": default: return fmt.Errorf("CHAT_TEMPLATE must be chatml, llama3, or none") } return nil } func getEnv(key, fallback string) string { if v := os.Getenv(key); v != "" { return v } return fallback } func getEnvInt(key string, fallback int) int { if v := os.Getenv(key); v != "" { if n, err := strconv.Atoi(v); err == nil { return n } } return fallback } func getEnvFloat(key string, fallback float64) float64 { if v := os.Getenv(key); v != "" { if f, err := strconv.ParseFloat(v, 64); err == nil { return f } } return fallback } func getEnvBool(key string, fallback bool) bool { if v := os.Getenv(key); v != "" { if b, err := strconv.ParseBool(v); err == nil { return b } } return fallback } func getEnvDuration(key string, fallback time.Duration) time.Duration { if v := os.Getenv(key); v != "" { if d, err := time.ParseDuration(v); err == nil { return d } } return fallback }