meeting-minutes: speaker diarization via siren's new endpoints - #11
Conversation
Requests word timestamps from siren, diarizes the whole recording once, and aligns words to speaker turns server-side, producing speaker-tagged transcript segments and a grouped "SPEAKER_NN:" prompt rendering in place of the raw transcript JSON wall. Diarization is best-effort like the calendar pass: any failure records itself in job status and falls back to the flat transcript; MEETING_MINUTES_DIARIZE=0 disables it. Trailing sub-second ffmpeg segment slivers are dropped via a decoding duration check, since segment-muxer FLAC headers report the cumulative end timestamp rather than the chunk length. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Diarization yields anonymous SPEAKER_NN labels; the minutes model already receives the attendee roster and calendar evidence, so let it resolve labels from content and turn-taking. Mapping sharpens attribution of decisions and follow-up ownership without surfacing per-line bylines, and the prompt warns about split/mislabeled speakers so content outweighs labels on conflict. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Restore timestamps to the speaker-tagged prompt rendering as [m:ss] SPEAKER_NN turn prefixes - the plain speaker rendering had silently removed every timestamp, breaking the screenshot-candidate selection that depends on them. Reject empty-turn diarization responses so a silent diarizer produces the flat-transcript fallback instead of one fabricated speaker, and drop curl retries from the diarize call so a slow whole-file pass is not re-run on the GPU after a client timeout. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Greptile SummaryThis PR adds the client-side speaker diarization pipeline to
Confidence Score: 5/5Safe to merge; diarization runs entirely in best-effort mode and every failure path degrades cleanly to the pre-existing flat transcript. All three new pipeline stages (diarize, align, render) are wrapped in their own isolated try/except blocks that catch subprocess, OS, and application errors and set a failed status before continuing. The transcript.json is only overwritten after valid_alignment passes, and atomic_write_json ensures no partial writes reach the prompt. The one structural gap noted (probe_duration loop not catching a non-zero ffprobe exit before reaching the ffmpeg fallback) affects an uncommon code path and has no impact on the new diarization logic, which correctly uses decode_duration for the sub-second sliver check. Files Needing Attention: bin/meeting-minutes — specifically probe_duration, which gained new fallback strategies but has an incomplete try/except structure in the loop.
|
| Filename | Overview |
|---|---|
| bin/meeting-minutes | Adds end-to-end speaker diarization (diarize → align → speaker-tagged prompt rendering) with graceful fallback; one structural gap in the probe_duration loop prevents the ffmpeg decode fallback from being reached when ffprobe exits non-zero. |
Sequence Diagram
sequenceDiagram
participant J as process_job
participant FC as filter_degenerate_tail_chunks
participant DR as diarize_recording
participant TC as transcribe_chunks
participant AT as align_transcript
participant S as Siren API
J->>FC: chunks (sorted FLACs)
FC->>FC: decode_duration(last chunk)
alt "duration < 1s"
FC-->>J: chunks minus trailing sliver
else
FC-->>J: chunks unchanged
end
J->>DR: diarize_recording(job)
DR->>S: POST /v1/audio/diarize (whole-recording FLAC)
S-->>DR: "{turns, speakers, duration}"
DR-->>J: diarization payload
J->>TC: transcribe_chunks(job, chunks)
TC->>S: "POST /v1/audio/transcriptions (per chunk, +timestamp_granularities[]=word)"
S-->>TC: verbose_json with word timestamps
TC-->>J: transcript.json
J->>AT: align_transcript(job, transcript_path, diarization)
AT->>AT: collect_transcript_words(job, chunks)
AT->>S: "POST /v1/audio/align ({words, turns})"
S-->>AT: "{speakers, segments with speaker labels}"
AT->>AT: overwrite transcript.json with speaker-tagged segments
AT-->>J: transcript_path
J->>J: "render_transcript_for_prompt -> [m:ss] SPEAKER_NN: ..."
J->>J: generate_minutes (Codex prompt)
Reviews (2): Last reviewed commit: "Address Greptile review findings" | Re-trigger Greptile
Reject empty alignment responses so a degenerate align result triggers the flat-transcript fallback instead of silently emptying the transcript, stop catching AssertionError in the job handler (the assert it covered was removed with the decode-based sliver check), and add the PEP 8 blank line before the nested timestamp helper. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Client half of the siren diarization rollout (M4/M5 + review hardening). Three commits touching only
bin/meeting-minutes./v1/audio/diarize), aligns words to turns server-side (/v1/audio/align), and writesspeakers+ per-segmentspeakerintotranscript.jsonadditively.[m:ss] SPEAKER_NN:turns (timestamps retained for the screenshot-candidate pass) plus an instruction to map labels to attendee names from content and turn-taking.MEETING_MINUTES_DIARIZE=0disables.Verified end-to-end on a real recorded meeting against the siren branch: 211/211 segments speaker-tagged, prompt renders 102 timestamped speaker turns, flat fallback exercised. Requires the siren
diarization-rolloutbranch server-side; degrades cleanly against older servers.🤖 Generated with Claude Code