package config import ( "fmt" "strings" ) type WorkDirRetention string const ( WorkDirRetentionAuto WorkDirRetention = "auto" WorkDirRetentionAlways WorkDirRetention = "always" WorkDirRetentionNever WorkDirRetention = "never" ) const ( DefaultModulesCSV = "glossary,homophones,glossary,spoken_word,grammar" DefaultPrimaryModel = "openrouter/google/gemma-4-31b-it" DefaultPrimaryBaseURL = "https://openrouter.ai/api/v1" DefaultPrimaryLLMTimeoutSeconds = 600 DefaultMaxRetries = 3 DefaultValidationMaxPromptTokens = 2048 DefaultMaxSectionTokens = 8192 DefaultMinSectionTokens = 2048 DefaultConfidenceThreshold = 0.8 DefaultNormalizeMaxSegmentGap = 4.0 DefaultNormalizeEllipsisGap = 3.5 DefaultNormalizeMaxSegmentDuration = 60.0 DefaultNormalizeMaxSegmentTokens = 2048 DefaultWorkDir = "/tmp/audita" DefaultWorkDirRetention WorkDirRetention = WorkDirRetentionAuto ) type Config struct { Modules []string PrimaryLLM LLMConfig ValidationLLM ValidationLLMConfig ValidationMaxPromptTokens int MaxSectionTokens int MinSectionTokens int TargetSections *int Thresholds ConfidenceThresholds Normalization NormalizationConfig WorkDir string WorkDirRetention WorkDirRetention } type LLMConfig struct { APIKey string Model string BaseURL string TimeoutSeconds int MaxRetries int } type ValidationLLMConfig struct { APIKey string Model string BaseURL string TimeoutSeconds *int MaxRetries *int } type ConfidenceThresholds struct { Glossary float64 Grammar float64 Homophones float64 SpokenWord float64 } type NormalizationConfig struct { MaxSegmentGap float64 EllipsisGap float64 MaxSegmentDuration float64 MaxSegmentTokens int } func Default() Config { modules, _ := ParseModulesCSV(DefaultModulesCSV) return Config{ Modules: modules, PrimaryLLM: LLMConfig{ Model: DefaultPrimaryModel, BaseURL: DefaultPrimaryBaseURL, TimeoutSeconds: DefaultPrimaryLLMTimeoutSeconds, MaxRetries: DefaultMaxRetries, }, ValidationLLM: ValidationLLMConfig{}, ValidationMaxPromptTokens: DefaultValidationMaxPromptTokens, MaxSectionTokens: DefaultMaxSectionTokens, MinSectionTokens: DefaultMinSectionTokens, TargetSections: nil, Thresholds: ConfidenceThresholds{ Glossary: DefaultConfidenceThreshold, Grammar: DefaultConfidenceThreshold, Homophones: DefaultConfidenceThreshold, SpokenWord: DefaultConfidenceThreshold, }, Normalization: NormalizationConfig{ MaxSegmentGap: DefaultNormalizeMaxSegmentGap, EllipsisGap: DefaultNormalizeEllipsisGap, MaxSegmentDuration: DefaultNormalizeMaxSegmentDuration, MaxSegmentTokens: DefaultNormalizeMaxSegmentTokens, }, WorkDir: DefaultWorkDir, WorkDirRetention: DefaultWorkDirRetention, } } func ParseModulesCSV(raw string) ([]string, error) { parts := strings.Split(raw, ",") modules := make([]string, 0, len(parts)) for _, part := range parts { trimmed := strings.TrimSpace(part) if trimmed == "" { return nil, fmt.Errorf("modules list contains an empty value") } modules = append(modules, trimmed) } if len(modules) == 0 { return nil, fmt.Errorf("modules list must not be empty") } return modules, nil } func (c Config) EffectiveValidationLLMConfig() LLMConfig { effective := c.PrimaryLLM if c.ValidationLLM.APIKey != "" { effective.APIKey = c.ValidationLLM.APIKey } if c.ValidationLLM.Model != "" { effective.Model = c.ValidationLLM.Model } if c.ValidationLLM.BaseURL != "" { effective.BaseURL = c.ValidationLLM.BaseURL } if c.ValidationLLM.TimeoutSeconds != nil { effective.TimeoutSeconds = *c.ValidationLLM.TimeoutSeconds } if c.ValidationLLM.MaxRetries != nil { effective.MaxRetries = *c.ValidationLLM.MaxRetries } return effective }