test_real_audio.py
Language Python3.1 KB76 lines
"""Integration tier: real models on real speech. Not run in CI (model downloads).TP_INTEGRATION=1 pytest tests/integration # local engine + alignmentTP_INTEGRATION=1 HF_TOKEN=hf_... pytest tests/integration # adds real pyannoteThe clip is four LibriSpeech utterances from two readers (tests/fixtures), sothe words and the speaker boundaries are known."""import jsonimport osfrom pathlib import Pathimport pytestfrom transcription_pipeline import stepsfrom transcription_pipeline.pipeline import Options, processfrom transcription_pipeline.secrets import get_secretpytestmark = pytest.mark.skipif(os.environ.get("TP_INTEGRATION") != "1",reason="set TP_INTEGRATION=1 to run the model tier")FIXTURES = Path(__file__).resolve().parents[1] / "fixtures"CLIP = FIXTURES / "two-speakers.flac"TRUTH = json.loads((FIXTURES / "two-speakers.json").read_text())["turns"]def norm(text):# The model hears LibriSpeech's "ANGOR" as "anger"; either is right.return [w.strip(".,?!").replace("angor", "anger") for w in text.lower().split()]def check(res, expect_speakers):data = json.loads(res.outputs["json"].read_text())sents = data["sentences"]words = [w for s in data["segments"] for w in s.get("words", []) if "start" in w]got = [w for s in sents for w in norm(s["text"])]assert [w for t in TRUTH for w in norm(t["text"]) if w not in got] == []starts = [w["start"] for w in words]assert starts == sorted(starts) and words[-1]["end"] <= res.duration + 0.05if not expect_speakers:return sentsassert [s["speaker"] for s in sents] == ["Speaker 1", "Speaker 2", "Speaker 1", "Speaker 2"]for s, t in zip(sents, TRUTH):assert norm(s["text"])[0] == norm(t["text"])[0]assert t["start"] - 0.3 <= s["start"] <= t["end"]return sentsdef test_local_engine_and_alignment(tmp_path):opts = Options(language="en", diarize=False)res = process(CLIP, tmp_path, opts, steps.transcriber(opts), steps.aligner(opts), None)check(res, expect_speakers=False)def test_speaker_assignment_on_real_words(tmp_path):"""Real transcription, alignment and whisperx.assign_word_speakers; only thediarizer is replaced by the known turns."""import pandas as pdimport whisperxturns = pd.DataFrame([{"start": t["start"], "end": t["end"],"speaker": f"SPEAKER_0{'AB'.index(t['speaker'])}"} for t in TRUTH])def known(result, wav, o):return whisperx.assign_word_speakers(turns, result)opts = Options(language="en")res = process(CLIP, tmp_path, opts, steps.transcriber(opts), steps.aligner(opts), known)check(res, expect_speakers=True)@pytest.mark.skipif(not get_secret("HF_TOKEN"), reason="needs a Hugging Face token for pyannote")def test_pyannote_diarization(tmp_path):opts = Options(language="en", min_speakers=2, max_speakers=2)res = process(CLIP, tmp_path, opts, steps.transcriber(opts), steps.aligner(opts),steps.diarizer(opts))check(res, expect_speakers=True)