From 79400188d8f2efda68f5ccfd192003757a1c70fe Mon Sep 17 00:00:00 2001 From: leo <260626284+cameledev@users.noreply.github.com> Date: Wed, 13 May 2026 18:38:06 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=8A(summary)=20improve=20logging=20of?= =?UTF-8?q?=20speaker=20assign?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Structure logging of speaker assignment in json format to help assess its performance. --- src/summary/summary/core/user_assign.py | 58 ++++++++++++++++++++----- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/src/summary/summary/core/user_assign.py b/src/summary/summary/core/user_assign.py index 84873b8d..1a8f9b98 100644 --- a/src/summary/summary/core/user_assign.py +++ b/src/summary/summary/core/user_assign.py @@ -8,9 +8,10 @@ Multiple speakers can map to the same participant (e.g. two people sharing one microphone). A participant with no matching speaker gets no assignment. """ +import json import logging from collections import defaultdict -from dataclasses import dataclass, field +from dataclasses import asdict, dataclass, field, is_dataclass from datetime import datetime from typing import Any @@ -318,6 +319,24 @@ def _build_speaker_timelines(transcription: Any) -> dict[str, list[Interval]]: return intervals +def _json_default(obj: Any) -> Any: + """Encode datetimes, dataclasses, and pydantic models for `json.dumps`. + + Intended to be used for logging of `resolve_speaker_identities` (input + and computed variables) + """ + if isinstance(obj, datetime): + return obj.isoformat() + if is_dataclass(obj) and not isinstance(obj, type): + return asdict(obj) + if hasattr(obj, "segments") and hasattr(obj, "word_segments"): + return {"segments": obj.segments, "word_segments": obj.word_segments} + if hasattr(obj, "model_dump"): + return obj.model_dump(mode="json") + + raise TypeError(f"Object of type {type(obj).__name__} is not JSON serializable") + + def resolve_speaker_identities( metadata: dict[str, Any], transcription: Any, @@ -344,17 +363,6 @@ def resolve_speaker_identities( ) speaker_timelines = _build_speaker_timelines(transcription) - logger.debug( - "Assignment inputs: %d participants, %d speakers\n%s\n%s\n%s", - len(participant_timelines), - len(speaker_timelines), - participant_timelines, - speaker_timelines, - _format_timelines_debug( - participant_timelines, participant_names, speaker_timelines - ), - ) - result = AssignmentResult() for speaker, speaker_intervals in speaker_timelines.items(): @@ -397,4 +405,30 @@ def resolve_speaker_identities( overlap_threshold, ) + logger.debug( + json.dumps( + { + "input": { + "recording_start_datetime": recording_start_datetime.isoformat(), + "recording_end_datetime": recording_end_datetime.isoformat(), + "metadata": metadata, + "transcription": transcription, + }, + "computed": { + "speaker_timelines": speaker_timelines, + "participant_timelines": participant_timelines, + "result": result, + }, + }, + default=_json_default, + indent=2, + ensure_ascii=False, + ), + ) + logger.debug( + _format_timelines_debug( + participant_timelines, participant_names, speaker_timelines + ), + ) + return result