Repository navigation
fix(backend): keep speaker identification and voice profiles working with Custom STT (#25148) - #25333
Conversation
Git-on-my-level
left a comment
There was a problem hiding this comment.
Verdict: the core fix is correct and nicely scoped — speaker identification and voice profiles stay live under Custom STT, degrade cleanly when raw audio is not forwarded, and upstream is_user/person_id labels survive merging and resolution. Nothing blocking from my side; one scope confirmation and two nits below.
Verified on head 0369e4e:
- backend/utils/transcribe_decisions.py — should_load_speech_profile / should_enable_speaker_identification no longer special-case Custom STT; the skip moves to queue time via the new custom_stt_without_raw_audio() (ring buffer present but empty => no embedding work), which handles sendRawAudioToOmi=false correctly. preserve_upstream_speaker_identity() defers to matcher provenance (segment_assignments, speaker_label_source, speaker_match_source), so Omi's own assignments still win.
- backend/routers/listen/speakers.py — dropping use_custom_stt from the _load_profiles eligibility predicate mirrors the decision-function change; owner/People profile loads stay bounded by the existing failed-load caps.
- backend/routers/listen/transcripts.py — _apply_speaker_identity_statuses skips re-assignment for upstream-identified segments; since should_queue_speaker_embedding already refuses segments with person_id/is_user set, upstream-labeled turns never enqueue embedding work either.
- backend/models/transcript_segment.py — both merge gates now require is_user and person_id equality. This is the widest-blast-radius line of the PR: it applies to every transcription session, not just Custom STT (adjacent same-speaker segments carrying different identities no longer merge). The tightening looks right and is pinned by test_combine_segments_preserves_distinct_upstream_identities_on_same_speaker_label — flagging for an explicit maintainer nod.
- backend/utils/conversations/speaker_resolution.py — the post-conversation preserve path is gated on conversation.uses_custom_stt, so regular conversations keep today's resolution semantics.
- backend/docs/transcription.md — documents speaker_id / is_user / person_id and the raw-audio note; matches the implemented behavior.
- Tests — test_user_speaker_embedding.py adds ~290 lines covering raw-audio on/off and identity preservation including matcher retraction; test_transcribe_decisions.py pins the new decision table. Good coverage.
Notes:
- preserve_upstream_speaker_identity in _apply_speaker_identity_statuses is not gated on Custom STT — any segment arriving with is_user/person_id and no matcher provenance is preserved in every flow. This seems intended (upstream = any provider/client), just confirming it is deliberate.
- Readability nit: the predicate mutates speaker_identity_status (unknown -> user/not_user), and the two process_loop / _process_v2_batches call sites use it purely for that side effect with the return value discarded; a mark_upstream_identity-style name would read better in a follow-up.
- The use_custom_stt parameters on the two decision functions are now ignored — consider dropping them in a follow-up cleanup.
CI: the Backend unit suite failure on this head is a CPU-duration guard flake in test_batch_upload_coalescing.py and test_conversation_erasure_queue_deploy.py (1.00s / 1.03s vs the 1.00s limit) — files this PR does not touch, and every test in them passed. Not related to this change.
Performance: embedding extraction only runs when raw audio actually exists in the ring buffer; no extra queries per segment. Cost: no new external calls. Security/privacy: raw audio is only processed for speaker ID when the user already forwards it; posture unchanged otherwise. Blast radius: non-Custom-STT users are only affected by the transcript_segment.py merge-gate tightening noted above.
USER IMPACT: with Custom STT plus 'Send raw audio to Omi' enabled, speaker names and owner attribution keep working; without raw audio, is_user / person_id labels supplied by your STT provider are preserved instead of being dropped.
Still wanted as a reviewer: maintainer sign-off on the merge-gate identity-equality tightening for non-Custom-STT sessions, and ideally one replay/e2e pass with a real Custom STT payload carrying is_user/person_id (unit coverage is thorough but synthetic). Leaving for human maintainer review before merge.
by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with need human response.
|
Thanks for the thorough review @Git-on-my-level!
|
4a3c983 to
d549f3d
Compare
d549f3d to
fa306d9
Compare
… use_custom_stt params
fa306d9 to
5fdb47b
Compare
What changed and why
Fixes #25148. Keep Omi's speaker identification and owner/People voice profiles enabled when Custom STT (
custom_stt_mode=enabled) is active so live speaker matching continues to work whenever raw audio is forwarded (sendRawAudioToOmi=true), skip embedding extraction cleanly when raw audio is not streamed (sendRawAudioToOmi=false), and preserve upstreamis_userandperson_idmetadata supplied directly by a Custom STT provider across segment merging, live transcript status projection, and post-conversation speaker resolution.Line-Count-Exception: backend/routers/listen/transcripts.py | 1816 -> 1834 | Guard custom STT speaker-embedding queueing without raw audio and preserve upstream speaker metadata
Line-Count-Exception: backend/utils/conversations/speaker_resolution.py | 1567 -> 1571 | Preserve upstream custom STT speaker metadata during post-conversation speaker resolution
Product invariants affected
none
How it was verified
_speaker_detection/_apply_speaker_identity_statuses, and post-conversationapply_speaker_resolutionpaths across Custom STT sessions both with and without streamed raw audio (sendRawAudioToOmi=trueandsendRawAudioToOmi=false), as well as upstreamis_userandperson_idpayloads:pytest tests/unit/test_user_speaker_embedding.py tests/unit/test_transcribe_decisions.py tests/unit/test_listen_custom_stt_without_raw_audio.py tests/unit/test_live_speaker_carry.py tests/unit/test_transcript_segment.py tests/unit/test_speaker_label_provenance.py tests/unit/test_listen_parity_runtime.py tests/unit/test_listen_runtime_regressions.py -q->223 passedtest_*ratchet*.py+test_reason_label_enums.py) ->103 passed2630 passedblack --check --line-length 120 --skip-string-normalization->8 files would be left unchangedTests
backend/tests/unit/test_user_speaker_embedding.py:TestTranscribeFirestoreLoading::test_custom_stt_loads_owner_and_people_voice_profilesandtest_excluded_profiles_are_not_read_or_loadedverify owner and People voice profiles load undercustom_stt_mode='enabled'whileinclude_speech_profile=False(freemium:on-device),onboarding_mode=True, and multi-channel sessions remain excluded.TestCustomSttSpeakerIdentificationverifies unlabeled Custom STT segments queue for speaker embedding matching when PCM audio is present inaudio_ring_buffer, skip queueing cleanly when raw audio is absent, and preserve upstreamis_user/person_idacrossTranscriptSegment.combine_segments,TranscriptProcessor._apply_speaker_identity_statuses, andapply_speaker_resolution.backend/tests/unit/test_transcribe_decisions.py:test_speech_profile_and_speaker_id_gatesandtest_upstream_speaker_identity_predicate_and_mutator_are_separatedverify decision gates withoutuse_custom_sttplus separatedhas_upstream_speaker_identity/mark_upstream_speaker_identityhelpers.UI evidence
UI-Evidence: none -- backend Custom STT speaker identification and voice profile pipeline fix with no UI changes
Failure class (fixes)
Failure-Class: none