Files
narratio/internal/config/validate_adapters.go

192 lines
6.4 KiB
Go

package config
import (
"fmt"
"net/url"
"strings"
)
func validateNotification(cfg NotificationConfig) error {
if strings.EqualFold(strings.TrimSpace(cfg.Mode), DefaultNotificationMode) {
return nil
}
return fmt.Errorf("pipeline.notification.mode must be %q until a notification provider is configured", DefaultNotificationMode)
}
func validateNormalize(cfg *NormalizeConfig) error {
if cfg == nil {
return nil
}
if strings.TrimSpace(cfg.OutputPath) == "" {
return fmt.Errorf("pipeline.normalize.output_path must be non-empty")
}
if err := validateSeriatimOutputSchema("pipeline.normalize.output_schema", cfg.OutputSchema); err != nil {
return err
}
return nil
}
func validateTrim(cfg *TrimConfig) error {
if cfg == nil {
return nil
}
if cfg.Enabled == nil {
return fmt.Errorf("pipeline.trim.enabled must be set (defaults should populate this)")
}
if !*cfg.Enabled {
return nil
}
if strings.TrimSpace(cfg.OutputPath) == "" {
return fmt.Errorf("pipeline.trim.output_path is required when pipeline.trim.enabled is true")
}
if strings.TrimSpace(cfg.Bounds.PromptID) == "" {
return fmt.Errorf("pipeline.trim.bounds.prompt_id is required when pipeline.trim.enabled is true")
}
if strings.TrimSpace(cfg.Bounds.TranscriptInputName) == "" {
return fmt.Errorf("pipeline.trim.bounds.transcript_input_name is required when pipeline.trim.enabled is true")
}
if strings.TrimSpace(cfg.Bounds.OutputPath) == "" {
return fmt.Errorf("pipeline.trim.bounds.output_path is required when pipeline.trim.enabled is true")
}
if err := validateDuration("pipeline.trim.bounds.timeout", cfg.Bounds.Timeout); err != nil {
return err
}
if cfg.Bounds.RenderDebug && strings.TrimSpace(cfg.Bounds.RenderOutputPath) == "" {
return fmt.Errorf("pipeline.trim.bounds.render_output_path is required when pipeline.trim.bounds.render_debug is true")
}
return nil
}
func validateRender(cfg *RenderConfig) error {
if cfg == nil {
return nil
}
if cfg.Enabled == nil {
return fmt.Errorf("pipeline.render.enabled must be set (defaults should populate this)")
}
if cfg.IncludeTimestamps == nil {
return fmt.Errorf("pipeline.render.include_timestamps must be set (defaults should populate this)")
}
if cfg.IncludeSegmentIDs == nil {
return fmt.Errorf("pipeline.render.include_segment_ids must be set (defaults should populate this)")
}
format := strings.TrimSpace(cfg.Format)
if format != "markdown" {
return fmt.Errorf("pipeline.render.format must be markdown")
}
if cfg.Title != "" && strings.TrimSpace(cfg.Title) == "" {
return fmt.Errorf("pipeline.render.title must be non-empty when provided")
}
return nil
}
func validateWhisperX(cfg WhisperXConfig) error {
if strings.TrimSpace(cfg.TranscribeURL) == "" {
return fmt.Errorf("pipeline.whisperx.transcribe_url is required")
}
u, err := url.Parse(cfg.TranscribeURL)
if err != nil || !u.IsAbs() || u.Host == "" || (strings.ToLower(u.Scheme) != "http" && strings.ToLower(u.Scheme) != "https") {
if err != nil {
return fmt.Errorf("pipeline.whisperx.transcribe_url must be an absolute http or https URL: %w", err)
}
return fmt.Errorf("pipeline.whisperx.transcribe_url must be an absolute http or https URL")
}
if err := validateDuration("pipeline.whisperx.timeout", cfg.Timeout); err != nil {
return err
}
if err := validateDuration("pipeline.whisperx.retry_delay", cfg.RetryDelay); err != nil {
return err
}
if cfg.Retries == nil {
return fmt.Errorf("pipeline.whisperx.retries must be set (defaults should populate this)")
}
if *cfg.Retries < 0 {
return fmt.Errorf("pipeline.whisperx.retries must be >= 0")
}
if cfg.Concurrency == nil {
return fmt.Errorf("pipeline.whisperx.concurrency must be set (defaults should populate this)")
}
if *cfg.Concurrency <= 0 {
return fmt.Errorf("pipeline.whisperx.concurrency must be > 0")
}
return nil
}
func validateSeriatim(cfg SeriatimConfig) error {
if strings.TrimSpace(cfg.Binary) == "" {
return fmt.Errorf("pipeline.seriatim.binary is required")
}
if err := validateDuration("pipeline.seriatim.timeout", cfg.Timeout); err != nil {
return err
}
if err := validateSeriatimOutputSchema("pipeline.seriatim.output_schema", cfg.OutputSchema); err != nil {
return err
}
if cfg.CoalesceGap == nil {
return fmt.Errorf("pipeline.seriatim.coalesce_gap must be set (defaults should populate this)")
}
if *cfg.CoalesceGap < 0 {
return fmt.Errorf("pipeline.seriatim.coalesce_gap must be >= 0")
}
for _, item := range []struct {
name string
value *float64
}{
{name: "pipeline.seriatim.env.overlap_word_run_gap", value: cfg.Env.OverlapWordRunGap},
{name: "pipeline.seriatim.env.overlap_word_run_reorder_window", value: cfg.Env.OverlapWordRunReorderWindow},
{name: "pipeline.seriatim.env.backchannel_max_duration", value: cfg.Env.BackchannelMaxDuration},
{name: "pipeline.seriatim.env.filler_max_duration", value: cfg.Env.FillerMaxDuration},
} {
if item.value != nil && *item.value <= 0 {
return fmt.Errorf("%s must be > 0 when provided", item.name)
}
}
return nil
}
func validateSeriatimOutputSchema(field, value string) error {
switch strings.TrimSpace(value) {
case "seriatim-minimal", "seriatim-intermediate", "seriatim-full":
return nil
default:
return fmt.Errorf("%s must be one of: seriatim-minimal, seriatim-intermediate, seriatim-full", field)
}
}
func validateAudita(cfg AuditaConfig) error {
if strings.TrimSpace(cfg.Binary) == "" {
return fmt.Errorf("pipeline.audita.binary is required")
}
if err := validateDuration("pipeline.audita.timeout", cfg.Timeout); err != nil {
return err
}
for i, m := range cfg.Modules {
module := strings.TrimSpace(m)
if module == "" {
return fmt.Errorf("pipeline.audita.modules[%d] must be non-empty", i)
}
switch module {
case "glossary", "homophones", "spoken_word", "grammar":
default:
return fmt.Errorf("pipeline.audita.modules[%d] must be one of: glossary, homophones, spoken_word, grammar", i)
}
}
if strings.TrimSpace(cfg.TranscriptDescription) == "" && cfg.TranscriptDescription != "" {
return fmt.Errorf("pipeline.audita.transcript_description must be non-empty when provided")
}
if strings.TrimSpace(cfg.ConfigPath) == "" && cfg.ConfigPath != "" {
return fmt.Errorf("pipeline.audita.config_path must be non-empty when provided")
}
switch strings.TrimSpace(cfg.OutputSchema) {
case "", "bare-segments", "audita-v1":
default:
return fmt.Errorf("pipeline.audita.output_schema must be one of: bare-segments, audita-v1")
}
return nil
}