Index applied changes for repair outcomes

This commit is contained in:
2026-11-11 12:29:42 +00:00
parent 8c93f728c0
commit 6b724fd3cc
3 changed files with 28 additions and 15 deletions

View File

@@ -114,22 +114,29 @@ type repairModule struct{ outcomeModule }
func (m repairModule) RepairPromptID() string { return "repair" }
func TestRunnerAppliesRevisionsInOriginalOrder(t *testing.T) {
sectionIndex := 0
candidates := []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "cat", CorrectedText: "dog", Confidence: .9}, {TargetSegmentID: 1, OriginalText: "cat", CorrectedText: "fox", Confidence: .1}}
client := &repairClient{outputs: []string{`{"proposal_ref":2,"action":"revise","correction":{"id":1,"original_text":"cat","corrected_text":"fox","confidence":0.9}}`}}
candidates := []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "cat", CorrectedText: "dog", Confidence: .9}, {TargetSegmentID: 1, OriginalText: "cat", CorrectedText: "fox", Confidence: .1}, {TargetSegmentID: 1, OriginalText: "bird", CorrectedText: "owl", Confidence: .1}}
client := &repairClient{outputs: []string{`{"proposal_ref":2,"action":"revise","correction":{"id":1,"original_text":"cat","corrected_text":"fox","confidence":0.9}}`,
`{"proposal_ref":3,"action":"revise","correction":{"id":1,"original_text":"bird","corrected_text":"owl","confidence":0.9}}`,
}}
module := repairModule{outcomeModule{fakeModule: fakeModule{key: "grammar", policy: proposals.ReplacementPolicyRequireUnique, validators: []contracts.Validator{validators.ProposalShapeValidator{}, validators.NoEffectValidator{}, validators.OriginalTextPresenceValidator{}, validators.ConfidenceThresholdValidator{}}}, proposeResult: func(req contracts.ProposalRequest) (contracts.ProposalResult, error) {
return contracts.ProposalResult{Proposals: candidates, SegmentReferences: []int{1, 1}, Exchange: &structuredoutput.Exchange{PromptID: "proposal"}}, nil
return contracts.ProposalResult{Proposals: candidates, SegmentReferences: []int{1, 1, 1}, Exchange: &structuredoutput.Exchange{PromptID: "proposal"}}, nil
}}}
runner := New(fakeFactory{modules: map[string]contracts.TranscriptModule{"grammar": module}})
out, err := runner.Run(context.Background(), RunInput{Transcript: &schema.Transcript{Segments: []schema.Segment{{ID: 1, Text: "cat"}}}, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "grammar", InstanceName: "grammar", RepairClient: client, Settings: config.Stage{ConfidenceThreshold: .8, LLM: config.LLM{CorrectionRepairAttempts: 1}}}}})
out, err := runner.Run(context.Background(), RunInput{Transcript: &schema.Transcript{Segments: []schema.Segment{{ID: 1, Text: "cat and bird"}}}, ModuleSpecs: []contracts.ModuleRunSpec{{ModuleKey: "grammar", InstanceName: "grammar", RepairClient: client, Settings: config.Stage{ConfidenceThreshold: .8, LLM: config.LLM{CorrectionRepairAttempts: 1}}}}})
if err != nil {
t.Fatal(err)
}
if out.FinalTranscript.Segments[0].Text != "dog" || len(out.WorkUnits) != 1 || len(out.WorkUnits[0].Repairs) != 1 || out.WorkUnits[0].Repairs[0].Outcome != "approved_skipped" || out.WorkUnits[0].CorrectionsApplied != 1 || len(out.ModuleResults[0].SkippedChanges) != 1 || out.ModuleResults[0].SkippedChanges[0].ProposalIndex != 1 {
t.Fatalf("output=%+v section=%d", out, sectionIndex)
if out.FinalTranscript.Segments[0].Text != "dog and owl" || len(out.WorkUnits) != 1 || len(out.WorkUnits[0].Repairs) != 2 || out.WorkUnits[0].Repairs[0].Outcome != "approved_skipped" || out.WorkUnits[0].Repairs[1].Outcome != "applied" || out.WorkUnits[0].CorrectionsApplied != 2 || len(out.ModuleResults[0].SkippedChanges) != 1 || out.ModuleResults[0].SkippedChanges[0].ProposalIndex != 1 {
t.Fatalf("output=%+v", out)
}
if len(out.WorkUnits[0].Operations) != 1 || out.WorkUnits[0].Operations[0].Reference.Kind != outcomes.OperationCorrectionRepair || len(out.WorkUnits[0].Operations[0].RecoveryReasons) != 1 || out.WorkUnits[0].Operations[0].RecoveryReasons[0] != outcomes.RecoveryCorrectionRevised {
t.Fatalf("repair operations: %+v", out.WorkUnits[0].Operations)
operations := out.WorkUnits[0].Operations
if len(operations) != 2 {
t.Fatalf("repair operations: %+v", operations)
}
for _, op := range operations {
if op.Reference.Kind != outcomes.OperationCorrectionRepair || len(op.RecoveryReasons) != 1 || op.RecoveryReasons[0] != outcomes.RecoveryCorrectionRevised {
t.Fatalf("repair operation: %+v", op)
}
}
}

View File

@@ -241,6 +241,7 @@ func (r *Runner) Run(ctx context.Context, input RunInput) (RunOutput, error) {
unit.State = outcomes.WorkCompleted
unit.CorrectionsApplied = appliedBySection[sectionPos]
}
var appliedIndices map[int]struct{}
for i := range workUnits[moduleIndex*len(sections) : (moduleIndex+1)*len(sections)] {
unit := &workUnits[moduleIndex*len(sections)+i]
for j := range unit.Repairs {
@@ -248,13 +249,16 @@ func (r *Runner) Run(ctx context.Context, input RunInput) (RunOutput, error) {
if branch.Outcome != "approved" {
continue
}
branch.Outcome = "approved_skipped"
for _, change := range applied {
if change.ProposalIndex == branch.ProposalIndex {
branch.Outcome = "applied"
break
if appliedIndices == nil {
appliedIndices = make(map[int]struct{}, len(applied))
for _, change := range applied {
appliedIndices[change.ProposalIndex] = struct{}{}
}
}
branch.Outcome = "approved_skipped"
if _, ok := appliedIndices[branch.ProposalIndex]; ok {
branch.Outcome = "applied"
}
}
}
attributeApplyFiltering(sections, pipelineResult.Approved, skipped, policy, workUnits[moduleIndex*len(sections):(moduleIndex+1)*len(sections)])