diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..7d31b55 --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,151 @@ +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 + + // 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), + + 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") + } + 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 +}