Skip to content

python bindings

python bindings #12

Workflow file for this run

name: python-wheels
# Builds, repairs, and proves the distributable Python artifacts (plan M5):
# - wheel-linux: manylinux_2_28 x86_64, CPU + Vulkan backend modules
# (Vulkan toolchain built from pinned Khronos tags inside
# the container; cached across runs)
# - wheel-macos: macosx arm64, Metal with embedded shaders — built AND
# Metal-compute-tested on a bare-metal self-hosted M4 mini
# (VM runners' paravirtual GPUs can't do Metal compute)
# - wheel-windows: win_amd64, CPU + Vulkan backend modules (LunarG SDK)
# - sdist: the from-source fallback, proven by actually compiling
# and transcribing from the tarball
# - api-wheel: the pure-Python transcribe-cpp package
# - co-import: numpy/torch coexistence with a real transcription, both
# import orders, on all three platforms
#
# Every native wheel is tested AFTER repair (auditwheel/delocate/delvewheel)
# in a fresh venv — cibuildwheel's test phase guarantees the tested artifact
# is the one that ships. Real-model tests need the private canary GGUFs and
# run when the HF_TOKEN secret exists; forks degrade to no-model tests.
on:
push:
branches: [main]
paths:
- "bindings/**"
- "include/**"
- "src/**"
- "ggml/**"
- "CMakeLists.txt"
- "cmake/**"
- "pyproject.toml"
- "scripts/ci/**"
- ".github/workflows/python-wheels.yml"
pull_request:
paths:
- "bindings/**"
- "include/**"
- "src/**"
- "ggml/**"
- "CMakeLists.txt"
- "cmake/**"
- "pyproject.toml"
- "scripts/ci/**"
- ".github/workflows/python-wheels.yml"
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
CIBW_PIN: "cibuildwheel>=4,<5"
jobs:
wheel-linux:
runs-on: ubuntu-latest
env:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
# Container path of the toolchain cache volume (see CIBW_CONTAINER_ENGINE).
VK_TOOLCHAIN_CACHE: /vk-toolchain-cache
steps:
- uses: actions/checkout@v4
- name: Install uv
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: Cache the source-built Vulkan toolchain
uses: actions/cache@v4
with:
path: vk-toolchain-cache
key: vk-toolchain-${{ hashFiles('scripts/ci/manylinux-vulkan-toolchain.sh') }}
- name: Canary model cache (avoid HF rate limits — 429s were real)
if: env.HF_TOKEN != ''
uses: actions/cache@v4
with:
path: canary
key: canary-models-v1
- name: Fetch canary models (cache miss only; container sees /project)
if: env.HF_TOKEN != ''
run: |
[ -f canary/whisper-tiny-Q5_K_M.gguf ] || \
uvx --from huggingface_hub hf download handy-computer/whisper-tiny-gguf \
whisper-tiny-Q5_K_M.gguf --local-dir canary
[ -f canary/moonshine-streaming-tiny-Q8_0.gguf ] || \
uvx --from huggingface_hub hf download handy-computer/moonshine-streaming-tiny-gguf \
moonshine-streaming-tiny-Q8_0.gguf --local-dir canary
# cibuildwheel bind-mounts the project at /project inside the
# manylinux container; these paths resolve there.
echo "TRANSCRIBE_SMOKE_MODEL=/project/canary/whisper-tiny-Q5_K_M.gguf" >> "$GITHUB_ENV"
echo "TRANSCRIBE_SMOKE_STREAMING_MODEL=/project/canary/moonshine-streaming-tiny-Q8_0.gguf" >> "$GITHUB_ENV"
- name: Build, repair, and test the wheel (cibuildwheel)
env:
CIBW_CONTAINER_ENGINE: "docker; create_args: --volume ${{ github.workspace }}/vk-toolchain-cache:/vk-toolchain-cache"
run: |
mkdir -p vk-toolchain-cache
uvx --from "$CIBW_PIN" cibuildwheel --output-dir wheelhouse
- uses: actions/upload-artifact@v4
with:
name: dist-native-linux-x86_64
path: wheelhouse/*.whl
wheel-macos:
# Bare-metal M4 Mac mini (self-hosted, runner "m4-mini"): builds the
# SHIPPING macOS wheel and proves Metal COMPUTE on it in one lane — the
# artifact that ships is the artifact tested on a real GPU. VM-based
# macOS CI cannot do this: the probe proved (2026-06-11) that Apple's
# Virtualization.framework paravirtual GPU decodes garbage on both
# GitHub-hosted and Blacksmith M4 VMs (the former Blacksmith lane was
# retired 2026-06-11; flip runs-on back to a hosted label if the mini's
# availability ever becomes a problem). Notes: cibuildwheel needs a
# python.org CPython framework on the runner (installed once, manually —
# the runner service cannot sudo); the Apple-clang CC/CXX pin in
# pyproject [tool.cibuildwheel.macos] is harmless here.
runs-on: [self-hosted, macOS, ARM64]
timeout-minutes: 45 # a sleeping/offline mini must not hang the run
env:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
steps:
- uses: actions/checkout@v4
- name: Install uv
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: Canary model cache (avoid HF rate limits)
if: env.HF_TOKEN != ''
uses: actions/cache@v4
with:
path: canary
key: canary-models-v1
- name: Fetch canary models (cache miss only)
if: env.HF_TOKEN != ''
run: |
[ -f canary/whisper-tiny-Q5_K_M.gguf ] || \
uvx --from huggingface_hub hf download handy-computer/whisper-tiny-gguf \
whisper-tiny-Q5_K_M.gguf --local-dir canary
[ -f canary/moonshine-streaming-tiny-Q8_0.gguf ] || \
uvx --from huggingface_hub hf download handy-computer/moonshine-streaming-tiny-gguf \
moonshine-streaming-tiny-Q8_0.gguf --local-dir canary
echo "TRANSCRIBE_SMOKE_MODEL=$PWD/canary/whisper-tiny-Q5_K_M.gguf" >> "$GITHUB_ENV"
echo "TRANSCRIBE_SMOKE_STREAMING_MODEL=$PWD/canary/moonshine-streaming-tiny-Q8_0.gguf" >> "$GITHUB_ENV"
- name: Build, repair, and test the wheel on real Metal (cibuildwheel)
run: uvx --from "$CIBW_PIN" cibuildwheel --output-dir wheelhouse
- uses: actions/upload-artifact@v4
with:
name: dist-native-macos-arm64
path: wheelhouse/*.whl
wheel-macos-x86:
# Intel macOS (GitHub macos-13 = x86_64): the CPU-ONLY x86 wheel. No Metal
# — Intel-Mac GPUs are out of scope (no fleet hardware to validate Metal
# compute, and the hosted runner is the same paravirtual-GPU VM whose Metal
# compute is broken); Metal / tuned CPU on Intel is served by the sdist.
# The smoke is CPU-steered (TRANSCRIBE_SMOKE_BACKEND=cpu): wheel_smoke.py
# asserts the artifact exposes only CPU (no metal/vulkan/cuda) and runs the
# full suite on CPU. The arm64/Metal lane is `wheel-macos`. cibuildwheel
# provides its own CPython here, so unlike the self-hosted mini there is no
# manual runtime setup. Overrides the pyproject macos defaults (arm64/metal)
# via CIBW_ARCHS_MACOS / CIBW_ENVIRONMENT_MACOS.
runs-on: macos-13
timeout-minutes: 45
env:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
CIBW_ARCHS_MACOS: x86_64
CIBW_ENVIRONMENT_MACOS: >-
TRANSCRIBE_WHEEL_LANE=cpu
TRANSCRIBE_SMOKE_BACKEND=cpu
MACOSX_DEPLOYMENT_TARGET=11.0
CC=/usr/bin/clang
CXX=/usr/bin/clang++
steps:
- uses: actions/checkout@v4
- name: Install uv
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: Canary model cache (avoid HF rate limits)
if: env.HF_TOKEN != ''
uses: actions/cache@v4
with:
path: canary
key: canary-models-v1
- name: Fetch canary models (cache miss only)
if: env.HF_TOKEN != ''
run: |
[ -f canary/whisper-tiny-Q5_K_M.gguf ] || \
uvx --from huggingface_hub hf download handy-computer/whisper-tiny-gguf \
whisper-tiny-Q5_K_M.gguf --local-dir canary
[ -f canary/moonshine-streaming-tiny-Q8_0.gguf ] || \
uvx --from huggingface_hub hf download handy-computer/moonshine-streaming-tiny-gguf \
moonshine-streaming-tiny-Q8_0.gguf --local-dir canary
echo "TRANSCRIBE_SMOKE_MODEL=$PWD/canary/whisper-tiny-Q5_K_M.gguf" >> "$GITHUB_ENV"
echo "TRANSCRIBE_SMOKE_STREAMING_MODEL=$PWD/canary/moonshine-streaming-tiny-Q8_0.gguf" >> "$GITHUB_ENV"
- name: Build, repair, and test the CPU-only wheel (cibuildwheel)
run: uvx --from "$CIBW_PIN" cibuildwheel --output-dir wheelhouse
- uses: actions/upload-artifact@v4
with:
name: dist-native-macos-x86_64
path: wheelhouse/*.whl
wheel-windows:
runs-on: windows-latest
env:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
# LunarG prunes old SDK downloads — when bumping, verify the URL exists.
VULKAN_VERSION: "1.4.350.0"
# The hf CLI prints ✓ marks; Windows' default cp1252 console codec
# chokes on them (charmap codec error). Force UTF-8 for all Python.
PYTHONUTF8: "1"
steps:
- uses: actions/checkout@v4
# vcvars for the whole job: the Ninja generator (CMAKE_GENERATOR in
# pyproject [tool.cibuildwheel.windows]) needs cl.exe on PATH — without
# it CMake silently picks the runner image's MinGW gcc (observed:
# gcc-flavored M_PI errors). The VS generator didn't need this but is
# ~3x slower on this lane.
- uses: ilammy/msvc-dev-cmd@v1
with:
arch: x64
- name: Install uv
run: |
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
Add-Content $env:GITHUB_PATH "$env:USERPROFILE\.local\bin"
- name: Install Vulkan SDK ${{ env.VULKAN_VERSION }}
run: |
curl.exe -o "$env:RUNNER_TEMP\vulkan_sdk.exe" -L "https://sdk.lunarg.com/sdk/download/${env:VULKAN_VERSION}/windows/vulkan_sdk.exe"
& "$env:RUNNER_TEMP\vulkan_sdk.exe" --accept-licenses --default-answer --confirm-command install
Add-Content $env:GITHUB_ENV "VULKAN_SDK=C:\VulkanSDK\${env:VULKAN_VERSION}"
Add-Content $env:GITHUB_PATH "C:\VulkanSDK\${env:VULKAN_VERSION}\bin"
- name: Canary model cache (avoid HF rate limits)
if: env.HF_TOKEN != ''
uses: actions/cache@v4
with:
path: canary
key: canary-models-v1
- name: Fetch canary models (cache miss only)
if: env.HF_TOKEN != ''
shell: bash
run: |
[ -f canary/whisper-tiny-Q5_K_M.gguf ] || \
uvx --from huggingface_hub hf download handy-computer/whisper-tiny-gguf \
whisper-tiny-Q5_K_M.gguf --local-dir canary
[ -f canary/moonshine-streaming-tiny-Q8_0.gguf ] || \
uvx --from huggingface_hub hf download handy-computer/moonshine-streaming-tiny-gguf \
moonshine-streaming-tiny-Q8_0.gguf --local-dir canary
# GITHUB_WORKSPACE keeps the Windows-style path Python can open
# (bash's $PWD here would be an MSYS path).
echo "TRANSCRIBE_SMOKE_MODEL=$GITHUB_WORKSPACE/canary/whisper-tiny-Q5_K_M.gguf" >> "$GITHUB_ENV"
echo "TRANSCRIBE_SMOKE_STREAMING_MODEL=$GITHUB_WORKSPACE/canary/moonshine-streaming-tiny-Q8_0.gguf" >> "$GITHUB_ENV"
- name: Install zlib (vcpkg, static — libtranscribe requires it)
# Static (-static-md: static lib, dynamic CRT) so no zlib1.dll exists
# at runtime and delvewheel has nothing to vendor for it. The
# toolchain file reaches scikit-build-core via CMAKE_ARGS.
run: |
vcpkg install zlib:x64-windows-static-md
# Forward slashes: CMAKE_ARGS values pass through scikit-build-core's
# CMakeInit.txt cache file, where backslashes are eaten as escapes.
$tc = "$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" -replace '\\','/'
Add-Content $env:GITHUB_ENV "CMAKE_ARGS=-DCMAKE_TOOLCHAIN_FILE=$tc -DVCPKG_TARGET_TRIPLET=x64-windows-static-md"
- name: Build, repair, and test the wheel (cibuildwheel)
run: uvx --from "$env:CIBW_PIN" cibuildwheel --output-dir wheelhouse
- uses: actions/upload-artifact@v4
with:
name: dist-native-windows-amd64
path: wheelhouse/*.whl
sdist:
# The universal fallback must actually work: build the sdist, audit what
# went into it, then compile-and-transcribe from the tarball alone.
runs-on: ubuntu-latest
env:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
steps:
- uses: actions/checkout@v4
- name: Install uv
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: Canary model cache (avoid HF rate limits)
if: env.HF_TOKEN != ''
uses: actions/cache@v4
with:
path: canary
key: canary-models-v1
- name: Fetch canary models (cache miss only)
if: env.HF_TOKEN != ''
run: |
[ -f canary/whisper-tiny-Q5_K_M.gguf ] || \
uvx --from huggingface_hub hf download handy-computer/whisper-tiny-gguf \
whisper-tiny-Q5_K_M.gguf --local-dir canary
[ -f canary/moonshine-streaming-tiny-Q8_0.gguf ] || \
uvx --from huggingface_hub hf download handy-computer/moonshine-streaming-tiny-gguf \
moonshine-streaming-tiny-Q8_0.gguf --local-dir canary
echo "TRANSCRIBE_SMOKE_MODEL=$PWD/canary/whisper-tiny-Q5_K_M.gguf" >> "$GITHUB_ENV"
echo "TRANSCRIBE_SMOKE_STREAMING_MODEL=$PWD/canary/moonshine-streaming-tiny-Q8_0.gguf" >> "$GITHUB_ENV"
- name: Build sdist
run: uv build --sdist
- name: Audit sdist contents
run: |
python3 - <<'EOF'
import glob, tarfile
[path] = glob.glob("dist/*.tar.gz")
with tarfile.open(path) as tf:
names = tf.getnames()
total = sum(m.size for m in tf.getmembers())
print(f"{path}: {len(names)} files, {total/1e6:.1f} MB uncompressed")
# Weights by extension anywhere; heavy repo dirs at the ROOT only
# (docs/models/*.md is documentation and belongs in the sdist).
ROOT_BANNED = {"models", "dumps", "reports", "canary", "wheelhouse"}
banned = [n for n in names if n.endswith((".gguf", ".safetensors", ".bin"))
or (len(n.split("/")) > 1 and n.split("/")[1] in ROOT_BANNED)]
assert not banned, f"sdist contains artifacts that must never ship: {banned[:10]}"
assert any(n.endswith("src/arch/canary/model.cpp") for n in names), \
"sdist excludes over-matched (root-anchoring regression)"
assert total < 40e6, f"sdist unexpectedly large: {total/1e6:.1f} MB (28 MB expected)"
for required in ("CMakeLists.txt", "include/transcribe.h",
"ggml/CMakeLists.txt", "cmake/python-wheel-install.cmake",
"bindings/python-native/_contract.py.in"):
assert any(n.endswith(required) for n in names), f"missing {required}"
print("sdist audit ok")
EOF
- name: Compile + transcribe from the sdist (no repo sources)
run: |
sudo apt-get update && sudo apt-get install -y zlib1g-dev
uv venv --seed sdist-venv --python 3.12
# The tarball is the only native source here: pip drives the full
# scikit-build-core compile (cmake/ninja arrive as build deps).
./sdist-venv/bin/pip install dist/*.tar.gz
./sdist-venv/bin/pip install pytest numpy huggingface_hub
./sdist-venv/bin/python scripts/ci/wheel_smoke.py "$PWD"
- uses: actions/upload-artifact@v4
with:
name: dist-sdist
path: dist/*.tar.gz
api-wheel:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install uv
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: Build transcribe-cpp (pure wheel + sdist)
run: |
cd bindings/python
rm -rf dist && uv build
- uses: actions/upload-artifact@v4
with:
name: dist-api
path: bindings/python/dist/*
# Test audio for the checkout-free clean-install job.
- uses: actions/upload-artifact@v4
with:
name: smoke-assets
path: samples/jfk.wav
cuda-wheel-modal:
# The cu12 provider is built and smoked on Modal, not on CI runners: the
# manylinux_2_28 + CUDA-toolkit environment is a cached Modal image
# layer, the 16-core build bills by the second, and the flow ends with a
# REAL T4 runtime smoke (scripts/ci/modal_cuda_build.py). This job is
# just the 2-vCPU driver. Gated on the Modal token secrets — absent on
# forks, steps no-op cleanly.
runs-on: ubuntu-latest
env:
MODAL_TOKEN_ID: ${{ secrets.MODAL_TOKEN_ID }}
MODAL_TOKEN_SECRET: ${{ secrets.MODAL_TOKEN_SECRET }}
HF_TOKEN: ${{ secrets.HF_TOKEN }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Build, repair, packaging-smoke, and T4 runtime smoke (Modal)
if: env.MODAL_TOKEN_ID != ''
run: |
pip install -q modal
modal run scripts/ci/modal_cuda_build.py
- name: Fetch the repaired wheel from the Modal volume
if: env.MODAL_TOKEN_ID != ''
run: |
mkdir -p wheelhouse-cu12
# Root-path download is recursive (glob patterns are deprecated in
# the modal CLI); assets/ comes along but the artifact glob below
# only picks the wheels.
modal volume get transcribe-cu12-wheelhouse / wheelhouse-cu12/ --force
ls -la wheelhouse-cu12/
# Named OUTSIDE the dist-* pattern on purpose: co-import merges dist-*
# and must not pick up the cu12 provider (it would outrank the default
# provider and change what that job tests).
- uses: actions/upload-artifact@v4
if: env.MODAL_TOKEN_ID != ''
with:
name: cuda-dist-linux-x86_64
path: wheelhouse-cu12/*.whl
clean-install:
# Punch-list "clean machine": a bare python:3.12-slim container — no
# compilers, no repo checkout, no env vars. Installs ONLY the built
# wheels (resolver pulls the native provider through the hard pin) and
# transcribes. This is also the shipped-artifact proof of the
# Vulkan-by-default degradation contract:
# tier A (bare container, no Vulkan loader): the bundled module stays
# quietly unloaded — CPU transcribes, vulkan answers unavailable,
# backend="vulkan" raises a clean error.
# tier B (apt install loader + lavapipe): the SAME installed wheel
# discovers a Vulkan device and transcribes on it.
needs: [wheel-linux, api-wheel]
runs-on: ubuntu-latest
container: python:3.12-slim
env:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
steps:
- uses: actions/download-artifact@v4
with:
name: dist-native-linux-x86_64
path: wheelhouse
- uses: actions/download-artifact@v4
with:
name: dist-api
path: wheelhouse
- uses: actions/download-artifact@v4
with:
name: smoke-assets
path: assets
- name: Canary model cache (avoid HF rate limits)
if: env.HF_TOKEN != ''
uses: actions/cache@v4
with:
path: canary
key: canary-models-v1
- name: Fetch canary model (cache miss only)
if: env.HF_TOKEN != ''
run: |
pip install -q huggingface_hub
[ -f canary/whisper-tiny-Q5_K_M.gguf ] || \
hf download handy-computer/whisper-tiny-gguf \
whisper-tiny-Q5_K_M.gguf --local-dir canary
- name: Install from the built wheels only (no index)
run: pip install --no-index --find-links wheelhouse transcribe-cpp
- name: "Tier A: bare machine — CPU transcribes, Vulkan quietly absent"
run: |
python - <<'EOF'
import array, os, wave
import transcribe_cpp as t
from transcribe_cpp import errors
devs = [(d.name, d.kind) for d in t.backends()]
print("devices (bare):", devs)
assert t.native_provider() == "transcribe-cpp-native"
assert t.backend_available("cpu")
assert not t.backend_available("vulkan"), devs
if not os.path.exists("canary/whisper-tiny-Q5_K_M.gguf"):
print("!! no canary (fork without HF_TOKEN) — install-only check")
raise SystemExit(0)
# An explicit vulkan request on a machine without a loader must be
# a clean Python exception, not a crash.
try:
t.Model("canary/whisper-tiny-Q5_K_M.gguf", backend="vulkan")
except errors.TranscribeError as e:
print("vulkan request correctly rejected:", e)
else:
raise AssertionError("backend='vulkan' must fail without a loader")
with wave.open("assets/jfk.wav", "rb") as w:
pcm16 = array.array("h"); pcm16.frombytes(w.readframes(w.getnframes()))
pcm = array.array("f", (s / 32768.0 for s in pcm16))
with t.Model("canary/whisper-tiny-Q5_K_M.gguf") as m, m.session() as s:
text = s.run(pcm).text
print("text (cpu):", text.strip())
assert "country" in text.lower(), text
print("ok: clean machine, one pip install, transcribed on CPU")
EOF
- name: "Tier B: install a Vulkan loader + lavapipe — same wheel uses it"
if: env.HF_TOKEN != ''
run: |
apt-get update -q && apt-get install -y -q libvulkan1 mesa-vulkan-drivers
# lavapipe is a CPU-type Vulkan device; ggml's default filter only
# admits real GPUs (CI-only override, real GPUs need none).
export GGML_VK_VISIBLE_DEVICES=0
python - <<'EOF'
import array, wave
import transcribe_cpp as t
devs = [(d.name, d.kind) for d in t.backends()]
print("devices (lavapipe):", devs)
assert t.backend_available("vulkan"), devs
with wave.open("assets/jfk.wav", "rb") as w:
pcm16 = array.array("h"); pcm16.frombytes(w.readframes(w.getnframes()))
pcm = array.array("f", (s / 32768.0 for s in pcm16))
with t.Model("canary/whisper-tiny-Q5_K_M.gguf", backend="vulkan") as m:
print("model backend:", m.backend)
with m.session() as s:
text = s.run(pcm).text
print("text (vulkan):", text.strip())
assert "country" in text.lower(), text
print("ok: the same installed wheel transcribed on the Vulkan device")
EOF
co-import:
# numpy/torch coexistence (the reason the wheels vendor no OpenMP/BLAS),
# proven by real transcriptions in both import orders. Also a de-facto
# clean-install test: pip resolves transcribe-cpp + its native pin purely
# from the built artifacts.
name: co-import (${{ matrix.label }})
needs: [wheel-linux, wheel-macos, wheel-windows, api-wheel]
strategy:
fail-fast: false
matrix:
include:
- label: ubuntu-latest
runner: ubuntu-latest
torch-args: "--index-url https://download.pytorch.org/whl/cpu"
# macOS arm64 runs on the bare-metal M4 mini, not a GitHub VM: the
# transcription here exercises the REAL shipping Metal path alongside
# numpy/torch, and avoids the broken "Apple Paravirtual device" Metal
# that GitHub-hosted macos runners expose (it decodes garbage — the
# exact reason wheel-macos is on the mini too).
- label: macos-arm64
runner: [self-hosted, macOS, ARM64]
torch-args: ""
- label: windows-latest
runner: windows-latest
torch-args: ""
runs-on: ${{ matrix.runner }}
timeout-minutes: 45 # bound the run if the self-hosted mini is offline
env:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- uses: actions/download-artifact@v4
with:
pattern: dist-*
merge-multiple: true
path: wheelhouse
- name: Install the built artifacts (resolver picks the platform wheel)
run: |
pip install --no-index --find-links wheelhouse transcribe-cpp
pip install numpy huggingface_hub
- name: Canary model cache (avoid HF rate limits)
if: env.HF_TOKEN != ''
uses: actions/cache@v4
with:
path: canary
key: canary-models-v1
- name: Fetch canary model (cache miss only)
if: env.HF_TOKEN != ''
shell: bash
run: |
[ -f canary/whisper-tiny-Q5_K_M.gguf ] || \
hf download handy-computer/whisper-tiny-gguf \
whisper-tiny-Q5_K_M.gguf --local-dir canary
echo "TRANSCRIBE_SMOKE_MODEL=$GITHUB_WORKSPACE/canary/whisper-tiny-Q5_K_M.gguf" >> "$GITHUB_ENV"
- name: numpy co-import smoke
run: python scripts/ci/co_import_smoke.py numpy .
- name: torch co-import smoke
run: |
pip install torch ${{ matrix.torch-args }}
python scripts/ci/co_import_smoke.py torch .