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 DefaultLLMConcurrency = 1 DefaultValidationMaxPromptTokens = 2048 DefaultMaxSectionTokens = 8192 DefaultMinSectionTokens = 2048 DefaultConfidenceThreshold = 0.8 DefaultNormalizeMaxSegmentGap = 4.0 DefaultNormalizeEllipsisGap = 3.5 DefaultNormalizeMaxSegmentDuration = 60.0 DefaultNormalizeMaxSegmentTokens = 2048 DefaultTranscriptDescriptionMaxChars = 500 DefaultWorkDir = "/tmp/audita" DefaultWorkDirRetention WorkDirRetention = WorkDirRetentionAuto ) type Config struct { Modules []string PrimaryLLM LLMConfig ValidationLLM ValidationLLMConfig TotalLLMConcurrency int ProposalLLMConcurrency int ValidationLLMConcurrency *int ValidationMaxPromptTokens int MaxSectionTokens int MinSectionTokens int TargetSections *int Thresholds ConfidenceThresholds Normalization NormalizationConfig TranscriptDescription string WorkDir string WorkDirRetention WorkDirRetention } type LLMConfig struct { APIKey string Model string BaseURL string TimeoutSeconds int MaxRetries int // Concurrency is retained as a backward-compatible alias for // TotalLLMConcurrency. Concurrency int } type ValidationLLMConfig struct { APIKey string Model string BaseURL string TimeoutSeconds *int MaxRetries *int // Concurrency is retained as a backward-compatible alias for // ValidationLLMConcurrency. Concurrency *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, Concurrency: DefaultLLMConcurrency, }, ValidationLLM: ValidationLLMConfig{}, TotalLLMConcurrency: DefaultLLMConcurrency, ProposalLLMConcurrency: DefaultLLMConcurrency, ValidationLLMConcurrency: nil, 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 } effective.Concurrency = c.EffectiveValidationLLMConcurrency() return effective } func (c Config) EffectiveValidationLLMConcurrency() int { if c.ValidationLLMConcurrency != nil { return *c.ValidationLLMConcurrency } return c.TotalLLMConcurrency } func (c Config) EffectiveProposalLLMConcurrency() int { if c.ProposalLLMConcurrency > 0 { return c.ProposalLLMConcurrency } return c.TotalLLMConcurrency } func (c *Config) syncLegacyConcurrencyAliases() { if c == nil { return } c.PrimaryLLM.Concurrency = c.TotalLLMConcurrency c.ValidationLLM.Concurrency = intPtr(c.ValidationLLMConcurrency) } func intPtr(v *int) *int { if v == nil { return nil } x := *v return &x }