Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions .github/workflows/hip-wheel.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
name: Linux HIP build and wheel validation

"on":
pull_request:
workflow_dispatch:

permissions:
contents: read

jobs:
hip:
runs-on: ubuntu-24.04
timeout-minutes: 60
env:
QWENTTS_REF: 6fae92914045cd83364d2845ceaa0f7969727319
ROCM_IMAGE: rocm/dev-ubuntu-24.04:7.2.4-complete@sha256:92f309c51b52cef8762867848f1529dee821624f23cd5df38455e819538f762f
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Free space for the ROCm development image
run: |
sudo rm -rf /usr/local/lib/android /opt/ghc /usr/share/dotnet
sudo apt-get clean
df -h /
- name: Fetch pinned native source
run: |
git init third_party/qwentts.cpp
git -C third_party/qwentts.cpp remote add origin https://github.com/ServeurpersoCom/qwentts.cpp
git -C third_party/qwentts.cpp fetch --depth 1 origin "$QWENTTS_REF"
git -C third_party/qwentts.cpp checkout --detach FETCH_HEAD
git -C third_party/qwentts.cpp submodule update --init --recursive --depth 1
- name: Start the pinned ROCm build container
run: |
# No GPU devices are passed through: gfx942 is an explicit compile target.
docker run --detach --name qwentts-hip \
-v "$GITHUB_WORKSPACE:/workspace" -w /workspace \
"$ROCM_IMAGE" sleep infinity
docker exec qwentts-hip bash -euxo pipefail -c '
apt-get update
apt-get install -y --no-install-recommends \
build-essential cmake ninja-build patchelf python3 python3-venv git ca-certificates
git config --global --add safe.directory /workspace/third_party/qwentts.cpp
git config --global --add safe.directory /workspace/third_party/qwentts.cpp/ggml
python3 -m venv /opt/hip-test
/opt/hip-test/bin/python -m pip install "setuptools>=77" wheel build twine pytest numpy huggingface-hub
# These remain system dependencies; no native build path is added.
printf "/opt/rocm/lib\n/opt/rocm/lib64\n" > /etc/ld.so.conf.d/qwentts-rocm.conf
ldconfig
/opt/rocm/bin/hipconfig --version
/opt/rocm/llvm/bin/clang++ --version
'
- name: Build the real HIP runtime and local-version wheel
run: |
docker exec qwentts-hip bash -euxo pipefail -c '
/opt/hip-test/bin/python scripts/build_native.py \
--source third_party/qwentts.cpp --build-dir build/hip --backend hip \
--hip-compiler /opt/rocm/llvm/bin/clang++ \
--hip-architectures gfx942 --rocm-path /opt/rocm --jobs 2 --clean \
--cmake-arg=-G --cmake-arg=Ninja
test -f src/qwentts_cpp/lib/libggml-hip.so.0
/opt/hip-test/bin/python scripts/set_local_version.py rocm724
/opt/hip-test/bin/python -m build --wheel
/opt/hip-test/bin/python -m twine check --strict dist/*.whl
PYTHONPATH=src QWENTTS_CPP_SOURCE=third_party/qwentts.cpp \
/opt/hip-test/bin/python -m pytest -q tests/test_native_abi.py tests/test_build_native.py
'
- name: Validate the installed HIP wheel without native source or build trees
run: |
docker exec qwentts-hip bash -euxo pipefail -c '
/opt/hip-test/bin/python -m pip install --no-deps dist/*.whl
rm -rf build third_party src/qwentts_cpp/lib
unset QWENTTS_CPP_LIBRARY QWEN_LIBRARY_PATH LD_LIBRARY_PATH PYTHONPATH
/opt/hip-test/bin/python -I -c "from pathlib import Path; from qwentts_cpp import QwenLibrary; lib = QwenLibrary(); print(lib.path, lib.version()); assert lib.path.is_relative_to(Path(\"/opt/hip-test\")); assert any(Path(dep._name).name == \"libggml-hip.so.0\" for dep in lib._dependency_handles)"
# Inspect actual ELF dependencies after removing the build trees.
for library in /opt/hip-test/lib/python*/site-packages/qwentts_cpp/lib/*.so*; do
test "$(patchelf --print-rpath "$library")" = '\''$ORIGIN'\''
dependencies="$(ldd "$library")"
printf "%s\n" "$dependencies"
if printf "%s\n" "$dependencies" | grep -E "not found|/workspace/"; then
exit 1
fi
done
# Native integration tests use the installed path, after the default
# loader above has independently succeeded without any override.
export QWENTTS_CPP_LIBRARY="$(/opt/hip-test/bin/python -I -c "from qwentts_cpp import QwenLibrary; print(QwenLibrary().path)")"
/opt/hip-test/bin/python -m pytest -q tests
'
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: hip-wheel-rocm724-gfx942-linux-x86_64
path: dist/*.whl
retention-days: 7
- name: Remove the build container
if: always()
run: docker rm --force qwentts-hip || true
29 changes: 29 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Python and Linux packaging tests

"on":
pull_request:
push:
branches: [main]

permissions:
contents: read

jobs:
tests:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install test and ELF relocation tools
run: |
sudo apt-get update
sudo apt-get install -y patchelf
python -m pip install 'setuptools>=77' wheel pytest numpy huggingface-hub
- name: Test wrapper and installed ELF fixture wheel
env:
PYTHONPATH: src
run: python -m pytest -q tests
9 changes: 8 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,21 @@ Pull requests do not run the Linux wheel matrix. Do not run the validation
workflow solely as a publishing prerequisite, because the publishing workflows
perform fresh builds.

The `.github/workflows/hip-wheel.yml` PR check builds one real Linux ROCm 7.2.4
HIP runtime for gfx942 on a standard Ubuntu worker. It validates a local-version
candidate wheel and clean installed loading without an AMD GPU. This is separate
from the dispatch-only Linux matrix and does not publish wheels. AMD synthesis
and device registration still require an AMD GPU environment.

## Updating the qwentts.cpp Pin

To rebuild against a newer qwentts.cpp revision:

1. Resolve the latest upstream `master` commit to its full SHA.
2. Update every default and event fallback for `QWENTTS_REF` in
`.github/workflows/wheels.yml`, `.github/workflows/publish.yml`,
`.github/workflows/publish-hf-wheels.yml`, and `.github/workflows/metal-wheel.yml`.
`.github/workflows/publish-hf-wheels.yml`, `.github/workflows/metal-wheel.yml`,
and `.github/workflows/hip-wheel.yml`.
Update `QWENTTS_NATIVE_REVISION` in `src/qwentts_cpp/_binding.py` and verify
ctypes layouts with `QWENTTS_CPP_SOURCE=/path/to/source python -m pytest tests/test_native_abi.py`.
3. Update the pinned revision and its summary in `README.md`.
Expand Down
130 changes: 130 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,136 @@ delocate-wheel --require-archs arm64 -w wheelhouse -v dist/*.whl
python -m twine check --strict wheelhouse/*.whl
```

### AMD ROCm / HIP (Linux source builds)

The build helper supports `--backend hip` or `QWENTTS_CPP_BACKEND=hip` on
Linux. It enables `GGML_HIP` and HIP graphs, builds shared GGML libraries,
and disables CUDA, Metal, BLAS, and host-specific CPU optimizations.
The pinned GGML requires ROCm/HIP **6.1 or newer** at configure time; this is
a build requirement, not a guarantee that every ROCm version or GPU works.
[Issue #35](https://github.com/andimarafioti/qwentts-cpp-python/issues/35)
records successful manual-build synthesis on **ROCm 7.2.4, AMD Instinct
MI300X VF (`gfx942`)**, in
`rocm/pytorch:rocm7.2.4_ubuntu24.04_py3.12_pytorch_release_2.9.1`.
Other versions and architectures have not been validated. The **Linux HIP
build and wheel validation** PR check compiles the real pinned source for
`gfx942` in AMD's ROCm 7.2.4 development container on an Ubuntu GitHub worker.
It checks ABI layouts and wheel metadata, deletes the native source/build
trees, checks installed ELF dependencies, and runs the wrapper tests against
the installed wheel. The candidate `0.5.0+rocm724` wheel is saved as a CI
artifact. These workers have no AMD GPU; synthesis and device registration
still require validation on AMD hardware. A manual build with an explicit
library path does not establish wheel portability.

Use a Linux ROCm development environment with a C/C++ compiler, CMake 3.21+,
HIP clang++, hipBLAS, rocBLAS, and `patchelf` (for example, install `patchelf`
with your system package manager). The matching AMD driver and GPU access
must be available; containers need the host's ROCm device access as well.
Build from this repository with the pinned native source:

```bash
python -m pip install cmake ninja build numpy huggingface-hub pytest
git clone https://github.com/ServeurpersoCom/qwentts.cpp third_party/qwentts.cpp
git -C third_party/qwentts.cpp checkout 6fae92914045cd83364d2845ceaa0f7969727319
git -C third_party/qwentts.cpp submodule update --init --recursive

python scripts/build_native.py \
--source third_party/qwentts.cpp --build-dir build/hip --backend hip \
--hip-compiler /opt/rocm/llvm/bin/clang++ \
--hip-architectures gfx942 --rocm-path /opt/rocm --jobs 8 --clean
python -m pip install -e .
QWENTTS_CPP_SOURCE="$PWD/third_party/qwentts.cpp" python -m pytest -q tests
python -c "from qwentts_cpp import QwenLibrary; lib = QwenLibrary(); print(lib.path, lib.version())"
```

No `QWENTTS_CPP_LIBRARY` or build-directory `LD_LIBRARY_PATH` is needed:
the helper copies `libqwen.so`, the GGML core/base/CPU libraries, and the
versioned HIP backend into `qwentts_cpp/lib`. It resolves the HIP symlink
to a regular `libggml-hip.so.0` file and requires successful `patchelf`
relocation to `$ORIGIN`, so these libraries resolve their packaged siblings.
The Python loader preloads the HIP backend before GGML and libqwen.
Building native libraries is an explicit step; `pip install -e .` alone
does not compile them.

`--hip-compiler`, `--hip-architectures`, and `--rocm-path` default to
`CMAKE_HIP_COMPILER`, `CMAKE_HIP_ARCHITECTURES`, and `ROCM_PATH`, respectively.
When omitted, CMake/upstream discovers the toolkit and GPU targets. Use the
target for your GPU; quote multiple targets such as `"gfx942;gfx950"`.
The ROCm prefix sets CMake's package search prefix and HIP compiler ROCm root.
For additional options, use `QWENTTS_CPP_CMAKE_ARGS` or repeat
`--cmake-arg=...`; these are applied last, so
`--cmake-arg=-DGGML_HIP_GRAPHS=OFF` disables graphs. Use a clean build when
changing backends or toolkits.

HIP packaging supports linked, versioned backends with `GGML_BACKEND_DL=OFF`
(set by the helper). Dynamic module backends with `GGML_BACKEND_DL=ON` are
rejected after configuration, before compilation. Copy-only `--skip-build`
runs also reject an unversioned HIP module before replacing packaged libraries.
Rebuild with `--clean` and `GGML_BACKEND_DL=OFF` if using an existing dynamic
backend build directory.

**System dependencies remain external.** This helper bundles qwentts/GGML
libraries only. It does not bundle `libamdhip64`, `libhipblas`, `libhipblaslt`,
`librocblas`, their transitive ROCm dependencies, or rocBLAS/Tensile data files.
Install a matching ROCm runtime (the tested container supplies these), along
with the usual Linux C/C++ and OpenMP runtime dependencies. The dynamic
loader must find the system libraries through its configured search paths;
if needed, set `LD_LIBRARY_PATH` to the system ROCm `lib`/`lib64` directories.
Do not add the native source/build directory to that path for an installed
package test. Inspect `ldd` on the packaged `.so` files and resolve any
`not found` entries in the system environment.

Run a GPU smoke test with local weights:

```bash
python scripts/smoke_stream.py \
--talker /path/to/qwen-talker-1.7b-voicedesign-BF16.gguf \
--codec /path/to/qwen-tokenizer-12hz-BF16.gguf \
--instruct "A warm, calm narrator." \
--text "Hello. This speech was generated on an AMD GPU." \
--require-rocm --full --output amd-smoke.wav
```

`--require-rocm` selects `GGML_BACKEND=ROCm0`, checks that the installed GGML
registry contains that device with an AMD description, and fails before model
loading if it is unavailable. It prints the library path, native revision,
and device description, with native backend logs at info level. `--full`
also runs buffered synthesis and writes `amd-smoke-full.wav`; streaming
writes `amd-smoke.wav`. Both must be finite, nonsilent, mono 24 kHz audio;
streaming must produce multiple chunks. Listen to both files to assess speech
quality. Repeat with BF16/Q4_K_M and the Base/CustomVoice models described in
the issue, using `--ref-spk` or `--speaker` for their conditioning. Device
availability and GPU backend selection do not exclude CPU execution of
individual unsupported operators.

For a local wheel, use a separate local version in a disposable checkout:

```bash
python scripts/set_local_version.py rocm724 # changes local version metadata
python -m build --wheel
```

This names the wheel `0.5.0+rocm724`; it is not a published download.
The same local version is used for the HIP PR-check artifact; that artifact
targets the ROCm 7.2.4 / Ubuntu 24.04 / gfx942 environment, without a
manylinux portability claim. ROCm publishing is not configured. Future ROCm
distributions must use a
separately selectable backend index/local version, such as a Hugging Face
`rocm724` flavor. Do not publish ROCm and CUDA wheels with identical public
package/version/platform tags to PyPI.

Before distributing a HIP wheel, copy only the wheel, smoke script, and local
weights to a fresh matching ROCm environment without the repository or native
build tree. Install the wheel, unset `QWENTTS_CPP_LIBRARY` and
`QWEN_LIBRARY_PATH`, and keep only declared system paths in `LD_LIBRARY_PATH`.
Verify `QwenLibrary()` loads the installed package and run the AMD smoke test
there. Run the full wrapper tests against the installed package (without a
source `PYTHONPATH`), inspect wheel contents/dependency paths, and record
GPU memory/backend logs and audio quality as described in issue #35.
The Linux packaging regression uses small ELF fixtures to test relocation
and clean installed loading; it does not validate real ROCm execution or
establish that a wheel is self-contained.

### Native ABI compatibility

The CI wheel build defaults to qwentts.cpp
Expand Down
Loading
Loading