2026-09-24 17:48:52 +00:00
|
|
|
package cli
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
2026-09-24 21:48:18 +00:00
|
|
|
"sync"
|
2026-09-24 17:48:52 +00:00
|
|
|
|
2026-10-11 01:40:38 +00:00
|
|
|
"gitea.maximumdirect.net/eric/audita/v3/internal/core/normalization"
|
|
|
|
|
"gitea.maximumdirect.net/eric/audita/v3/internal/core/outputschema"
|
|
|
|
|
"gitea.maximumdirect.net/eric/audita/v3/internal/core/reporting"
|
|
|
|
|
"gitea.maximumdirect.net/eric/audita/v3/internal/core/schema"
|
|
|
|
|
"gitea.maximumdirect.net/eric/audita/v3/internal/framework/llm"
|
2026-11-10 17:12:28 +00:00
|
|
|
commandkit "go.maximumdirect.net/commandkit"
|
2026-09-24 17:48:52 +00:00
|
|
|
)
|
|
|
|
|
|
2026-11-10 17:12:28 +00:00
|
|
|
// processResult carries the runner's transcript and ordinary report facts.
|
|
|
|
|
type processResult struct {
|
|
|
|
|
Transcript *schema.Transcript
|
|
|
|
|
Snapshot reporting.Snapshot
|
2026-09-24 17:48:52 +00:00
|
|
|
}
|
|
|
|
|
|
2026-11-10 17:12:28 +00:00
|
|
|
func runProcess(inv commandkit.Invocation, input processInput) commandkit.Outcome {
|
|
|
|
|
loaded, err := loadProcessInputs(inv.Context, input, inv.ReportingMode())
|
2026-09-24 17:48:52 +00:00
|
|
|
if err != nil {
|
2026-11-10 17:12:28 +00:00
|
|
|
if inv.Context.Err() != nil || llm.IsAssetError(err) {
|
|
|
|
|
return commandkit.Failure("Could not prepare processing.").WithCause(err)
|
|
|
|
|
}
|
|
|
|
|
return commandkit.Usage(err.Error()).WithCause(err)
|
2026-09-24 17:48:52 +00:00
|
|
|
}
|
2026-11-10 17:12:28 +00:00
|
|
|
prepared, err := prepareProcess(inv.Context, input, loaded, inv.Reporter().Output())
|
2026-09-24 17:48:52 +00:00
|
|
|
if err != nil {
|
2026-11-10 17:12:28 +00:00
|
|
|
return commandkit.Failure(loaded.Runtime.RedactError(inv.Context, err)).WithCause(err)
|
2026-09-24 17:48:52 +00:00
|
|
|
}
|
2026-11-10 17:12:28 +00:00
|
|
|
defer prepared.Prepared.Output.Close()
|
|
|
|
|
var warningsMu sync.Mutex
|
|
|
|
|
warn := func(err error) {
|
|
|
|
|
if err == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
warningsMu.Lock()
|
|
|
|
|
prepared.Prepared.Warnings = append(prepared.Prepared.Warnings, err)
|
|
|
|
|
warningsMu.Unlock()
|
2026-09-24 17:48:52 +00:00
|
|
|
}
|
2026-11-10 17:12:28 +00:00
|
|
|
debug := prepared.Prepared.Debug
|
|
|
|
|
if debug != nil {
|
|
|
|
|
warn(debug.WriteSourceTranscript(loaded.Source, loaded.RawTranscript))
|
2026-09-24 17:48:52 +00:00
|
|
|
}
|
2026-11-10 17:12:28 +00:00
|
|
|
cfg := &loaded.Configuration.Config
|
2026-09-24 17:48:52 +00:00
|
|
|
normalizer := normalization.NewNormalizer(normalization.NormalizationConfig{
|
2026-11-10 17:12:28 +00:00
|
|
|
MaxSegmentGap: cfg.Normalization.MaxSegmentGap, EllipsisGap: cfg.Normalization.EllipsisGap,
|
|
|
|
|
MaxSegmentDuration: cfg.Normalization.MaxSegmentDuration, MaxSegmentTokens: cfg.Normalization.MaxSegmentTokens,
|
2026-09-24 17:48:52 +00:00
|
|
|
})
|
2026-11-10 17:12:28 +00:00
|
|
|
normalized, summary := normalizer.Normalize(sourceToCanonicalTranscript(loaded.Source))
|
|
|
|
|
if debug != nil {
|
|
|
|
|
warn(debug.WriteNormalizedTranscript(normalized))
|
|
|
|
|
warn(debug.WriteNormalizationSummary(summary))
|
2026-09-24 17:48:52 +00:00
|
|
|
}
|
2026-11-10 17:12:28 +00:00
|
|
|
result, runErr := executePipeline(inv.Context, loaded, prepared, normalized, warn)
|
2026-09-24 17:48:52 +00:00
|
|
|
if runErr != nil {
|
2026-11-10 17:12:28 +00:00
|
|
|
return finishProcess(inv, input, loaded, prepared, result.Snapshot, nil, runErr)
|
2026-09-24 17:48:52 +00:00
|
|
|
}
|
2026-11-10 17:12:28 +00:00
|
|
|
encoder, err := outputschema.Resolve(cfg.Output.Schema)
|
2026-09-24 17:48:52 +00:00
|
|
|
if err != nil {
|
2026-11-10 17:12:28 +00:00
|
|
|
return finishProcess(inv, input, loaded, prepared, result.Snapshot, nil, err)
|
2026-09-24 17:48:52 +00:00
|
|
|
}
|
2026-11-10 17:12:28 +00:00
|
|
|
if result.Transcript == nil {
|
|
|
|
|
return finishProcess(inv, input, loaded, prepared, result.Snapshot, nil, fmt.Errorf("processing returned no transcript"))
|
2026-09-24 17:48:52 +00:00
|
|
|
}
|
2026-11-10 17:12:28 +00:00
|
|
|
payload, err := encoder.Encoder(result.Transcript)
|
2026-09-24 17:48:52 +00:00
|
|
|
if err != nil {
|
2026-11-10 17:12:28 +00:00
|
|
|
return finishProcess(inv, input, loaded, prepared, result.Snapshot, nil, err)
|
2026-09-24 17:48:52 +00:00
|
|
|
}
|
2026-11-10 17:12:28 +00:00
|
|
|
return finishProcess(inv, input, loaded, prepared, result.Snapshot, payload, nil)
|
2026-09-24 17:48:52 +00:00
|
|
|
}
|
|
|
|
|
|
2026-11-10 17:12:28 +00:00
|
|
|
// DebugPath returns the retained artifact directory, if its preparation succeeded.
|
|
|
|
|
func (p *preparedProcess) DebugPath() string {
|
|
|
|
|
if p == nil || p.Prepared == nil || p.Prepared.Debug == nil {
|
|
|
|
|
return ""
|
2026-09-24 21:48:18 +00:00
|
|
|
}
|
2026-11-10 17:12:28 +00:00
|
|
|
return p.Prepared.Debug.Path()
|
2026-09-24 21:48:18 +00:00
|
|
|
}
|