| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
fix(dub): the speaker-count hint is honored on every diarization path + clone-purity guard (#952) * fix(dub): the speaker-count hint is honored on every diarization path + clone-purity guard The dub "Speakers" count reached _diarize() and then died on 3 of its 4 branches, so setting it changed nothing, speakers blended, and auto-clones were cut from mixed-speaker audio ("made up" voices): - FunASR inline-turns shortcut returned before the hint was ever consulted → now an explicit num_speakers routes the job through pyannote (the one engine that honors an exact count); turns stay the fast path only when no hint is set, and remain the fallback (with an honest "hint ignored" warning) when pyannote can't load or crashes mid-run. - pyannote-unavailable fallback used a hardcoded 2-speaker silence-gap heuristic → assign_speakers_heuristic now takes num_speakers and cycles N labels on gap boundaries (1 → single speaker; None → legacy alternation), and the existing diarization warning says the hint is only approximately honored. - pyannote-crash fallback dropped the hint the same way → same treatment. No branch drops the hint silently anymore: every degraded path extends the existing `warning` SSE payload (detail + a machine-readable speaker_hint field) that the frontend already renders. Parity + purity: - POST /dub/transcribe/{job_id} (the CLI's endpoint) gains the same clamped num_speakers query param, forwarded to pyannote and the heuristic; the omnivoice-dub CLI gains --speakers N. - Clone-purity guard: _pick_reference_slices rejects sub-1.5s slices, prefers slices not temporally adjacent (<0.3s) to another speaker's turn (scoring preference, not a hard filter), and extract_speaker_clones skips extraction entirely when labels came from the heuristic (labels_source kwarg threaded from _diarize; missing kwarg keeps the old behavior) — with a user-facing warning pointing at Settings → Models → pyannote. Tests: fail-before/pass-after coverage in tests/test_speaker_hint.py (all four _diarize branches driven through the real SSE stream), clone-purity guards in tests/test_speaker_clone_purity.py, heuristic hint semantics in tests/test_segmentation.py. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(changelog): add the speaker-hint + clone-purity fix under [Unreleased] (#952) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: mergetest <test@local> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> | 2 个月前 | |
chore: flatten project by moving all contents from submodule to root | 5 个月前 | |
chore: flatten project by moving all contents from submodule to root | 5 个月前 | |
fix(clone): long references clone on OmniVoice; engines report the reference length they use (#2286) * fix(clone): let long references clone and say what each engine uses OmniVoice rejects a transcript paired with a clip over 20 s, and every automatic path produced exactly that pair: Voice Clone and /generate transcribed the whole clip, and saved profiles kept that transcript. A 25 s saved voice was permanently unusable on the default engine even though the model can pick its best 15 s passage from clips up to 75 s. - Drop whole-clip transcripts on over-long OmniVoice references at the engine boundary (prompt cache, inline fallback, sidecar) and skip the wasted whole-clip ASR; a transcript typed on /generate still gets the actionable [clone_ref_too_long] error. - VoxCPM2 ignores a whole-clip transcript once its 30 s cap cuts the clip. - Engines advertise max_ref_seconds/ref_strategy via list_backends(). - Electron Voice Clone shows an engine-aware note instead of the 15 s trim toast and leaves the transcript to engines that pick a passage. Refs #2281 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(clone): patch the sidecar backend's own base class Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(clone): review fixes for long references (#2281) - A transcript sent with a saved voice over the limit gets the same [clone_ref_too_long] 400 as an upload instead of being dropped. - omnivoice_ref_text treats blank text as no transcript. - The VoxCPM2 sidecar advertises its 30 s head limit; a parity test pins every sidecar's reference metadata to its in-process engine. - The Voice Clone transcript hook withdraws its own whole-clip transcript when the engine starts picking the passage itself, keeping user edits. - Changelog credits use the standard form. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(generate): probe reference length off the event loop Non-WAV clips decode through ffmpeg to measure length; doing it inline on the async route stalled every other request. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> | 1 天前 | |
chore: flatten project by moving all contents from submodule to root | 5 个月前 | |
chore: flatten project by moving all contents from submodule to root | 5 个月前 | |
fix(clone): long references clone on OmniVoice; engines report the reference length they use (#2286) * fix(clone): let long references clone and say what each engine uses OmniVoice rejects a transcript paired with a clip over 20 s, and every automatic path produced exactly that pair: Voice Clone and /generate transcribed the whole clip, and saved profiles kept that transcript. A 25 s saved voice was permanently unusable on the default engine even though the model can pick its best 15 s passage from clips up to 75 s. - Drop whole-clip transcripts on over-long OmniVoice references at the engine boundary (prompt cache, inline fallback, sidecar) and skip the wasted whole-clip ASR; a transcript typed on /generate still gets the actionable [clone_ref_too_long] error. - VoxCPM2 ignores a whole-clip transcript once its 30 s cap cuts the clip. - Engines advertise max_ref_seconds/ref_strategy via list_backends(). - Electron Voice Clone shows an engine-aware note instead of the 15 s trim toast and leaves the transcript to engines that pick a passage. Refs #2281 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(clone): patch the sidecar backend's own base class Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(clone): review fixes for long references (#2281) - A transcript sent with a saved voice over the limit gets the same [clone_ref_too_long] 400 as an upload instead of being dropped. - omnivoice_ref_text treats blank text as no transcript. - The VoxCPM2 sidecar advertises its 30 s head limit; a parity test pins every sidecar's reference metadata to its in-process engine. - The Voice Clone transcript hook withdraws its own whole-clip transcript when the engine starts picking the passage itself, keeping user edits. - Changelog credits use the standard form. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(generate): probe reference length off the event loop Non-WAV clips decode through ffmpeg to measure length; doing it inline on the async route stalled every other request. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> | 1 天前 | |
fix(startup): don't let one optional transformers symbol kill the backend (#1229) `backend/api/routers/profiles.py` imports two pure-stdlib regex helpers from `omnivoice.utils.voice_design`. That import pulled in `omnivoice/__init__`, which eagerly imported `omnivoice.models.omnivoice` — torch, torchaudio, transformers, flex_attention, the whole model definition — including a top-level `from transformers import HiggsAudioV2TokenizerModel`. transformers exposes that class through its lazy module and gates it on the torchaudio backend, so the *attribute access* raises when torchaudio is missing, ABI-mismatched, or installed without discoverable distribution metadata — Colab's system Python, an interrupted `uv pip install`. It raised during `backend/main.py`'s module import, before FastAPI existed: TTS, dubbing, ASR and Settings all dead, the user left with a uvicorn traceback and "Backend did not become healthy within 5 minutes". Two changes, both structural rather than Colab-specific: - `omnivoice/__init__` resolves its model exports lazily (PEP 562). Importing `omnivoice.utils.*` no longer costs — or risks — the model stack. `from omnivoice import OmniVoice` is unchanged; only the timing moves. `backend.spec` already lists `omnivoice.models.omnivoice` as a hidden import, so the frozen build is unaffected. - `HiggsAudioV2TokenizerModel` resolves at its single use site in `from_pretrained`, and a failure there raises an ImportError naming torchaudio and the reinstall. Deferred into a request, `core.failure .classify()` maps it to TRANSFORMERS_IMPORT and attaches a repair hint — whose text now names torchaudio too, instead of only transformers + an ASR workaround irrelevant to this path. Colab notebook: cell 2's sanity check imports the model stack (and prints torchaudio/transformers versions), so a broken env fails there with the real error instead of as a health timeout two cells later. Regression test: tests/test_omnivoice_lazy_model_import.py pins that the utils import loads no heavy module, the lazy exports still resolve, and the deferred failure is actionable and classified. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> | 2 个月前 |