Add canonical audio preparation and meeting context support
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import json
|
||||
import shutil
|
||||
import tempfile
|
||||
import unittest
|
||||
from datetime import datetime
|
||||
@@ -6,6 +7,11 @@ from pathlib import Path
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from scripts import run_mvp_meeting
|
||||
from src.meeting_lab.audio import PreparedAudio
|
||||
from src.meeting_lab.audio.preparation import (
|
||||
DEFAULT_NORMALIZATION_FILTER,
|
||||
DEFAULT_NORMALIZATION_METHOD,
|
||||
)
|
||||
from src.meeting_lab.orchestration import mvp as mvp_api
|
||||
from src.meeting_lab.protocol.generate_direct_protocol import DirectProtocolResult
|
||||
from src.meeting_lab.diarization.backend import DiarizationResult
|
||||
@@ -106,7 +112,28 @@ def fake_diarize(audio_path, output_dir, device_mode, **kwargs):
|
||||
)
|
||||
|
||||
|
||||
def fake_prepare(source, destination, **kwargs):
|
||||
destination.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copyfile(source, destination)
|
||||
normalization_enabled = kwargs.get("normalization_enabled", True)
|
||||
return PreparedAudio(
|
||||
source,
|
||||
source.suffix.removeprefix("."),
|
||||
destination,
|
||||
"ffmpeg",
|
||||
kwargs.get("ffmpeg_executable", "ffmpeg"),
|
||||
normalization_enabled,
|
||||
DEFAULT_NORMALIZATION_METHOD if normalization_enabled else None,
|
||||
DEFAULT_NORMALIZATION_FILTER if normalization_enabled else None,
|
||||
)
|
||||
|
||||
|
||||
class MvpOrchestratorTests(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
patcher = patch.object(mvp_api, "prepare_audio", side_effect=fake_prepare)
|
||||
self.prepare_audio = patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def create_inputs(self, root: Path) -> tuple[Path, Path, Path]:
|
||||
audio = root / "team meeting.wav"
|
||||
whisper_model = root / "ggml-model.bin"
|
||||
@@ -151,6 +178,8 @@ class MvpOrchestratorTests(unittest.TestCase):
|
||||
expected = {
|
||||
"run_metadata.json",
|
||||
"audio/input_manifest.json",
|
||||
"audio/prepared.wav",
|
||||
"audio/preparation_metadata.json",
|
||||
"transcript/whisper_raw.json",
|
||||
"transcript/transcript.json",
|
||||
"transcript/transcript.txt",
|
||||
@@ -167,14 +196,60 @@ class MvpOrchestratorTests(unittest.TestCase):
|
||||
self.assertEqual(metadata["model"], "chosen:model")
|
||||
self.assertIsNone(metadata["failure"])
|
||||
self.assertEqual(whisper.call_count, 1)
|
||||
self.assertEqual(
|
||||
whisper.call_args.args[0], run_dir / "audio" / "prepared.wav"
|
||||
)
|
||||
self.assertEqual(protocol.call_count, 1)
|
||||
self.assertTrue(
|
||||
self.prepare_audio.call_args.kwargs["normalization_enabled"]
|
||||
)
|
||||
preparation = json.loads(
|
||||
(run_dir / "audio" / "preparation_metadata.json").read_text()
|
||||
)
|
||||
self.assertTrue(preparation["normalization_enabled"])
|
||||
|
||||
def test_cli_can_disable_audio_normalization_without_bypassing_preparation(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
args = self.args(root, ["--no-audio-normalization"])
|
||||
with (
|
||||
patch.object(mvp_api, "transcribe_audio", side_effect=fake_transcribe) as whisper,
|
||||
patch.object(
|
||||
mvp_api,
|
||||
"generate_direct_protocol",
|
||||
return_value=protocol_result(),
|
||||
),
|
||||
):
|
||||
code, run_dir, _ = run_mvp_meeting.run(args)
|
||||
|
||||
self.assertEqual(code, 0)
|
||||
self.prepare_audio.assert_called_once()
|
||||
self.assertFalse(
|
||||
self.prepare_audio.call_args.kwargs["normalization_enabled"]
|
||||
)
|
||||
self.assertEqual(
|
||||
whisper.call_args.args[0], run_dir / "audio" / "prepared.wav"
|
||||
)
|
||||
preparation = json.loads(
|
||||
(run_dir / "audio" / "preparation_metadata.json").read_text()
|
||||
)
|
||||
self.assertFalse(preparation["normalization_enabled"])
|
||||
self.assertIsNone(preparation["normalization_method"])
|
||||
self.assertIsNone(preparation["normalization_filter"])
|
||||
|
||||
def test_context_model_endpoint_and_whisper_options_are_forwarded(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
args = self.args(
|
||||
root,
|
||||
["--whisper-executable", "/tools/whisper-cli", "--threads", "4"],
|
||||
[
|
||||
"--whisper-executable",
|
||||
"/tools/whisper-cli",
|
||||
"--ffmpeg-executable",
|
||||
"/tools/ffmpeg",
|
||||
"--threads",
|
||||
"4",
|
||||
],
|
||||
)
|
||||
with (
|
||||
patch.object(mvp_api, "transcribe_audio", side_effect=fake_transcribe) as whisper,
|
||||
@@ -190,6 +265,10 @@ class MvpOrchestratorTests(unittest.TestCase):
|
||||
self.assertEqual(whisper.call_args.args[3], "de")
|
||||
self.assertEqual(whisper.call_args.kwargs["executable"], "/tools/whisper-cli")
|
||||
self.assertEqual(whisper.call_args.kwargs["threads"], "4")
|
||||
self.assertEqual(
|
||||
self.prepare_audio.call_args.kwargs["ffmpeg_executable"],
|
||||
"/tools/ffmpeg",
|
||||
)
|
||||
self.assertEqual(protocol.call_args.args[1], run_dir / "context/meeting_context.yaml")
|
||||
self.assertEqual(protocol.call_args.kwargs["model"], "chosen:model")
|
||||
self.assertEqual(
|
||||
@@ -245,6 +324,9 @@ class MvpOrchestratorTests(unittest.TestCase):
|
||||
|
||||
self.assertEqual(code, 0)
|
||||
self.assertEqual(diarization.call_args.args[2], "gpu")
|
||||
self.assertEqual(
|
||||
diarization.call_args.args[0], run_dir / "audio" / "prepared.wav"
|
||||
)
|
||||
self.assertEqual(diarization.call_args.kwargs["runtime"], "container")
|
||||
self.assertEqual(
|
||||
diarization.call_args.kwargs["container_args"], ("--device=/dev/kfd",)
|
||||
|
||||
Reference in New Issue
Block a user