Arch / EndeavourOS now ships CUDA 13 (libcublas.so.13); ctranslate2
4.7.2 (which faster-whisper rides) wants CUDA 12 + cuDNN 9 and won't
load against the system libs. Travis got it working ad-hoc with a
shell export of LD_LIBRARY_PATH + manual pip install, but systemd
doesn't inherit either, so the next reboot would refire the
libcublas.so.12 load error.
Making it permanent + reproducible:
- pyproject `tower` extra now pins the CUDA-12 runtime as pip wheels
alongside faster-whisper:
nvidia-cublas-cu12; sys_platform == 'linux'
nvidia-cudnn-cu12>=9,<10; sys_platform == 'linux'
uv.lock resolves nvidia-cublas-cu12 12.9.2.10 + nvidia-cudnn-cu12
9.22.0.52. Dev side (no --extra tower) stays clean — verified by a
no-extra `uv sync` followed by `uv pip list | grep nvidia` returning
empty.
- deploy/tower/run-transcribe-worker.sh (new, +x): computes the venv's
CUDA-12 lib dirs at runtime via `uv run python` (resolving
nvidia.cublas / nvidia.cudnn through __path__ — they're PEP 420
namespace packages with no __file__), prepends them to
LD_LIBRARY_PATH, then execs `uv run second-brain transcribe-worker`.
No hard-coded python3.XX path so it survives Python upgrades. If the
wheels aren't installed it aborts with a clear "uv sync --extra
tower" hint instead of a silent libcublas load failure deep inside
ctranslate2.
- second-brain-transcribe.service: ExecStart now points at the
wrapper. Also moves StartLimitIntervalSec / StartLimitBurst from
[Service] into [Unit] where modern systemd expects them
(systemd-analyze verify previously flagged the misplaced keys as
silently ignored). Restart=always, EnvironmentFile, After=/Wants=
wg-quick@wg-lan.service, User=herbyadmin all unchanged.
- second-brain-transcribe.env.example: trimmed to just
SECOND_BRAIN_DATABASE_URL with the placeholder spelled out, plus a
clear pointer to the ready-to-scp env file generated on herbys-dev
at /opt/backups/postgres-consolidation/second-brain-transcribe.env
(mode 0600, regeneratable from credentials.env without ever echoing
the password). The committed example never carries a real secret.
- deploy/tower/README.md: documents the CUDA-13-vs-CUDA-12 gotcha
upfront ("don't `pacman -S cuda cudnn`"), the wrapper-based
ExecStart, the scp-from-dev EnvironmentFile recipe with the
password-regen one-liner, and the EnvironmentFile-vs-shell-export
note.
Verified locally on dev (no GPU):
- uv.lock resolves with the new tower deps.
- A throwaway venv installed with the same `nvidia-cublas-cu12
nvidia-cudnn-cu12>=9,<10` pins produces lib dirs containing
libcublas.so.12 and libcudnn.so.9 via the wrapper's path probe.
- systemd-analyze verify is clean except the expected
"/opt/projects/... not executable on this host" warning (the
wrapper exists only in the tower's checkout).
- 27 passed / 2 skipped in pytest; zero-check 5/5.
GPU large-v3 + live service start under systemd remain tower-only
validation steps.
65 lines
2.5 KiB
Bash
Executable File
65 lines
2.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# Tower-side launcher for the transcribe worker.
|
|
#
|
|
# CTranslate2 4.7.2 needs libcublas.so.12 + libcudnn.so.9. Arch/EndeavourOS
|
|
# now ships CUDA 13, so we install the matching CUDA-12 runtime as pip
|
|
# wheels under the `tower` extra (nvidia-cublas-cu12, nvidia-cudnn-cu12).
|
|
# Those wheels drop their .so files into the venv's
|
|
# `site-packages/nvidia/{cublas,cudnn}/lib/` dirs, but nothing exports
|
|
# those onto LD_LIBRARY_PATH — systemd doesn't inherit Travis's shell.
|
|
#
|
|
# This wrapper resolves those paths at runtime from the venv's own
|
|
# Python (so it's robust to python-3.X upgrades, venv moves, and wheel
|
|
# version bumps), prepends them to LD_LIBRARY_PATH, and execs the
|
|
# worker. If the nvidia packages aren't importable we abort loudly so
|
|
# systemd surfaces a clear error instead of a silent libcublas load
|
|
# failure deep inside ctranslate2.
|
|
|
|
set -euo pipefail
|
|
|
|
cd "$(dirname "$(readlink -f "$0")")/../.." # project root: deploy/tower → ../..
|
|
|
|
# Resolve lib dirs from inside the project venv via `uv run`. Both
|
|
# packages must import cleanly — uv exits non-zero if not, and the
|
|
# `set -e` above propagates that.
|
|
NVIDIA_LIB_DIRS="$(
|
|
uv run python - <<'PY'
|
|
import os, sys
|
|
# nvidia.cublas / nvidia.cudnn are PEP 420 namespace packages — they
|
|
# have __path__ but no __init__.py and no __file__. Resolve via __path__
|
|
# (which is a _NamespacePath; treat as iterable of strings).
|
|
mods = ("nvidia.cublas", "nvidia.cudnn")
|
|
dirs = []
|
|
for m in mods:
|
|
try:
|
|
pkg = __import__(m, fromlist=["_"])
|
|
except ImportError as exc:
|
|
sys.stderr.write(
|
|
f"[run-transcribe-worker] missing CUDA-12 runtime package {m!r}: {exc}\n"
|
|
"Install with: uv sync --extra tower\n"
|
|
)
|
|
sys.exit(1)
|
|
paths = list(getattr(pkg, "__path__", []) or [])
|
|
if not paths:
|
|
sys.stderr.write(
|
|
f"[run-transcribe-worker] {m} has no __path__ — broken install?\n"
|
|
)
|
|
sys.exit(1)
|
|
lib = os.path.join(paths[0], "lib")
|
|
if not os.path.isdir(lib):
|
|
sys.stderr.write(
|
|
f"[run-transcribe-worker] {m} is installed but {lib!r} is not a directory\n"
|
|
)
|
|
sys.exit(1)
|
|
dirs.append(lib)
|
|
print(":".join(dirs))
|
|
PY
|
|
)"
|
|
|
|
# Prepend so our pinned CUDA-12 libs win over any system CUDA 13 left on
|
|
# the loader path. Preserve any existing LD_LIBRARY_PATH (rare but cheap).
|
|
export LD_LIBRARY_PATH="${NVIDIA_LIB_DIRS}${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}"
|
|
|
|
exec uv run second-brain transcribe-worker "$@"
|