fix: normalize faster-whisper TranscriptionInfo to dict

Colab run 4 (A100): same namedtuple bug as segments — faster-whisper
returns TranscriptionInfo (namedtuple) but batch.py reads
outcome['info'].get('language') -> AttributeError 'TranscriptionInfo'
object has no attribute 'get' on every real transcription, failing the
CLI, API auto-worker job, worker drain, and bench clip alike.

FasterWhisperEngine now normalizes info to a dict at the boundary
(_to_dict_info: _asdict -> dataclasses.asdict -> known-field fallback).

+ 2 unit tests (namedtuple/dict info); 138 tests pass, ruff clean.
This commit is contained in:
2026-08-12 17:39:15 +09:00
parent 20777386fe
commit be5f505410
2 changed files with 61 additions and 3 deletions
@@ -133,7 +133,7 @@ class FasterWhisperEngine(TranscriptionEngine):
wrapped = _CancellableSegmentIterator(
self._to_dict_segments(segments_iter), should_cancel or (lambda: False)
)
return TranscriptionOutcome(wrapped, info=info)
return TranscriptionOutcome(wrapped, info=self._to_dict_info(info))
@staticmethod
def _to_dict_segments(segments):
@@ -169,6 +169,32 @@ class FasterWhisperEngine(TranscriptionEngine):
if (v := getattr(seg, k, None)) is not None
}
@staticmethod
def _to_dict_info(info):
"""faster-whisper TranscriptionInfo(namedtuple) → dict.
다운스트림(배치 파이프라인)은 info를 dict 계약으로 접근한다
(``info.get("language")``) — GPU 실전에서만 재현되는 버그:
'TranscriptionInfo' object has no attribute 'get'.
"""
if info is None or isinstance(info, dict):
return info
asdict = getattr(info, "_asdict", None)
if asdict is not None:
return dict(asdict())
try:
import dataclasses
if dataclasses.is_dataclass(info):
return dataclasses.asdict(info)
except Exception:
pass
return {
k: v
for k in ("language", "language_probability", "duration", "duration_after_vad")
if (v := getattr(info, k, None)) is not None
}
@staticmethod
def _split_device(device: str) -> tuple[str, int]:
"""'cuda:N' → ('cuda', N). CTranslate2는 device='cuda'만 허용하므로 분리한다.