fix: split 'cuda:N' into device+device_index for CTranslate2

Colab A100 run exposed a GPU-only bug: DeviceManager returns
selected_device='cuda:0' and the engine passed it verbatim to
faster-whisper, but CTranslate2 only accepts device='cuda' with a
separate device_index arg -> 'unsupported device cuda:0' on every GPU
transcription. Fix: FasterWhisperEngine._split_device() splits
'cuda:N' -> ('cuda', N) and passes device_index to WhisperModel.

Notebook: server cell now pkills stale servers (old process holds
port 8000 and 401s on new keys since KeyStore loads at startup),
sets LUKESCRIBE_API_KEY_FILE explicitly, and verifies auth with
RAW_KEY before proceeding; worker cell guards read_result() is None.

+ 4 unit tests (device split contract), 131 tests pass, ruff clean.
This commit is contained in:
2026-08-12 17:20:12 +09:00
parent 236ca0acf4
commit 741bce9fc6
4 changed files with 171 additions and 28 deletions
@@ -86,10 +86,15 @@ class FasterWhisperEngine(TranscriptionEngine):
with self._lock:
model = self._models.get(model_key)
if model is None:
# CTranslate2는 device 문자열로 "cuda"만 허용한다 ("cuda:0" 형태는
# "unsupported device cuda:0" 오류). DeviceManager가 내려주는
# "cuda:N"을 device + device_index로 분리해 전달한다.
device_name, device_index = self._split_device(options.device)
try:
model = WhisperModel(
options.model,
device=options.device,
device=device_name,
device_index=device_index,
compute_type=options.compute_type,
download_root=self._download_root(),
)
@@ -128,6 +133,20 @@ class FasterWhisperEngine(TranscriptionEngine):
wrapped = _CancellableSegmentIterator(segments_iter, should_cancel or (lambda: False))
return TranscriptionOutcome(wrapped, info=info)
@staticmethod
def _split_device(device: str) -> tuple[str, int]:
"""'cuda:N' → ('cuda', N). CTranslate2는 device='cuda'만 허용하므로 분리한다.
'cpu'/'cuda' 같은 인덱스 없는 값은 그대로 (device, 0)을 반환한다.
"""
if device.startswith("cuda") and ":" in device:
name, _, idx = device.partition(":")
try:
return name, int(idx)
except ValueError:
pass
return device, 0
def _download_root(self) -> str | None:
from ..config import get_settings