fix(speakers): respect Auto-create Speakers setting end-to-end#7574
Conversation
The app's 'Auto-create Speakers' toggle (autoCreateSpeakersEnabled) was stored but never sent to the backend, and the backend created a person on text-based name detection unconditionally — speaker_auto_assign only gated whether the person_id was disclosed to the client, not whether the person was created. So new speakers were always created regardless of the user's choice. - app: send create_speakers=<pref> on the /v4/listen socket (separate from the speaker_auto_assign capability flag, which stays hardcoded enabled). - backend: thread create_speakers through listen_handler -> _listen -> _stream_handler; skip create_person for unmatched names when disabled, still surfacing the detected name as a manual-tag suggestion. Defaults to true, so old clients and web listen are unchanged.
Greptile SummaryThis PR fixes the end-to-end enforcement of the "Auto-create Speakers" setting by introducing a dedicated
Confidence Score: 4/5Safe to merge — the core fix correctly gates The logic change is narrow and well-isolated: the only new path is the backend/routers/transcribe.py around the Important Files Changed
Sequence DiagramsequenceDiagram
participant App as Flutter App
participant WS as /v4/listen WebSocket
participant LH as listen_handler
participant SH as _stream_handler
participant DB as user_db
App->>WS: "connect(?speaker_auto_assign=enabled&create_speakers=false)"
WS->>LH: "listen_handler(speaker_auto_assign="enabled", create_speakers=False)"
LH->>SH: "_stream_handler(speaker_auto_assign_enabled=True, create_speakers=False)"
Note over SH: Audio segment arrives, text-based detection runs
SH->>DB: get_person_by_name(uid, detected_name)
alt Person exists
DB-->>SH: person record
SH->>App: "SpeakerLabelSuggestionEvent(person_id=id, person_name=name)"
SH->>SH: update speaker_to_person_map and segment_person_assignment_map
else "create_speakers=True (default)"
DB-->>SH: null
SH->>DB: "create_person(uid, {id, name, ...})"
SH->>App: "SpeakerLabelSuggestionEvent(person_id=new_id, person_name=name)"
SH->>SH: update speaker_to_person_map and segment_person_assignment_map
else "create_speakers=False (new path)"
DB-->>SH: null
SH->>App: "SpeakerLabelSuggestionEvent(person_id="", person_name=name)"
Note over SH: Maps NOT updated — person_id=None
end
Reviews (1): Last reviewed commit: "fix(speakers): respect Auto-create Speak..." | Re-trigger Greptile |
| if person_id: | ||
| if should_update_speaker_to_person_map(segment.speaker_id): | ||
| speaker_to_person_map[segment.speaker_id] = (person_id, detected_name) | ||
| segment_person_assignment_map[segment.id] = person_id | ||
| suggested_segments.add(segment.id) |
There was a problem hiding this comment.
Repeated suggestions for same speaker when
create_speakers=False
When person_id is None (unmatched name, auto-create disabled), speaker_to_person_map is not updated for that speaker_id. This means every subsequent segment from the same speaker whose text also triggers detect_speaker_from_text will fall through the map lookup and emit another SpeakerLabelSuggestionEvent(person_id="", person_name=detected_name). With create_speakers=True the map entry short-circuits future segments, but here it stays empty, so the client may receive many duplicate unresolved suggestions for the same speaker within a single session. Consider storing a sentinel in speaker_to_person_map (e.g. (None, detected_name)) so subsequent segments from that speaker_id are handled via the map path and only one suggestion is emitted.
Problem
Users who toggled off Auto-create Speakers still got new speaker/person records created during transcription. The setting was effectively dead-wired:
autoCreateSpeakersEnabledpref was stored but never sent to the backend. The/v4/listensocket hardcodedspeaker_auto_assign=enabledand nothing else.create_personfor an unmatched name.speaker_auto_assignonly gated whether the resultingperson_idwas disclosed to the client (a backward-compat capability flag, introduced in Fix speaker auto-assignment: backend-owned with cache refresh [#4554] #4580), not whether the person was created.So a new speaker was created regardless of the user's choice.
Fix
Introduce a dedicated
create_speakersflag, kept separate from thespeaker_auto_assigncapability flag.create_speakers=<autoCreateSpeakersEnabled>on the listen socket.speaker_auto_assign=enabledstays hardcoded — it's a capability flag, not a user preference, and conflating them would break existing-person assignment/diarization display.create_speakersthroughlisten_handler→_listen→_stream_handler. When a detected name has no existing match andcreate_speakersis false, skipcreate_personand don't populate the speaker maps — but still emit the detected name as a suggestion (emptyperson_id) so the user can tag it manually. Existing-person matches still assign as before.Defaults to
true, so old app clients (which don't send the param) and/v4/web/listenare unchanged.Scope
Covers the live
/v4/listenpath (the reported symptom). Batch/pre-recorded and sync creation paths are not touched in this PR.Test plan
create_speakersparam) → unchanged (creates).