Implemented a custom validator for the glossary module that allows changes from one protected glossary term to another

This commit is contained in:
2026-04-25 13:58:20 -05:00
parent bf05d79fc6
commit 619c615d21
6 changed files with 131 additions and 6 deletions

View File

@@ -130,7 +130,14 @@ def test_process_transcript_result_writes_report_and_preserves_skips_per_policy(
assert (result.run_dir / "normalization" / "summary.json").exists()
assert [validator["name"] for validator in result.report.modules[0].to_dict()["validators"]] == [
"proposal_confidence_guard",
"protected_glossary_guard",
"glossary_stage_protected_glossary_guard",
"non_empty_segment_guard",
"spoken_form_plausibility_review",
"meaning_reversal_review",
]
assert [validator["name"] for validator in result.report.modules[2].to_dict()["validators"]] == [
"proposal_confidence_guard",
"glossary_stage_protected_glossary_guard",
"non_empty_segment_guard",
"spoken_form_plausibility_review",
"meaning_reversal_review",
@@ -184,7 +191,7 @@ def test_default_module_specs_expose_final_validator_order():
]
assert [validator.name for validator in specs[0].module.validators()] == [
"proposal_confidence_guard",
"protected_glossary_guard",
"glossary_stage_protected_glossary_guard",
"non_empty_segment_guard",
"spoken_form_plausibility_review",
"meaning_reversal_review",
@@ -198,7 +205,7 @@ def test_default_module_specs_expose_final_validator_order():
]
assert [validator.name for validator in specs[2].module.validators()] == [
"proposal_confidence_guard",
"protected_glossary_guard",
"glossary_stage_protected_glossary_guard",
"non_empty_segment_guard",
"spoken_form_plausibility_review",
"meaning_reversal_review",

View File

@@ -1,6 +1,10 @@
from audita.core.schemas import parse_glossary_yaml, parse_transcript_json
from audita.framework.models import CorrectionProposal, ModuleRunSpec
from audita.validators import ProtectedGlossaryTermsValidator, ProtectedVocabulary
from audita.validators import (
GlossaryStageProtectedGlossaryTermsValidator,
ProtectedGlossaryTermsValidator,
ProtectedVocabulary,
)
from audita.validators.base import ValidationContext
@@ -46,6 +50,22 @@ def test_protected_vocabulary_blocks_replacing_protected_term():
)
def test_protected_vocabulary_glossary_stage_allows_glossary_to_glossary_changes():
vocabulary = ProtectedVocabulary.from_glossary(_glossary())
assert vocabulary.violation_reason("Hrank moves.", "Popov moves.") == "correction changes protected glossary term usage"
assert vocabulary.glossary_stage_violation_reason("Hrank moves.", "Popov moves.") is None
def test_protected_vocabulary_glossary_stage_still_blocks_glossary_to_nonglossary_changes():
vocabulary = ProtectedVocabulary.from_glossary(_glossary())
assert (
vocabulary.glossary_stage_violation_reason("Hrank moves.", "Frank moves.")
== "correction changes protected glossary term usage"
)
def test_protected_vocabulary_blocks_noncanonical_capitalization():
vocabulary = ProtectedVocabulary.from_glossary(_glossary())
@@ -57,6 +77,10 @@ def test_protected_vocabulary_blocks_noncanonical_capitalization():
vocabulary.violation_reason("gestures", "jesters")
== "correction changes protected glossary term capitalization"
)
assert (
vocabulary.glossary_stage_violation_reason("Hrank moves.", "POPOV moves.")
== "correction changes protected glossary term capitalization"
)
def test_protected_vocabulary_allows_corrections_toward_protected_terms():
@@ -155,6 +179,54 @@ def test_protected_glossary_terms_validator_returns_proposal_indexed_decisions()
assert result.decisions[1].approved is True
def test_glossary_stage_protected_glossary_terms_validator_allows_glossary_to_glossary_replacement():
validator = GlossaryStageProtectedGlossaryTermsValidator("glossary_stage_protected_glossary_guard")
transcript = parse_transcript_json(
"""
[
{"id": 1, "speaker": "Eric", "start": 0.0, "end": 1.0, "text": "Hrank moves."},
{"id": 2, "speaker": "Eric", "start": 1.0, "end": 2.0, "text": "Hrank moves."}
]
"""
)
proposals = [
CorrectionProposal(
proposal_index=0,
module_instance="glossary_1",
module_key="glossary",
id=1,
original_text="Hrank",
corrected_text="Popov",
confidence=0.9,
),
CorrectionProposal(
proposal_index=1,
module_instance="glossary_1",
module_key="glossary",
id=2,
original_text="Hrank",
corrected_text="POPOV",
confidence=0.9,
),
]
result = validator.validate(
ValidationContext(
proposals=proposals,
transcript=transcript,
glossary=_glossary(),
config=None, # type: ignore[arg-type]
run_spec=ModuleRunSpec(instance_name="glossary_1", module_key="glossary", module=None), # type: ignore[arg-type]
run_dir=__import__("pathlib").Path("."),
)
)
assert [decision.proposal_index for decision in result.decisions] == [0, 1]
assert result.decisions[0].approved is True
assert result.decisions[1].approved is False
assert result.decisions[1].reason == "correction changes protected glossary term capitalization"
def test_protected_glossary_terms_validator_uses_proposal_span_only():
validator = ProtectedGlossaryTermsValidator("protected_glossary_guard")
glossary = parse_glossary_yaml(