Adopt Audita v2 configuration and subprocess contract
This commit is contained in:
@@ -67,39 +67,23 @@ func (polishStage) Run(ctx context.Context, env *Env, m *manifest.Manifest) (*St
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("polish: resolve run-local processed transcript path: %w", err)
|
||||
}
|
||||
canonicalReportPath := filepath.Join(paths.ArtifactsDir, "audita.report.json")
|
||||
reportPath := canonicalReportPath
|
||||
if runLayout.Enabled {
|
||||
reportPath, err = runLocalPathForCanonical(runLayout, paths, canonicalReportPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("polish: resolve run-local report path: %w", err)
|
||||
}
|
||||
}
|
||||
workDir := filepath.Join(paths.ArtifactsDir, "audita-work")
|
||||
stdoutPath := filepath.Join(paths.LogsDir, "audita.stdout.log")
|
||||
stderrPath := filepath.Join(paths.LogsDir, "audita.stderr.log")
|
||||
generatedConfigPath := filepath.Join(paths.ConfigDir, "audita.generated.yml")
|
||||
if runLayout.Enabled {
|
||||
workDir = filepath.Join(runLayout.ScratchDir, "audita-work")
|
||||
stdoutPath = filepath.Join(runLayout.LogsDir, "audita.stdout.log")
|
||||
stderrPath = filepath.Join(runLayout.LogsDir, "audita.stderr.log")
|
||||
generatedConfigPath = filepath.Join(runLayout.ConfigDir, "audita.generated.yml")
|
||||
}
|
||||
|
||||
reportEnabled := env.Config.Pipeline.Audita.Report != nil && *env.Config.Pipeline.Audita.Report
|
||||
req := audita.PolishRequest{
|
||||
GeneratedConfigPath: generatedConfigPath,
|
||||
MergedTranscriptPath: mergedPath,
|
||||
OutputProcessedPath: processedPath,
|
||||
GlossaryPath: glossaryPath,
|
||||
ReportPath: "",
|
||||
WorkDir: workDir,
|
||||
StdoutLogPath: stdoutPath,
|
||||
StderrLogPath: stderrPath,
|
||||
}
|
||||
if reportEnabled {
|
||||
req.ReportPath = reportPath
|
||||
}
|
||||
|
||||
res, err := env.Audita.Run(ctx, req)
|
||||
if err != nil {
|
||||
@@ -114,16 +98,6 @@ func (polishStage) Run(ctx context.Context, env *Env, m *manifest.Manifest) (*St
|
||||
return nil, fmt.Errorf("polish: processed transcript %q invalid: %w", finalProcessedPath, err)
|
||||
}
|
||||
|
||||
finalReportPath, err := authoritativeOutputPath(req.ReportPath, res.ReportPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("polish: %w", err)
|
||||
}
|
||||
if reportEnabled {
|
||||
if err := validateTranscriptJSONFile(finalReportPath); err != nil {
|
||||
return nil, fmt.Errorf("polish: report %q invalid: %w", finalReportPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
materializedProcessed, err := materializeRunLocalOutput(env.ArtifactStore, finalProcessedPath, canonicalProcessedPath, artifacts.Ref{
|
||||
Kind: "transcript_polished",
|
||||
Category: "transcripts",
|
||||
@@ -133,39 +107,6 @@ func (polishStage) Run(ctx context.Context, env *Env, m *manifest.Manifest) (*St
|
||||
return nil, fmt.Errorf("polish: materialize canonical polished transcript: %w", err)
|
||||
}
|
||||
outputs := []artifacts.Ref{materializedProcessed}
|
||||
if reportEnabled {
|
||||
materializedReport, err := materializeRunLocalOutput(env.ArtifactStore, finalReportPath, canonicalReportPath, artifacts.Ref{
|
||||
Kind: "audita_report",
|
||||
Category: "artifacts",
|
||||
SessionID: sessionID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("polish: materialize canonical report: %w", err)
|
||||
}
|
||||
outputs = append(outputs, materializedReport)
|
||||
}
|
||||
|
||||
var validationConcurrency any
|
||||
if env.Config.Pipeline.Audita.ValidationLLMConcurrency != nil {
|
||||
validationConcurrency = *env.Config.Pipeline.Audita.ValidationLLMConcurrency
|
||||
}
|
||||
var totalLLMConcurrency any
|
||||
if env.Config.Pipeline.Audita.TotalLLMConcurrency != nil {
|
||||
totalLLMConcurrency = *env.Config.Pipeline.Audita.TotalLLMConcurrency
|
||||
}
|
||||
var proposalLLMConcurrency any
|
||||
if env.Config.Pipeline.Audita.ProposalLLMConcurrency != nil {
|
||||
proposalLLMConcurrency = *env.Config.Pipeline.Audita.ProposalLLMConcurrency
|
||||
}
|
||||
reportCanonicalPath := ""
|
||||
if reportEnabled {
|
||||
reportCanonicalPath = canonicalReportPath
|
||||
}
|
||||
credentialReference := ""
|
||||
if env.Config.Pipeline.Audita.LLMAPIKey != nil {
|
||||
credentialReference = env.Config.Pipeline.Audita.LLMAPIKey.EnvName()
|
||||
}
|
||||
|
||||
meta := map[string]any{
|
||||
"stage": "polish",
|
||||
"merged_transcript_path": mergedPath,
|
||||
@@ -173,22 +114,13 @@ func (polishStage) Run(ctx context.Context, env *Env, m *manifest.Manifest) (*St
|
||||
"glossary_path": glossaryPath,
|
||||
"run_output_path": finalProcessedPath,
|
||||
"output_path": canonicalProcessedPath,
|
||||
"run_report_path": finalReportPath,
|
||||
"report_path": reportCanonicalPath,
|
||||
"audita_work_dir": workDir,
|
||||
"report_enabled": reportEnabled,
|
||||
"modules": append([]string(nil), env.Config.Pipeline.Audita.Modules...),
|
||||
"base_url": env.Config.Pipeline.Audita.BaseURL,
|
||||
"model": env.Config.Pipeline.Audita.Model,
|
||||
"modules": append(env.Config.Pipeline.Audita.Modules[:0:0], env.Config.Pipeline.Audita.Modules...),
|
||||
"overlay": env.Config.Pipeline.Audita.Overlay,
|
||||
"proposal_profile": env.Config.Pipeline.Audita.ProposalProfile,
|
||||
"validation_profile": env.Config.Pipeline.Audita.ValidationProfile,
|
||||
"transcript_description": env.Config.Pipeline.Audita.TranscriptDescription,
|
||||
"config_path": env.Config.Pipeline.Audita.ConfigPath,
|
||||
"output_schema": env.Config.Pipeline.Audita.OutputSchema,
|
||||
"work_dir_retention": env.Config.Pipeline.Audita.WorkDirRetention,
|
||||
"validation_model": env.Config.Pipeline.Audita.ValidationModel,
|
||||
"total_llm_concurrency": totalLLMConcurrency,
|
||||
"proposal_llm_concurrency": proposalLLMConcurrency,
|
||||
"validation_llm_concurrency": validationConcurrency,
|
||||
"llm_api_key": credentialReference,
|
||||
"timeout": env.Config.Pipeline.Audita.Timeout,
|
||||
"binary": env.Config.Pipeline.Audita.Binary,
|
||||
"generated_config_path": generatedConfigPath,
|
||||
@@ -198,22 +130,12 @@ func (polishStage) Run(ctx context.Context, env *Env, m *manifest.Manifest) (*St
|
||||
"adapter_exit_code": res.ExitCode,
|
||||
"adapter_invoked_binary": res.InvokedBinary,
|
||||
"adapter_processed_output_path": res.ProcessedTranscriptPath,
|
||||
"adapter_report_path": res.ReportPath,
|
||||
"adapter_generated_config_path": res.GeneratedConfigPath,
|
||||
"adapter_work_dir": res.WorkDir,
|
||||
"adapter_stdout_log_path": res.StdoutLogPath,
|
||||
"adapter_stderr_log_path": res.StderrLogPath,
|
||||
"credential_env_var": credentialReference,
|
||||
"credential_present": false,
|
||||
}
|
||||
if res.Metadata != nil {
|
||||
meta["adapter_metadata"] = res.Metadata
|
||||
if value, ok := res.Metadata["credential_present"]; ok {
|
||||
meta["credential_present"] = value
|
||||
}
|
||||
if value, ok := res.Metadata["credential_env_var"]; ok {
|
||||
meta["credential_env_var"] = value
|
||||
}
|
||||
}
|
||||
|
||||
return &StageResult{
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
configkit "gitea.maximumdirect.net/eric/ConfigKit"
|
||||
"gitea.maximumdirect.net/eric/narratio/internal/adapters/audita"
|
||||
"gitea.maximumdirect.net/eric/narratio/internal/artifacts"
|
||||
"gitea.maximumdirect.net/eric/narratio/internal/config"
|
||||
@@ -43,18 +44,12 @@ func TestPolishStagePolishesMergedTranscriptAndRecordsMetadata(t *testing.T) {
|
||||
if req.GlossaryPath != filepath.Join(paths.InputsDir, "glossary.yml") {
|
||||
t.Fatalf("glossary path = %q", req.GlossaryPath)
|
||||
}
|
||||
if req.WorkDir != filepath.Join(paths.ArtifactsDir, "audita-work") {
|
||||
t.Fatalf("work dir = %q", req.WorkDir)
|
||||
}
|
||||
if len(result.Outputs) != 2 {
|
||||
t.Fatalf("outputs len = %d, want 2", len(result.Outputs))
|
||||
if len(result.Outputs) != 1 {
|
||||
t.Fatalf("outputs len = %d, want 1", len(result.Outputs))
|
||||
}
|
||||
if result.Outputs[0].Kind != "transcript_polished" {
|
||||
t.Fatalf("output[0] kind = %q, want transcript_polished", result.Outputs[0].Kind)
|
||||
}
|
||||
if result.Outputs[1].Kind != "audita_report" {
|
||||
t.Fatalf("output[1] kind = %q, want audita_report", result.Outputs[1].Kind)
|
||||
}
|
||||
if len(result.Logs) != 2 {
|
||||
t.Fatalf("logs = %#v, want 2", result.Logs)
|
||||
}
|
||||
@@ -65,18 +60,6 @@ func TestPolishStagePolishesMergedTranscriptAndRecordsMetadata(t *testing.T) {
|
||||
if result.Metadata["stage"] != "polish" {
|
||||
t.Fatalf("metadata stage = %#v, want polish", result.Metadata["stage"])
|
||||
}
|
||||
if result.Metadata["report_enabled"] != true {
|
||||
t.Fatalf("metadata report_enabled = %#v, want true", result.Metadata["report_enabled"])
|
||||
}
|
||||
if result.Metadata["audita_work_dir"] != filepath.Join(paths.ArtifactsDir, "audita-work") {
|
||||
t.Fatalf("metadata audita_work_dir = %#v", result.Metadata["audita_work_dir"])
|
||||
}
|
||||
if result.Metadata["total_llm_concurrency"] != 3 {
|
||||
t.Fatalf("metadata total_llm_concurrency = %#v, want 3", result.Metadata["total_llm_concurrency"])
|
||||
}
|
||||
if result.Metadata["proposal_llm_concurrency"] != 2 {
|
||||
t.Fatalf("metadata proposal_llm_concurrency = %#v, want 2", result.Metadata["proposal_llm_concurrency"])
|
||||
}
|
||||
if result.Metadata["output_schema"] != "audita-v1" {
|
||||
t.Fatalf("metadata output_schema = %#v, want audita-v1", result.Metadata["output_schema"])
|
||||
}
|
||||
@@ -185,25 +168,6 @@ func TestPolishStageFailsWhenProcessedOutputInvalid(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPolishStageFailsWhenReportInvalid(t *testing.T) {
|
||||
env, m := setupPolishEnv(t)
|
||||
paths := sessionPathsForEnv(env, m.SessionID)
|
||||
writeFile(t, filepath.Join(paths.TranscriptsDir, "base.json"), `{"segments":[]}`)
|
||||
setCurrentBuiltInEvidence(t, m, "merge", artifacts.TranscriptOutputKindBase, artifacts.ArtifactTranscriptBase, filepath.Join(paths.TranscriptsDir, "base.json"))
|
||||
writeFile(t, filepath.Join(paths.InputsDir, "glossary.yml"), "terms: []\n")
|
||||
badReport := filepath.Join(paths.ArtifactsDir, "bad.report.json")
|
||||
writeFile(t, badReport, "not-json")
|
||||
env.Audita = &audita.FakeRunner{Result: audita.PolishResult{ReportPath: badReport}}
|
||||
|
||||
_, err := (polishStage{}).Run(context.Background(), env, m)
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "report") {
|
||||
t.Fatalf("error = %q", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestPolishStageUsesRunLocalPathsAndMaterializesCanonical(t *testing.T) {
|
||||
env, m := setupPolishEnv(t)
|
||||
env.Config.Session.Campaign = "sample-campaign"
|
||||
@@ -244,32 +208,19 @@ func setupPolishEnv(t *testing.T) (*Env, *manifest.Manifest) {
|
||||
writeFile(t, sessionPath, "session_id: 2026-05-03\n")
|
||||
writeFile(t, pipelinePath, "workspace:\n root: "+workspace+"\n")
|
||||
|
||||
report := true
|
||||
totalLLMConcurrency := 3
|
||||
proposalLLMConcurrency := 2
|
||||
validationLLMConcurrency := 2
|
||||
|
||||
cfg := &config.Config{
|
||||
PipelinePath: pipelinePath,
|
||||
SessionPath: sessionPath,
|
||||
Pipeline: &config.PipelineConfig{
|
||||
Workspace: config.WorkspaceConfig{Root: workspace},
|
||||
Audita: config.AuditaConfig{
|
||||
Binary: "audita",
|
||||
Timeout: "3h",
|
||||
LLMAPIKey: testSecretReference("AUDITA_LLM_API_KEY"),
|
||||
Modules: []string{"glossary", "homophones", "grammar"},
|
||||
BaseURL: "https://openrouter.ai/api/v1",
|
||||
Model: "openrouter/google/gemma-4-31b-it",
|
||||
TranscriptDescription: "Campaign Session 42",
|
||||
ConfigPath: "/etc/audita/config.yml",
|
||||
OutputSchema: "audita-v1",
|
||||
WorkDirRetention: "auto",
|
||||
ValidationModel: "openrouter/google/gemma-4-31b-it",
|
||||
TotalLLMConcurrency: &totalLLMConcurrency,
|
||||
ProposalLLMConcurrency: &proposalLLMConcurrency,
|
||||
ValidationLLMConcurrency: &validationLLMConcurrency,
|
||||
Report: &report,
|
||||
Binary: "audita",
|
||||
Timeout: "3h",
|
||||
Credentials: []configkit.Secret{*testSecretReference("AUDITA_LLM_API_KEY")},
|
||||
Modules: []string{"glossary", "homophones", "grammar"},
|
||||
TranscriptDescription: "Campaign Session 42",
|
||||
ConfigPath: "/etc/audita/config.yml",
|
||||
OutputSchema: "audita-v1",
|
||||
},
|
||||
},
|
||||
Session: &config.SessionConfig{
|
||||
|
||||
@@ -18,9 +18,6 @@ const (
|
||||
)
|
||||
|
||||
type polishSemanticConfig struct {
|
||||
ServiceURL string `json:"service_url"`
|
||||
Model string `json:"model"`
|
||||
ValidationModel string `json:"validation_model"`
|
||||
Modules []string `json:"modules"`
|
||||
TranscriptDescription string `json:"transcript_description"`
|
||||
OutputSchema string `json:"output_schema"`
|
||||
@@ -74,9 +71,6 @@ func (polishStage) SemanticConfigFingerprint(env *Env) (manifest.SemanticConfigF
|
||||
}
|
||||
sort.Strings(modules)
|
||||
return FingerprintSemanticConfig(polishSemanticConfigVersion, polishSemanticConfig{
|
||||
ServiceURL: strings.TrimSpace(audita.BaseURL),
|
||||
Model: strings.TrimSpace(audita.Model),
|
||||
ValidationModel: strings.TrimSpace(audita.ValidationModel),
|
||||
Modules: modules,
|
||||
TranscriptDescription: strings.TrimSpace(audita.TranscriptDescription),
|
||||
OutputSchema: strings.TrimSpace(audita.OutputSchema),
|
||||
|
||||
@@ -3,6 +3,7 @@ package stage
|
||||
import (
|
||||
"testing"
|
||||
|
||||
configkit "gitea.maximumdirect.net/eric/ConfigKit"
|
||||
"gitea.maximumdirect.net/eric/narratio/internal/config"
|
||||
)
|
||||
|
||||
@@ -11,13 +12,10 @@ func TestPolishSemanticConfigSensitivity(t *testing.T) {
|
||||
name string
|
||||
mutate func(*Env)
|
||||
}{
|
||||
{name: "model", mutate: func(env *Env) { env.Config.Pipeline.Audita.Model = "production" }},
|
||||
{name: "validation model", mutate: func(env *Env) { env.Config.Pipeline.Audita.ValidationModel = "validator-v2" }},
|
||||
{name: "modules", mutate: func(env *Env) { env.Config.Pipeline.Audita.Modules = []string{"terms", "speakers", "style"} }},
|
||||
{name: "description", mutate: func(env *Env) { env.Config.Pipeline.Audita.TranscriptDescription = "live campaign" }},
|
||||
{name: "schema", mutate: func(env *Env) { env.Config.Pipeline.Audita.OutputSchema = "audita.transcript.v2" }},
|
||||
{name: "external config", mutate: func(env *Env) { env.Config.Pipeline.Audita.ConfigPath = "configs/other.yml" }},
|
||||
{name: "service", mutate: func(env *Env) { env.Config.Pipeline.Audita.BaseURL = "https://other.example" }},
|
||||
}
|
||||
assertRefinementChanges(t, polishStage{}, semantic)
|
||||
assertRefinementUnchanged(t, polishStage{}, []struct {
|
||||
@@ -28,12 +26,7 @@ func TestPolishSemanticConfigSensitivity(t *testing.T) {
|
||||
{name: "runner settings", mutate: func(env *Env) {
|
||||
env.Config.Pipeline.Audita.Binary = "/opt/audita"
|
||||
env.Config.Pipeline.Audita.Timeout = "1h"
|
||||
env.Config.Pipeline.Audita.TotalLLMConcurrency = intPointer(20)
|
||||
env.Config.Pipeline.Audita.ProposalLLMConcurrency = intPointer(10)
|
||||
env.Config.Pipeline.Audita.ValidationLLMConcurrency = intPointer(10)
|
||||
env.Config.Pipeline.Audita.LLMAPIKey = testSecretReference("OTHER_KEY")
|
||||
env.Config.Pipeline.Audita.WorkDirRetention = "always"
|
||||
env.Config.Pipeline.Audita.Report = boolPointer(false)
|
||||
env.Config.Pipeline.Audita.Credentials = []configkit.Secret{*testSecretReference("OTHER_KEY")}
|
||||
}},
|
||||
})
|
||||
}
|
||||
@@ -119,12 +112,9 @@ func refinementSemanticEnv() *Env {
|
||||
env := initialSemanticEnv()
|
||||
env.Config.Session.Title = "Session title"
|
||||
env.Config.Pipeline.Audita = config.AuditaConfig{
|
||||
Binary: "audita", Timeout: "10m", LLMAPIKey: testSecretReference("AUDITA_KEY"),
|
||||
Modules: []string{"speakers", "terms"}, BaseURL: "https://audita.example",
|
||||
Model: "testing", ValidationModel: "validator", TranscriptDescription: "tabletop session",
|
||||
Binary: "audita", Timeout: "10m", Credentials: []configkit.Secret{*testSecretReference("AUDITA_KEY")},
|
||||
Modules: []string{"speakers", "terms"}, TranscriptDescription: "tabletop session",
|
||||
ConfigPath: "configs/audita.yml", OutputSchema: "audita.transcript.v1",
|
||||
TotalLLMConcurrency: intPointer(4), ProposalLLMConcurrency: intPointer(2),
|
||||
ValidationLLMConcurrency: intPointer(2), WorkDirRetention: "failure", Report: boolPointer(true),
|
||||
}
|
||||
env.Config.Pipeline.Normalize = &config.NormalizeConfig{
|
||||
OutputPath: "transcripts/final.json", OutputSchema: "seriatim.transcript.v1", Report: boolPointer(true),
|
||||
|
||||
Reference in New Issue
Block a user