Add render artifact model, config, and Seriatim adapter contracts
This commit is contained in:
@@ -28,6 +28,7 @@ type PipelineConfig struct {
|
||||
Audita AuditaConfig `yaml:"audita"`
|
||||
Normalize *NormalizeConfig `yaml:"normalize"`
|
||||
Trim *TrimConfig `yaml:"trim"`
|
||||
Render *RenderConfig `yaml:"render"`
|
||||
Scriptorium *ScriptoriumConfig `yaml:"scriptorium"`
|
||||
Notification NotificationConfig `yaml:"notification"`
|
||||
}
|
||||
@@ -209,6 +210,16 @@ type TrimSeriatimConfig struct {
|
||||
Report *bool `yaml:"report"`
|
||||
}
|
||||
|
||||
// RenderConfig configures render-stage output formatting behavior.
|
||||
type RenderConfig struct {
|
||||
Enabled *bool `yaml:"enabled"`
|
||||
Format string `yaml:"format"`
|
||||
Title string `yaml:"title"`
|
||||
IncludeTimestamps *bool `yaml:"include_timestamps"`
|
||||
IncludeSegmentIDs bool `yaml:"include_segment_ids"`
|
||||
IncludeMetadata bool `yaml:"include_metadata"`
|
||||
}
|
||||
|
||||
// ScriptoriumConfig configures Scriptorium-backed artifact generation.
|
||||
type ScriptoriumConfig struct {
|
||||
Binary string `yaml:"binary"`
|
||||
|
||||
@@ -40,6 +40,12 @@ const (
|
||||
|
||||
DefaultTrimBoundsTimeout = "10m"
|
||||
DefaultTrimSeriatimReport = false
|
||||
DefaultRenderEnabled = true
|
||||
DefaultRenderFormat = "markdown"
|
||||
DefaultRenderTitle = ""
|
||||
DefaultRenderTimestamps = true
|
||||
DefaultRenderSegmentIDs = false
|
||||
DefaultRenderMetadata = false
|
||||
|
||||
DefaultNormalizeOutputPath = artifactmodel.TranscriptPathFinal
|
||||
DefaultNormalizeOutputSchema = "seriatim-intermediate"
|
||||
|
||||
@@ -337,6 +337,7 @@ func applyPipelineDefaults(cfg *PipelineConfig) {
|
||||
}
|
||||
applyNormalizeDefaults(cfg.Normalize)
|
||||
applyTrimDefaults(cfg.Trim)
|
||||
applyRenderDefaults(&cfg.Render)
|
||||
applyScriptoriumDefaults(cfg.Scriptorium)
|
||||
}
|
||||
|
||||
@@ -507,6 +508,27 @@ func applyTrimDefaults(cfg *TrimConfig) {
|
||||
}
|
||||
}
|
||||
|
||||
func applyRenderDefaults(cfg **RenderConfig) {
|
||||
if cfg == nil {
|
||||
return
|
||||
}
|
||||
if *cfg == nil {
|
||||
*cfg = &RenderConfig{}
|
||||
}
|
||||
if (*cfg).Enabled == nil {
|
||||
(*cfg).Enabled = boolPtr(DefaultRenderEnabled)
|
||||
}
|
||||
if strings.TrimSpace((*cfg).Format) == "" {
|
||||
(*cfg).Format = DefaultRenderFormat
|
||||
}
|
||||
if strings.TrimSpace((*cfg).Title) == "" {
|
||||
(*cfg).Title = DefaultRenderTitle
|
||||
}
|
||||
if (*cfg).IncludeTimestamps == nil {
|
||||
(*cfg).IncludeTimestamps = boolPtr(DefaultRenderTimestamps)
|
||||
}
|
||||
}
|
||||
|
||||
func applyNormalizeDefaults(cfg *NormalizeConfig) {
|
||||
if cfg == nil {
|
||||
return
|
||||
|
||||
128
internal/config/render_test.go
Normal file
128
internal/config/render_test.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRenderLoadAndValidate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
renderYAML string
|
||||
wantLoadErr string
|
||||
wantValidateErr string
|
||||
assert func(t *testing.T, cfg *Config)
|
||||
}{
|
||||
{
|
||||
name: "render defaults when omitted",
|
||||
renderYAML: "",
|
||||
assert: func(t *testing.T, cfg *Config) {
|
||||
t.Helper()
|
||||
if cfg.Pipeline.Render == nil {
|
||||
t.Fatal("render config should be present via defaults")
|
||||
}
|
||||
if cfg.Pipeline.Render.Enabled == nil || !*cfg.Pipeline.Render.Enabled {
|
||||
t.Fatalf("render.enabled = %#v, want true", cfg.Pipeline.Render.Enabled)
|
||||
}
|
||||
if cfg.Pipeline.Render.Format != "markdown" {
|
||||
t.Fatalf("render.format = %q, want markdown", cfg.Pipeline.Render.Format)
|
||||
}
|
||||
if cfg.Pipeline.Render.IncludeTimestamps == nil || !*cfg.Pipeline.Render.IncludeTimestamps {
|
||||
t.Fatalf("render.include_timestamps = %#v, want true", cfg.Pipeline.Render.IncludeTimestamps)
|
||||
}
|
||||
if cfg.Pipeline.Render.IncludeSegmentIDs {
|
||||
t.Fatalf("render.include_segment_ids = true, want false")
|
||||
}
|
||||
if cfg.Pipeline.Render.IncludeMetadata {
|
||||
t.Fatalf("render.include_metadata = true, want false")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid explicit render config",
|
||||
renderYAML: `render:
|
||||
enabled: false
|
||||
format: markdown
|
||||
title: Session Render
|
||||
include_timestamps: false
|
||||
include_segment_ids: true
|
||||
include_metadata: true
|
||||
`,
|
||||
assert: func(t *testing.T, cfg *Config) {
|
||||
t.Helper()
|
||||
if cfg.Pipeline.Render == nil {
|
||||
t.Fatal("render config should be present")
|
||||
}
|
||||
if cfg.Pipeline.Render.Enabled == nil || *cfg.Pipeline.Render.Enabled {
|
||||
t.Fatalf("render.enabled = %#v, want false", cfg.Pipeline.Render.Enabled)
|
||||
}
|
||||
if cfg.Pipeline.Render.IncludeTimestamps == nil || *cfg.Pipeline.Render.IncludeTimestamps {
|
||||
t.Fatalf("render.include_timestamps = %#v, want false", cfg.Pipeline.Render.IncludeTimestamps)
|
||||
}
|
||||
if !cfg.Pipeline.Render.IncludeSegmentIDs {
|
||||
t.Fatalf("render.include_segment_ids = false, want true")
|
||||
}
|
||||
if !cfg.Pipeline.Render.IncludeMetadata {
|
||||
t.Fatalf("render.include_metadata = false, want true")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid render format fails",
|
||||
renderYAML: `render:
|
||||
format: html
|
||||
`,
|
||||
wantValidateErr: "pipeline.render.format must be markdown",
|
||||
},
|
||||
{
|
||||
name: "unknown render field fails strict decoding",
|
||||
renderYAML: `render:
|
||||
format: markdown
|
||||
unknown: true
|
||||
`,
|
||||
wantLoadErr: "strict decode failed",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
pipelineYAML := testPipelineBaseYAML
|
||||
if tt.renderYAML != "" {
|
||||
pipelineYAML += "\n" + tt.renderYAML
|
||||
}
|
||||
pipelinePath, sessionPath := writeConfigFiles(t, pipelineYAML, testSessionBaseYAML)
|
||||
|
||||
cfg, err := Load(pipelinePath, sessionPath)
|
||||
if tt.wantLoadErr != "" {
|
||||
if err == nil {
|
||||
t.Fatalf("expected load error containing %q, got nil", tt.wantLoadErr)
|
||||
}
|
||||
if !strings.Contains(err.Error(), tt.wantLoadErr) {
|
||||
t.Fatalf("load error = %q, want to contain %q", err.Error(), tt.wantLoadErr)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("Load() error = %v", err)
|
||||
}
|
||||
|
||||
if tt.assert != nil {
|
||||
tt.assert(t, cfg)
|
||||
}
|
||||
|
||||
err = Validate(cfg)
|
||||
if tt.wantValidateErr != "" {
|
||||
if err == nil {
|
||||
t.Fatalf("expected validation error containing %q, got nil", tt.wantValidateErr)
|
||||
}
|
||||
if !strings.Contains(err.Error(), tt.wantValidateErr) {
|
||||
t.Fatalf("validation error = %q, want to contain %q", err.Error(), tt.wantValidateErr)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("Validate() error = %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -88,6 +88,9 @@ func validatePipeline(cfg *PipelineConfig) error {
|
||||
if err := validateTrim(cfg.Trim); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateRender(cfg.Render); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateScriptorium(cfg.Scriptorium); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -299,6 +302,26 @@ func validateTrim(cfg *TrimConfig) error {
|
||||
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)")
|
||||
}
|
||||
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")
|
||||
|
||||
Reference in New Issue
Block a user