[vLLM] Speech-to-Text: 음성 인식 API
들어가며
vLLM은 텍스트 생성뿐만 아니라 음성 인식(Speech-to-Text) API도 제공한다. vllm/entrypoints/openai/speech_to_text/ 모듈에서 OpenAI의 Audio API와 호환되는 Transcription과 Translation 엔드포인트를 구현하고 있다. Whisper 같은 인코더-디코더 모델로 오디오를 텍스트로 변환한다.
핵심 구조/코드 분석
모듈 구조
vllm/entrypoints/openai/speech_to_text/
├── __init__.py
├── api_router.py # FastAPI 라우터 등록
├── protocol.py # 요청/응답 프로토콜 정의
├── speech_to_text.py # 공통 STT 처리 로직
└── serving.py # Transcription/Translation 서빙
OpenAIServingTranscription
class OpenAIServingTranscription(OpenAISpeechToText):
def __init__(self, engine_client, models, *, request_logger=None,
return_tokens_as_token_ids=False, enable_force_include_usage=False):
super().__init__(
engine_client=engine_client, models=models,
request_logger=request_logger,
return_tokens_as_token_ids=return_tokens_as_token_ids,
task_type="transcribe",
enable_force_include_usage=enable_force_include_usage,
)
async def create_transcription(self, audio_data: bytes, request: TranscriptionRequest,
raw_request: Request | None = None):
return await self._create_speech_to_text(
audio_data=audio_data, request=request, raw_request=raw_request,
response_class=(
TranscriptionResponseVerbose
if request.response_format == "verbose_json"
else TranscriptionResponse
),
stream_generator_method=self.transcription_stream_generator,
)
task_type="transcribe"로 초기화된다. 응답 형식은 response_format에 따라 일반(TranscriptionResponse)과 상세(TranscriptionResponseVerbose)로 분기한다.
OpenAIServingTranslation
class OpenAIServingTranslation(OpenAISpeechToText):
def __init__(self, engine_client, models, *, request_logger=None, ...):
super().__init__(
engine_client=engine_client, models=models,
task_type="translate",
...
)
async def create_translation(self, audio_data, request, raw_request=None):
return await self._create_speech_to_text(
audio_data=audio_data, request=request, raw_request=raw_request,
response_class=(
TranslationResponseVerbose
if request.response_format == "verbose_json"
else TranslationResponse
),
stream_generator_method=self.translation_stream_generator,
)
Translation은 task_type="translate"로, 비영어 오디오를 영어로 번역한다. 구조적으로 Transcription과 동일한 패턴이며, task_type과 응답 클래스만 다르다.
스트리밍 지원
async def transcription_stream_generator(
self, request, result_generator, request_id, request_metadata, audio_duration_s
) -> AsyncGenerator[str, None]:
generator = self._speech_to_text_stream_generator(
request=request,
list_result_generator=result_generator,
request_id=request_id,
request_metadata=request_metadata,
audio_duration_s=audio_duration_s,
chunk_object_type="transcription.chunk",
response_stream_choice_class=TranscriptionResponseStreamChoice,
stream_response_class=TranscriptionStreamResponse,
)
async for chunk in generator:
yield chunk
스트리밍 응답은 SSE(Server-Sent Events) 형식으로 전송된다. list_result_generator로부터 부분 결과를 받아 TranscriptionStreamResponse로 포맷팅하여 전달한다.
프로토콜 정의
프로토콜 모듈에서 정의하는 주요 타입:
TranscriptionRequest/TranslationRequest: 요청 파라미터TranscriptionResponse/TranslationResponse: 간단한 응답TranscriptionResponseVerbose/TranslationResponseVerbose: 타임스탬프 등 상세 정보 포함TranscriptionStreamResponse/TranslationStreamResponse: 스트리밍 청크
공통 STT 로직
speech_to_text.py의 OpenAISpeechToText 기본 클래스가 공통 로직을 담당한다:
- 오디오 데이터 전처리
- 엔진에 요청 전송
- 결과 수집 및 포맷팅
- 오디오 길이 계산
왜 이 설계인가
-
OpenAI API 호환성: OpenAI의 Audio API(
/v1/audio/transcriptions,/v1/audio/translations)와 동일한 인터페이스를 제공한다. 기존 OpenAI 클라이언트 코드를 수정 없이 vLLM으로 마이그레이션할 수 있다. -
Transcribe/Translate 분리: Whisper 모델은 transcribe(원본 언어로 전사)와 translate(영어로 번역) 두 가지 태스크를 지원한다. 이를 별도 클래스로 분리하여 각 엔드포인트의 요청/응답 타입을 명확하게 정의했다.
-
공통 기반 클래스:
OpenAISpeechToText에 공통 로직을 모으고, Transcription과 Translation이 이를 상속한다. 오디오 전처리, 엔진 호출, 에러 핸들링 등이 중복되지 않는다. -
스트리밍 아키텍처: 긴 오디오 파일의 경우 전체 결과를 기다리면 지연이 크다. 부분 결과를 스트리밍하여 사용자가 점진적으로 결과를 받을 수 있다. 실시간 자막 생성 같은 사용 사례에 적합하다.
참고 자료
관련 포스트
vLLM 의 다른글
- 이전글 [vLLM] CPU/XPU Worker: 비NVIDIA 하드웨어 워커
- 현재글 : [vLLM] Speech-to-Text: 음성 인식 API
- 다음글 없음
댓글