This log preserves the environment experiments that led to the current WSL and AMD GPU setup.
The initial target was local movie subtitle generation and translation on:
- WSL2 with Ubuntu;
- AMD Radeon RX 9070 XT (
gfx1201); - ROCm 7.2.0.
The first ASR route used a ROCm fork of
CTranslate2, the runtime behind
faster-whisper.
The experiment used:
- repository:
https://github.com/arlo-phoenix/CTranslate2-rocm.git; - compiler: AMD clang++ from ROCm 7.2.0;
- target:
gfx1201.
Configuration required CMake policy adjustments and replacing an unavailable
Intel OpenMP dependency (libiomp5) with libomp. Compilation then failed in
the CUDA-to-HIP compatibility layer, including:
error: no template named 'counting_iterator' in namespace 'thrust'
error: use of undeclared identifier 'hipblasGemmEx_v2'
The available fork depended on Thrust and hipBLAS APIs that did not match the installed ROCm headers. Adapting and maintaining that native fork was outside the project scope, so this route was abandoned. This result applies to the tested fork and toolchain; it is not a general claim that CTranslate2 cannot run on ROCm.
The next experiment installed ROCm-specific PyTorch wheels and ran the official
openai-whisper package with large-v3-turbo. It successfully used the RX 9070
XT and established that the WSL driver path, PyTorch ROCm runtime, and Whisper
weights were functional.
This became the first working ASR baseline. It was later superseded as the runtime boundary because long-form decoding remained inside Python and did not provide the desired native execution profile.
The first working design proposed:
- a downloaded static FFmpeg under
tools/ffmpeg/, intended to avoidsudoand differences between system packages; openai-whisperwithlarge-v3-turbo, producing SRT directly;- regex-based cleanup for hallucinated credits such as “Subtitle by Amara.org”
and SDH text such as
[Music]; - local Qwen translation through vLLM, with requests submitted in batches to improve GPU use and keep subtitle content on the workstation.
These decisions captured a functional prototype, not a reproducible environment contract. In particular, a bundled FFmpeg introduced another platform-specific binary and update mechanism, while Python dependencies, native builds, and model weights still lacked clear ownership.
The environment work was revisited with the following completion criteria:
- a locked Python 3.12 environment;
- working PyTorch and vLLM ROCm extensions;
- a reproducible HIP build of whisper.cpp;
- verified model files at fixed project paths;
- repeatable checks for the assembled runtime and downloaded assets.
The verified machine now uses system ROCm 7.2.4. System ROCm did not need to be downgraded to match the patch version encoded by the Python wheels.
The final design separates three dependency layers:
| Layer | Owner | Contents |
|---|---|---|
| System | OS and ROCm installation | FFmpeg, CMake, Git, hipcc, rocminfo |
| Python | uv and uv.lock |
Python 3.12, ModelScope, test tools, vLLM and its ROCm stack |
| Native assets | Project scripts | whisper.cpp CLI, build manifest, model weights |
uv manages Python packages only. Project scripts build or download external runtime assets, and the operating system provides drivers and general-purpose native tools.
Building vLLM from source was considered when no ROCm 7.2.4 wheel was apparent.
The vLLM wheel index instead provided a coordinated rocm723 stack for vLLM
0.25.1, including PyTorch, Triton, AITER, and compiled ROCm extensions. Reusing
that stack was more reproducible than rebuilding the same dependency graph.
pyproject.toml uses the Tsinghua mirror as the default index and binds vLLM to:
https://wheels.vllm.ai/rocm/0.25.1/rocm723
uv uses first-index, and the committed lock records both sources. A locked
sync therefore requires no local index override and cannot silently resolve
vLLM from a general-purpose index.
The verified packages are:
vLLM: 0.25.1+rocm723
PyTorch: 2.11.0+gitd0c8b1f
Triton: 3.6.0
Torch HIP runtime: 7.2.53211
The system ROCm 7.2.4 compiler remains responsible for native HIP builds. Its patch version and the Python wheel stack do not need to be identical when the runtime compatibility checks pass.
The prebuilt wheel loads and computes on the RX 9070 XT, but WSL blocks the AMD SMI path used by parts of vLLM platform discovery. Two failures were observed:
- ROCm detection could stop before falling back to
torch.version.hip. - Early
warning_oncecalls could import distributed state while platform initialization was incomplete, causing a circular import.
scripts/patch_vllm.sh applies the local workaround. It accepts only
0.25.1+rocm723, validates original or already-patched file hashes, modifies the
known files, compiles them, and runs a GPU import smoke test. Unexpected wheel
contents fail rather than receiving a best-effort patch.
The workaround is tied to upstream vLLM work in #38434 and should be removed after a compatible wheel passes the repository checks without it.
whisper.cpp is neither a Python dependency nor a retained submodule.
scripts/install_whisper_cli.sh performs a transient, pinned build:
- fetch the pinned commit into a temporary directory;
- detect AMD targets through
rocminfo; - build only
whisper-cliwith HIP in Release mode; - reject unexpected shared whisper.cpp dependencies;
- install the executable at
.venv/bin/whisper-cli; - record commit, target, toolchain, and binary hashes in
.venv/.whisper-cli-build.json; - remove the temporary source and build trees.
This provides a reproducible native ASR tool without keeping a second source tree in the repository or treating CMake output as a uv package.
The project-local static FFmpeg proposal was retired. ffmpeg and ffprobe are
system prerequisites and must be present on PATH.
This avoids storing a large third-party binary and avoids adding a separate platform-specific download mechanism. The application verifies both tools and uses FFmpeg's machine-readable progress output.
Large weights are managed outside uv under fixed project paths:
models/whisper/ggml-large-v3-turbo.bin
models/whisper/ggml-silero-v6.2.0.bin
models/Qwen3.5-9B-AWQ-4bit/
ModelScope is a base Python dependency and provides the primary download path
for the Whisper and translation models. The Silero VAD file comes from the
official whisper.cpp Hugging Face repository. scripts/download_weights.sh
pins the translation revision, checks required files and SHA-256 hashes, and
refuses to overwrite unexpected existing content. Checksums also fix the exact
Whisper and VAD artifacts when their source URLs use moving branch names.
Weights are prepared after the software environment. They are runtime assets,
not Python packages, and do not belong in uv.lock.
The environment can be reproduced with:
uv python install 3.12
uv sync --locked --managed-python --group dev --group vllm
source .venv/bin/activate
./scripts/patch_vllm.sh
./scripts/install_whisper_cli.sh
./scripts/download_weights.sh
./scripts/doctor.shThe doctor checks system tools, Python 3.12, ModelScope, PyTorch, Triton, vLLM, ROCm platform selection, BF16 GPU computation, native vLLM extensions, whisper-cli linkage, and the two ASR weights. The download script separately verifies the translation model.
At this point each environment component had one owner and a reproducible verification path. This completed the environment-preparation phase.