Add speaker-attributed meeting transcription

This commit is contained in:
2026-06-12 12:18:35 +00:00
parent 7516e0ad61
commit 8fd5e5b464
7 changed files with 571 additions and 13 deletions
+78 -7
View File
@@ -70,13 +70,14 @@ def test_build_retry_notice_mentions_reason_and_path():
assert "`/retry`" in message
def test_meeting_recorder_wraps_wave_sink_with_silence_generator(tmp_path, monkeypatch):
def test_meeting_recorder_wraps_meeting_sink_with_silence_generator(tmp_path, monkeypatch):
created = {}
class FakeWaveSink:
def __init__(self, output_path):
class FakeMeetingSink:
def __init__(self, output_path, tracks_dir):
self.output_path = output_path
created["wave"] = self
self.tracks_dir = tracks_dir
created["meeting"] = self
class FakeSilenceGeneratorSink:
def __init__(self, destination):
@@ -94,7 +95,7 @@ def test_meeting_recorder_wraps_wave_sink_with_silence_generator(tmp_path, monke
self.listened_sink = sink
created["after"] = after
monkeypatch.setattr(voice.voice_recv, "WaveSink", FakeWaveSink)
monkeypatch.setattr(voice, "MeetingSink", FakeMeetingSink)
monkeypatch.setattr(voice.voice_recv, "SilenceGeneratorSink", FakeSilenceGeneratorSink)
vc = FakeVoiceClient()
@@ -103,8 +104,9 @@ def test_meeting_recorder_wraps_wave_sink_with_silence_generator(tmp_path, monke
asyncio.run(recorder.start(lambda error: None))
assert created["wave"].output_path == str(output_path)
assert created["silence"].destination is created["wave"]
assert created["meeting"].output_path == str(output_path)
assert created["meeting"].tracks_dir == str(tmp_path / "tracks")
assert created["silence"].destination is created["meeting"]
assert vc.listened_sink is created["silence"]
assert recorder.recording is True
@@ -129,3 +131,72 @@ def test_wait_for_file_ready_accepts_valid_wav(tmp_path):
ready = asyncio.run(bot.wait_for_file_ready(str(valid), attempts=3, delay=0.001))
assert ready is True
def test_load_speaker_tracks_resolves_manifest_tracks_next_to_recording(tmp_path):
meeting = tmp_path / "meeting.wav"
meeting.write_bytes(b"RIFFdemo")
tracks_dir = tmp_path / "tracks"
tracks_dir.mkdir()
alice = tracks_dir / "track-111.wav"
alice.write_bytes(b"alice")
missing = tracks_dir / "track-222.wav"
(tracks_dir / "tracks.json").write_text(
'{"tracks":[{"display_name":"Alice","path":"missing-prefix/track-111.wav"},'
'{"display_name":"Bob","path":"' + str(missing) + '"}]}'
)
tracks = bot._load_speaker_tracks(str(meeting))
assert tracks == [{"display_name": "Alice", "path": str(alice)}]
def test_transcribe_recording_uses_speaker_tracks_when_manifest_exists(tmp_path, monkeypatch):
meeting = tmp_path / "meeting.wav"
meeting.write_bytes(b"RIFFdemo")
tracks_dir = tmp_path / "tracks"
tracks_dir.mkdir()
alice = tracks_dir / "track-111.wav"
alice.write_bytes(b"alice")
(tracks_dir / "tracks.json").write_text(
'{"tracks":[{"display_name":"Alice","path":"' + str(alice) + '"}]}'
)
calls = []
async def fake_transcribe_tracks(tracks):
calls.append(("tracks", tracks))
return "[Alice]\nhello"
async def fake_transcribe(path):
calls.append(("single", path))
return "single transcript"
monkeypatch.setattr(bot, "transcribe_tracks", fake_transcribe_tracks)
monkeypatch.setattr(bot, "transcribe", fake_transcribe)
result = asyncio.run(bot._transcribe_recording(str(meeting), guild_id=123))
assert result == "[Alice]\nhello"
assert calls == [("tracks", [{"display_name": "Alice", "path": str(alice)}])]
def test_transcribe_recording_falls_back_to_single_file_for_old_recordings(tmp_path, monkeypatch):
meeting = tmp_path / "meeting.wav"
meeting.write_bytes(b"RIFFdemo")
calls = []
async def fake_transcribe_tracks(tracks):
calls.append(("tracks", tracks))
return "should not be used"
async def fake_transcribe(path):
calls.append(("single", path))
return "single transcript"
monkeypatch.setattr(bot, "transcribe_tracks", fake_transcribe_tracks)
monkeypatch.setattr(bot, "transcribe", fake_transcribe)
result = asyncio.run(bot._transcribe_recording(str(meeting), guild_id=123))
assert result == "single transcript"
assert calls == [("single", str(meeting))]