Wire process command flags
This commit is contained in:
@@ -12,6 +12,19 @@ import (
|
|||||||
|
|
||||||
const processNotImplementedMessage = "process command is not implemented yet"
|
const processNotImplementedMessage = "process command is not implemented yet"
|
||||||
|
|
||||||
|
type processInvocation struct {
|
||||||
|
TranscriptPath string
|
||||||
|
GlossaryPath string
|
||||||
|
OutputPath string
|
||||||
|
ReportJSONPath string
|
||||||
|
Config config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
var processRunner = func(inv processInvocation) error {
|
||||||
|
_ = inv
|
||||||
|
return errors.New(processNotImplementedMessage)
|
||||||
|
}
|
||||||
|
|
||||||
// Run executes the Audita CLI with the provided arguments and streams.
|
// Run executes the Audita CLI with the provided arguments and streams.
|
||||||
func Run(args []string, stdout, stderr io.Writer) int {
|
func Run(args []string, stdout, stderr io.Writer) int {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
@@ -34,92 +47,29 @@ func Run(args []string, stdout, stderr io.Writer) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runProcess(args []string, stdout, stderr io.Writer) int {
|
func runProcess(args []string, stdout, stderr io.Writer) int {
|
||||||
if isHelpCommand(args) || hasHelpFlag(args) {
|
|
||||||
writeProcessUsage(stdout)
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg, err := config.LoadFromEnv()
|
cfg, err := config.LoadFromEnv()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(stderr, "audita process: invalid environment configuration: %v\n", err)
|
fmt.Fprintf(stderr, "audita process: invalid environment configuration: %v\n", err)
|
||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
|
|
||||||
fs := flag.NewFlagSet("process", flag.ContinueOnError)
|
fs, pFlags := newProcessFlagSet(cfg, stderr)
|
||||||
fs.SetOutput(stderr)
|
|
||||||
|
|
||||||
glossaryPath := fs.String("glossary", "", "Path to glossary YAML file")
|
if isHelpCommand(args) || hasHelpFlag(args) {
|
||||||
outputPath := fs.String("output", "", "Path to corrected transcript JSON output file")
|
writeProcessUsage(stdout, fs)
|
||||||
reportJSONPath := fs.String("report-json", "", "Path to machine-readable report JSON output file")
|
return 0
|
||||||
modules := fs.String("modules", strings.Join(cfg.Modules, ","), "Comma-separated module sequence override")
|
|
||||||
llmAPIKey := fs.String("llm-api-key", cfg.PrimaryLLM.APIKey, "Primary LLM API key")
|
|
||||||
validationLLMAPIKey := fs.String("validation-llm-api-key", cfg.ValidationLLM.APIKey, "Validation LLM API key")
|
|
||||||
model := fs.String("model", cfg.PrimaryLLM.Model, "Primary LLM model name")
|
|
||||||
validationModel := fs.String("validation-model", cfg.ValidationLLM.Model, "Validation LLM model name")
|
|
||||||
baseURL := fs.String("base-url", cfg.PrimaryLLM.BaseURL, "Primary OpenAI-compatible base URL")
|
|
||||||
validationBaseURL := fs.String("validation-base-url", cfg.ValidationLLM.BaseURL, "Validation OpenAI-compatible base URL")
|
|
||||||
llmTimeoutSeconds := fs.Int("llm-timeout-seconds", cfg.PrimaryLLM.TimeoutSeconds, "Primary LLM timeout in seconds")
|
|
||||||
validationTimeoutSecondsDefault := cfg.PrimaryLLM.TimeoutSeconds
|
|
||||||
if cfg.ValidationLLM.TimeoutSeconds != nil {
|
|
||||||
validationTimeoutSecondsDefault = *cfg.ValidationLLM.TimeoutSeconds
|
|
||||||
}
|
}
|
||||||
validationLLMTimeoutSeconds := fs.Int("validation-llm-timeout-seconds", validationTimeoutSecondsDefault, "Validation LLM timeout in seconds")
|
|
||||||
maxRetries := fs.Int("max-retries", cfg.PrimaryLLM.MaxRetries, "Maximum structured-output retries")
|
|
||||||
validationMaxRetriesDefault := cfg.PrimaryLLM.MaxRetries
|
|
||||||
if cfg.ValidationLLM.MaxRetries != nil {
|
|
||||||
validationMaxRetriesDefault = *cfg.ValidationLLM.MaxRetries
|
|
||||||
}
|
|
||||||
validationMaxRetries := fs.Int("validation-max-retries", validationMaxRetriesDefault, "Validation structured-output retries")
|
|
||||||
validationMaxPromptTokens := fs.Int("validation-max-prompt-tokens", cfg.ValidationMaxPromptTokens, "Validation max prompt tokens")
|
|
||||||
maxSectionTokens := fs.Int("max-section-tokens", cfg.MaxSectionTokens, "Max section tokens")
|
|
||||||
minSectionTokens := fs.Int("min-section-tokens", cfg.MinSectionTokens, "Min section tokens")
|
|
||||||
targetSectionsDefault := 0
|
|
||||||
if cfg.TargetSections != nil {
|
|
||||||
targetSectionsDefault = *cfg.TargetSections
|
|
||||||
}
|
|
||||||
targetSections := fs.Int("target-sections", targetSectionsDefault, "Target number of transcript sections")
|
|
||||||
glossaryConfidenceThreshold := fs.Float64("glossary-confidence-threshold", cfg.Thresholds.Glossary, "Glossary confidence threshold")
|
|
||||||
grammarConfidenceThreshold := fs.Float64("grammar-confidence-threshold", cfg.Thresholds.Grammar, "Grammar confidence threshold")
|
|
||||||
homophonesConfidenceThreshold := fs.Float64("homophones-confidence-threshold", cfg.Thresholds.Homophones, "Homophones confidence threshold")
|
|
||||||
spokenWordConfidenceThreshold := fs.Float64("spoken-word-confidence-threshold", cfg.Thresholds.SpokenWord, "Spoken-word confidence threshold")
|
|
||||||
normalizeMaxSegmentGap := fs.Float64("normalize-max-segment-gap", cfg.Normalization.MaxSegmentGap, "Maximum same-speaker merge gap")
|
|
||||||
normalizeEllipsisGap := fs.Float64("normalize-ellipsis-gap", cfg.Normalization.EllipsisGap, "Gap threshold for ellipsis insertion")
|
|
||||||
normalizeMaxSegmentDuration := fs.Float64("normalize-max-segment-duration", cfg.Normalization.MaxSegmentDuration, "Maximum merged segment duration")
|
|
||||||
normalizeMaxSegmentTokens := fs.Int("normalize-max-segment-tokens", cfg.Normalization.MaxSegmentTokens, "Maximum merged segment token estimate")
|
|
||||||
workDir := fs.String("work-dir", cfg.WorkDir, "Per-run work directory")
|
|
||||||
workDirRetention := fs.String("work-dir-retention", string(cfg.WorkDirRetention), "Work-dir retention policy: auto|always|never")
|
|
||||||
|
|
||||||
_ = glossaryPath
|
parseArgs := args
|
||||||
_ = outputPath
|
transcriptFromFront := ""
|
||||||
_ = reportJSONPath
|
if len(args) > 0 && !strings.HasPrefix(args[0], "-") {
|
||||||
_ = llmAPIKey
|
transcriptFromFront = args[0]
|
||||||
_ = validationLLMAPIKey
|
parseArgs = args[1:]
|
||||||
_ = model
|
}
|
||||||
_ = validationModel
|
|
||||||
_ = baseURL
|
|
||||||
_ = validationBaseURL
|
|
||||||
_ = llmTimeoutSeconds
|
|
||||||
_ = validationLLMTimeoutSeconds
|
|
||||||
_ = maxRetries
|
|
||||||
_ = validationMaxRetries
|
|
||||||
_ = validationMaxPromptTokens
|
|
||||||
_ = maxSectionTokens
|
|
||||||
_ = minSectionTokens
|
|
||||||
_ = targetSections
|
|
||||||
_ = glossaryConfidenceThreshold
|
|
||||||
_ = grammarConfidenceThreshold
|
|
||||||
_ = homophonesConfidenceThreshold
|
|
||||||
_ = spokenWordConfidenceThreshold
|
|
||||||
_ = normalizeMaxSegmentGap
|
|
||||||
_ = normalizeEllipsisGap
|
|
||||||
_ = normalizeMaxSegmentDuration
|
|
||||||
_ = normalizeMaxSegmentTokens
|
|
||||||
_ = workDir
|
|
||||||
_ = workDirRetention
|
|
||||||
|
|
||||||
if err := fs.Parse(args); err != nil {
|
if err := fs.Parse(parseArgs); err != nil {
|
||||||
if errors.Is(err, flag.ErrHelp) {
|
if errors.Is(err, flag.ErrHelp) {
|
||||||
writeProcessUsage(stdout)
|
writeProcessUsage(stdout, fs)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return 2
|
return 2
|
||||||
@@ -129,55 +79,57 @@ func runProcess(args []string, stdout, stderr io.Writer) int {
|
|||||||
fs.Visit(func(f *flag.Flag) {
|
fs.Visit(func(f *flag.Flag) {
|
||||||
switch f.Name {
|
switch f.Name {
|
||||||
case "modules":
|
case "modules":
|
||||||
overrides.ModulesCSV = modules
|
overrides.ModulesCSV = pFlags.modules
|
||||||
case "llm-api-key":
|
case "llm-api-key":
|
||||||
overrides.PrimaryLLMAPIKey = llmAPIKey
|
overrides.PrimaryLLMAPIKey = pFlags.llmAPIKey
|
||||||
case "validation-llm-api-key":
|
case "validation-llm-api-key":
|
||||||
overrides.ValidationLLMAPIKey = validationLLMAPIKey
|
overrides.ValidationLLMAPIKey = pFlags.validationLLMAPIKey
|
||||||
case "model":
|
case "model":
|
||||||
overrides.PrimaryModel = model
|
overrides.PrimaryModel = pFlags.model
|
||||||
case "validation-model":
|
case "validation-model":
|
||||||
overrides.ValidationModel = validationModel
|
overrides.ValidationModel = pFlags.validationModel
|
||||||
case "base-url":
|
case "base-url":
|
||||||
overrides.PrimaryBaseURL = baseURL
|
overrides.PrimaryBaseURL = pFlags.baseURL
|
||||||
case "validation-base-url":
|
case "validation-base-url":
|
||||||
overrides.ValidationBaseURL = validationBaseURL
|
overrides.ValidationBaseURL = pFlags.validationBaseURL
|
||||||
case "llm-timeout-seconds":
|
case "llm-timeout-seconds":
|
||||||
overrides.PrimaryLLMTimeoutSeconds = llmTimeoutSeconds
|
overrides.PrimaryLLMTimeoutSeconds = pFlags.llmTimeoutSeconds
|
||||||
case "validation-llm-timeout-seconds":
|
case "validation-llm-timeout-seconds":
|
||||||
overrides.ValidationLLMTimeoutSeconds = validationLLMTimeoutSeconds
|
overrides.ValidationLLMTimeoutSeconds = pFlags.validationLLMTimeoutSeconds
|
||||||
case "max-retries":
|
case "max-retries":
|
||||||
overrides.MaxRetries = maxRetries
|
overrides.MaxRetries = pFlags.maxRetries
|
||||||
case "validation-max-retries":
|
case "validation-max-retries":
|
||||||
overrides.ValidationMaxRetries = validationMaxRetries
|
overrides.ValidationMaxRetries = pFlags.validationMaxRetries
|
||||||
|
case "validation-llm-concurrency":
|
||||||
|
overrides.ValidationLLMConcurrency = pFlags.validationLLMConcurrency
|
||||||
case "validation-max-prompt-tokens":
|
case "validation-max-prompt-tokens":
|
||||||
overrides.ValidationMaxPromptTokens = validationMaxPromptTokens
|
overrides.ValidationMaxPromptTokens = pFlags.validationMaxPromptTokens
|
||||||
case "max-section-tokens":
|
case "max-section-tokens":
|
||||||
overrides.MaxSectionTokens = maxSectionTokens
|
overrides.MaxSectionTokens = pFlags.maxSectionTokens
|
||||||
case "min-section-tokens":
|
case "min-section-tokens":
|
||||||
overrides.MinSectionTokens = minSectionTokens
|
overrides.MinSectionTokens = pFlags.minSectionTokens
|
||||||
case "target-sections":
|
case "target-sections":
|
||||||
overrides.TargetSections = targetSections
|
overrides.TargetSections = pFlags.targetSections
|
||||||
case "glossary-confidence-threshold":
|
case "glossary-confidence-threshold":
|
||||||
overrides.GlossaryConfidenceThreshold = glossaryConfidenceThreshold
|
overrides.GlossaryConfidenceThreshold = pFlags.glossaryConfidenceThreshold
|
||||||
case "grammar-confidence-threshold":
|
case "grammar-confidence-threshold":
|
||||||
overrides.GrammarConfidenceThreshold = grammarConfidenceThreshold
|
overrides.GrammarConfidenceThreshold = pFlags.grammarConfidenceThreshold
|
||||||
case "homophones-confidence-threshold":
|
case "homophones-confidence-threshold":
|
||||||
overrides.HomophonesConfidenceThreshold = homophonesConfidenceThreshold
|
overrides.HomophonesConfidenceThreshold = pFlags.homophonesConfidenceThreshold
|
||||||
case "spoken-word-confidence-threshold":
|
case "spoken-word-confidence-threshold":
|
||||||
overrides.SpokenWordConfidenceThreshold = spokenWordConfidenceThreshold
|
overrides.SpokenWordConfidenceThreshold = pFlags.spokenWordConfidenceThreshold
|
||||||
case "normalize-max-segment-gap":
|
case "normalize-max-segment-gap":
|
||||||
overrides.NormalizeMaxSegmentGap = normalizeMaxSegmentGap
|
overrides.NormalizeMaxSegmentGap = pFlags.normalizeMaxSegmentGap
|
||||||
case "normalize-ellipsis-gap":
|
case "normalize-ellipsis-gap":
|
||||||
overrides.NormalizeEllipsisGap = normalizeEllipsisGap
|
overrides.NormalizeEllipsisGap = pFlags.normalizeEllipsisGap
|
||||||
case "normalize-max-segment-duration":
|
case "normalize-max-segment-duration":
|
||||||
overrides.NormalizeMaxSegmentDuration = normalizeMaxSegmentDuration
|
overrides.NormalizeMaxSegmentDuration = pFlags.normalizeMaxSegmentDuration
|
||||||
case "normalize-max-segment-tokens":
|
case "normalize-max-segment-tokens":
|
||||||
overrides.NormalizeMaxSegmentTokens = normalizeMaxSegmentTokens
|
overrides.NormalizeMaxSegmentTokens = pFlags.normalizeMaxSegmentTokens
|
||||||
case "work-dir":
|
case "work-dir":
|
||||||
overrides.WorkDir = workDir
|
overrides.WorkDir = pFlags.workDir
|
||||||
case "work-dir-retention":
|
case "work-dir-retention":
|
||||||
overrides.WorkDirRetention = workDirRetention
|
overrides.WorkDirRetention = pFlags.workDirRetention
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -187,15 +139,126 @@ func runProcess(args []string, stdout, stderr io.Writer) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
remaining := fs.Args()
|
remaining := fs.Args()
|
||||||
if len(remaining) != 1 {
|
positional := make([]string, 0, len(remaining)+1)
|
||||||
|
if transcriptFromFront != "" {
|
||||||
|
positional = append(positional, transcriptFromFront)
|
||||||
|
}
|
||||||
|
positional = append(positional, remaining...)
|
||||||
|
|
||||||
|
if len(positional) != 1 {
|
||||||
fmt.Fprintln(stderr, "audita process: expected exactly 1 transcript JSON path argument")
|
fmt.Fprintln(stderr, "audita process: expected exactly 1 transcript JSON path argument")
|
||||||
fmt.Fprintln(stderr)
|
return 2
|
||||||
writeProcessUsage(stderr)
|
}
|
||||||
|
if strings.TrimSpace(*pFlags.glossaryPath) == "" {
|
||||||
|
fmt.Fprintln(stderr, "audita process: --glossary is required")
|
||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(stderr, "audita process: %s\n", processNotImplementedMessage)
|
inv := processInvocation{
|
||||||
|
TranscriptPath: positional[0],
|
||||||
|
GlossaryPath: *pFlags.glossaryPath,
|
||||||
|
OutputPath: *pFlags.outputPath,
|
||||||
|
ReportJSONPath: *pFlags.reportJSONPath,
|
||||||
|
Config: cfg,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := processRunner(inv); err != nil {
|
||||||
|
fmt.Fprintf(stderr, "audita process: %v\n", err)
|
||||||
return 1
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type processFlags struct {
|
||||||
|
glossaryPath *string
|
||||||
|
outputPath *string
|
||||||
|
reportJSONPath *string
|
||||||
|
modules *string
|
||||||
|
llmAPIKey *string
|
||||||
|
validationLLMAPIKey *string
|
||||||
|
model *string
|
||||||
|
validationModel *string
|
||||||
|
baseURL *string
|
||||||
|
validationBaseURL *string
|
||||||
|
llmTimeoutSeconds *int
|
||||||
|
validationLLMTimeoutSeconds *int
|
||||||
|
validationMaxPromptTokens *int
|
||||||
|
targetSections *int
|
||||||
|
maxRetries *int
|
||||||
|
validationMaxRetries *int
|
||||||
|
validationLLMConcurrency *int
|
||||||
|
maxSectionTokens *int
|
||||||
|
minSectionTokens *int
|
||||||
|
glossaryConfidenceThreshold *float64
|
||||||
|
grammarConfidenceThreshold *float64
|
||||||
|
homophonesConfidenceThreshold *float64
|
||||||
|
spokenWordConfidenceThreshold *float64
|
||||||
|
normalizeMaxSegmentGap *float64
|
||||||
|
normalizeEllipsisGap *float64
|
||||||
|
normalizeMaxSegmentDuration *float64
|
||||||
|
normalizeMaxSegmentTokens *int
|
||||||
|
workDir *string
|
||||||
|
workDirRetention *string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newProcessFlagSet(cfg config.Config, stderr io.Writer) (*flag.FlagSet, processFlags) {
|
||||||
|
fs := flag.NewFlagSet("process", flag.ContinueOnError)
|
||||||
|
fs.SetOutput(stderr)
|
||||||
|
|
||||||
|
validationTimeoutSecondsDefault := cfg.PrimaryLLM.TimeoutSeconds
|
||||||
|
if cfg.ValidationLLM.TimeoutSeconds != nil {
|
||||||
|
validationTimeoutSecondsDefault = *cfg.ValidationLLM.TimeoutSeconds
|
||||||
|
}
|
||||||
|
|
||||||
|
validationMaxRetriesDefault := cfg.PrimaryLLM.MaxRetries
|
||||||
|
if cfg.ValidationLLM.MaxRetries != nil {
|
||||||
|
validationMaxRetriesDefault = *cfg.ValidationLLM.MaxRetries
|
||||||
|
}
|
||||||
|
|
||||||
|
validationLLMConcurrencyDefault := cfg.PrimaryLLM.Concurrency
|
||||||
|
if cfg.ValidationLLM.Concurrency != nil {
|
||||||
|
validationLLMConcurrencyDefault = *cfg.ValidationLLM.Concurrency
|
||||||
|
}
|
||||||
|
|
||||||
|
targetSectionsDefault := 0
|
||||||
|
if cfg.TargetSections != nil {
|
||||||
|
targetSectionsDefault = *cfg.TargetSections
|
||||||
|
}
|
||||||
|
|
||||||
|
pFlags := processFlags{
|
||||||
|
glossaryPath: fs.String("glossary", "", "Path to glossary YAML file"),
|
||||||
|
outputPath: fs.String("output", "", "Path to corrected transcript JSON output file"),
|
||||||
|
reportJSONPath: fs.String("report-json", "", "Path to machine-readable report JSON output file"),
|
||||||
|
modules: fs.String("modules", strings.Join(cfg.Modules, ","), "Comma-separated module sequence override"),
|
||||||
|
llmAPIKey: fs.String("llm-api-key", cfg.PrimaryLLM.APIKey, "Primary LLM API key"),
|
||||||
|
validationLLMAPIKey: fs.String("validation-llm-api-key", cfg.ValidationLLM.APIKey, "Validation LLM API key"),
|
||||||
|
model: fs.String("model", cfg.PrimaryLLM.Model, "Primary LLM model name"),
|
||||||
|
validationModel: fs.String("validation-model", cfg.ValidationLLM.Model, "Validation LLM model name"),
|
||||||
|
baseURL: fs.String("base-url", cfg.PrimaryLLM.BaseURL, "Primary OpenAI-compatible base URL"),
|
||||||
|
validationBaseURL: fs.String("validation-base-url", cfg.ValidationLLM.BaseURL, "Validation OpenAI-compatible base URL"),
|
||||||
|
llmTimeoutSeconds: fs.Int("llm-timeout-seconds", cfg.PrimaryLLM.TimeoutSeconds, "Primary LLM timeout in seconds"),
|
||||||
|
validationLLMTimeoutSeconds: fs.Int("validation-llm-timeout-seconds", validationTimeoutSecondsDefault, "Validation LLM timeout in seconds"),
|
||||||
|
validationMaxPromptTokens: fs.Int("validation-max-prompt-tokens", cfg.ValidationMaxPromptTokens, "Validation max prompt tokens"),
|
||||||
|
targetSections: fs.Int("target-sections", targetSectionsDefault, "Target number of transcript sections"),
|
||||||
|
maxRetries: fs.Int("max-retries", cfg.PrimaryLLM.MaxRetries, "Maximum structured-output retries"),
|
||||||
|
validationMaxRetries: fs.Int("validation-max-retries", validationMaxRetriesDefault, "Validation structured-output retries"),
|
||||||
|
validationLLMConcurrency: fs.Int("validation-llm-concurrency", validationLLMConcurrencyDefault, "Validation LLM concurrency"),
|
||||||
|
maxSectionTokens: fs.Int("max-section-tokens", cfg.MaxSectionTokens, "Maximum section tokens"),
|
||||||
|
minSectionTokens: fs.Int("min-section-tokens", cfg.MinSectionTokens, "Minimum section tokens"),
|
||||||
|
glossaryConfidenceThreshold: fs.Float64("glossary-confidence-threshold", cfg.Thresholds.Glossary, "Glossary confidence threshold"),
|
||||||
|
grammarConfidenceThreshold: fs.Float64("grammar-confidence-threshold", cfg.Thresholds.Grammar, "Grammar confidence threshold"),
|
||||||
|
homophonesConfidenceThreshold: fs.Float64("homophones-confidence-threshold", cfg.Thresholds.Homophones, "Homophones confidence threshold"),
|
||||||
|
spokenWordConfidenceThreshold: fs.Float64("spoken-word-confidence-threshold", cfg.Thresholds.SpokenWord, "Spoken-word confidence threshold"),
|
||||||
|
normalizeMaxSegmentGap: fs.Float64("normalize-max-segment-gap", cfg.Normalization.MaxSegmentGap, "Maximum same-speaker merge gap"),
|
||||||
|
normalizeEllipsisGap: fs.Float64("normalize-ellipsis-gap", cfg.Normalization.EllipsisGap, "Gap threshold for ellipsis insertion"),
|
||||||
|
normalizeMaxSegmentDuration: fs.Float64("normalize-max-segment-duration", cfg.Normalization.MaxSegmentDuration, "Maximum merged segment duration"),
|
||||||
|
normalizeMaxSegmentTokens: fs.Int("normalize-max-segment-tokens", cfg.Normalization.MaxSegmentTokens, "Maximum merged segment token estimate"),
|
||||||
|
workDir: fs.String("work-dir", cfg.WorkDir, "Per-run work directory"),
|
||||||
|
workDirRetention: fs.String("work-dir-retention", string(cfg.WorkDirRetention), "Work-dir retention policy: auto|always|never"),
|
||||||
|
}
|
||||||
|
|
||||||
|
return fs, pFlags
|
||||||
}
|
}
|
||||||
|
|
||||||
func isHelpCommand(args []string) bool {
|
func isHelpCommand(args []string) bool {
|
||||||
@@ -239,22 +302,16 @@ func writeRootUsage(w io.Writer) {
|
|||||||
fmt.Fprintln(w, " audita process transcript.json --glossary glossary.yaml --output corrected.json")
|
fmt.Fprintln(w, " audita process transcript.json --glossary glossary.yaml --output corrected.json")
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeProcessUsage(w io.Writer) {
|
func writeProcessUsage(w io.Writer, fs *flag.FlagSet) {
|
||||||
fmt.Fprintln(w, "Process a transcript JSON file.")
|
fmt.Fprintln(w, "Process a transcript JSON file.")
|
||||||
fmt.Fprintln(w)
|
fmt.Fprintln(w)
|
||||||
fmt.Fprintln(w, "Usage:")
|
fmt.Fprintln(w, "Usage:")
|
||||||
fmt.Fprintln(w, " audita process <transcript.json> [flags]")
|
fmt.Fprintln(w, " audita process <transcript.json> [flags]")
|
||||||
fmt.Fprintln(w)
|
fmt.Fprintln(w)
|
||||||
fmt.Fprintln(w, "Flags:")
|
fmt.Fprintln(w, "Flags:")
|
||||||
fmt.Fprintln(w, " --glossary <path> Path to glossary YAML file")
|
fs.VisitAll(func(f *flag.Flag) {
|
||||||
fmt.Fprintln(w, " --output <path> Path to corrected transcript JSON output file")
|
fmt.Fprintf(w, " --%s\n", f.Name)
|
||||||
fmt.Fprintln(w, " --report-json <path> Path to machine-readable report JSON output file")
|
})
|
||||||
fmt.Fprintln(w, " --modules <list> Comma-separated module sequence override")
|
|
||||||
fmt.Fprintln(w, " --llm-api-key <key> Primary LLM API key")
|
|
||||||
fmt.Fprintln(w, " --model <name> Primary LLM model")
|
|
||||||
fmt.Fprintln(w, " --base-url <url> Primary OpenAI-compatible API base URL")
|
|
||||||
fmt.Fprintln(w, " --work-dir <path> Per-run work directory")
|
|
||||||
fmt.Fprintln(w, " --work-dir-retention <mode> auto|always|never")
|
|
||||||
fmt.Fprintln(w)
|
fmt.Fprintln(w)
|
||||||
fmt.Fprintln(w, "Example:")
|
fmt.Fprintln(w, "Example:")
|
||||||
fmt.Fprintln(w, " audita process transcript.json --glossary glossary.yaml --output corrected.json")
|
fmt.Fprintln(w, " audita process transcript.json --glossary glossary.yaml --output corrected.json")
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package cli
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@@ -25,7 +26,7 @@ func TestRunRootHelp(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRunProcessHelp(t *testing.T) {
|
func TestRunProcessHelpListsExpectedFlags(t *testing.T) {
|
||||||
var stdout bytes.Buffer
|
var stdout bytes.Buffer
|
||||||
var stderr bytes.Buffer
|
var stderr bytes.Buffer
|
||||||
|
|
||||||
@@ -33,12 +34,43 @@ func TestRunProcessHelp(t *testing.T) {
|
|||||||
if exitCode != 0 {
|
if exitCode != 0 {
|
||||||
t.Fatalf("expected exit code 0, got %d", exitCode)
|
t.Fatalf("expected exit code 0, got %d", exitCode)
|
||||||
}
|
}
|
||||||
if !strings.Contains(stdout.String(), "audita process <transcript.json> [flags]") {
|
|
||||||
t.Fatalf("expected process usage in stdout, got %q", stdout.String())
|
for _, expectedFlag := range []string{
|
||||||
|
"--glossary",
|
||||||
|
"--output",
|
||||||
|
"--report-json",
|
||||||
|
"--modules",
|
||||||
|
"--llm-api-key",
|
||||||
|
"--validation-llm-api-key",
|
||||||
|
"--model",
|
||||||
|
"--validation-model",
|
||||||
|
"--base-url",
|
||||||
|
"--validation-base-url",
|
||||||
|
"--llm-timeout-seconds",
|
||||||
|
"--validation-llm-timeout-seconds",
|
||||||
|
"--validation-max-prompt-tokens",
|
||||||
|
"--target-sections",
|
||||||
|
"--max-retries",
|
||||||
|
"--validation-max-retries",
|
||||||
|
"--validation-llm-concurrency",
|
||||||
|
"--max-section-tokens",
|
||||||
|
"--min-section-tokens",
|
||||||
|
"--glossary-confidence-threshold",
|
||||||
|
"--grammar-confidence-threshold",
|
||||||
|
"--homophones-confidence-threshold",
|
||||||
|
"--spoken-word-confidence-threshold",
|
||||||
|
"--normalize-max-segment-gap",
|
||||||
|
"--normalize-ellipsis-gap",
|
||||||
|
"--normalize-max-segment-duration",
|
||||||
|
"--normalize-max-segment-tokens",
|
||||||
|
"--work-dir",
|
||||||
|
"--work-dir-retention",
|
||||||
|
} {
|
||||||
|
if !strings.Contains(stdout.String(), expectedFlag) {
|
||||||
|
t.Fatalf("expected process help to include %q, got %q", expectedFlag, stdout.String())
|
||||||
}
|
}
|
||||||
if !strings.Contains(stdout.String(), "--glossary") {
|
|
||||||
t.Fatalf("expected glossary flag in process help, got %q", stdout.String())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if stderr.Len() != 0 {
|
if stderr.Len() != 0 {
|
||||||
t.Fatalf("expected empty stderr, got %q", stderr.String())
|
t.Fatalf("expected empty stderr, got %q", stderr.String())
|
||||||
}
|
}
|
||||||
@@ -60,13 +92,61 @@ func TestRunUnknownCommand(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRunProcessNotImplemented(t *testing.T) {
|
func TestRunProcessMissingTranscriptPath(t *testing.T) {
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
|
||||||
|
exitCode := Run([]string{"process", "--glossary", "glossary.yaml"}, &stdout, &stderr)
|
||||||
|
if exitCode == 0 {
|
||||||
|
t.Fatalf("expected nonzero exit code")
|
||||||
|
}
|
||||||
|
if stdout.Len() != 0 {
|
||||||
|
t.Fatalf("expected empty stdout, got %q", stdout.String())
|
||||||
|
}
|
||||||
|
if !strings.Contains(stderr.String(), "expected exactly 1 transcript JSON path argument") {
|
||||||
|
t.Fatalf("expected missing transcript error, got %q", stderr.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunProcessMissingGlossary(t *testing.T) {
|
||||||
var stdout bytes.Buffer
|
var stdout bytes.Buffer
|
||||||
var stderr bytes.Buffer
|
var stderr bytes.Buffer
|
||||||
|
|
||||||
exitCode := Run([]string{"process", "transcript.json"}, &stdout, &stderr)
|
exitCode := Run([]string{"process", "transcript.json"}, &stdout, &stderr)
|
||||||
if exitCode == 0 {
|
if exitCode == 0 {
|
||||||
t.Fatalf("expected nonzero exit code for not-implemented process")
|
t.Fatalf("expected nonzero exit code")
|
||||||
|
}
|
||||||
|
if stdout.Len() != 0 {
|
||||||
|
t.Fatalf("expected empty stdout, got %q", stdout.String())
|
||||||
|
}
|
||||||
|
if !strings.Contains(stderr.String(), "--glossary is required") {
|
||||||
|
t.Fatalf("expected missing glossary error, got %q", stderr.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunProcessInvalidCLIConfig(t *testing.T) {
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
|
||||||
|
exitCode := Run([]string{"process", "transcript.json", "--glossary", "glossary.yaml", "--work-dir-retention", "invalid"}, &stdout, &stderr)
|
||||||
|
if exitCode != 2 {
|
||||||
|
t.Fatalf("expected exit code 2, got %d", exitCode)
|
||||||
|
}
|
||||||
|
if stdout.Len() != 0 {
|
||||||
|
t.Fatalf("expected empty stdout, got %q", stdout.String())
|
||||||
|
}
|
||||||
|
if !strings.Contains(stderr.String(), "invalid CLI configuration") {
|
||||||
|
t.Fatalf("expected invalid config error, got %q", stderr.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunProcessNotImplemented(t *testing.T) {
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
|
||||||
|
exitCode := Run([]string{"process", "transcript.json", "--glossary", "glossary.yaml"}, &stdout, &stderr)
|
||||||
|
if exitCode != 1 {
|
||||||
|
t.Fatalf("expected exit code 1 for not-implemented process, got %d", exitCode)
|
||||||
}
|
}
|
||||||
if stdout.Len() != 0 {
|
if stdout.Len() != 0 {
|
||||||
t.Fatalf("expected empty stdout, got %q", stdout.String())
|
t.Fatalf("expected empty stdout, got %q", stdout.String())
|
||||||
@@ -75,3 +155,45 @@ func TestRunProcessNotImplemented(t *testing.T) {
|
|||||||
t.Fatalf("expected not-implemented message in stderr, got %q", stderr.String())
|
t.Fatalf("expected not-implemented message in stderr, got %q", stderr.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRunProcessCLIOverridesEnvironment(t *testing.T) {
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
|
||||||
|
t.Setenv("AUDITA_MODEL", "env-model")
|
||||||
|
t.Setenv("AUDITA_VALIDATION_LLM_CONCURRENCY", "2")
|
||||||
|
|
||||||
|
var captured processInvocation
|
||||||
|
originalRunner := processRunner
|
||||||
|
processRunner = func(inv processInvocation) error {
|
||||||
|
captured = inv
|
||||||
|
return errors.New(processNotImplementedMessage)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
processRunner = originalRunner
|
||||||
|
})
|
||||||
|
|
||||||
|
exitCode := Run([]string{
|
||||||
|
"process",
|
||||||
|
"transcript.json",
|
||||||
|
"--glossary",
|
||||||
|
"glossary.yaml",
|
||||||
|
"--model",
|
||||||
|
"cli-model",
|
||||||
|
"--validation-llm-concurrency",
|
||||||
|
"5",
|
||||||
|
}, &stdout, &stderr)
|
||||||
|
|
||||||
|
if exitCode != 1 {
|
||||||
|
t.Fatalf("expected exit code 1 for not implemented, got %d", exitCode)
|
||||||
|
}
|
||||||
|
if captured.Config.PrimaryLLM.Model != "cli-model" {
|
||||||
|
t.Fatalf("expected CLI model override, got %q", captured.Config.PrimaryLLM.Model)
|
||||||
|
}
|
||||||
|
if captured.Config.ValidationLLM.Concurrency == nil || *captured.Config.ValidationLLM.Concurrency != 5 {
|
||||||
|
t.Fatalf("expected CLI validation concurrency override, got %#v", captured.Config.ValidationLLM.Concurrency)
|
||||||
|
}
|
||||||
|
if captured.GlossaryPath != "glossary.yaml" {
|
||||||
|
t.Fatalf("unexpected glossary path: %q", captured.GlossaryPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ const (
|
|||||||
DefaultPrimaryBaseURL = "https://openrouter.ai/api/v1"
|
DefaultPrimaryBaseURL = "https://openrouter.ai/api/v1"
|
||||||
DefaultPrimaryLLMTimeoutSeconds = 600
|
DefaultPrimaryLLMTimeoutSeconds = 600
|
||||||
DefaultMaxRetries = 3
|
DefaultMaxRetries = 3
|
||||||
|
DefaultLLMConcurrency = 1
|
||||||
DefaultValidationMaxPromptTokens = 2048
|
DefaultValidationMaxPromptTokens = 2048
|
||||||
DefaultMaxSectionTokens = 8192
|
DefaultMaxSectionTokens = 8192
|
||||||
DefaultMinSectionTokens = 2048
|
DefaultMinSectionTokens = 2048
|
||||||
@@ -51,6 +52,7 @@ type LLMConfig struct {
|
|||||||
BaseURL string
|
BaseURL string
|
||||||
TimeoutSeconds int
|
TimeoutSeconds int
|
||||||
MaxRetries int
|
MaxRetries int
|
||||||
|
Concurrency int
|
||||||
}
|
}
|
||||||
|
|
||||||
type ValidationLLMConfig struct {
|
type ValidationLLMConfig struct {
|
||||||
@@ -59,6 +61,7 @@ type ValidationLLMConfig struct {
|
|||||||
BaseURL string
|
BaseURL string
|
||||||
TimeoutSeconds *int
|
TimeoutSeconds *int
|
||||||
MaxRetries *int
|
MaxRetries *int
|
||||||
|
Concurrency *int
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConfidenceThresholds struct {
|
type ConfidenceThresholds struct {
|
||||||
@@ -85,6 +88,7 @@ func Default() Config {
|
|||||||
BaseURL: DefaultPrimaryBaseURL,
|
BaseURL: DefaultPrimaryBaseURL,
|
||||||
TimeoutSeconds: DefaultPrimaryLLMTimeoutSeconds,
|
TimeoutSeconds: DefaultPrimaryLLMTimeoutSeconds,
|
||||||
MaxRetries: DefaultMaxRetries,
|
MaxRetries: DefaultMaxRetries,
|
||||||
|
Concurrency: DefaultLLMConcurrency,
|
||||||
},
|
},
|
||||||
ValidationLLM: ValidationLLMConfig{},
|
ValidationLLM: ValidationLLMConfig{},
|
||||||
ValidationMaxPromptTokens: DefaultValidationMaxPromptTokens,
|
ValidationMaxPromptTokens: DefaultValidationMaxPromptTokens,
|
||||||
@@ -142,6 +146,9 @@ func (c Config) EffectiveValidationLLMConfig() LLMConfig {
|
|||||||
if c.ValidationLLM.MaxRetries != nil {
|
if c.ValidationLLM.MaxRetries != nil {
|
||||||
effective.MaxRetries = *c.ValidationLLM.MaxRetries
|
effective.MaxRetries = *c.ValidationLLM.MaxRetries
|
||||||
}
|
}
|
||||||
|
if c.ValidationLLM.Concurrency != nil {
|
||||||
|
effective.Concurrency = *c.ValidationLLM.Concurrency
|
||||||
|
}
|
||||||
|
|
||||||
return effective
|
return effective
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,12 +24,18 @@ func TestDefaultConfigValues(t *testing.T) {
|
|||||||
if cfg.PrimaryLLM.MaxRetries != DefaultMaxRetries {
|
if cfg.PrimaryLLM.MaxRetries != DefaultMaxRetries {
|
||||||
t.Fatalf("unexpected default max retries: %d", cfg.PrimaryLLM.MaxRetries)
|
t.Fatalf("unexpected default max retries: %d", cfg.PrimaryLLM.MaxRetries)
|
||||||
}
|
}
|
||||||
|
if cfg.PrimaryLLM.Concurrency != DefaultLLMConcurrency {
|
||||||
|
t.Fatalf("unexpected default llm concurrency: %d", cfg.PrimaryLLM.Concurrency)
|
||||||
|
}
|
||||||
if cfg.ValidationLLM.TimeoutSeconds != nil {
|
if cfg.ValidationLLM.TimeoutSeconds != nil {
|
||||||
t.Fatalf("expected validation timeout to be unset by default")
|
t.Fatalf("expected validation timeout to be unset by default")
|
||||||
}
|
}
|
||||||
if cfg.ValidationLLM.MaxRetries != nil {
|
if cfg.ValidationLLM.MaxRetries != nil {
|
||||||
t.Fatalf("expected validation max retries to be unset by default")
|
t.Fatalf("expected validation max retries to be unset by default")
|
||||||
}
|
}
|
||||||
|
if cfg.ValidationLLM.Concurrency != nil {
|
||||||
|
t.Fatalf("expected validation llm concurrency to be unset by default")
|
||||||
|
}
|
||||||
if cfg.TargetSections != nil {
|
if cfg.TargetSections != nil {
|
||||||
t.Fatalf("expected target sections to be unset by default")
|
t.Fatalf("expected target sections to be unset by default")
|
||||||
}
|
}
|
||||||
@@ -50,6 +56,8 @@ func TestLoadFromEnvOverridesAndFallback(t *testing.T) {
|
|||||||
"AUDITA_BASE_URL": "https://api.openai.com/v1",
|
"AUDITA_BASE_URL": "https://api.openai.com/v1",
|
||||||
"AUDITA_LLM_TIMEOUT_SECONDS": "120",
|
"AUDITA_LLM_TIMEOUT_SECONDS": "120",
|
||||||
"AUDITA_MAX_RETRIES": "7",
|
"AUDITA_MAX_RETRIES": "7",
|
||||||
|
"AUDITA_LLM_CONCURRENCY": "6",
|
||||||
|
"AUDITA_VALIDATION_LLM_CONCURRENCY": "2",
|
||||||
"AUDITA_VALIDATION_MAX_PROMPT_TOKENS": "4096",
|
"AUDITA_VALIDATION_MAX_PROMPT_TOKENS": "4096",
|
||||||
"AUDITA_MAX_SECTION_TOKENS": "9000",
|
"AUDITA_MAX_SECTION_TOKENS": "9000",
|
||||||
"AUDITA_MIN_SECTION_TOKENS": "3000",
|
"AUDITA_MIN_SECTION_TOKENS": "3000",
|
||||||
@@ -84,6 +92,12 @@ func TestLoadFromEnvOverridesAndFallback(t *testing.T) {
|
|||||||
if cfg.TargetSections == nil || *cfg.TargetSections != 5 {
|
if cfg.TargetSections == nil || *cfg.TargetSections != 5 {
|
||||||
t.Fatalf("unexpected target sections: %#v", cfg.TargetSections)
|
t.Fatalf("unexpected target sections: %#v", cfg.TargetSections)
|
||||||
}
|
}
|
||||||
|
if cfg.PrimaryLLM.Concurrency != 6 {
|
||||||
|
t.Fatalf("unexpected primary llm concurrency: %d", cfg.PrimaryLLM.Concurrency)
|
||||||
|
}
|
||||||
|
if cfg.ValidationLLM.Concurrency == nil || *cfg.ValidationLLM.Concurrency != 2 {
|
||||||
|
t.Fatalf("unexpected validation llm concurrency: %#v", cfg.ValidationLLM.Concurrency)
|
||||||
|
}
|
||||||
if cfg.WorkDirRetention != WorkDirRetentionAlways {
|
if cfg.WorkDirRetention != WorkDirRetentionAlways {
|
||||||
t.Fatalf("unexpected work dir retention: %q", cfg.WorkDirRetention)
|
t.Fatalf("unexpected work dir retention: %q", cfg.WorkDirRetention)
|
||||||
}
|
}
|
||||||
@@ -137,6 +151,7 @@ func TestApplyCLIOverridesPrecedence(t *testing.T) {
|
|||||||
func TestValidationFailures(t *testing.T) {
|
func TestValidationFailures(t *testing.T) {
|
||||||
cfg := Default()
|
cfg := Default()
|
||||||
cfg.PrimaryLLM.TimeoutSeconds = -1
|
cfg.PrimaryLLM.TimeoutSeconds = -1
|
||||||
|
cfg.PrimaryLLM.Concurrency = 0
|
||||||
cfg.ValidationMaxPromptTokens = 0
|
cfg.ValidationMaxPromptTokens = 0
|
||||||
cfg.MaxSectionTokens = 100
|
cfg.MaxSectionTokens = 100
|
||||||
cfg.MinSectionTokens = 200
|
cfg.MinSectionTokens = 200
|
||||||
@@ -151,6 +166,7 @@ func TestValidationFailures(t *testing.T) {
|
|||||||
message := err.Error()
|
message := err.Error()
|
||||||
for _, expected := range []string{
|
for _, expected := range []string{
|
||||||
"primary llm timeout seconds",
|
"primary llm timeout seconds",
|
||||||
|
"primary llm concurrency",
|
||||||
"validation max prompt tokens",
|
"validation max prompt tokens",
|
||||||
"min section tokens",
|
"min section tokens",
|
||||||
"grammar confidence threshold",
|
"grammar confidence threshold",
|
||||||
@@ -169,9 +185,10 @@ func TestEffectiveValidationLLMInheritance(t *testing.T) {
|
|||||||
cfg.PrimaryLLM.BaseURL = "https://primary.example/v1"
|
cfg.PrimaryLLM.BaseURL = "https://primary.example/v1"
|
||||||
cfg.PrimaryLLM.TimeoutSeconds = 111
|
cfg.PrimaryLLM.TimeoutSeconds = 111
|
||||||
cfg.PrimaryLLM.MaxRetries = 2
|
cfg.PrimaryLLM.MaxRetries = 2
|
||||||
|
cfg.PrimaryLLM.Concurrency = 7
|
||||||
|
|
||||||
effective := cfg.EffectiveValidationLLMConfig()
|
effective := cfg.EffectiveValidationLLMConfig()
|
||||||
if effective.APIKey != "primary-key" || effective.Model != "primary-model" || effective.BaseURL != "https://primary.example/v1" || effective.TimeoutSeconds != 111 || effective.MaxRetries != 2 {
|
if effective.APIKey != "primary-key" || effective.Model != "primary-model" || effective.BaseURL != "https://primary.example/v1" || effective.TimeoutSeconds != 111 || effective.MaxRetries != 2 || effective.Concurrency != 7 {
|
||||||
t.Fatalf("unexpected inherited config: %#v", effective)
|
t.Fatalf("unexpected inherited config: %#v", effective)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,9 +199,11 @@ func TestEffectiveValidationLLMInheritance(t *testing.T) {
|
|||||||
cfg.ValidationLLM.BaseURL = "https://validation.example/v1"
|
cfg.ValidationLLM.BaseURL = "https://validation.example/v1"
|
||||||
cfg.ValidationLLM.TimeoutSeconds = &validationTimeout
|
cfg.ValidationLLM.TimeoutSeconds = &validationTimeout
|
||||||
cfg.ValidationLLM.MaxRetries = &validationRetries
|
cfg.ValidationLLM.MaxRetries = &validationRetries
|
||||||
|
validationConcurrency := 4
|
||||||
|
cfg.ValidationLLM.Concurrency = &validationConcurrency
|
||||||
|
|
||||||
effective = cfg.EffectiveValidationLLMConfig()
|
effective = cfg.EffectiveValidationLLMConfig()
|
||||||
if effective.APIKey != "validation-key" || effective.Model != "validation-model" || effective.BaseURL != "https://validation.example/v1" || effective.TimeoutSeconds != 222 || effective.MaxRetries != 9 {
|
if effective.APIKey != "validation-key" || effective.Model != "validation-model" || effective.BaseURL != "https://validation.example/v1" || effective.TimeoutSeconds != 222 || effective.MaxRetries != 9 || effective.Concurrency != 4 {
|
||||||
t.Fatalf("unexpected overridden validation config: %#v", effective)
|
t.Fatalf("unexpected overridden validation config: %#v", effective)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,6 +68,13 @@ func loadFromLookup(lookup func(string) (string, bool)) (Config, error) {
|
|||||||
}
|
}
|
||||||
cfg.PrimaryLLM.MaxRetries = value
|
cfg.PrimaryLLM.MaxRetries = value
|
||||||
}
|
}
|
||||||
|
if raw, ok := lookup("AUDITA_LLM_CONCURRENCY"); ok {
|
||||||
|
value, err := parseInt(raw)
|
||||||
|
if err != nil {
|
||||||
|
return Config{}, fmt.Errorf("AUDITA_LLM_CONCURRENCY: %w", err)
|
||||||
|
}
|
||||||
|
cfg.PrimaryLLM.Concurrency = value
|
||||||
|
}
|
||||||
|
|
||||||
if raw, ok := lookup("AUDITA_VALIDATION_MAX_RETRIES"); ok {
|
if raw, ok := lookup("AUDITA_VALIDATION_MAX_RETRIES"); ok {
|
||||||
value, err := parseInt(raw)
|
value, err := parseInt(raw)
|
||||||
@@ -76,6 +83,13 @@ func loadFromLookup(lookup func(string) (string, bool)) (Config, error) {
|
|||||||
}
|
}
|
||||||
cfg.ValidationLLM.MaxRetries = &value
|
cfg.ValidationLLM.MaxRetries = &value
|
||||||
}
|
}
|
||||||
|
if raw, ok := lookup("AUDITA_VALIDATION_LLM_CONCURRENCY"); ok {
|
||||||
|
value, err := parseInt(raw)
|
||||||
|
if err != nil {
|
||||||
|
return Config{}, fmt.Errorf("AUDITA_VALIDATION_LLM_CONCURRENCY: %w", err)
|
||||||
|
}
|
||||||
|
cfg.ValidationLLM.Concurrency = &value
|
||||||
|
}
|
||||||
|
|
||||||
if raw, ok := lookup("AUDITA_VALIDATION_MAX_PROMPT_TOKENS"); ok {
|
if raw, ok := lookup("AUDITA_VALIDATION_MAX_PROMPT_TOKENS"); ok {
|
||||||
value, err := parseInt(raw)
|
value, err := parseInt(raw)
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ type CLIOverrides struct {
|
|||||||
ValidationLLMTimeoutSeconds *int
|
ValidationLLMTimeoutSeconds *int
|
||||||
MaxRetries *int
|
MaxRetries *int
|
||||||
ValidationMaxRetries *int
|
ValidationMaxRetries *int
|
||||||
|
ValidationLLMConcurrency *int
|
||||||
ValidationMaxPromptTokens *int
|
ValidationMaxPromptTokens *int
|
||||||
MaxSectionTokens *int
|
MaxSectionTokens *int
|
||||||
MinSectionTokens *int
|
MinSectionTokens *int
|
||||||
@@ -71,6 +72,10 @@ func (c *Config) ApplyCLIOverrides(overrides CLIOverrides) error {
|
|||||||
value := *overrides.ValidationMaxRetries
|
value := *overrides.ValidationMaxRetries
|
||||||
c.ValidationLLM.MaxRetries = &value
|
c.ValidationLLM.MaxRetries = &value
|
||||||
}
|
}
|
||||||
|
if overrides.ValidationLLMConcurrency != nil {
|
||||||
|
value := *overrides.ValidationLLMConcurrency
|
||||||
|
c.ValidationLLM.Concurrency = &value
|
||||||
|
}
|
||||||
if overrides.ValidationMaxPromptTokens != nil {
|
if overrides.ValidationMaxPromptTokens != nil {
|
||||||
c.ValidationMaxPromptTokens = *overrides.ValidationMaxPromptTokens
|
c.ValidationMaxPromptTokens = *overrides.ValidationMaxPromptTokens
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ func (c Config) Validate() error {
|
|||||||
if c.PrimaryLLM.MaxRetries < 0 {
|
if c.PrimaryLLM.MaxRetries < 0 {
|
||||||
issues = append(issues, "max retries must be zero or greater")
|
issues = append(issues, "max retries must be zero or greater")
|
||||||
}
|
}
|
||||||
|
if c.PrimaryLLM.Concurrency <= 0 {
|
||||||
|
issues = append(issues, "primary llm concurrency must be greater than zero")
|
||||||
|
}
|
||||||
|
|
||||||
if c.ValidationLLM.TimeoutSeconds != nil && *c.ValidationLLM.TimeoutSeconds <= 0 {
|
if c.ValidationLLM.TimeoutSeconds != nil && *c.ValidationLLM.TimeoutSeconds <= 0 {
|
||||||
issues = append(issues, "validation llm timeout seconds must be greater than zero")
|
issues = append(issues, "validation llm timeout seconds must be greater than zero")
|
||||||
@@ -31,6 +34,9 @@ func (c Config) Validate() error {
|
|||||||
if c.ValidationLLM.MaxRetries != nil && *c.ValidationLLM.MaxRetries < 0 {
|
if c.ValidationLLM.MaxRetries != nil && *c.ValidationLLM.MaxRetries < 0 {
|
||||||
issues = append(issues, "validation max retries must be zero or greater")
|
issues = append(issues, "validation max retries must be zero or greater")
|
||||||
}
|
}
|
||||||
|
if c.ValidationLLM.Concurrency != nil && *c.ValidationLLM.Concurrency <= 0 {
|
||||||
|
issues = append(issues, "validation llm concurrency must be greater than zero")
|
||||||
|
}
|
||||||
|
|
||||||
if c.ValidationMaxPromptTokens <= 0 {
|
if c.ValidationMaxPromptTokens <= 0 {
|
||||||
issues = append(issues, "validation max prompt tokens must be greater than zero")
|
issues = append(issues, "validation max prompt tokens must be greater than zero")
|
||||||
|
|||||||
Reference in New Issue
Block a user