feat!: use whole-recording Pyannote speaker diarization

Sampled Gemini voice matching could leave one person under separate IDs
across transcription chunks. Use hosted Precision-2 on the complete
recording and align its exclusive speaker timeline with MAI word timing.
Preserve transcription text, order, and timestamps; leave ambiguous word
assignments unknown instead of merging identities to satisfy a count.

Upload lossless FLAC, bound polling, retry transient status reads against
the same job, and validate results before emitting stdout. Keep optional
speaker-count and hosted model overrides. Disable MAI diarization and use
30-minute transcription uploads in both modes. Respect environment keys,
keep authentication out of process arguments, and save optional diagnostics
without audio, credentials, or signed upload URLs.

Test Plan:
- python3 -B -m unittest -v: all 101 regression tests passed, including
  mocked provider upload, polling, alignment, failure, and cleanup paths.
- bash -n transcribe and shellcheck transcribe: passed.
- git diff --cached --check: passed.
- Prepared the 74-minute input locally and verified identical decoded PCM
  hashes for the normalized WAV and full-recording lossless FLAC.
- Live API testing was explicitly deferred by the user; mocked results do
  not establish speaker accuracy on the recording.

BREAKING CHANGE: Default diarization requires PYANNOTE_API_KEY or its
1Password reference. --speaker-model accepts hosted precision-2 or
community-1 instead of OpenRouter audio-chat model IDs.
This commit is contained in:
ddidderr committed 2026-09-12 11:18:30 +02:00
1 parent d7827d39f5
commit 3042b7743b
7 files changed
+1336 -864

No files matched your search

+22 -10
View File
@@ -24,18 +24,30 @@
- `chunks.py`: upload boundaries and ordered overlap stitching.
- `subtitles.py`: timestamp validation and SRT cue formatting.
- `transcript.py`: readable text paragraphs and speaker markers.
- `diarization.py`: automatic OpenRouter audio sample comparison and speaker ID reconciliation.
- `diarization.py`: whole-recording Pyannote jobs and speaker timeline alignment.
- `test_*.py` and `tests/fake_provider.py`: local regression tests without real
credentials or provider requests.
Keep the CLI and its four Python helpers together. Preserve transcript order
and emit stdout only after all chunks and output conversion succeed. Provider
speaker IDs are request-local; reuse transcript IDs across requests only with
supporting overlap or explicit shared-audio comparison evidence. Voice comparison
must preserve transcription text and timestamps, validate complete sample
assignments, and leave uncertain or conflicting identities separate. Keep chunk
details out of the final transcript.
and emit stdout only after all chunks, the enabled Pyannote job, and output
conversion succeed. MAI provides text and timestamps with provider diarization
disabled. Speaker identities come from one Pyannote job on the whole recording;
MAI request-local speaker IDs must not establish identity. Align Pyannote's
exclusive timeline without changing transcription text, word order, or
timestamps. Validate the returned timeline and leave ambiguous or tied word
assignments unknown with warnings. Keep chunk details out of the final transcript.
Progress, processing details, and errors belong on stderr.
The normal command detects the speaker count and reconciles voices automatically;
`--speakers` and `--speaker-model` are optional overrides. `--no-diarization`
disables both provider diarization and the voice comparison request.
The normal command uses hosted `precision-2` and detects the speaker count
automatically. `--speakers` supplies an optional known count; `--speaker-model`
accepts hosted `precision-2` or `community-1`. `--no-diarization` skips Pyannote
and strips unsolicited speaker labels from MAI responses. MAI uploads default to
30-minute chunks in either mode, with eight concurrent workers.
Respect existing `OPENROUTER_API_KEY` and `PYANNOTE_API_KEY` environment values.
Resolve missing keys through `op` only when using the relevant key reference;
do not invent a default `PYANNOTE_API_KEY_REF`. Diagnostics are opt-in, belong in
a new private directory, and may retain completed stages after a later failure.
Save only `chunks.json`, `transcript.json`, `diarization.json`, and `labeled.json`
as applicable. Do not save audio, credentials, or signed URLs in diagnostics,
and never commit diagnostics or generated transcripts.