Complete Phase 10 LLM validators
This commit is contained in:
@@ -614,8 +614,8 @@ func TestRunProcessReportJSONIncludesChunkingSummary(t *testing.T) {
|
||||
if report.Chunking.MaxSectionTokens == 0 {
|
||||
t.Errorf("expected max_section_tokens in report")
|
||||
}
|
||||
if report.Phase != "phase8-validators" {
|
||||
t.Errorf("expected phase 'phase8-validators', got %q", report.Phase)
|
||||
if report.Phase != "phase10-llm-validators" {
|
||||
t.Errorf("expected phase 'phase10-llm-validators', got %q", report.Phase)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -659,6 +659,26 @@ func (v fakeValidator) Validate(ctx context.Context, req contracts.ValidationReq
|
||||
return v.validateF(req)
|
||||
}
|
||||
|
||||
type fakeStructuredLLMClient struct {
|
||||
responses []validators.LLMValidationResponse
|
||||
err error
|
||||
}
|
||||
|
||||
func (f *fakeStructuredLLMClient) CompleteStructured(ctx context.Context, req contracts.StructuredCompletionRequest, out any) (contracts.StructuredCompletionResponse, error) {
|
||||
_ = ctx
|
||||
_ = req
|
||||
if f.err != nil {
|
||||
return contracts.StructuredCompletionResponse{}, f.err
|
||||
}
|
||||
if len(f.responses) == 0 {
|
||||
return contracts.StructuredCompletionResponse{}, errors.New("unexpected llm call")
|
||||
}
|
||||
target := out.(*validators.LLMValidationResponse)
|
||||
*target = f.responses[0]
|
||||
f.responses = f.responses[1:]
|
||||
return contracts.StructuredCompletionResponse{}, nil
|
||||
}
|
||||
|
||||
func TestRunProcessInjectedFactoryExecutesRunnerAndReportsModules(t *testing.T) {
|
||||
allow := fakeValidator{name: "allow", validateF: func(req contracts.ValidationRequest) (validators.Result, error) {
|
||||
decisions := make([]validators.Decision, len(req.CandidateProposal))
|
||||
@@ -744,6 +764,56 @@ func TestRunProcessInjectedFactoryExecutesRunnerAndReportsModules(t *testing.T)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunProcessInjectedFactoryLLMValidatorResultsInReports(t *testing.T) {
|
||||
llmValidator, err := validators.NewLLMBackedValidator("spoken_form_plausibility_review", validators.LLMValidatorTypeSpokenFormPlausibility, "")
|
||||
if err != nil {
|
||||
t.Fatalf("NewLLMBackedValidator: %v", err)
|
||||
}
|
||||
processValidationLLMClient = &fakeStructuredLLMClient{
|
||||
responses: []validators.LLMValidationResponse{{
|
||||
Validations: []validators.LLMValidationDecision{{CorrectionIndex: 0, Approved: false, Confidence: 0.99, Reason: "reject"}},
|
||||
}},
|
||||
}
|
||||
processModuleFactory = fakeModuleFactory{modules: map[string]contracts.TranscriptModule{
|
||||
"m": fakeModule{key: "m", policy: proposals.ReplacementPolicyRequireUnique, validators: []contracts.Validator{llmValidator}, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) {
|
||||
return []proposals.CorrectionProposal{{TargetSegmentID: 1, OriginalText: "Hello", CorrectedText: "Hi", Confidence: 1}}, nil
|
||||
}},
|
||||
}}
|
||||
t.Cleanup(func() {
|
||||
processModuleFactory = nil
|
||||
processValidationLLMClient = nil
|
||||
processValidationLLMScheduler = nil
|
||||
})
|
||||
|
||||
var stdout, stderr bytes.Buffer
|
||||
workDir := t.TempDir()
|
||||
reportPath := filepath.Join(t.TempDir(), "report.json")
|
||||
outputPath := filepath.Join(t.TempDir(), "out.json")
|
||||
transcriptPath := writeFile(t, "transcript.json", `[
|
||||
{"id":1,"speaker":"Alice","start":0.0,"end":1.0,"text":"Hello world"}
|
||||
]`)
|
||||
exitCode := Run([]string{
|
||||
"process", transcriptPath,
|
||||
"--glossary", fixturePath("tiny_glossary.yaml"),
|
||||
"--modules", "m",
|
||||
"--output", outputPath,
|
||||
"--report-json", reportPath,
|
||||
"--work-dir", workDir,
|
||||
"--work-dir-retention", "always",
|
||||
}, &stdout, &stderr)
|
||||
if exitCode != 0 {
|
||||
t.Fatalf("expected success, got %d stderr=%q", exitCode, stderr.String())
|
||||
}
|
||||
report := readProcessReport(t, reportPath)
|
||||
if len(report.ModuleResults) != 1 || len(report.ModuleResults[0].ValidatorDecisions) == 0 {
|
||||
t.Fatalf("expected llm validator decisions in external report")
|
||||
}
|
||||
runReport := readProcessReport(t, filepath.Join(onlyRunDir(t, workDir), "report.json"))
|
||||
if len(runReport.ModuleResults) != 1 || len(runReport.ModuleResults[0].ValidatorRejected) != 1 {
|
||||
t.Fatalf("expected llm validator rejection in run report")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunProcessInjectedFactorySkippedKeepsAutoRetention(t *testing.T) {
|
||||
processModuleFactory = fakeModuleFactory{modules: map[string]contracts.TranscriptModule{
|
||||
"m1": fakeModule{key: "m1", policy: proposals.ReplacementPolicyRequireUnique, proposeF: func(req contracts.ProposalRequest) ([]proposals.CorrectionProposal, error) {
|
||||
|
||||
Reference in New Issue
Block a user