2.7 KiB
Speaker diarization
Extracted from CLAUDE.md. Subject to the same maintenance rule: when you change diarization behavior, update this file in the same change, and keep the one-line summary in CLAUDE.md accurate.
Opt-in per-profile flag TranscriptionConfig.diarize?: boolean (src/types.ts). When on, the capable adapter embeds Speaker X: labels into the returned transcript string (the v1 shape from FEATURES.md item 4; the transcribe(): Promise<string> interface is unchanged, and cleanup/insert treat the labels as ordinary text). Capability is centralized in transcriptionProviderSupportsDiarization(id) (src/transcription/index.ts), true only for assemblyai / deepgram / revai. The settings tab (src/settings/tab.ts) shows an "Identify speakers" toggle in the per-profile transcription block gated on that helper; the setup card is intentionally left without it (not a "fill in the basics" field).
Per-template override
A template can force diarization on for its own runs via NoteTemplate.diarize?: boolean (frontmatter diarize: true), independent of the profile toggle. src/pipeline.ts collectTranscript merges { ...profile.transcriptionConfig, diarize: true } for the transcribe call ONLY when template.diarize is set AND transcriptionProviderSupportsDiarization(profile.transcriptionProvider) is true; on a non-capable provider it leaves the config untouched (the flag is a documented no-op, not an error). The profile config is never mutated. The Meeting transcript default ships with this set. The override raises the effective setting only (a template never turns OFF a profile's diarization).
Per-adapter behavior
src/transcription/assemblyai.ts sets speaker_labels: true and formats the returned utterances[] (Speaker A: ..., native letter labels); src/transcription/deepgram.ts adds diarize=true and groups per-word speaker indices via formatDiarizedWords (0-based bumped to Speaker 1); src/transcription/revai.ts fetches the JSON transcript (Accept: application/vnd.rev.transcript.v1.0+json) instead of text/plain and rebuilds labels from monologues[] via formatMonologues (0-based bumped to Speaker 1). Each adapter falls back to its flat-text path when the labeled payload is missing, so toggling off is a clean no-op. Label survival through cleanup is handled by a clause in DEFAULT_SHARED_CORE (src/shared-core.ts) telling the LLM to preserve Speaker X: prefixes; the Podcast default template already tolerates labeled and unlabeled input.