Add reporting and debug settings with context-aware CLI selection
This commit is contained in:
@@ -1,11 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"gitea.maximumdirect.net/eric/audita/internal/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
os.Exit(cli.Run(os.Args[1:], os.Stdout, os.Stderr))
|
||||
os.Exit(run())
|
||||
}
|
||||
|
||||
func run() int {
|
||||
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||
defer stop()
|
||||
return cli.Run(ctx, os.Args[1:], os.Stdout, os.Stderr)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@@ -23,7 +24,7 @@ func TestProcessSubprocessHelper(t *testing.T) {
|
||||
}
|
||||
for i, arg := range os.Args {
|
||||
if arg == "--" {
|
||||
os.Exit(cli.Run(os.Args[i+1:], os.Stdout, os.Stderr))
|
||||
os.Exit(cli.Run(context.Background(), os.Args[i+1:], os.Stdout, os.Stderr))
|
||||
}
|
||||
}
|
||||
os.Exit(2)
|
||||
|
||||
@@ -589,6 +589,16 @@ focused runner/runtime race tests when those packages are testable.
|
||||
|
||||
## Stage 7 — Add Reporting and Debug Settings and CLI Selection
|
||||
|
||||
**Completed 2026-09-24.** Version-2 settings now resolve reporting and debug
|
||||
independently, with typed visited boolean overrides and destination selection
|
||||
only for enabled reports. The old JSON-report and retention flags/settings were
|
||||
removed; CLI entry points now carry a context from interrupt/SIGTERM handling.
|
||||
ConfigKit tests and focused CLI parser tests cover defaults, overlay false
|
||||
overrides, bare booleans, destination selection, and removed flags. `go test
|
||||
./internal/core/config`, `go test internal/cli/config_input.go
|
||||
internal/cli/config_input_test.go`, and `git diff --check` passed. Whole CLI tests
|
||||
remain unavailable until Stage 8 replaces the report builder's warning fields.
|
||||
|
||||
**Work:** Implement A in `core/config/config.go`, `cli/config_input.go`, invocation
|
||||
state, and usage text. Add boolean flags with visited-value overrides and update
|
||||
safe effective-setting/provenance output. Remove the old external JSON report flag
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -23,7 +24,6 @@ type configInput struct {
|
||||
TranscriptPath string
|
||||
GlossaryPath string
|
||||
OutputPath string
|
||||
ReportPath string
|
||||
Overrides []ck.Override
|
||||
}
|
||||
|
||||
@@ -61,12 +61,16 @@ func parseConfigInput(command string, args []string, output io.Writer) (configIn
|
||||
"spoken-word-confidence-threshold": ck.Keys("thresholds", "spoken_word"),
|
||||
"grammar-confidence-threshold": ck.Keys("thresholds", "grammar"),
|
||||
"transcript-description": ck.Keys("context", "description"),
|
||||
"reporting": ck.Keys("reporting", "enabled"),
|
||||
"report-dir": ck.Keys("reporting", "stdout_dir"),
|
||||
"debug": ck.Keys("diagnostics", "debug"),
|
||||
"work-dir": ck.Keys("diagnostics", "work_dir"),
|
||||
"work-dir-retention": ck.Keys("diagnostics", "retention"),
|
||||
"modules": ck.Keys("pipeline", "modules"),
|
||||
}
|
||||
for name := range settingPaths {
|
||||
switch name {
|
||||
case "reporting", "debug":
|
||||
fs.Bool(name, false, "Setting override")
|
||||
case "validation-max-prompt-tokens", "target-sections", "max-section-tokens", "min-section-tokens", "normalize-max-segment-tokens":
|
||||
fs.Int(name, 0, "Setting override")
|
||||
case "normalize-max-segment-gap", "normalize-ellipsis-gap", "normalize-max-segment-duration", "glossary-confidence-threshold", "homophones-confidence-threshold", "spoken-word-confidence-threshold", "grammar-confidence-threshold":
|
||||
@@ -78,7 +82,6 @@ func parseConfigInput(command string, args []string, output io.Writer) (configIn
|
||||
if command == "process" {
|
||||
fs.String("glossary", "", "Glossary YAML file")
|
||||
fs.String("output", "", "Corrected transcript output file")
|
||||
fs.String("report-json", "", "Process report output file")
|
||||
}
|
||||
parseArgs := args
|
||||
front := ""
|
||||
@@ -102,7 +105,6 @@ func parseConfigInput(command string, args []string, output io.Writer) (configIn
|
||||
input.TranscriptPath = positions[0]
|
||||
input.GlossaryPath = fs.Lookup("glossary").Value.String()
|
||||
input.OutputPath = fs.Lookup("output").Value.String()
|
||||
input.ReportPath = fs.Lookup("report-json").Value.String()
|
||||
if strings.TrimSpace(input.GlossaryPath) == "" {
|
||||
return configInput{}, false, fmt.Errorf("--glossary is required")
|
||||
}
|
||||
@@ -128,6 +130,8 @@ func parseConfigInput(command string, args []string, output io.Writer) (configIn
|
||||
}
|
||||
var value any = f.Value.String()
|
||||
switch f.Name {
|
||||
case "reporting", "debug":
|
||||
value, _ = strconv.ParseBool(f.Value.String())
|
||||
case "modules":
|
||||
modules, err := config.ParseModulesCSV(f.Value.String())
|
||||
if err != nil {
|
||||
@@ -203,3 +207,16 @@ func configInputError(err error) string {
|
||||
}
|
||||
return strings.Join(messages, "; ")
|
||||
}
|
||||
|
||||
func selectedReportDir(cfg config.Config, outputPath string) (string, error) {
|
||||
if !cfg.Reporting.Enabled {
|
||||
return "", nil
|
||||
}
|
||||
if strings.TrimSpace(outputPath) != "" {
|
||||
return filepath.Dir(outputPath), nil
|
||||
}
|
||||
if strings.TrimSpace(cfg.Reporting.StdoutDir) == "" {
|
||||
return "", fmt.Errorf("report directory is required when reporting is enabled and transcript output is stdout")
|
||||
}
|
||||
return cfg.Reporting.StdoutDir, nil
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"testing"
|
||||
|
||||
ck "gitea.maximumdirect.net/eric/ConfigKit"
|
||||
"gitea.maximumdirect.net/eric/audita/internal/core/config"
|
||||
)
|
||||
|
||||
func TestSharedConfigInput(t *testing.T) {
|
||||
@@ -43,7 +44,7 @@ func TestSharedConfigInput(t *testing.T) {
|
||||
|
||||
func TestConfigInputRejectsRemovedAndProcessOnlyFlags(t *testing.T) {
|
||||
for _, command := range []string{"process", "validate", "print-effective"} {
|
||||
for _, flag := range []string{"--model", "--llm-api-key", "--max-retries", "--llm-concurrency"} {
|
||||
for _, flag := range []string{"--model", "--llm-api-key", "--max-retries", "--llm-concurrency", "--report-json", "--work-dir-retention"} {
|
||||
args := []string{flag, "value"}
|
||||
if command == "process" {
|
||||
args = append([]string{"transcript.json", "--glossary", "glossary.yml"}, args...)
|
||||
@@ -169,3 +170,81 @@ func TestSharedLoadingUsesSelectedFileOverlayAndVisitedOverrides(t *testing.T) {
|
||||
t.Fatal("unmodified invalid lower-precedence section relation accepted")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReportingAndDebugOverridesRemainIndependent(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
if err := os.Mkdir(filepath.Join(dir, "overlays"), 0700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
main := filepath.Join(dir, "config.yml")
|
||||
base := "version: 2\nbackends:\n local:\n endpoint: https://local.invalid/v1\nllm:\n proposal_profile: base\n"
|
||||
if err := os.WriteFile(main, []byte(base), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(dir, "overlays", "testing.yml"), []byte("reporting:\n enabled: true\n stdout_dir: reports\ndiagnostics:\n debug: true\n"), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defaultInput, _, err := parseConfigInput("validate", []string{"--config", main}, io.Discard)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defaults, err := loadConfigInput(defaultInput, "validate")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !defaults.Result.Config.Reporting.Enabled || defaults.Result.Config.Reporting.StdoutDir != "." || defaults.Result.Config.Diagnostics.Debug {
|
||||
t.Fatalf("unexpected defaults: %+v", defaults.Result.Config)
|
||||
}
|
||||
for _, command := range []string{"process", "validate", "print-effective"} {
|
||||
args := []string{"--config", main, "--overlay", "testing", "--reporting=false", "--debug=false"}
|
||||
if command == "process" {
|
||||
args = append([]string{"input.json", "--glossary", "glossary.yml"}, args...)
|
||||
}
|
||||
input, _, err := parseConfigInput(command, args, io.Discard)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(input.Overrides) != 2 || input.Overrides[0].Value != false || input.Overrides[1].Value != false {
|
||||
t.Fatalf("boolean overrides must remain typed: %+v", input.Overrides)
|
||||
}
|
||||
selected, err := loadConfigInput(input, command)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if selected.Result.Config.Reporting.Enabled || selected.Result.Config.Diagnostics.Debug || selected.Result.Config.Reporting.StdoutDir != "reports" {
|
||||
t.Fatalf("%s did not honor explicit false: %+v", command, selected.Result.Config)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReportDirectorySelectionOnlyUsesActiveDestination(t *testing.T) {
|
||||
cfg := config.Config{Reporting: config.Reporting{Enabled: true, StdoutDir: "reports"}}
|
||||
if got, err := selectedReportDir(cfg, ""); err != nil || got != "reports" {
|
||||
t.Fatalf("stdout destination: %q %v", got, err)
|
||||
}
|
||||
cfg.Reporting.StdoutDir = ""
|
||||
if got, err := selectedReportDir(cfg, filepath.Join("out", "corrected.json")); err != nil || got != "out" {
|
||||
t.Fatalf("file destination ignored stdout setting: %q %v", got, err)
|
||||
}
|
||||
if _, err := selectedReportDir(cfg, ""); err == nil {
|
||||
t.Fatal("enabled stdout reporting accepted blank destination")
|
||||
}
|
||||
cfg.Reporting.Enabled = false
|
||||
if got, err := selectedReportDir(cfg, ""); err != nil || got != "" {
|
||||
t.Fatalf("disabled reporting probed destination: %q %v", got, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBareBooleanFlagsAndReportDirectoryOverride(t *testing.T) {
|
||||
input, _, err := parseConfigInput("process", []string{"input.json", "--glossary", "glossary.yml", "--reporting", "--debug", "--report-dir", "reports"}, io.Discard)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
values := map[string]any{}
|
||||
for _, override := range input.Overrides {
|
||||
values[override.Path.String()] = override.Value
|
||||
}
|
||||
if values[ck.Keys("reporting", "enabled").String()] != true || values[ck.Keys("diagnostics", "debug").String()] != true || values[ck.Keys("reporting", "stdout_dir").String()] != "reports" {
|
||||
t.Fatalf("incorrect visited overrides: %+v", values)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
coreio "gitea.maximumdirect.net/eric/audita/internal/core/io"
|
||||
"gitea.maximumdirect.net/eric/audita/internal/core/normalization"
|
||||
"gitea.maximumdirect.net/eric/audita/internal/core/outputschema"
|
||||
"gitea.maximumdirect.net/eric/audita/internal/core/reporting"
|
||||
"gitea.maximumdirect.net/eric/audita/internal/core/schema"
|
||||
"gitea.maximumdirect.net/eric/audita/internal/framework/contracts"
|
||||
"gitea.maximumdirect.net/eric/audita/internal/framework/llm"
|
||||
@@ -28,7 +27,7 @@ type processInvocation struct {
|
||||
TranscriptPath string
|
||||
GlossaryPath string
|
||||
OutputPath string
|
||||
ReportJSONPath string
|
||||
ReportDir string
|
||||
Config config.Config
|
||||
ConfigResult *ck.Result[config.Config]
|
||||
LLMRuntime *llm.Runtime
|
||||
@@ -38,8 +37,8 @@ type processInvocation struct {
|
||||
ConfigVersion *int
|
||||
}
|
||||
|
||||
var processRunner = func(inv processInvocation, stdout io.Writer) (*normalization.NormalizationSummary, *chunking.Summary, *runner.RunOutput, *diagnostics.RunDirectory, error) {
|
||||
runDir, err := diagnostics.NewRunDirectory(inv.Config.Diagnostics.WorkDir, string(inv.Config.Diagnostics.Retention), llm.ConfiguredSecrets(&inv.Config)...)
|
||||
var processRunner = func(ctx context.Context, inv processInvocation, stdout io.Writer) (*normalization.NormalizationSummary, *chunking.Summary, *runner.RunOutput, *diagnostics.RunDirectory, error) {
|
||||
runDir, err := diagnostics.NewRunDirectory(inv.Config.Diagnostics.WorkDir, "always", llm.ConfiguredSecrets(&inv.Config)...)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, fmt.Errorf("run_dir_creation: %w", err)
|
||||
}
|
||||
@@ -54,7 +53,6 @@ var processRunner = func(inv processInvocation, stdout io.Writer) (*normalizatio
|
||||
TranscriptPath: inv.TranscriptPath,
|
||||
GlossaryPath: inv.GlossaryPath,
|
||||
OutputPath: inv.OutputPath,
|
||||
ReportJSONPath: inv.ReportJSONPath,
|
||||
ConfigPath: inv.ConfigPath,
|
||||
ConfigSource: inv.ConfigSource,
|
||||
Overlay: inv.Overlay,
|
||||
@@ -168,7 +166,7 @@ var processRunner = func(inv processInvocation, stdout io.Writer) (*normalizatio
|
||||
return fail("runner_setup", err, nil)
|
||||
}
|
||||
|
||||
runnerResult, runErr := runner.New(moduleFactory).Run(context.Background(), runner.RunInput{
|
||||
runnerResult, runErr := runner.New(moduleFactory).Run(ctx, runner.RunInput{
|
||||
Config: &inv.Config,
|
||||
Transcript: normalizedTranscript,
|
||||
Glossary: glossary,
|
||||
@@ -231,7 +229,7 @@ func sourceToCanonicalTranscript(source *schema.SourceTranscript) *schema.Transc
|
||||
}
|
||||
|
||||
// Run executes the Audita CLI with the provided arguments and streams.
|
||||
func Run(args []string, stdout, stderr io.Writer) int {
|
||||
func Run(ctx context.Context, args []string, stdout, stderr io.Writer) int {
|
||||
if len(args) == 0 {
|
||||
writeRootUsage(stdout)
|
||||
return 0
|
||||
@@ -253,10 +251,10 @@ func Run(args []string, stdout, stderr io.Writer) int {
|
||||
}
|
||||
|
||||
if args[0] == "process" {
|
||||
return runProcess(args[1:], stdout, stderr)
|
||||
return runProcess(ctx, args[1:], stdout, stderr)
|
||||
}
|
||||
if args[0] == "config" {
|
||||
return runConfig(args[1:], stdout, stderr)
|
||||
return runConfig(ctx, args[1:], stdout, stderr)
|
||||
}
|
||||
|
||||
fmt.Fprintf(stderr, "audita: unknown command %q\n\n", args[0])
|
||||
@@ -264,7 +262,7 @@ func Run(args []string, stdout, stderr io.Writer) int {
|
||||
return 2
|
||||
}
|
||||
|
||||
func runProcess(args []string, stdout, stderr io.Writer) int {
|
||||
func runProcess(ctx context.Context, args []string, stdout, stderr io.Writer) int {
|
||||
startedAt := time.Now().UTC()
|
||||
input, help, err := parseConfigInput("process", args, stderr)
|
||||
if err != nil {
|
||||
@@ -280,7 +278,16 @@ func runProcess(args []string, stdout, stderr io.Writer) int {
|
||||
fmt.Fprintf(stderr, "audita process: %s\n", configInputError(err))
|
||||
return 2
|
||||
}
|
||||
llmRuntime, err := llm.NewRuntime(context.Background(), selected.Result.Config, selected.Path, nil)
|
||||
reportDir, err := selectedReportDir(selected.Result.Config, input.OutputPath)
|
||||
if err != nil {
|
||||
fmt.Fprintf(stderr, "audita process: %s\n", configInputError(err))
|
||||
return 2
|
||||
}
|
||||
if selected.Result.Config.Diagnostics.Debug && strings.TrimSpace(selected.Result.Config.Diagnostics.WorkDir) == "" {
|
||||
fmt.Fprintln(stderr, "audita process: debug work directory is required")
|
||||
return 2
|
||||
}
|
||||
llmRuntime, err := llm.NewRuntime(ctx, selected.Result.Config, selected.Path, nil)
|
||||
if err != nil {
|
||||
fmt.Fprintf(stderr, "audita process: %s\n", llm.RedactError(err, selected.Result.Config))
|
||||
if llm.IsAssetError(err) {
|
||||
@@ -294,7 +301,7 @@ func runProcess(args []string, stdout, stderr io.Writer) int {
|
||||
TranscriptPath: input.TranscriptPath,
|
||||
GlossaryPath: input.GlossaryPath,
|
||||
OutputPath: input.OutputPath,
|
||||
ReportJSONPath: input.ReportPath,
|
||||
ReportDir: reportDir,
|
||||
Config: selected.Result.Config,
|
||||
ConfigResult: selected.Result,
|
||||
LLMRuntime: llmRuntime,
|
||||
@@ -304,7 +311,7 @@ func runProcess(args []string, stdout, stderr io.Writer) int {
|
||||
ConfigVersion: &version,
|
||||
}
|
||||
|
||||
normSummary, chunkSummary, runOutput, runDir, runErr := processRunner(inv, stdout)
|
||||
normSummary, chunkSummary, runOutput, runDir, runErr := processRunner(ctx, inv, stdout)
|
||||
completedAt := time.Now().UTC()
|
||||
|
||||
if runErr != nil {
|
||||
@@ -320,17 +327,8 @@ func runProcess(args []string, stdout, stderr io.Writer) int {
|
||||
errorMessage = llm.RedactError(fmt.Errorf("%s", errorMessage), inv.Config)
|
||||
report := processreport.Build(processReportInput("failed", inv, runDir, startedAt, completedAt, errorMessage, errorPhase, nil, nil, runOutput))
|
||||
|
||||
if strings.TrimSpace(inv.ReportJSONPath) != "" {
|
||||
if err := reporting.WriteProcessReport(inv.ReportJSONPath, report, llm.ConfiguredSecrets(&inv.Config)...); err != nil {
|
||||
fmt.Fprintf(stderr, "audita process: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
if runDir != nil {
|
||||
_ = runDir.WriteReport(report)
|
||||
_ = runDir.ApplyRetention(diagnostics.RetentionDecisionInput{
|
||||
RunSucceeded: false,
|
||||
})
|
||||
}
|
||||
|
||||
fmt.Fprintf(stderr, "audita process: %s\n", llm.RedactError(runErr, inv.Config))
|
||||
@@ -351,28 +349,8 @@ func runProcess(args []string, stdout, stderr io.Writer) int {
|
||||
|
||||
report := processreport.Build(processReportInput("success", inv, runDir, startedAt, completedAt, "", "", normSummary, chunkSummary, runOutput))
|
||||
|
||||
if strings.TrimSpace(inv.ReportJSONPath) != "" {
|
||||
if err := reporting.WriteProcessReport(inv.ReportJSONPath, report, llm.ConfiguredSecrets(&inv.Config)...); err != nil {
|
||||
if runDir != nil {
|
||||
_ = runDir.WriteErrorLog(fmt.Sprintf("report_write: %v", err))
|
||||
_ = runDir.ApplyRetention(diagnostics.RetentionDecisionInput{
|
||||
RunSucceeded: false,
|
||||
})
|
||||
}
|
||||
fmt.Fprintf(stderr, "audita process: %v\n", err)
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
if runDir != nil {
|
||||
_ = runDir.WriteReport(report)
|
||||
if err := runDir.ApplyRetention(diagnostics.RetentionDecisionInput{
|
||||
RunSucceeded: true,
|
||||
HasSkippedCorrections: processreport.HasSkippedCorrections(runOutput),
|
||||
}); err != nil {
|
||||
fmt.Fprintf(stderr, "audita process: failed to apply work-dir retention: %v\n", err)
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
@@ -386,7 +364,7 @@ func writeExecutionArtifacts(runDir *diagnostics.RunDirectory, output *runner.Ru
|
||||
_ = runDir.WriteJSONArtifact(diagnostics.ArtifactUtilizationSummary, processreport.BuildUtilization(output, cfg.Backends))
|
||||
}
|
||||
|
||||
func runConfig(args []string, stdout, stderr io.Writer) int {
|
||||
func runConfig(ctx context.Context, args []string, stdout, stderr io.Writer) int {
|
||||
if len(args) == 0 || isHelpCommand(args) || args[0] == "--help" || args[0] == "-h" {
|
||||
writeConfigUsage(stdout)
|
||||
return 0
|
||||
@@ -394,9 +372,9 @@ func runConfig(args []string, stdout, stderr io.Writer) int {
|
||||
|
||||
switch args[0] {
|
||||
case "validate":
|
||||
return runConfigValidate(args[1:], stdout, stderr)
|
||||
return runConfigValidate(ctx, args[1:], stdout, stderr)
|
||||
case "print-effective":
|
||||
return runConfigPrintEffective(args[1:], stdout, stderr)
|
||||
return runConfigPrintEffective(ctx, args[1:], stdout, stderr)
|
||||
default:
|
||||
fmt.Fprintf(stderr, "audita config: unknown command %q\n\n", args[0])
|
||||
writeConfigUsage(stderr)
|
||||
@@ -404,7 +382,7 @@ func runConfig(args []string, stdout, stderr io.Writer) int {
|
||||
}
|
||||
}
|
||||
|
||||
func runConfigValidate(args []string, stdout, stderr io.Writer) int {
|
||||
func runConfigValidate(ctx context.Context, args []string, stdout, stderr io.Writer) int {
|
||||
input, help, err := parseConfigInput("validate", args, stderr)
|
||||
if err != nil {
|
||||
fmt.Fprintf(stderr, "audita config validate: %s\n", configInputError(err))
|
||||
@@ -419,7 +397,7 @@ func runConfigValidate(args []string, stdout, stderr io.Writer) int {
|
||||
fmt.Fprintf(stderr, "audita config validate: %s\n", configInputError(err))
|
||||
return 2
|
||||
}
|
||||
if _, err := llm.NewRuntime(context.Background(), selected.Result.Config, selected.Path, nil); err != nil {
|
||||
if _, err := llm.NewRuntime(ctx, selected.Result.Config, selected.Path, nil); err != nil {
|
||||
fmt.Fprintf(stderr, "audita config validate: %s\n", llm.RedactError(err, selected.Result.Config))
|
||||
if llm.IsAssetError(err) {
|
||||
return 1
|
||||
@@ -430,7 +408,7 @@ func runConfigValidate(args []string, stdout, stderr io.Writer) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func runConfigPrintEffective(args []string, stdout, stderr io.Writer) int {
|
||||
func runConfigPrintEffective(ctx context.Context, args []string, stdout, stderr io.Writer) int {
|
||||
input, help, err := parseConfigInput("print-effective", args, stderr)
|
||||
if err != nil {
|
||||
fmt.Fprintf(stderr, "audita config print-effective: %s\n", configInputError(err))
|
||||
@@ -445,7 +423,7 @@ func runConfigPrintEffective(args []string, stdout, stderr io.Writer) int {
|
||||
fmt.Fprintf(stderr, "audita config print-effective: %s\n", configInputError(err))
|
||||
return 2
|
||||
}
|
||||
llmRuntime, err := llm.NewRuntime(context.Background(), selected.Result.Config, selected.Path, nil)
|
||||
llmRuntime, err := llm.NewRuntime(ctx, selected.Result.Config, selected.Path, nil)
|
||||
if err != nil {
|
||||
fmt.Fprintf(stderr, "audita config print-effective: %s\n", llm.RedactError(err, selected.Result.Config))
|
||||
if llm.IsAssetError(err) {
|
||||
@@ -575,15 +553,18 @@ func writeProcessUsage(w io.Writer) {
|
||||
fmt.Fprintln(w)
|
||||
fmt.Fprintln(w, "Flags:")
|
||||
for _, name := range []string{
|
||||
"config", "overlay", "glossary", "output", "report-json",
|
||||
"config", "overlay", "glossary", "output",
|
||||
"proposal-profile", "validation-profile", "modules", "output-schema",
|
||||
"validation-max-prompt-tokens", "target-sections", "max-section-tokens", "min-section-tokens",
|
||||
"normalize-max-segment-gap", "normalize-ellipsis-gap", "normalize-max-segment-duration", "normalize-max-segment-tokens",
|
||||
"glossary-confidence-threshold", "homophones-confidence-threshold", "spoken-word-confidence-threshold", "grammar-confidence-threshold",
|
||||
"transcript-description", "work-dir", "work-dir-retention",
|
||||
"transcript-description", "work-dir",
|
||||
} {
|
||||
fmt.Fprintf(w, " --%s\n", name)
|
||||
}
|
||||
fmt.Fprintln(w, " --reporting[=true|false] Write the three-file report bundle (default true)")
|
||||
fmt.Fprintln(w, " --report-dir DIR Report directory for stdout output only; ignored with --output or --reporting=false")
|
||||
fmt.Fprintln(w, " --debug[=true|false] Persist detailed diagnostics (default false)")
|
||||
fmt.Fprintln(w)
|
||||
fmt.Fprintln(w, "Example:")
|
||||
fmt.Fprintln(w, " audita process transcript.json --glossary glossary.yaml --output corrected.json")
|
||||
|
||||
@@ -10,15 +10,10 @@ import (
|
||||
"gitea.maximumdirect.net/eric/audita/internal/core/outputschema"
|
||||
)
|
||||
|
||||
type WorkDirRetention string
|
||||
|
||||
const (
|
||||
WorkDirRetentionAuto WorkDirRetention = "auto"
|
||||
WorkDirRetentionAlways WorkDirRetention = "always"
|
||||
WorkDirRetentionNever WorkDirRetention = "never"
|
||||
DefaultModulesCSV = "glossary,homophones,glossary,spoken_word,grammar"
|
||||
DefaultTranscriptDescriptionMaxChars = 500
|
||||
DefaultValidationMaxPromptTokens = 2048
|
||||
DefaultModulesCSV = "glossary,homophones,glossary,spoken_word,grammar"
|
||||
DefaultTranscriptDescriptionMaxChars = 500
|
||||
DefaultValidationMaxPromptTokens = 2048
|
||||
)
|
||||
|
||||
// Config is the version-2 application configuration. Invocation paths are not settings.
|
||||
@@ -34,6 +29,7 @@ type Config struct {
|
||||
Normalization Normalization `yaml:"normalization"`
|
||||
Thresholds Thresholds `yaml:"thresholds"`
|
||||
Context Context `yaml:"context"`
|
||||
Reporting Reporting `yaml:"reporting"`
|
||||
Diagnostics Diagnostics `yaml:"diagnostics"`
|
||||
}
|
||||
|
||||
@@ -80,8 +76,12 @@ type Context struct {
|
||||
Description string `yaml:"description"`
|
||||
}
|
||||
type Diagnostics struct {
|
||||
WorkDir string `yaml:"work_dir"`
|
||||
Retention WorkDirRetention `yaml:"retention"`
|
||||
Debug bool `yaml:"debug"`
|
||||
WorkDir string `yaml:"work_dir"`
|
||||
}
|
||||
type Reporting struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
StdoutDir string `yaml:"stdout_dir"`
|
||||
}
|
||||
|
||||
func defaults() Config {
|
||||
@@ -94,7 +94,8 @@ func defaults() Config {
|
||||
Chunking: Chunking{MaxSectionTokens: 8192, MinSectionTokens: 2048},
|
||||
Normalization: Normalization{MaxSegmentGap: 4, EllipsisGap: 3.5, MaxSegmentDuration: 60, MaxSegmentTokens: 2048},
|
||||
Thresholds: Thresholds{Glossary: .8, Homophones: .8, SpokenWord: .8, Grammar: .8},
|
||||
Diagnostics: Diagnostics{WorkDir: "/tmp/audita", Retention: WorkDirRetentionAuto},
|
||||
Reporting: Reporting{Enabled: true, StdoutDir: "."},
|
||||
Diagnostics: Diagnostics{WorkDir: "/tmp/audita"},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,8 +171,6 @@ func validate(c *Config, v *ck.Validation) {
|
||||
check(threshold >= 0 && threshold <= 1, ck.Keys("thresholds", name), "confidence threshold must be between zero and one")
|
||||
}
|
||||
check(len(strings.TrimSpace(c.Context.Description)) <= DefaultTranscriptDescriptionMaxChars, ck.Keys("context", "description"), fmt.Sprintf("description must be at most %d bytes after trimming", DefaultTranscriptDescriptionMaxChars))
|
||||
check(strings.TrimSpace(c.Diagnostics.WorkDir) != "", ck.Keys("diagnostics", "work_dir"), "work directory is required")
|
||||
check(c.Diagnostics.Retention == WorkDirRetentionAuto || c.Diagnostics.Retention == WorkDirRetentionAlways || c.Diagnostics.Retention == WorkDirRetentionNever, ck.Keys("diagnostics", "retention"), "retention must be auto, always, or never")
|
||||
}
|
||||
|
||||
// ParseModulesCSV is used only for an explicitly supplied --modules override.
|
||||
|
||||
@@ -56,7 +56,7 @@ func TestStrictInputAndConstraints(t *testing.T) {
|
||||
{"invalid section relation", base + "chunking:\n min_section_tokens: 10000\n"},
|
||||
{"string normalization", base + "normalization:\n max_segment_gap: 4s\n"},
|
||||
{"invalid threshold", base + "thresholds:\n glossary: 1.1\n"},
|
||||
{"invalid retention", base + "diagnostics:\n retention: sometimes\n"},
|
||||
{"removed retention", base + "diagnostics:\n retention: always\n"},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if _, err := loadText(tc.text, ""); err == nil {
|
||||
|
||||
Reference in New Issue
Block a user