Implemented the spoken_word LLM review module
This commit is contained in:
@@ -6,10 +6,11 @@ from audita.core.errors import AuditaLLMError
|
||||
from audita.core.schemas import parse_glossary_yaml, parse_transcript_json
|
||||
from audita.framework.models import CorrectionProposal, ModuleRunSpec
|
||||
from audita.validators.base import ValidationContext
|
||||
from audita.validators.llm import MeaningReversalValidator, SpokenFormPlausibilityValidator
|
||||
from audita.validators.llm import MeaningReversalValidator, SpokenFormPlausibilityValidator, SpokenWordValidator
|
||||
from audita.validators.prompts import (
|
||||
build_meaning_reversal_messages,
|
||||
build_spoken_form_plausibility_messages,
|
||||
build_spoken_word_messages,
|
||||
)
|
||||
import audita.validators.llm as llm_module
|
||||
|
||||
@@ -204,6 +205,110 @@ def test_meaning_reversal_validator_rejects_reversal_and_approves_nonreversal(tm
|
||||
assert "The figure became invisible in the doorway." in prompt_text
|
||||
|
||||
|
||||
def test_spoken_word_validator_approves_cleanup_and_rejects_rewrite(tmp_path):
|
||||
transcript = parse_transcript_json(
|
||||
"""
|
||||
[
|
||||
{"id": 1, "speaker": "A", "start": 0.0, "end": 1.0, "text": "I, uh, I think we should go."},
|
||||
{"id": 2, "speaker": "A", "start": 1.0, "end": 2.0, "text": "We should maybe proceed carefully."}
|
||||
]
|
||||
"""
|
||||
)
|
||||
proposals = [
|
||||
CorrectionProposal(
|
||||
proposal_index=0,
|
||||
module_instance="spoken_word",
|
||||
module_key="spoken_word",
|
||||
id=1,
|
||||
original_text="I, uh, I think",
|
||||
corrected_text="I think",
|
||||
confidence=0.95,
|
||||
),
|
||||
CorrectionProposal(
|
||||
proposal_index=1,
|
||||
module_instance="spoken_word",
|
||||
module_key="spoken_word",
|
||||
id=2,
|
||||
original_text="maybe proceed carefully",
|
||||
corrected_text="go now",
|
||||
confidence=0.95,
|
||||
),
|
||||
]
|
||||
client = FakeStructuredLLMClient(
|
||||
[
|
||||
{
|
||||
"validations": [
|
||||
{
|
||||
"correction_index": 0,
|
||||
"approved": True,
|
||||
"confidence": 0.97,
|
||||
"reason": "Reasonable dysfluency cleanup that preserves meaning.",
|
||||
},
|
||||
{
|
||||
"correction_index": 1,
|
||||
"approved": False,
|
||||
"confidence": 0.99,
|
||||
"reason": "This changes the substance of the segment rather than cleaning a dysfluency.",
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
result = SpokenWordValidator("spoken_word_review").validate(
|
||||
_context(proposals=proposals, transcript=transcript, llm_client=client, tmp_path=tmp_path)
|
||||
)
|
||||
|
||||
assert [(decision.proposal_index, decision.approved) for decision in result.decisions] == [
|
||||
(0, True),
|
||||
(1, False),
|
||||
]
|
||||
prompt_text = client.calls[0]["messages"][1]["content"]
|
||||
assert "I think we should go." in prompt_text
|
||||
assert "go now" in prompt_text
|
||||
|
||||
|
||||
def test_spoken_word_validator_allows_punctuation_cleanup_tied_to_dysfluency(tmp_path):
|
||||
transcript = parse_transcript_json(
|
||||
"""
|
||||
[
|
||||
{"id": 1, "speaker": "A", "start": 0.0, "end": 1.0, "text": "Well ... I think we should go."}
|
||||
]
|
||||
"""
|
||||
)
|
||||
proposals = [
|
||||
CorrectionProposal(
|
||||
proposal_index=0,
|
||||
module_instance="spoken_word",
|
||||
module_key="spoken_word",
|
||||
id=1,
|
||||
original_text="Well ... ",
|
||||
corrected_text="",
|
||||
confidence=0.95,
|
||||
)
|
||||
]
|
||||
client = FakeStructuredLLMClient(
|
||||
[
|
||||
{
|
||||
"validations": [
|
||||
{
|
||||
"correction_index": 0,
|
||||
"approved": True,
|
||||
"confidence": 0.95,
|
||||
"reason": "Removes a hesitation artifact without changing substantive meaning.",
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
result = SpokenWordValidator("spoken_word_review").validate(
|
||||
_context(proposals=proposals, transcript=transcript, llm_client=client, tmp_path=tmp_path)
|
||||
)
|
||||
|
||||
assert [(decision.proposal_index, decision.approved) for decision in result.decisions] == [(0, True)]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("validator", "payload", "message_fragment"),
|
||||
[
|
||||
@@ -246,6 +351,20 @@ def test_meaning_reversal_validator_rejects_reversal_and_approves_nonreversal(tm
|
||||
},
|
||||
"unknown correction_index",
|
||||
),
|
||||
(
|
||||
SpokenWordValidator("spoken_word_review"),
|
||||
{
|
||||
"validations": [
|
||||
{
|
||||
"correction_index": 99,
|
||||
"approved": True,
|
||||
"confidence": 0.9,
|
||||
"reason": "unknown",
|
||||
}
|
||||
]
|
||||
},
|
||||
"unknown correction_index",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_llm_validators_reject_bad_correction_indexes(tmp_path, validator, payload, message_fragment):
|
||||
@@ -356,6 +475,27 @@ def test_meaning_reversal_prompt_emphasizes_antonyms_and_segment_context():
|
||||
assert "original_segment_text" in messages[1]["content"]
|
||||
|
||||
|
||||
def test_spoken_word_prompt_emphasizes_dysfluency_cleanup():
|
||||
messages = build_spoken_word_messages(
|
||||
[
|
||||
{
|
||||
"correction_index": 0,
|
||||
"id": 1,
|
||||
"original_segment_text": "I, uh, I think we should go.",
|
||||
"corrected_segment_text": "I think we should go.",
|
||||
"original_text": "I, uh, I think",
|
||||
"corrected_text": "I think",
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
combined = messages[0]["content"] + messages[1]["content"]
|
||||
assert "dysfluencies" in combined
|
||||
assert "punctuation" in combined
|
||||
assert "substantive meaning" in combined
|
||||
assert "original_segment_text" in messages[1]["content"]
|
||||
|
||||
|
||||
def test_llm_validators_use_shared_token_batching_helper(monkeypatch, tmp_path):
|
||||
transcript = parse_transcript_json(
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user