diff --git a/src/backend/core/api/viewsets.py b/src/backend/core/api/viewsets.py index 75880908..3ed46f0d 100644 --- a/src/backend/core/api/viewsets.py +++ b/src/backend/core/api/viewsets.py @@ -346,6 +346,7 @@ class RoomViewSet( ): try: MetadataCollectorService().start(recording) + logger.info("Started MetadataCollectorService") except MetadataCollectorException: logger.warning("Failed to start MetadataCollectorService") diff --git a/src/summary/summary/core/user_assign.py b/src/summary/summary/core/user_assign.py index 5d68fdcd..4e1c2770 100644 --- a/src/summary/summary/core/user_assign.py +++ b/src/summary/summary/core/user_assign.py @@ -181,12 +181,13 @@ def _build_participant_timelines( def _build_speaker_timelines( - diarization: dict[str, Any], + transcription: Any, ) -> dict[str, list[Interval]]: - """Build interval timelines from WhisperX diarization segments.""" + """Build interval timelines from WhisperX transcription segments.""" intervals: dict[str, list[Interval]] = {} - for segment in diarization.get("segments", []): + segments = transcription.segments if hasattr(transcription, "segments") else [] + for segment in segments: speaker = segment.get("speaker") if speaker is None: continue @@ -202,7 +203,7 @@ def _build_speaker_timelines( def assign_speakers( metadata: dict[str, Any], - diarization: dict[str, Any], + transcription: Any, recording_start_datetime: datetime, overlap_threshold: float = DEFAULT_OVERLAP_THRESHOLD, ) -> AssignmentResult: @@ -210,7 +211,7 @@ def assign_speakers( Args: metadata: User metadata with `events` and `participants`. - diarization: WhisperX JSON output containing `segments`. + transcription: WhisperX Transcription object with a `segments` attribute. recording_start_datetime: UTC datetime for t=0 reference. overlap_threshold: Minimum overlap/speaker_duration to accept. @@ -221,7 +222,7 @@ def assign_speakers( participant_timelines, participant_names = _build_participant_timelines( metadata, recording_start_datetime ) - speaker_timelines = _build_speaker_timelines(diarization) + speaker_timelines = _build_speaker_timelines(transcription) result = AssignmentResult() diff --git a/src/summary/tests/api/test_api_tasks.py b/src/summary/tests/api/test_api_tasks.py index d10ef10d..2e4f6ec7 100644 --- a/src/summary/tests/api/test_api_tasks.py +++ b/src/summary/tests/api/test_api_tasks.py @@ -31,7 +31,6 @@ class TestTasks: }, ) - print(response) assert response.status_code == 200 assert response.json() == {"id": "task-id-abc", "message": "Task created"} diff --git a/src/summary/tests/unit/test_user_assign.py b/src/summary/tests/unit/test_user_assign.py index 9a4eec79..97ca05d4 100644 --- a/src/summary/tests/unit/test_user_assign.py +++ b/src/summary/tests/unit/test_user_assign.py @@ -1,5 +1,6 @@ """Tests for the speaker-to-user assignment service.""" +from dataclasses import dataclass, field from datetime import datetime from summary.core.user_assign import ( @@ -12,6 +13,13 @@ from summary.core.user_assign import ( assign_speakers, ) + +@dataclass +class FakeTranscription: + """Mimics the OpenAI Transcription pydantic model for testing.""" + + segments: list = field(default_factory=list) + RECORDING_START = datetime.fromisoformat("2026-03-17T15:30:33.000001") METADATA_SINGLE_USER = { @@ -55,8 +63,8 @@ METADATA_SINGLE_USER = { ], } -DIARIZATION_SINGLE_SPEAKER = { - "segments": [ +DIARIZATION_SINGLE_SPEAKER = FakeTranscription( + segments=[ { "start": 1.363, "end": 3.545, @@ -70,7 +78,7 @@ DIARIZATION_SINGLE_SPEAKER = { "speaker": "SPEAKER_00", }, ], -} +) USER_ID = "da8d39ff-3b1c-4e8d-9a70-c630c9871bcb" @@ -209,13 +217,13 @@ class TestAssignSpeakers: ], "participants": [{"participantId": "user-a", "name": "Shared Mic"}], } - diarization = { - "segments": [ + transcription = FakeTranscription( + segments=[ {"start": 1.0, "end": 3.0, "speaker": "SPEAKER_00"}, {"start": 5.0, "end": 7.0, "speaker": "SPEAKER_01"}, ], - } - result = assign_speakers(metadata, diarization, RECORDING_START) + ) + result = assign_speakers(metadata, transcription, RECORDING_START) assert len(result.assignments) == 2 pids = {a.participant_id for a in result.assignments} assert pids == {"user-a"} @@ -250,13 +258,13 @@ class TestAssignSpeakers: {"participantId": "user-b", "name": "Bob"}, ], } - diarization = { - "segments": [ + transcription = FakeTranscription( + segments=[ {"start": 1.5, "end": 3.5, "speaker": "SPEAKER_00"}, {"start": 5.5, "end": 7.5, "speaker": "SPEAKER_01"}, ], - } - result = assign_speakers(metadata, diarization, RECORDING_START) + ) + result = assign_speakers(metadata, transcription, RECORDING_START) assert len(result.assignments) == 2 by_speaker = {a.speaker_label: a for a in result.assignments} assert by_speaker["SPEAKER_00"].participant_name == "Alice" @@ -296,14 +304,14 @@ class TestAssignSpeakers: {"participantId": "user-b", "name": "Bob"}, ], } - diarization = { - "segments": [ + transcription = FakeTranscription( + segments=[ {"start": 1.5, "end": 5.5, "speaker": "SPEAKER_00"}, {"start": 4.0, "end": 7.5, "speaker": "SPEAKER_01"}, ], - } + ) result = assign_speakers( - metadata, diarization, RECORDING_START, overlap_threshold=0.3 + metadata, transcription, RECORDING_START, overlap_threshold=0.3 ) assert len(result.assignments) == 2 by_speaker = {a.speaker_label: a for a in result.assignments} @@ -328,14 +336,14 @@ class TestAssignSpeakers: ], "participants": [{"participantId": "user-a", "name": "Brief User"}], } - diarization = { - "segments": [ + transcription = FakeTranscription( + segments=[ {"start": 1.0, "end": 10.0, "speaker": "SPEAKER_00"}, ], - } + ) result = assign_speakers( metadata, - diarization, + transcription, RECORDING_START, overlap_threshold=0.5, ) @@ -359,30 +367,30 @@ class TestAssignSpeakers: ], "participants": [{"participantId": "user-a", "name": "Early User"}], } - diarization = { - "segments": [ + transcription = FakeTranscription( + segments=[ {"start": 0.0, "end": 3.0, "speaker": "SPEAKER_00"}, ], - } - result = assign_speakers(metadata, diarization, RECORDING_START) + ) + result = assign_speakers(metadata, transcription, RECORDING_START) assert len(result.assignments) == 1 assert result.assignments[0].participant_name == "Early User" def test_empty_diarization(self): """No segments produces empty result.""" result = assign_speakers( - METADATA_SINGLE_USER, {"segments": []}, RECORDING_START + METADATA_SINGLE_USER, FakeTranscription(segments=[]), RECORDING_START ) assert result == AssignmentResult() def test_segment_without_speaker_ignored(self): """Segments missing speaker key are skipped.""" - diarization = { - "segments": [ + transcription = FakeTranscription( + segments=[ {"start": 1.0, "end": 3.0, "text": "no speaker"}, ], - } - result = assign_speakers(METADATA_SINGLE_USER, diarization, RECORDING_START) + ) + result = assign_speakers(METADATA_SINGLE_USER, transcription, RECORDING_START) assert result == AssignmentResult()