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
163 changes: 100 additions & 63 deletions .github/workflows/build-wheels.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
name: Build PyWhisperCPP Wheels

# CUDA wheels only; CPU comes from PyPI. Version and variant suffix are derived
# from lib/src/backend_installer.py so the workflow never restates the contract.

on:
workflow_dispatch:
inputs:
pywhispercpp_commit:
description: 'PyWhisperCPP commit hash'
description: 'PyWhisperCPP commit (defaults to backend_installer.PYWHISPERCPP_PINNED_COMMIT)'
required: false
default: '4ab96165f84e8eb579077dfc3d0476fa5606affe'
default: 'd8f202f4435cf3e5e0bf735723eaacbe84c6853c' # v1.4.1
release:
types: [published]

env:
PYWHISPERCPP_COMMIT: ${{ github.event.inputs.pywhispercpp_commit || '4ab96165f84e8eb579077dfc3d0476fa5606affe' }}
PYWHISPERCPP_COMMIT: ${{ github.event.inputs.pywhispercpp_commit || 'd8f202f4435cf3e5e0bf735723eaacbe84c6853c' }}
CUDA_BUILD_VERSION: '12.6' # one 12.x build serves all CUDA 12

jobs:
build-cpu:
name: Build CPU wheel (Python ${{ matrix.python-version }})
build-cuda:
name: Build CUDA wheel (Python ${{ matrix.python-version }})
runs-on: ubuntu-22.04
strategy:
fail-fast: false
Expand All @@ -30,6 +34,37 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true

- name: Derive installer contract (from backend_installer.py)
run: |
ver=$(PYTHONPATH=lib/src python -c "import backend_installer as b; print(b.PYWHISPERCPP_VERSION)")
pin=$(PYTHONPATH=lib/src python -c "import backend_installer as b; print(b.PYWHISPERCPP_PINNED_COMMIT)")
variant=$(PYTHONPATH=lib/src python -c "import backend_installer as b; print(b._get_wheel_variant('${CUDA_BUILD_VERSION}'))")
echo "EXPECTED_VERSION=$ver" >> "$GITHUB_ENV"
echo "VARIANT=$variant" >> "$GITHUB_ENV"
echo "contract: version=$ver variant=$variant pin=$pin"
if [ "$variant" != "cuda12" ]; then
echo "::error::Installer maps CUDA ${CUDA_BUILD_VERSION} to '$variant', expected 'cuda12'."
exit 1
fi
if [ "${PYWHISPERCPP_COMMIT}" != "$pin" ]; then
echo "::warning::Building ${PYWHISPERCPP_COMMIT}, installer pins $pin (v$ver)."
fi

- name: Install CUDA Toolkit ${{ env.CUDA_BUILD_VERSION }}
uses: Jimver/cuda-toolkit@v0.2.35
id: cuda-toolkit
with:
cuda: ${{ env.CUDA_BUILD_VERSION }}.0
method: 'network'
sub-packages: '["nvcc", "cudart-dev"]'

- name: Install additional CUDA libraries
run: |
sudo apt-get update
CUDA_VER_DASH="${CUDA_BUILD_VERSION//./-}"
sudo apt-get install -y libcublas-dev-${CUDA_VER_DASH}

- name: Install build dependencies
run: |
Expand All @@ -44,35 +79,46 @@ jobs:
git checkout ${{ env.PYWHISPERCPP_COMMIT }}
git submodule update --init --recursive

- name: Build wheel
- name: Build wheel with CUDA support
env:
GGML_CUDA: 'ON'
CUDACXX: ${{ steps.cuda-toolkit.outputs.CUDA_PATH }}/bin/nvcc
NO_REPAIR: '1' # CUDA driver libs come from the user's system
run: |
cd pywhispercpp-src
pip wheel . --no-deps --wheel-dir ../dist

- name: Rename wheel with variant suffix
- name: Assert built version matches installer
run: |
cd dist
built="$(ls pywhispercpp-*.whl | head -n1)"
version="$(echo "$built" | sed -E 's/^pywhispercpp-([^-]+)-.*/\1/')"
if [ "$version" != "${EXPECTED_VERSION}" ]; then
echo "::error::Built $version != installer's ${EXPECTED_VERSION}."
exit 1
fi

- name: Rename wheel with CUDA variant suffix
run: |
cd dist
for f in *.whl; do
# Add +cpu suffix before .whl extension
newname="${f%.whl}+cpu.whl"
mv "$f" "$newname"
done
for f in *.whl; do mv "$f" "${f%.whl}+${VARIANT}.whl"; done
ls -la

