package config import ( "fmt" "strings" "gitea.maximumdirect.net/eric/audita/internal/core/modulecatalog" "gitea.maximumdirect.net/eric/audita/internal/core/outputschema" ) func (c Config) Validate() error { var issues []string if len(c.Modules) == 0 { issues = append(issues, "modules must not be empty") } for _, module := range c.Modules { moduleKey := strings.TrimSpace(module) if moduleKey == "" { issues = append(issues, "modules must not contain empty values") break } if !modulecatalog.IsSupported(moduleKey) { issues = append(issues, fmt.Sprintf("unsupported module key %q", moduleKey)) } } if strings.TrimSpace(c.OutputSchema) == "" { issues = append(issues, "output schema must not be empty") } else if !outputschema.IsSupported(c.OutputSchema) { issues = append(issues, fmt.Sprintf("unsupported output schema %q", c.OutputSchema)) } if c.PrimaryLLM.TimeoutSeconds <= 0 { issues = append(issues, "primary llm timeout seconds must be greater than zero") } if c.PrimaryLLM.MaxRetries < 0 { issues = append(issues, "max retries must be zero or greater") } if c.TotalLLMConcurrency <= 0 { issues = append(issues, "total llm concurrency must be greater than zero") } if c.ProposalLLMConcurrency <= 0 { issues = append(issues, "proposal llm concurrency must be greater than zero") } if c.ProposalLLMConcurrency > c.TotalLLMConcurrency { issues = append(issues, "proposal llm concurrency must be less than or equal to total llm concurrency") } if c.ValidationLLM.TimeoutSeconds != nil && *c.ValidationLLM.TimeoutSeconds <= 0 { issues = append(issues, "validation llm timeout seconds must be greater than zero") } if c.ValidationLLM.MaxRetries != nil && *c.ValidationLLM.MaxRetries < 0 { issues = append(issues, "validation max retries must be zero or greater") } if c.ValidationLLMConcurrency != nil && *c.ValidationLLMConcurrency <= 0 { issues = append(issues, "validation llm concurrency must be greater than zero") } if c.ValidationLLMConcurrency != nil && *c.ValidationLLMConcurrency > c.TotalLLMConcurrency { issues = append(issues, "validation llm concurrency must be less than or equal to total llm concurrency") } if c.ValidationMaxPromptTokens <= 0 { issues = append(issues, "validation max prompt tokens must be greater than zero") } if c.MaxSectionTokens <= 0 { issues = append(issues, "max section tokens must be greater than zero") } if c.MinSectionTokens <= 0 { issues = append(issues, "min section tokens must be greater than zero") } if c.MinSectionTokens > c.MaxSectionTokens { issues = append(issues, "min section tokens must be less than or equal to max section tokens") } if c.TargetSections != nil && *c.TargetSections <= 0 { issues = append(issues, "target sections must be greater than zero when set") } if err := validateConfidence("glossary", c.Thresholds.Glossary); err != nil { issues = append(issues, err.Error()) } if err := validateConfidence("grammar", c.Thresholds.Grammar); err != nil { issues = append(issues, err.Error()) } if err := validateConfidence("homophones", c.Thresholds.Homophones); err != nil { issues = append(issues, err.Error()) } if err := validateConfidence("spoken-word", c.Thresholds.SpokenWord); err != nil { issues = append(issues, err.Error()) } if c.Normalization.MaxSegmentGap < 0 { issues = append(issues, "normalize max segment gap must be zero or greater") } if c.Normalization.EllipsisGap < 0 { issues = append(issues, "normalize ellipsis gap must be zero or greater") } if c.Normalization.MaxSegmentDuration <= 0 { issues = append(issues, "normalize max segment duration must be greater than zero") } if c.Normalization.MaxSegmentTokens <= 0 { issues = append(issues, "normalize max segment tokens must be greater than zero") } if len(strings.TrimSpace(c.TranscriptDescription)) > DefaultTranscriptDescriptionMaxChars { issues = append(issues, fmt.Sprintf("transcript description must be %d characters or fewer", DefaultTranscriptDescriptionMaxChars)) } if strings.TrimSpace(c.WorkDir) == "" { issues = append(issues, "work dir must not be empty") } if err := validateRetention(c.WorkDirRetention); err != nil { issues = append(issues, err.Error()) } if len(issues) > 0 { return fmt.Errorf("invalid config: %s", strings.Join(issues, "; ")) } return nil } func validateConfidence(name string, threshold float64) error { if threshold < 0.0 || threshold > 1.0 { return fmt.Errorf("%s confidence threshold must be between 0.0 and 1.0", name) } return nil } func validateRetention(retention WorkDirRetention) error { switch retention { case WorkDirRetentionAuto, WorkDirRetentionAlways, WorkDirRetentionNever: return nil default: return fmt.Errorf("work dir retention must be one of: auto, always, never") } }