Make module-stage LLM handling resilient and report warnings
This commit is contained in:
@@ -147,6 +147,8 @@ func skipReasonMessage(reason ProposalSkipReason) string {
|
||||
return "original_text matched multiple spans under require_unique policy"
|
||||
case SkipReasonNoEffect:
|
||||
return "original_text and corrected_text are identical"
|
||||
case SkipReasonEmptyResultingText:
|
||||
return "proposal would leave the segment empty"
|
||||
case SkipReasonInvalidProposal:
|
||||
return "proposal failed structural or policy validation"
|
||||
default:
|
||||
|
||||
@@ -15,6 +15,7 @@ const (
|
||||
SkipReasonMissingOriginalText ProposalSkipReason = "missing_original_text"
|
||||
SkipReasonAmbiguousOriginal ProposalSkipReason = "ambiguous_original_text"
|
||||
SkipReasonNoEffect ProposalSkipReason = "no_effect"
|
||||
SkipReasonEmptyResultingText ProposalSkipReason = "empty_resulting_segment"
|
||||
SkipReasonInvalidProposal ProposalSkipReason = "invalid_proposal"
|
||||
)
|
||||
|
||||
@@ -62,6 +63,9 @@ func PreviewProposalForSegment(segment *schema.Segment, proposal CorrectionPropo
|
||||
}
|
||||
|
||||
corrected := strings.Replace(segment.Text, proposal.OriginalText, proposal.CorrectedText, 1)
|
||||
if strings.TrimSpace(corrected) == "" {
|
||||
return SegmentPreviewResult{SkipReason: SkipReasonEmptyResultingText}
|
||||
}
|
||||
return SegmentPreviewResult{
|
||||
Applicable: true,
|
||||
CorrectedSegmentText: corrected,
|
||||
@@ -70,6 +74,9 @@ func PreviewProposalForSegment(segment *schema.Segment, proposal CorrectionPropo
|
||||
|
||||
case ReplacementPolicyReplaceAll:
|
||||
corrected := strings.ReplaceAll(segment.Text, proposal.OriginalText, proposal.CorrectedText)
|
||||
if strings.TrimSpace(corrected) == "" {
|
||||
return SegmentPreviewResult{SkipReason: SkipReasonEmptyResultingText}
|
||||
}
|
||||
return SegmentPreviewResult{
|
||||
Applicable: true,
|
||||
CorrectedSegmentText: corrected,
|
||||
|
||||
@@ -121,6 +121,42 @@ func TestPreviewProposalForSegmentNoEffectReplacement(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreviewProposalForSegmentAllowsEmptyCorrectedTextWhenSegmentRemainsNonEmpty(t *testing.T) {
|
||||
segment := &schema.Segment{ID: 8, Text: "uh hello"}
|
||||
proposal := CorrectionProposal{
|
||||
TargetSegmentID: 8,
|
||||
OriginalText: "uh ",
|
||||
CorrectedText: "",
|
||||
Confidence: 0.9,
|
||||
}
|
||||
|
||||
result := PreviewProposalForSegment(segment, proposal, ReplacementPolicyRequireUnique)
|
||||
if !result.Applicable {
|
||||
t.Fatalf("expected applicable preview, got skip reason %q", result.SkipReason)
|
||||
}
|
||||
if result.CorrectedSegmentText != "hello" {
|
||||
t.Fatalf("unexpected corrected text: %q", result.CorrectedSegmentText)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreviewProposalForSegmentRejectsEmptyResultingSegment(t *testing.T) {
|
||||
segment := &schema.Segment{ID: 8, Text: "uh"}
|
||||
proposal := CorrectionProposal{
|
||||
TargetSegmentID: 8,
|
||||
OriginalText: "uh",
|
||||
CorrectedText: "",
|
||||
Confidence: 0.9,
|
||||
}
|
||||
|
||||
result := PreviewProposalForSegment(segment, proposal, ReplacementPolicyRequireUnique)
|
||||
if result.Applicable {
|
||||
t.Fatal("expected non-applicable preview")
|
||||
}
|
||||
if result.SkipReason != SkipReasonEmptyResultingText {
|
||||
t.Fatalf("expected skip reason %q, got %q", SkipReasonEmptyResultingText, result.SkipReason)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreviewProposalForSegmentPreservesInputSegment(t *testing.T) {
|
||||
segment := &schema.Segment{ID: 9, Speaker: "A", Start: 1.0, End: 2.0, Text: "rank rank"}
|
||||
original := *segment
|
||||
|
||||
@@ -38,9 +38,6 @@ func (p CorrectionProposal) Validate() error {
|
||||
if strings.TrimSpace(p.OriginalText) == "" {
|
||||
return fmt.Errorf("proposal original_text must not be empty")
|
||||
}
|
||||
if strings.TrimSpace(p.CorrectedText) == "" {
|
||||
return fmt.Errorf("proposal corrected_text must not be empty")
|
||||
}
|
||||
if p.Confidence < 0.0 || p.Confidence > 1.0 {
|
||||
return fmt.Errorf("proposal confidence must be between 0.0 and 1.0")
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ func TestCorrectionProposalValidate_InvalidEmptyOriginalText(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCorrectionProposalValidate_InvalidEmptyCorrectedText(t *testing.T) {
|
||||
func TestCorrectionProposalValidate_AllowsEmptyCorrectedText(t *testing.T) {
|
||||
proposal := CorrectionProposal{
|
||||
TargetSegmentID: 42,
|
||||
OriginalText: "gestures",
|
||||
@@ -43,12 +43,8 @@ func TestCorrectionProposalValidate_InvalidEmptyCorrectedText(t *testing.T) {
|
||||
Confidence: 0.95,
|
||||
}
|
||||
|
||||
err := proposal.Validate()
|
||||
if err == nil {
|
||||
t.Fatal("expected validation error, got nil")
|
||||
}
|
||||
if err.Error() != "proposal corrected_text must not be empty" {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
if err := proposal.Validate(); err != nil {
|
||||
t.Fatalf("expected empty corrected_text to be allowed, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user