- name: Upload wheel artifact
uses: actions/upload-artifact@v4
with:
name: wheel-cpu-py${{ matrix.python-version }}
name: wheel-cuda12-py${{ matrix.python-version }}
path: dist/*.whl

build-cuda:
name: Build CUDA ${{ matrix.cuda-version }} wheel (Python ${{ matrix.python-version }})
verify-wheels:
# Derive the filename the installer will request and assert the build made it.
name: Verify wheel (Python ${{ matrix.python-version }})
needs: [build-cuda]
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
cuda-version: ['12.6', '12.9']

steps:
- name: Checkout hyprwhspr
Expand All @@ -82,64 +128,55 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true

- name: Install CUDA Toolkit ${{ matrix.cuda-version }}
uses: Jimver/cuda-toolkit@v0.2.35
id: cuda-toolkit
with:
cuda: ${{ matrix.cuda-version }}.0
method: 'network'
sub-packages: '["nvcc", "cudart-dev"]'

- name: Install additional CUDA libraries
- name: Derive expected filename from installer
run: |
CUDA_VER="${{ matrix.cuda-version }}"
CUDA_VER_DASH="${CUDA_VER//./-}" # 11.8 -> 11-8
sudo apt-get install -y libcublas-dev-${CUDA_VER_DASH}
py='${{ matrix.python-version }}'
variant=$(PYTHONPATH=lib/src python -c "import backend_installer as b; print(b._get_wheel_variant('${CUDA_BUILD_VERSION}'))")
dl=$(PYTHONPATH=lib/src python -c "import backend_installer as b; print(b._get_wheel_filename('${py}', '${variant}', True))")
pip=$(PYTHONPATH=lib/src python -c "import backend_installer as b; print(b._get_wheel_filename('${py}', '${variant}', False))")
echo "EXPECTED_WHEEL=$dl" >> "$GITHUB_ENV"
echo "WHEEL_PIP_NAME=$pip" >> "$GITHUB_ENV"
echo "installer wants: $dl"

- name: Download wheel artifact
uses: actions/download-artifact@v4
with:
name: wheel-cuda12-py${{ matrix.python-version }}
path: dist

- name: Install build dependencies
- name: Assert build produced the requested file
run: |
sudo apt-get update
sudo apt-get install -y build-essential cmake git
pip install --upgrade pip wheel setuptools build
if [ ! -f "dist/${EXPECTED_WHEEL}" ]; then
echo "::error::Installer wants ${EXPECTED_WHEEL}; build did not produce it."
ls -la dist
exit 1
fi

- name: Clone pywhispercpp
- name: Confirm wheel installs on this Python
run: |
git clone --recurse-submodules https://github.com/Absadiki/pywhispercpp.git pywhispercpp-src
cd pywhispercpp-src
git checkout ${{ env.PYWHISPERCPP_COMMIT }}
git submodule update --init --recursive
cp "dist/${EXPECTED_WHEEL}" "dist/${WHEEL_PIP_NAME}"
# --no-deps: validate cp tag only; import needs a GPU absent on CI.
pip install --no-deps "dist/${WHEEL_PIP_NAME}"

- name: Build wheel with CUDA support
env:
GGML_CUDA: 'ON'
CUDACXX: ${{ steps.cuda-toolkit.outputs.CUDA_PATH }}/bin/nvcc
# Skip wheel repair - CUDA driver libs must come from user's system
NO_REPAIR: '1'
run: |
cd pywhispercpp-src
pip wheel . --no-deps --wheel-dir ../dist

- name: Rename wheel with CUDA variant suffix
- name: Smoke-check native lib linkage
run: |
cd dist
cuda_suffix="cuda${{ matrix.cuda-version }}"
cuda_suffix="${cuda_suffix//.}" # Remove dots: cuda118, cuda122
for f in *.whl; do
newname="${f%.whl}+${cuda_suffix}.whl"
mv "$f" "$newname"
done
ls -la

- name: Upload wheel artifact
uses: actions/upload-artifact@v4
with:
name: wheel-cuda${{ matrix.cuda-version }}-py${{ matrix.python-version }}
path: dist/*.whl
site=$(python -c "import sysconfig; print(sysconfig.get_paths()['platlib'])")
sos=$(find "$site" -maxdepth 2 \( -name '*.so' -o -name '*.so.*' \) | grep -iE 'pywhispercpp|whisper|ggml' || true)
[ -n "$sos" ] || { echo "::error::no pywhispercpp native libs found in $site"; exit 1; }
# CUDA driver/runtime libs are legitimately absent on GPU-less CI; anything else unresolved means a broken wheel.
missing=$(ldd $sos 2>/dev/null | grep 'not found' | grep -viE 'libcuda|libnvidia|libcublas|libcudart|libnvrtc' | sort -u || true)
if [ -n "$missing" ]; then
echo "::error::Non-CUDA shared libs unresolved (wheel likely unusable):"
echo "$missing"
exit 1
fi
echo "OK: only CUDA libs unresolved (expected without a GPU)"

publish-wheels:
name: Publish wheels to release
needs: [build-cpu, build-cuda]
needs: [build-cuda, verify-wheels]
runs-on: ubuntu-latest
if: github.event_name == 'release' || github.event_name == 'workflow_dispatch'
permissions:
Expand Down
6 changes: 5 additions & 1 deletion docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ Set model in config (pywhispercpp only — faster-whisper uses `faster_whisper_m
```jsonc
{
"model": "small.en", // .en = English-only; omit suffix for multilingual
"threads": 4 // CPU threads for whisper processing (default: 4)
// "threads": 6 // optional; omit for auto = min(8, CPU count)
}
```

Expand All @@ -456,6 +456,8 @@ For multi-language detection, ensure you select a model which does not say `.en`
}
```

Auto-detect runs an extra detection pass per utterance. Setting `language` explicitly skips it and lowers latency.

Language options:

- **`null`** (default) - Auto-detect language from audio
Expand Down Expand Up @@ -766,6 +768,8 @@ Customize transcriptions:

Use empty string `""` to delete words entirely.

`{"hyper whisper": "hyprwhspr"}` ships as the default (the product name is spoken "hyper whisper").

Single-character overrides match anywhere in a word (not just at word boundaries):

```jsonc
Expand Down
Loading
Loading