fix: keep chunk details out of speaker headers
Render transcript-wide speaker numbers instead of exposing upload chunks in reader-facing headers. Reconcile adjacent requests using at least two unambiguous word-and-time matches in their shared audio, including when provider speaker IDs change between requests. Reject conflicting mappings and never treat matching numeric provider IDs as identity evidence. Assign fresh IDs where continuity cannot be established and report that uncertainty on stderr. Number only speakers retained after overlap trimming. Document that overlap matching cannot identify a returning speaker who was absent from the overlap, and add the reader-facing boundary to AGENTS.md. Test Plan: - python3 -B -m unittest -v: all 46 tests passed, including swapped IDs, ambiguous evidence, continuity across three requests, and plain headers. - git diff --cached --check: passed. - Tests use synthetic speaker labels; live diarization accuracy was not verified because provider credentials are unavailable in this environment.
This commit is contained in:
1 parent
6e94379a25
commit
5dd1b5811b
7 files changed
+129
-32
No files matched your search
+40
-1
@@ -6,7 +6,7 @@ import tempfile
|
||||
import unittest
|
||||
import wave
|
||||
|
||||
from chunks import CONTEXT, ENCODING_MARGIN, merge, plan, stitch
|
||||
from chunks import CONTEXT, ENCODING_MARGIN, merge, plan, speaker_mapping, stitch
|
||||
from subtitles import convert
|
||||
|
||||
|
||||
@@ -73,6 +73,45 @@ class ChunkPlanTests(unittest.TestCase):
|
||||
|
||||
|
||||
class ChunkMergeTests(unittest.TestCase):
|
||||
def test_speaker_mapping_uses_shared_words_even_when_provider_ids_swap(self):
|
||||
left = [dict(word(text, start), speaker=speaker) for text, start, speaker in (
|
||||
("one", 58, 0), ("two", 58.5, 0), ("three", 60, 1), ("four", 60.5, 1),
|
||||
)]
|
||||
right = [dict(w, speaker=1 - w["speaker"]) for w in left]
|
||||
self.assertEqual(speaker_mapping(left, right, 60), {1: 0, 0: 1})
|
||||
|
||||
def test_speaker_mapping_rejects_missing_sparse_and_conflicting_evidence(self):
|
||||
left = [dict(word(text, start), speaker=0) for text, start in (
|
||||
("one", 58), ("two", 58.5), ("three", 60), ("four", 60.5),
|
||||
)]
|
||||
self.assertEqual(speaker_mapping(left, [dict(left[0], speaker=5)], 60), {})
|
||||
self.assertEqual(speaker_mapping(left, [dict(word("elsewhere", 90), speaker=0)], 60), {})
|
||||
conflicting = [dict(w, speaker=i // 2) for i, w in enumerate(left)]
|
||||
self.assertEqual(speaker_mapping(left, conflicting, 60), {})
|
||||
self.assertEqual(speaker_mapping(conflicting, left, 60), {})
|
||||
repeated = [dict(word("yes", 60), speaker=0)]
|
||||
self.assertEqual(speaker_mapping(repeated, repeated * 2, 60), {})
|
||||
|
||||
def test_overlap_keeps_speaker_identity_through_three_requests(self):
|
||||
chunks = [{"start": 0, "end": 63, "seam": 0},
|
||||
{"start": 57, "end": 120, "seam": 60},
|
||||
{"start": 114, "end": 140, "seam": 117}]
|
||||
responses = [
|
||||
{"words": [dict(word(text, start), speaker=7) for text, start in
|
||||
(("First", 1), ("shared", 59), ("words", 60))]},
|
||||
{"words": [dict(word(text, start), speaker=2) for text, start in
|
||||
(("shared", 2), ("words", 3), ("more", 59), ("speech", 60))]},
|
||||
{"words": [dict(word(text, start), speaker=9) for text, start in
|
||||
(("more", 2), ("speech", 3), ("Last", 20))]},
|
||||
]
|
||||
original = copy.deepcopy(responses)
|
||||
with contextlib.redirect_stderr(io.StringIO()) as errors:
|
||||
result = merge(chunks, responses)
|
||||
self.assertEqual(result["text"], "First shared words more speech Last")
|
||||
self.assertEqual({w["speaker"] for w in result["words"]}, {0})
|
||||
self.assertEqual(errors.getvalue(), "")
|
||||
self.assertEqual(responses, original)
|
||||
|
||||
def test_boundary_word_survives_once_despite_timestamp_drift(self):
|
||||
left = [word("before", 8), word("boundary,", 9.8, 10.4), word("after", 10.5)]
|
||||
right = [word("Boundary", 10.0, 10.6), word("after", 10.6), word("later", 14)]
|
||||
|
||||
Reference in new issue
Block a user