Simplify downstream tool configuration
All checks were successful
ci/woodpecker/tag/release Pipeline was successful
All checks were successful
ci/woodpecker/tag/release Pipeline was successful
This commit is contained in:
@@ -108,18 +108,14 @@ func NewSubprocessRunner(cfg SubprocessRunnerConfig) (*SubprocessRunner, error)
|
||||
return nil, fmt.Errorf("audita module at index %d is empty", i)
|
||||
}
|
||||
}
|
||||
if strings.TrimSpace(cfg.BaseURL) == "" {
|
||||
return nil, fmt.Errorf("audita base url is required")
|
||||
}
|
||||
u, err := url.Parse(cfg.BaseURL)
|
||||
if err != nil || u.Scheme == "" || u.Host == "" {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("audita base url %q is invalid: %w", cfg.BaseURL, err)
|
||||
if strings.TrimSpace(cfg.BaseURL) != "" {
|
||||
u, err := url.Parse(cfg.BaseURL)
|
||||
if err != nil || u.Scheme == "" || u.Host == "" {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("audita base url %q is invalid: %w", cfg.BaseURL, err)
|
||||
}
|
||||
return nil, fmt.Errorf("audita base url %q is invalid", cfg.BaseURL)
|
||||
}
|
||||
return nil, fmt.Errorf("audita base url %q is invalid", cfg.BaseURL)
|
||||
}
|
||||
if strings.TrimSpace(cfg.Model) == "" {
|
||||
return nil, fmt.Errorf("audita model is required")
|
||||
}
|
||||
if cfg.TotalLLMConcurrency != nil && *cfg.TotalLLMConcurrency <= 0 {
|
||||
return nil, fmt.Errorf("audita total llm concurrency must be > 0 when provided")
|
||||
@@ -307,10 +303,14 @@ func (r *SubprocessRunner) buildArgs(req PolishRequest, modules []string) []stri
|
||||
req.MergedTranscriptPath,
|
||||
"--glossary", req.GlossaryPath,
|
||||
"--output", req.OutputProcessedPath,
|
||||
"--base-url", r.baseURL,
|
||||
"--model", r.model,
|
||||
"--work-dir", req.WorkDir,
|
||||
}
|
||||
if r.baseURL != "" {
|
||||
args = append(args, "--base-url", r.baseURL)
|
||||
}
|
||||
if r.model != "" {
|
||||
args = append(args, "--model", r.model)
|
||||
}
|
||||
if len(modules) > 0 {
|
||||
args = append(args, "--modules", strings.Join(modules, ","))
|
||||
}
|
||||
|
||||
@@ -99,9 +99,9 @@ func TestSubprocessRunnerSuccessArgsEnvAndValidation(t *testing.T) {
|
||||
"process", req.MergedTranscriptPath,
|
||||
"--glossary", req.GlossaryPath,
|
||||
"--output", req.OutputProcessedPath,
|
||||
"--work-dir", req.WorkDir,
|
||||
"--base-url", "https://openrouter.ai/api/v1",
|
||||
"--model", "openrouter/google/gemma-4-31b-it",
|
||||
"--work-dir", req.WorkDir,
|
||||
"--modules", "glossary,homophones,glossary",
|
||||
"--report-json", req.ReportPath,
|
||||
"--transcript-description", "Campaign Session 42",
|
||||
@@ -248,6 +248,36 @@ func TestSubprocessRunnerOmitsModulesFlagWhenNotConfigured(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubprocessRunnerOmitsBaseURLAndModelFlagsWhenNotConfigured(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("helper wrapper script uses /bin/sh")
|
||||
}
|
||||
t.Setenv("GO_WANT_AUDITA_HELPER", "1")
|
||||
t.Setenv("AUDITA_HELPER_MODE", "success")
|
||||
recordPath := filepath.Join(t.TempDir(), "record.json")
|
||||
t.Setenv("AUDITA_HELPER_RECORD_PATH", recordPath)
|
||||
|
||||
runner := mustAuditaRunner(t, SubprocessRunnerConfig{
|
||||
Binary: writeAuditaHelperWrapper(t),
|
||||
Timeout: mustParseAuditaDuration(t, "2s"),
|
||||
LLMAPIKeyEnv: "",
|
||||
Report: false,
|
||||
})
|
||||
req := auditaReqForTest(t, false)
|
||||
if _, err := runner.Run(context.Background(), req); err != nil {
|
||||
t.Fatalf("Run() error = %v", err)
|
||||
}
|
||||
rec := readAuditaHelperRecord(t, recordPath)
|
||||
for i := 0; i < len(rec.Args); i++ {
|
||||
if rec.Args[i] == "--base-url" {
|
||||
t.Fatalf("args contained --base-url unexpectedly: %#v", rec.Args)
|
||||
}
|
||||
if rec.Args[i] == "--model" {
|
||||
t.Fatalf("args contained --model unexpectedly: %#v", rec.Args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubprocessRunnerSubprocessFailure(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("helper wrapper script uses /bin/sh")
|
||||
|
||||
@@ -250,7 +250,7 @@ func buildDefaultAuditaRunner(cfg *config.Config) (audita.Runner, error) {
|
||||
}
|
||||
|
||||
a := cfg.Pipeline.Audita
|
||||
if strings.TrimSpace(a.Binary) == "" || strings.TrimSpace(a.Timeout) == "" || strings.TrimSpace(a.BaseURL) == "" || strings.TrimSpace(a.Model) == "" {
|
||||
if strings.TrimSpace(a.Binary) == "" || strings.TrimSpace(a.Timeout) == "" {
|
||||
// Compatibility fallback for tests or internal call paths that bypass config validation/defaults.
|
||||
return &audita.NoopRunner{}, nil
|
||||
}
|
||||
|
||||
@@ -497,6 +497,55 @@ func testConfig(t *testing.T) *config.Config {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildDefaultRunnersWithOmittedToolSections(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
pipelinePath := filepath.Join(dir, "pipeline.yml")
|
||||
sessionPath := filepath.Join(dir, "session.yml")
|
||||
pipelineYAML := `workspace:
|
||||
root: ` + t.TempDir() + `
|
||||
whisperx:
|
||||
transcribe_url: https://example.com/transcribe
|
||||
analyzer:
|
||||
timeout: 20m
|
||||
notification:
|
||||
timeout: 10s
|
||||
`
|
||||
sessionYAML := `session_id: 2026-05-03
|
||||
campaign: sample-campaign
|
||||
inputs:
|
||||
audio_dir: ./audio
|
||||
speakers_file: ./speakers.yml
|
||||
autocorrect_file: ./autocorrect.yml
|
||||
glossary_file: ./glossary.yml
|
||||
`
|
||||
mustWriteFile(t, pipelinePath, pipelineYAML)
|
||||
mustWriteFile(t, sessionPath, sessionYAML)
|
||||
|
||||
cfg, err := config.Load(pipelinePath, sessionPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Load() error = %v", err)
|
||||
}
|
||||
if err := config.Validate(cfg); err != nil {
|
||||
t.Fatalf("Validate() error = %v", err)
|
||||
}
|
||||
|
||||
serRunner, err := buildDefaultSeriatimRunner(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("buildDefaultSeriatimRunner() error = %v", err)
|
||||
}
|
||||
if _, ok := serRunner.(*seriatim.SubprocessRunner); !ok {
|
||||
t.Fatalf("seriatim runner type = %T, want *seriatim.SubprocessRunner", serRunner)
|
||||
}
|
||||
|
||||
audRunner, err := buildDefaultAuditaRunner(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("buildDefaultAuditaRunner() error = %v", err)
|
||||
}
|
||||
if _, ok := audRunner.(*audita.SubprocessRunner); !ok {
|
||||
t.Fatalf("audita runner type = %T, want *audita.SubprocessRunner", audRunner)
|
||||
}
|
||||
}
|
||||
|
||||
func mustWriteFile(t *testing.T, path, contents string) {
|
||||
t.Helper()
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||
|
||||
@@ -244,6 +244,9 @@ func applySeriatimDefaults(cfg *SeriatimConfig) {
|
||||
if cfg == nil {
|
||||
return
|
||||
}
|
||||
if cfg.Binary == "" {
|
||||
cfg.Binary = "seriatim"
|
||||
}
|
||||
if cfg.Timeout == "" {
|
||||
cfg.Timeout = "10m"
|
||||
}
|
||||
@@ -262,15 +265,12 @@ func applyAuditaDefaults(cfg *AuditaConfig) {
|
||||
if cfg == nil {
|
||||
return
|
||||
}
|
||||
if cfg.Binary == "" {
|
||||
cfg.Binary = "audita"
|
||||
}
|
||||
if cfg.Timeout == "" {
|
||||
cfg.Timeout = "3h"
|
||||
}
|
||||
if cfg.BaseURL == "" {
|
||||
cfg.BaseURL = "https://openrouter.ai/api/v1"
|
||||
}
|
||||
if cfg.Model == "" {
|
||||
cfg.Model = "openrouter/google/gemma-4-31b-it"
|
||||
}
|
||||
if cfg.Report == nil {
|
||||
cfg.Report = boolPtr(true)
|
||||
}
|
||||
@@ -280,6 +280,9 @@ func applyScriptoriumDefaults(cfg *ScriptoriumConfig) {
|
||||
if cfg == nil {
|
||||
return
|
||||
}
|
||||
if cfg.Binary == "" {
|
||||
cfg.Binary = "scriptorium"
|
||||
}
|
||||
if cfg.Timeout == "" {
|
||||
cfg.Timeout = "10m"
|
||||
}
|
||||
|
||||
@@ -37,6 +37,26 @@ inputs:
|
||||
speakers_file: ./speakers.yml
|
||||
autocorrect_file: ./autocorrect.yml
|
||||
glossary_file: ./glossary.yml
|
||||
`,
|
||||
checkDefault: true,
|
||||
},
|
||||
{
|
||||
name: "seriatim and audita sections can be omitted",
|
||||
pipelineYAML: `workspace:
|
||||
root: /tmp/narratio
|
||||
whisperx:
|
||||
transcribe_url: https://transcription.ai.rakestrawhome.com/transcribe
|
||||
analyzer:
|
||||
timeout: 20m
|
||||
notification:
|
||||
timeout: 15s
|
||||
`,
|
||||
sessionYAML: `session_id: 2026-05-03
|
||||
inputs:
|
||||
audio_dir: ./audio
|
||||
speakers_file: ./speakers.yml
|
||||
autocorrect_file: ./autocorrect.yml
|
||||
glossary_file: ./glossary.yml
|
||||
`,
|
||||
checkDefault: true,
|
||||
},
|
||||
@@ -296,7 +316,7 @@ inputs:
|
||||
wantLoadErr: "strict decode failed",
|
||||
},
|
||||
{
|
||||
name: "missing seriatim binary fails",
|
||||
name: "missing seriatim binary uses default",
|
||||
pipelineYAML: `workspace:
|
||||
root: /tmp/narratio
|
||||
whisperx:
|
||||
@@ -311,7 +331,6 @@ inputs:
|
||||
autocorrect_file: ./autocorrect.yml
|
||||
glossary_file: ./glossary.yml
|
||||
`,
|
||||
wantValidate: "pipeline config \"pipeline.yml\" invalid: pipeline.seriatim.binary is required",
|
||||
},
|
||||
{
|
||||
name: "invalid seriatim timeout fails",
|
||||
@@ -412,7 +431,7 @@ inputs:
|
||||
wantLoadErr: "strict decode failed",
|
||||
},
|
||||
{
|
||||
name: "missing audita binary fails",
|
||||
name: "missing audita binary uses default",
|
||||
pipelineYAML: `workspace:
|
||||
root: /tmp/narratio
|
||||
whisperx:
|
||||
@@ -429,7 +448,6 @@ inputs:
|
||||
autocorrect_file: ./autocorrect.yml
|
||||
glossary_file: ./glossary.yml
|
||||
`,
|
||||
wantValidate: "pipeline config \"pipeline.yml\" invalid: pipeline.audita.binary is required",
|
||||
},
|
||||
{
|
||||
name: "invalid audita timeout fails",
|
||||
@@ -713,6 +731,9 @@ inputs:
|
||||
if cfg.Pipeline.Seriatim.Timeout != "10m" {
|
||||
t.Fatalf("seriatim.timeout = %q, want %q", cfg.Pipeline.Seriatim.Timeout, "10m")
|
||||
}
|
||||
if cfg.Pipeline.Seriatim.Binary != "seriatim" {
|
||||
t.Fatalf("seriatim.binary = %q, want %q", cfg.Pipeline.Seriatim.Binary, "seriatim")
|
||||
}
|
||||
if cfg.Pipeline.Seriatim.OutputSchema != "seriatim-intermediate" {
|
||||
t.Fatalf("seriatim.output_schema = %q, want %q", cfg.Pipeline.Seriatim.OutputSchema, "seriatim-intermediate")
|
||||
}
|
||||
@@ -725,17 +746,20 @@ inputs:
|
||||
if cfg.Pipeline.Audita.Timeout != "3h" {
|
||||
t.Fatalf("audita.timeout = %q, want %q", cfg.Pipeline.Audita.Timeout, "3h")
|
||||
}
|
||||
if cfg.Pipeline.Audita.Binary != "audita" {
|
||||
t.Fatalf("audita.binary = %q, want %q", cfg.Pipeline.Audita.Binary, "audita")
|
||||
}
|
||||
if cfg.Pipeline.Audita.LLMAPIKeyEnv != "" {
|
||||
t.Fatalf("audita.llm_api_key_env = %q, want empty by default", cfg.Pipeline.Audita.LLMAPIKeyEnv)
|
||||
}
|
||||
if cfg.Pipeline.Audita.Modules != nil {
|
||||
t.Fatalf("audita.modules = %#v, want nil default (optional override)", cfg.Pipeline.Audita.Modules)
|
||||
}
|
||||
if cfg.Pipeline.Audita.BaseURL != "https://openrouter.ai/api/v1" {
|
||||
t.Fatalf("audita.base_url = %q, want %q", cfg.Pipeline.Audita.BaseURL, "https://openrouter.ai/api/v1")
|
||||
if cfg.Pipeline.Audita.BaseURL != "" {
|
||||
t.Fatalf("audita.base_url = %q, want empty default", cfg.Pipeline.Audita.BaseURL)
|
||||
}
|
||||
if cfg.Pipeline.Audita.Model != "openrouter/google/gemma-4-31b-it" {
|
||||
t.Fatalf("audita.model = %q, want %q", cfg.Pipeline.Audita.Model, "openrouter/google/gemma-4-31b-it")
|
||||
if cfg.Pipeline.Audita.Model != "" {
|
||||
t.Fatalf("audita.model = %q, want empty default", cfg.Pipeline.Audita.Model)
|
||||
}
|
||||
if cfg.Pipeline.Audita.ValidationModel != "" {
|
||||
t.Fatalf("audita.validation_model = %q, want empty default", cfg.Pipeline.Audita.ValidationModel)
|
||||
|
||||
@@ -49,11 +49,19 @@ func TestScriptoriumLoadAndValidate(t *testing.T) {
|
||||
wantLoadErr: "strict decode failed",
|
||||
},
|
||||
{
|
||||
name: "missing binary fails when section present",
|
||||
name: "missing binary defaults when section present",
|
||||
scriptoriumYAML: `scriptorium:
|
||||
timeout: 10m
|
||||
`,
|
||||
wantValidateErr: "pipeline.scriptorium.binary is required",
|
||||
assert: func(t *testing.T, cfg *Config) {
|
||||
t.Helper()
|
||||
if cfg.Pipeline.Scriptorium == nil {
|
||||
t.Fatal("scriptorium config should be present")
|
||||
}
|
||||
if cfg.Pipeline.Scriptorium.Binary != "scriptorium" {
|
||||
t.Fatalf("scriptorium.binary = %q, want scriptorium", cfg.Pipeline.Scriptorium.Binary)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "enabled artifact missing prompt id fails",
|
||||
|
||||
@@ -276,9 +276,6 @@ func validateAudita(cfg AuditaConfig) error {
|
||||
return fmt.Errorf("pipeline.audita.base_url must be a valid URL")
|
||||
}
|
||||
}
|
||||
if strings.TrimSpace(cfg.Model) == "" {
|
||||
return fmt.Errorf("pipeline.audita.model is required")
|
||||
}
|
||||
if cfg.TotalLLMConcurrency != nil && *cfg.TotalLLMConcurrency <= 0 {
|
||||
return fmt.Errorf("pipeline.audita.total_llm_concurrency must be > 0")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user