Skip to content

QVAC-23544 feat[api]: CosyVoice3 zero-shot / cross-lingual voice cloning - #3872

Merged
mexxik merged 11 commits into
mainfrom
feat/QVAC-23544-cosyvoice-clone
Aug 18, 2026
Merged

QVAC-23544 feat[api]: CosyVoice3 zero-shot / cross-lingual voice cloning#3872
mexxik merged 11 commits into
mainfrom
feat/QVAC-23544-cosyvoice-clone

Conversation

@mexxik

@mexxik mexxik commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Enables CosyVoice3 zero-shot / cross-lingual voice cloning in @qvac/tts-ggml, consuming speech-cpp 2026-08-14 (registry #313, ext-lib #147).

What changes

  • vcpkg-configuration.json baseline repoint 3689b261 → 5dd51e90: the speech-cpp umbrella dependency carries no version floor, so the registry baseline is the version lever. The range contains only the three speech bumps (tts-cpp/speech-cpp Audio8 Vulkan fix, speech-cpp cloning).
  • No addon C++ logic changes — the referenceAudio / promptText / cosyvoiceS3tokModel / cosyvoiceCampplusModel plumbing shipped earlier as reserved; the new engine makes it real. This PR de-reserves the docs and hardens the surface:
    • _assertCosyvoiceCloneConsistent: a clone request that cannot resolve the s3tok + campplus GGUFs (no explicit paths, no model dir) is rejected at construction with an actionable message; promptText stays optional (its absence selects cross-lingual) and legal on its own (baked-voice transcript override). Both sides unit-tested.
    • JSDoc / CosyvoiceConfig.hpp comments / README (voice-cloning section, decision guide, capability matrix, options + files tables, model layout) / CHANGELOG, regenerated index.d.ts / index.js.
    • runCosyvoiceTTS cloning knobs + examples/cosyvoice-tts.js --reference-audio / --prompt-text.
    • Integration tests: zero-shot (jfk.wav + verbatim transcript), cross-lingual (reference only, Chinese text), and the fail-closed construction path.
    • COSYVOICE_CLONE_FILES: a separate optional download tier (s3tok q8_0 + campplus f32, cosy_voice/2026-08-13) — deliberately not folded into the all-or-nothing COSYVOICE_FILES, so existing staged dirs stay valid and non-cloning tests never pay the ~300 MB download.

Semantics (mirror the upstream frontends)

inputs mode
referenceAudio + promptText (verbatim transcript) zero-shot: LM prompted with transcript + reference speech tokens
referenceAudio only cross-lingual: timbre-only conditioning through the flow
promptText only baked-voice transcript override (pre-existing behavior)

Reference limits: mono, 0.5–30 s hard (5–15 s recommended), finite samples. The one-time bake costs ~1 s CPU at load(); per-synthesis cost is unchanged. Every bake failure throws — no silent fallback to the baked voice. instruct composes with a cloned voice. Mobile manifests are untouched (cloning is desktop-scoped for now; the add-on tier is not in the 6-entry mobile pin).

Engine-side validation (ext-lib #147)

Asserting parity suites on CPU, Vulkan (RTX 3090), Metal (Apple Silicon): tokenizer f32/f16/q8_0 vs the upstream token stream (86-87/87), front-end round trip vs the voice.gguf enrollment fixtures, engine-level cloned-vs-baked equivalence and cross-lingual bit-equality under a pinned trajectory, fail-closed guards, mel-extraction thread-safety.

Consumes speech-cpp 2026-08-14 (registry baseline repoint; the umbrella
port carries no version floor): with referenceAudio set, the native
front-end bakes the voice from the recording at load -
speech_tokenizer_v3 tokens, CAM++ speaker embedding, prompt mel -
replacing the baked default voice. promptText selects the mode per the
upstream frontends: verbatim transcript = zero-shot, omitted =
cross-lingual (timbre only). Fail-closed everywhere: a clone request
that cannot resolve the s3tok + campplus add-on GGUFs is rejected at
construction by the new consistency assert, and the engine rejects
missing models, unreadable or non-finite audio, and durations outside
0.5-30 s rather than silently keeping the baked voice.

The JS/addon plumbing already existed (shipped reserved); this change
de-reserves the docs (referenceAudio / promptText /
cosyvoiceS3tokModel / cosyvoiceCampplusModel JSDoc, CosyvoiceConfig
comments, README voice-cloning section + tables + model layout,
CHANGELOG), adds the load-time consistency assert with both-sides unit
coverage, runner and example cloning knobs, zero-shot / cross-lingual /
fail-closed integration tests, and an optional COSYVOICE_CLONE_FILES
download tier (separate from the all-or-nothing COSYVOICE_FILES so
non-cloning tests keep their staged dirs and skip the ~300 MB
download).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mexxik
mexxik requested review from a team as code owners August 14, 2026 13:11
@mexxik mexxik added the run-desktop-addon-tests CI: run desktop integration tests (requires verified) label Aug 14, 2026
@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Review Status

Current Status: ❌ PENDING
Approvals so far: Team Lead: 1

Pending reviews: Needs 1 more from Management, Team Lead, or Member.

@github-actions

Copy link
Copy Markdown
Contributor

License compliance — clean

No new dependency license findings in this PR.

Warn-only (shadow) mode — this check does not block merges yet.

Updated automatically by the canonical license compliance workflow.

NOTICE presence (advisory)

Missing NOTICE (advisory, does not block):

  • ./.github/actions/release-merge-guard
  • ./docs/website
  • ./packages/ggml-coload-smoke
  • ./packages/fabric/test/integration
  • ./packages/inference-addon-cpp/mobile
  • ./packages/sdk/e2e
  • ./packages/llm-llamacpp/benchmarks/performance
  • ./packages/llm-llamacpp/benchmarks/server
  • ./packages/vla-ggml/sim/server
  • ./packages/embed-llamacpp/benchmarks/performance
  • ./packages/embed-llamacpp/benchmarks/server
  • ./packages/asr-ggml/benchmarks/server

…y date

The s3tok + campplus GGUFs were published under cosy_voice/2026-08-14
(qvac#3861), not the 2026-08-13 folder proposed in the handoff; the
desktop lanes failed only on that MODEL_NOT_FOUND.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@ogad-tether ogad-tether left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the diff. Overall solid — the risky part lives behind the speech-cpp baseline repoint, and the JS surface checks out.

Verified locally on the branch (all pass):

  • node scripts/check-generated.mjsindex.js / index.d.ts in sync with src/index.ts
  • brittle-bare test/unit/cosyvoice3.inference.test.js — 31/31, 88 asserts
  • brittle-bare test/unit/example-clone-args.test.js — 3/3, 13 asserts
  • node --test scripts/__tests__/mobile-test-groups.test.js + package-contract.test.js
  • node scripts/validate-mobile-tests.js — up to date
  • prettier --check "test/**/*.js" — clean

Also traced: cosyvoiceS3tokModel / ...Path both normalize correctly, _cosyvoiceModelDir falls back to files.modelDir so _assertCosyvoiceCloneConsistent isn't over-strict, _load() already destroys the addon on a failed activate(), per-call referenceAudio stays Audio8-only, and jfk.wav is mono/11 s — inside the documented 0.5–30 s window.

Three inline comments below (one doc bug, two test-hygiene). Nothing blocking.

Non-blocking notes:

  • The third integration test asserts on /s3tok/i from the native error string; nothing in this repo pins that wording (CosyvoiceModel.cpp validateModelPaths only checks explicitly-set paths — the discovery-failure message comes from speech-cpp). Fine given ext-lib #147, just brittle to upstream rewording.
  • test-groups.json's _comment mentions runParlerWerTest and runCosyvoice3CloneTest but not the newly-excluded runAudio8Test.
  • PR description says the clone tier is cosy_voice/2026-08-13; the code uses 2026-08-14. Description typo only.
  • The baseline repoint moves every default-registry port, not just speech-cpp. Taking the "range contains only the three speech bumps" claim on trust — not verifiable from this repo.

Comment thread packages/tts-ggml/README.md Outdated
Comment thread packages/tts-ggml/test/integration/cosyvoice3-clone.test.js Outdated
Comment thread packages/tts-ggml/test/integration/cosyvoice3-clone.test.js Outdated
- The instruct section's "cloned one" link pointed at #cosyvoice3-1, an id
  GitHub never emits here: only the new ### CosyVoice3 heading slugifies to
  cosyvoice3 (## CosyVoice3 instruct becomes cosyvoice3-instruct), so the
  link went nowhere.
- The base-only fail-closed test unloads in a finally, so an unexpectedly
  successful load - the regression it guards - cannot leave a live native
  instance holding the models.
- That test stages its dir from the base names downloadModel.js now exports
  instead of a hardcoded copy, and falls back to copyFileSync when
  linkSync cannot span filesystems, so a shared model cache on another
  mount or a re-quantized tier fails the assertion rather than the run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ogad-tether
ogad-tether previously approved these changes Aug 17, 2026
GustavoA1604
GustavoA1604 previously approved these changes Aug 17, 2026
mexxik and others added 3 commits August 17, 2026 15:22
…oice-clone

# Conflicts:
#	README.md
#	packages/tts-ggml/CHANGELOG.md
#	packages/tts-ggml/vcpkg.json
Merging main renamed [Unreleased] to [0.7.1], sweeping the cloning entry
into a version that shipped without it (main has no cloning). Moved it
back under a fresh [Unreleased]; 0.7.1 keeps only the Audio8 OpenCL work
it actually released.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Security baseline — findings detected

  • TruffleHog: success
  • CodeQL: failure
  • Severity threshold: high

See the job summary and the repository Security tab for details.

This comment is updated automatically by the canonical security workflow.

GustavoA1604 and others added 2 commits August 17, 2026 14:24
The CodeQL default-setup run for this PR failed uploading its results
during the GitHub incident ('No server is currently available'), and a
GitHub-managed dynamic run cannot be retried through the Actions API, so
a fresh head is the only way to re-run it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

run-desktop-addon-tests CI: run desktop integration tests (requires verified)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants