diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 63a59b2..ef934ee 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -1,24 +1,26 @@ name: qwentts-cpp-python wheels "on": + pull_request: + branches: + - main + paths: + - ".github/workflows/wheels.yml" + - "pyproject.toml" + - "setup.py" + - "scripts/**" + - "src/**" + - "tests/**" workflow_dispatch: inputs: qwentts_ref: description: qwentts.cpp commit, tag, or branch to build required: true - default: eda8b59092b8d7b7142177c599b08909b87f63b4 - cuda_architectures: - description: CMAKE_CUDA_ARCHITECTURES for portable CUDA binaries - required: true - default: 75-virtual;80-virtual;86-real;89-real;120a-real;121a-real - wheel_build_tag: - description: Optional wheel build tag - required: true - default: 1cu130 + default: f79b23a56702584d51e3a66509b881cd99a95bba jobs: linux-cuda: - name: Linux ${{ matrix.arch }} CUDA wheel + name: Linux ${{ matrix.arch }} CUDA ${{ matrix.cuda_version }} wheel runs-on: ${{ matrix.runner }} strategy: fail-fast: false @@ -27,13 +29,41 @@ jobs: - arch: x86_64 runner: ubuntu-24.04 cuda_repo_arch: x86_64 + cuda_version: "12.8" + cuda_package_suffix: "12-8" + cuda_home: /usr/local/cuda-12.8 + wheel_build_tag: 1cu128 + cuda_architectures: 75-virtual;80-virtual;86-real;89-real;90-real;100-real;120-real;120-virtual - arch: aarch64 runner: ubuntu-24.04-arm cuda_repo_arch: sbsa + cuda_version: "12.8" + cuda_package_suffix: "12-8" + cuda_home: /usr/local/cuda-12.8 + wheel_build_tag: 1cu128 + cuda_architectures: 75-virtual;80-virtual;86-real;89-real;90-real;100-real;120-real;120-virtual + - arch: x86_64 + runner: ubuntu-24.04 + cuda_repo_arch: x86_64 + cuda_version: "13.0" + cuda_package_suffix: "13-0" + cuda_home: /usr/local/cuda-13.0 + wheel_build_tag: 1cu130 + cuda_architectures: 75-virtual;80-virtual;86-real;89-real;90-real;100-real;120-real;120-virtual;121-real;121-virtual + - arch: aarch64 + runner: ubuntu-24.04-arm + cuda_repo_arch: sbsa + cuda_version: "13.0" + cuda_package_suffix: "13-0" + cuda_home: /usr/local/cuda-13.0 + wheel_build_tag: 1cu130 + cuda_architectures: 75-virtual;80-virtual;86-real;89-real;90-real;100-real;120-real;120-virtual;121-real;121-virtual env: QWENTTS_CPP_BACKEND: cuda QWENTTS_CPP_BUILD_JOBS: "2" - QWENTTS_CPP_WHEEL_BUILD_TAG: ${{ inputs.wheel_build_tag }} + QWENTTS_CPP_WHEEL_BUILD_TAG: ${{ matrix.wheel_build_tag }} + QWENTTS_REF: ${{ inputs.qwentts_ref || 'f79b23a56702584d51e3a66509b881cd99a95bba' }} + CUDA_ARCHITECTURES: ${{ matrix.cuda_architectures }} steps: - uses: actions/checkout@v4 @@ -58,17 +88,17 @@ jobs: sudo dpkg -i cuda-keyring_1.1-1_all.deb sudo apt-get update sudo apt-get install -y --no-install-recommends \ - cuda-nvcc-13-0 \ - cuda-cudart-dev-13-0 \ - libcublas-dev-13-0 - echo "/usr/local/cuda-13.0/bin" >> "$GITHUB_PATH" - echo "CUDA_HOME=/usr/local/cuda-13.0" >> "$GITHUB_ENV" - echo "CMAKE_CUDA_COMPILER=/usr/local/cuda-13.0/bin/nvcc" >> "$GITHUB_ENV" + cuda-nvcc-${{ matrix.cuda_package_suffix }} \ + cuda-cudart-dev-${{ matrix.cuda_package_suffix }} \ + libcublas-dev-${{ matrix.cuda_package_suffix }} + echo "${{ matrix.cuda_home }}/bin" >> "$GITHUB_PATH" + echo "CUDA_HOME=${{ matrix.cuda_home }}" >> "$GITHUB_ENV" + echo "CMAKE_CUDA_COMPILER=${{ matrix.cuda_home }}/bin/nvcc" >> "$GITHUB_ENV" - name: Fetch qwentts.cpp run: | git clone --recursive https://github.com/ServeurpersoCom/qwentts.cpp third_party/qwentts.cpp - git -C third_party/qwentts.cpp checkout "${{ inputs.qwentts_ref }}" + git -C third_party/qwentts.cpp checkout "$QWENTTS_REF" git -C third_party/qwentts.cpp submodule update --init --recursive - name: Build wheel @@ -79,12 +109,64 @@ jobs: --clean \ --cmake-arg=-G \ --cmake-arg=Ninja \ - --cmake-arg="-DCMAKE_CUDA_ARCHITECTURES=${{ inputs.cuda_architectures }}" + --cmake-arg="-DCMAKE_CUDA_ARCHITECTURES=$CUDA_ARCHITECTURES" + python -m build --wheel + mkdir -p wheelhouse + cp dist/*.whl wheelhouse/ + + - uses: actions/upload-artifact@v4 + with: + name: qwentts-cpp-python-linux-${{ matrix.arch }}-cuda-${{ matrix.wheel_build_tag }} + path: wheelhouse/*.whl + + linux-cpu: + name: Linux ${{ matrix.arch }} CPU wheel + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + include: + - arch: x86_64 + runner: ubuntu-24.04 + - arch: aarch64 + runner: ubuntu-24.04-arm + env: + QWENTTS_CPP_BACKEND: cpu + QWENTTS_CPP_BUILD_JOBS: "2" + QWENTTS_CPP_WHEEL_BUILD_TAG: 1cpu + QWENTTS_REF: ${{ inputs.qwentts_ref || 'f79b23a56702584d51e3a66509b881cd99a95bba' }} + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install build tools + run: | + python -m pip install --upgrade pip build wheel + sudo apt-get update + sudo apt-get install -y --no-install-recommends cmake ninja-build patchelf ca-certificates + + - name: Fetch qwentts.cpp + run: | + git clone --recursive https://github.com/ServeurpersoCom/qwentts.cpp third_party/qwentts.cpp + git -C third_party/qwentts.cpp checkout "$QWENTTS_REF" + git -C third_party/qwentts.cpp submodule update --init --recursive + + - name: Build wheel + run: | + python scripts/build_native.py \ + --source third_party/qwentts.cpp \ + --backend cpu \ + --clean \ + --cmake-arg=-G \ + --cmake-arg=Ninja python -m build --wheel mkdir -p wheelhouse cp dist/*.whl wheelhouse/ - uses: actions/upload-artifact@v4 with: - name: qwentts-cpp-python-linux-${{ matrix.arch }}-cuda + name: qwentts-cpp-python-linux-${{ matrix.arch }}-cpu path: wheelhouse/*.whl diff --git a/README.md b/README.md index ea623d3..c53160e 100644 --- a/README.md +++ b/README.md @@ -16,13 +16,28 @@ python scripts/build_native.py \ --source /path/to/qwentts.cpp \ --backend cuda \ --clean -QWENTTS_CPP_WHEEL_BUILD_TAG=1cu130 python -m build --wheel +QWENTTS_CPP_WHEEL_BUILD_TAG=1cu128 python -m build --wheel +``` + +CPU development build: + +```bash +python scripts/build_native.py \ + --source /path/to/qwentts.cpp \ + --backend cpu \ + --clean +QWENTTS_CPP_WHEEL_BUILD_TAG=1cpu python -m build --wheel ``` `--backend cuda` is the default because `faster-qwen3-tts` is a CUDA-first package. CPU builds are still useful for development and smoke tests, but they are not the primary release target. +The CI wheel build defaults to qwentts.cpp +`f79b23a56702584d51e3a66509b881cd99a95bba`, which includes the scheduler reset +before talker graph allocation. CPU and CUDA both stay on the backend +prompt-projection path. + `QWENTTS_CPP_WHEEL_BUILD_TAG` is useful for local wheelhouses. For public indexes, publish one backend flavor per package/version/platform compatibility tag; otherwise pip has no way to choose between CPU and CUDA binaries. diff --git a/samples/pr-wheel-audio/README.md b/samples/pr-wheel-audio/README.md new file mode 100644 index 0000000..f48dd1f --- /dev/null +++ b/samples/pr-wheel-audio/README.md @@ -0,0 +1,40 @@ +# PR wheel audio samples + +These samples were generated on the local GB10 machine, using cached GGUF +files and no model downloads. All files were generated from native ref +`eda8b59092b8d7b7142177c599b08909b87f63b4` plus the scheduler reset that is now +merged upstream in qwentts.cpp `f79b23a56702584d51e3a66509b881cd99a95bba`. + +Common settings: + +- Model: `Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice` +- Quantization: `BF16` +- Speaker: `aiden` +- Language: `english` +- Seed: `42` +- Sampling settings: `temperature=0.9`, `top_k=50`, `top_p=1.0`, + `repetition_penalty=1.05` +- Streaming chunk: `8 / 12.5` seconds, with `2.0` seconds left context + +Files: + +| File | Mode | Prompt | Duration | +| --- | --- | --- | --- | +| `cpu_greedy_20f_customvoice_bf16.wav` | Greedy, 20 frames | `This is a short deterministic backend comparison.` | 1.60s | +| `cu128_greedy_20f_customvoice_bf16.wav` | Greedy, 20 frames | `This is a short deterministic backend comparison.` | 1.60s | +| `cu130_greedy_20f_customvoice_bf16.wav` | Greedy, 20 frames | `This is a short deterministic backend comparison.` | 1.60s | +| `cpu_sampled_seed42_customvoice_bf16.wav` | Sampled, seed 42 | `Hello from the qwentts cpp Python wheel. This sample compares the same Qwen three custom voice settings across backends.` | 6.40s | +| `cu128_sampled_seed42_customvoice_bf16.wav` | Sampled, seed 42 | `Hello from the qwentts cpp Python wheel. This sample compares the same Qwen three custom voice settings across backends.` | 6.40s | +| `cu130_sampled_seed42_customvoice_bf16.wav` | Sampled, seed 42 | `Hello from the qwentts cpp Python wheel. This sample compares the same Qwen three custom voice settings across backends.` | 6.40s | + +Validation: + +- STT validator: `nano-parakeet` with `nvidia/parakeet-tdt-0.6b-v3`. +- All six final WAVs produced non-empty, prompt-matching transcripts. +- Before this patch, the same current-ref CPU wheel transcribed as only + `D` / `What uh` on this machine. +- The broader all-options validation artifacts are linked from the PR comment + and include CPU reset-before local, CUDA 13 reset-before local, CUDA 12.8 + wheel, and CUDA 13.0 wheel runs. + +See `manifest.json` for the measured generation metadata and STT transcripts. diff --git a/samples/pr-wheel-audio/cpu_greedy_20f_customvoice_bf16.wav b/samples/pr-wheel-audio/cpu_greedy_20f_customvoice_bf16.wav new file mode 100644 index 0000000..31df50f Binary files /dev/null and b/samples/pr-wheel-audio/cpu_greedy_20f_customvoice_bf16.wav differ diff --git a/samples/pr-wheel-audio/cpu_sampled_seed42_customvoice_bf16.wav b/samples/pr-wheel-audio/cpu_sampled_seed42_customvoice_bf16.wav new file mode 100644 index 0000000..975522b Binary files /dev/null and b/samples/pr-wheel-audio/cpu_sampled_seed42_customvoice_bf16.wav differ diff --git a/samples/pr-wheel-audio/cu128_greedy_20f_customvoice_bf16.wav b/samples/pr-wheel-audio/cu128_greedy_20f_customvoice_bf16.wav new file mode 100644 index 0000000..1efca74 Binary files /dev/null and b/samples/pr-wheel-audio/cu128_greedy_20f_customvoice_bf16.wav differ diff --git a/samples/pr-wheel-audio/cu128_sampled_seed42_customvoice_bf16.wav b/samples/pr-wheel-audio/cu128_sampled_seed42_customvoice_bf16.wav new file mode 100644 index 0000000..2fa04e0 Binary files /dev/null and b/samples/pr-wheel-audio/cu128_sampled_seed42_customvoice_bf16.wav differ diff --git a/samples/pr-wheel-audio/cu130_greedy_20f_customvoice_bf16.wav b/samples/pr-wheel-audio/cu130_greedy_20f_customvoice_bf16.wav new file mode 100644 index 0000000..08e821c Binary files /dev/null and b/samples/pr-wheel-audio/cu130_greedy_20f_customvoice_bf16.wav differ diff --git a/samples/pr-wheel-audio/cu130_sampled_seed42_customvoice_bf16.wav b/samples/pr-wheel-audio/cu130_sampled_seed42_customvoice_bf16.wav new file mode 100644 index 0000000..18c4fb5 Binary files /dev/null and b/samples/pr-wheel-audio/cu130_sampled_seed42_customvoice_bf16.wav differ diff --git a/samples/pr-wheel-audio/manifest.json b/samples/pr-wheel-audio/manifest.json new file mode 100644 index 0000000..7a02d2b --- /dev/null +++ b/samples/pr-wheel-audio/manifest.json @@ -0,0 +1,176 @@ +{ + "codec_chunk_sec": 0.64, + "codec_left_context_sec": 2.0, + "lang": "english", + "model": "Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice", + "native_patch_note": "Scheduler reset before talker graph allocation is merged upstream in qwentts.cpp f79b23a56702584d51e3a66509b881cd99a95bba; no package-side patch is applied.", + "native_ref": "f79b23a56702584d51e3a66509b881cd99a95bba", + "quant": "BF16", + "repetition_penalty": 1.05, + "seed": 42, + "speaker": "aiden", + "stt_validator": "nano-parakeet nvidia/parakeet-tdt-0.6b-v3", + "temperature": 0.9, + "top_k": 50, + "top_p": 1.0, + "wheels": [ + { + "backend": "cpu", + "load_s": 1.7071470492519438, + "native_ref": "f79b23a56702584d51e3a66509b881cd99a95bba", + "results": [ + { + "audio_s": 1.6, + "backend": "cpu", + "bytes": 76844, + "chunks": 3, + "do_sample": false, + "file": "cpu_greedy_20f_customvoice_bf16.wav", + "first_samples": 15360, + "first_yield_s": 2.87988292099908, + "max": 0.679128885269165, + "max_new_tokens": 20, + "min": -0.8063646554946899, + "mode": "greedy_20f", + "native_synthesize_ms": 5892.7193018607795, + "profile_first_yield_ms": 2879.8656091094017, + "rms": 0.14570343494415283, + "sample_rate": 24000, + "samples": 38400, + "stt_error": null, + "stt_text": "This is a short deterministic.", + "text": "This is a short deterministic backend comparison.", + "total_s": 5.893251145258546 + }, + { + "audio_s": 6.4, + "backend": "cpu", + "bytes": 307244, + "chunks": 10, + "do_sample": true, + "file": "cpu_sampled_seed42_customvoice_bf16.wav", + "first_samples": 15360, + "first_yield_s": 4.1715703047811985, + "max": 0.8430225253105164, + "max_new_tokens": 80, + "min": -0.7545617818832397, + "mode": "sampled_seed42", + "native_synthesize_ms": 28630.53220277652, + "profile_first_yield_ms": 4171.557087916881, + "rms": 0.16638672351837158, + "sample_rate": 24000, + "samples": 153600, + "stt_error": null, + "stt_text": "Hello from the Quince TPP Python wheel. This sample compares the same Quen 3 custom voice settings.", + "text": "Hello from the qwentts cpp Python wheel. This sample compares the same Qwen three custom voice settings across backends.", + "total_s": 28.630834685172886 + } + ], + "version": "eda8b59 (2026-05-31)" + }, + { + "backend": "cu128", + "load_s": 7.176388549618423, + "native_ref": "f79b23a56702584d51e3a66509b881cd99a95bba", + "results": [ + { + "audio_s": 1.6, + "backend": "cu128", + "bytes": 76844, + "chunks": 3, + "do_sample": false, + "file": "cu128_greedy_20f_customvoice_bf16.wav", + "first_callback_enter_ms": 734.0356330387294, + "first_yield_s": 0.7349824379198253, + "max": 0.6689432859420776, + "max_new_tokens": 20, + "min": -0.8774434328079224, + "mode": "greedy_20f", + "native_synthesize_ms": 1143.9477880485356, + "rms": 0.13154427288141302, + "sample_rate": 24000, + "samples": 38400, + "stt_error": null, + "stt_text": "This is a short deterministic.", + "text": "This is a short deterministic backend comparison.", + "total_s": 1.1447814558632672 + }, + { + "audio_s": 6.4, + "backend": "cu128", + "bytes": 307244, + "chunks": 10, + "do_sample": true, + "file": "cu128_sampled_seed42_customvoice_bf16.wav", + "first_callback_enter_ms": 271.0315310396254, + "first_yield_s": 0.27122986782342196, + "max": 0.6914042234420776, + "max_new_tokens": 80, + "min": -0.6923848390579224, + "mode": "sampled_seed42", + "native_synthesize_ms": 2731.2098373658955, + "rms": 0.12360686647515703, + "sample_rate": 24000, + "samples": 153600, + "stt_error": null, + "stt_text": "Hello from the Quent's TP by Python wheel. This sample compares the same Quen 3 custom voice setting.", + "text": "Hello from the qwentts cpp Python wheel. This sample compares the same Qwen three custom voice settings across backends.", + "total_s": 2.7314665098674595 + } + ], + "version": "eda8b59 (2026-05-31)" + }, + { + "backend": "cu130", + "load_s": 7.110981802921742, + "native_ref": "f79b23a56702584d51e3a66509b881cd99a95bba", + "results": [ + { + "audio_s": 1.6, + "backend": "cu130", + "bytes": 76844, + "chunks": 3, + "do_sample": false, + "file": "cu130_greedy_20f_customvoice_bf16.wav", + "first_callback_enter_ms": 678.5042937844992, + "first_yield_s": 0.6793792098760605, + "max": 0.6005839109420776, + "max_new_tokens": 20, + "min": -0.7905293703079224, + "mode": "greedy_20f", + "native_synthesize_ms": 1089.799968060106, + "rms": 0.15053639747356867, + "sample_rate": 24000, + "samples": 38400, + "stt_error": null, + "stt_text": "This is a short determination.", + "text": "This is a short deterministic backend comparison.", + "total_s": 1.0909001021645963 + }, + { + "audio_s": 6.4, + "backend": "cu130", + "bytes": 307244, + "chunks": 10, + "do_sample": true, + "file": "cu130_sampled_seed42_customvoice_bf16.wav", + "first_callback_enter_ms": 259.57830622792244, + "first_yield_s": 0.259771378710866, + "max": 0.7456034421920776, + "max_new_tokens": 80, + "min": -0.7358418703079224, + "mode": "sampled_seed42", + "native_synthesize_ms": 2733.761377632618, + "rms": 0.12078344219599677, + "sample_rate": 24000, + "samples": 153600, + "stt_error": null, + "stt_text": "Hello from the Quint GPP Python wheel. This sample compares the same Quen 3 custom voice settings.", + "text": "Hello from the qwentts cpp Python wheel. This sample compares the same Qwen three custom voice settings across backends.", + "total_s": 2.734125139657408 + } + ], + "version": "eda8b59 (2026-05-31)" + } + ] +}