package config import ( "os" "strconv" "strings" "time" ) type Config struct { HTTPHost string HTTPPort int DatabaseURL string MigrateOnStart bool APIAuthToken string LLMBaseURL string LLMAPIKey string LLMModel string LLMTimeout time.Duration WhisperXURL string WhisperXTimeout time.Duration WhisperXLeadSilence time.Duration FfmpegPath string AIStatsSidecarURL string AIStatsTimeout time.Duration WorkerID string WorkerHTTPHost string WorkerHTTPPort int WorkerPollInterval time.Duration WorkerClaimLimit int WorkerLeaseTimeout time.Duration WorkerTaskTypes []string WorkerModelProfiles []string } func Load() Config { return Config{ HTTPHost: envString("HTTP_HOST", "0.0.0.0"), HTTPPort: envInt("HTTP_PORT", 8080), DatabaseURL: envString("DATABASE_URL", ""), MigrateOnStart: envBool("MIGRATE_ON_START", true), APIAuthToken: envString("AI_SERVICE_TOKEN", ""), LLMBaseURL: envString("LLM_BASE_URL", ""), LLMAPIKey: envString("LLM_API_KEY", ""), LLMModel: envString("LLM_MODEL", "qwen2.5-14b"), LLMTimeout: envDuration("LLM_TIMEOUT", 5*time.Minute), WhisperXURL: envString("WHISPERX_URL", ""), WhisperXTimeout: envDuration("WHISPERX_TIMEOUT", 10*time.Minute), WhisperXLeadSilence: envDuration("WHISPERX_LEAD_SILENCE", 800*time.Millisecond), FfmpegPath: envString("FFMPEG_PATH", "/usr/bin/ffmpeg"), AIStatsSidecarURL: envString("AI_STATS_SIDECAR_URL", ""), AIStatsTimeout: envDuration("AI_STATS_TIMEOUT", 8*time.Second), WorkerID: envString("WORKER_ID", hostname()), WorkerHTTPHost: envString("WORKER_HTTP_HOST", "0.0.0.0"), WorkerHTTPPort: envInt("WORKER_HTTP_PORT", 8081), WorkerPollInterval: envDuration("WORKER_POLL_INTERVAL", 2*time.Second), WorkerClaimLimit: envInt("WORKER_CLAIM_LIMIT", 4), WorkerLeaseTimeout: envDuration("WORKER_LEASE_TIMEOUT", 15*time.Minute), WorkerTaskTypes: envCSV("WORKER_TASK_TYPES"), WorkerModelProfiles: envCSV("WORKER_MODEL_PROFILES"), } } func envString(key, fallback string) string { if v := os.Getenv(key); v != "" { return v } return fallback } func envInt(key string, fallback int) int { raw := os.Getenv(key) if raw == "" { return fallback } v, err := strconv.Atoi(raw) if err != nil { return fallback } return v } func envBool(key string, fallback bool) bool { raw := os.Getenv(key) if raw == "" { return fallback } v, err := strconv.ParseBool(raw) if err != nil { return fallback } return v } func envDuration(key string, fallback time.Duration) time.Duration { raw := os.Getenv(key) if raw == "" { return fallback } v, err := time.ParseDuration(raw) if err != nil { return fallback } return v } func envCSV(key string) []string { raw := strings.TrimSpace(os.Getenv(key)) if raw == "" { return nil } parts := strings.Split(raw, ",") out := make([]string, 0, len(parts)) for _, part := range parts { if v := strings.TrimSpace(part); v != "" { out = append(out, v) } } return out } func hostname() string { h, err := os.Hostname() if err != nil || h == "" { return "ai-service-worker" } return h }