156 lines
6.7 KiB
Go
156 lines
6.7 KiB
Go
package cli
|
|
|
|
import (
|
|
"path/filepath"
|
|
"sort"
|
|
|
|
"gitea.maximumdirect.net/eric/audita/internal/framework/runner"
|
|
)
|
|
|
|
const (
|
|
correctionDispositionApplied = "applied"
|
|
correctionDispositionRejected = "rejected"
|
|
correctionDispositionSkipped = "skipped"
|
|
correctionDispositionFailed = "failed"
|
|
)
|
|
|
|
type correctionLedgerEntry struct {
|
|
RunID string `json:"run_id,omitempty"`
|
|
ModuleKey string `json:"module_key"`
|
|
ModuleInstance string `json:"module_instance"`
|
|
ProposalIndex int `json:"proposal_index"`
|
|
SegmentID int `json:"segment_id,omitempty"`
|
|
OriginalText string `json:"original_text,omitempty"`
|
|
ProposedCorrectedText string `json:"proposed_corrected_text,omitempty"`
|
|
AppliedCorrectedText string `json:"applied_corrected_text,omitempty"`
|
|
ReplacementPolicy string `json:"replacement_policy,omitempty"`
|
|
Disposition string `json:"disposition"`
|
|
DispositionReasonCode string `json:"disposition_reason_code,omitempty"`
|
|
DispositionMessage string `json:"disposition_message,omitempty"`
|
|
DeterministicValidatorResults []ledgerValidatorDecisionRecord `json:"deterministic_validator_decisions,omitempty"`
|
|
LLMValidatorResults []ledgerValidatorDecisionRecord `json:"llm_validator_decisions,omitempty"`
|
|
}
|
|
|
|
type ledgerValidatorDecisionRecord struct {
|
|
ValidatorKey string `json:"validator_key"`
|
|
Approved bool `json:"approved"`
|
|
ReasonCode string `json:"reason_code"`
|
|
Message string `json:"message,omitempty"`
|
|
}
|
|
|
|
func buildCorrectionLedger(runDirPath string, runOutput *runner.RunOutput) []correctionLedgerEntry {
|
|
if runOutput == nil || len(runOutput.ModuleResults) == 0 {
|
|
return nil
|
|
}
|
|
runID := ""
|
|
if runDirPath != "" {
|
|
runID = filepath.Base(runDirPath)
|
|
}
|
|
|
|
entries := make([]correctionLedgerEntry, 0)
|
|
llmBacked := map[string]bool{
|
|
"spoken_form_plausibility": true,
|
|
"meaning_reversal_review": true,
|
|
"editorial_review": true,
|
|
}
|
|
|
|
for _, module := range runOutput.ModuleResults {
|
|
decisionsByProposal := make(map[int][]runner.ValidatorDecisionRecord)
|
|
for _, decision := range module.ValidatorDecisions {
|
|
decisionsByProposal[decision.ProposalIndex] = append(decisionsByProposal[decision.ProposalIndex], decision)
|
|
}
|
|
|
|
for _, change := range module.AppliedChanges {
|
|
entries = append(entries, correctionLedgerEntry{
|
|
RunID: runID,
|
|
ModuleKey: module.ModuleKey,
|
|
ModuleInstance: module.ModuleInstance,
|
|
ProposalIndex: change.ProposalIndex,
|
|
SegmentID: change.TargetSegmentID,
|
|
OriginalText: change.OriginalText,
|
|
ProposedCorrectedText: change.CorrectedText,
|
|
AppliedCorrectedText: change.CorrectedText,
|
|
ReplacementPolicy: string(module.ReplacementPolicy),
|
|
Disposition: correctionDispositionApplied,
|
|
DeterministicValidatorResults: filterLedgerDecisions(decisionsByProposal[change.ProposalIndex], false, llmBacked),
|
|
LLMValidatorResults: filterLedgerDecisions(decisionsByProposal[change.ProposalIndex], true, llmBacked),
|
|
})
|
|
}
|
|
for _, change := range module.SkippedChanges {
|
|
entries = append(entries, correctionLedgerEntry{
|
|
RunID: runID,
|
|
ModuleKey: module.ModuleKey,
|
|
ModuleInstance: module.ModuleInstance,
|
|
ProposalIndex: change.ProposalIndex,
|
|
SegmentID: change.TargetSegmentID,
|
|
OriginalText: change.OriginalText,
|
|
ProposedCorrectedText: change.CorrectedText,
|
|
ReplacementPolicy: string(module.ReplacementPolicy),
|
|
Disposition: correctionDispositionSkipped,
|
|
DispositionReasonCode: string(change.SkipReason),
|
|
DispositionMessage: change.Message,
|
|
DeterministicValidatorResults: filterLedgerDecisions(decisionsByProposal[change.ProposalIndex], false, llmBacked),
|
|
LLMValidatorResults: filterLedgerDecisions(decisionsByProposal[change.ProposalIndex], true, llmBacked),
|
|
})
|
|
}
|
|
for _, rejection := range module.ValidatorRejected {
|
|
entries = append(entries, correctionLedgerEntry{
|
|
RunID: runID,
|
|
ModuleKey: module.ModuleKey,
|
|
ModuleInstance: module.ModuleInstance,
|
|
ProposalIndex: rejection.ProposalIndex,
|
|
SegmentID: rejection.TargetSegmentID,
|
|
OriginalText: rejection.OriginalText,
|
|
ProposedCorrectedText: rejection.CorrectedText,
|
|
ReplacementPolicy: string(module.ReplacementPolicy),
|
|
Disposition: correctionDispositionRejected,
|
|
DispositionReasonCode: rejection.ReasonCode,
|
|
DispositionMessage: rejection.Message,
|
|
DeterministicValidatorResults: filterLedgerDecisions(decisionsByProposal[rejection.ProposalIndex], false, llmBacked),
|
|
LLMValidatorResults: filterLedgerDecisions(decisionsByProposal[rejection.ProposalIndex], true, llmBacked),
|
|
})
|
|
}
|
|
if module.Status == runner.ModuleStatusFailed {
|
|
entries = append(entries, correctionLedgerEntry{
|
|
RunID: runID,
|
|
ModuleKey: module.ModuleKey,
|
|
ModuleInstance: module.ModuleInstance,
|
|
Disposition: correctionDispositionFailed,
|
|
DispositionReasonCode: "module_failed",
|
|
DispositionMessage: module.ErrorMessage,
|
|
})
|
|
}
|
|
}
|
|
|
|
sort.SliceStable(entries, func(i, j int) bool {
|
|
if entries[i].ModuleInstance != entries[j].ModuleInstance {
|
|
return entries[i].ModuleInstance < entries[j].ModuleInstance
|
|
}
|
|
if entries[i].ProposalIndex != entries[j].ProposalIndex {
|
|
return entries[i].ProposalIndex < entries[j].ProposalIndex
|
|
}
|
|
return entries[i].Disposition < entries[j].Disposition
|
|
})
|
|
|
|
return entries
|
|
}
|
|
|
|
func filterLedgerDecisions(in []runner.ValidatorDecisionRecord, wantLLM bool, llmBacked map[string]bool) []ledgerValidatorDecisionRecord {
|
|
if len(in) == 0 {
|
|
return nil
|
|
}
|
|
out := make([]ledgerValidatorDecisionRecord, 0, len(in))
|
|
for _, decision := range in {
|
|
if llmBacked[decision.ValidatorName] != wantLLM {
|
|
continue
|
|
}
|
|
out = append(out, ledgerValidatorDecisionRecord{
|
|
ValidatorKey: decision.ValidatorName,
|
|
Approved: decision.Approved,
|
|
ReasonCode: decision.ReasonCode,
|
|
Message: decision.Message,
|
|
})
|
|
}
|
|
return out
|
|
}
|