본문으로 건너뛰기

[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.pyOpenAISpeechToText 기본 클래스가 공통 로직을 담당한다:

  • 오디오 데이터 전처리
  • 엔진에 요청 전송
  • 결과 수집 및 포맷팅
  • 오디오 길이 계산

왜 이 설계인가

  1. OpenAI API 호환성: OpenAI의 Audio API(/v1/audio/transcriptions, /v1/audio/translations)와 동일한 인터페이스를 제공한다. 기존 OpenAI 클라이언트 코드를 수정 없이 vLLM으로 마이그레이션할 수 있다.

  2. Transcribe/Translate 분리: Whisper 모델은 transcribe(원본 언어로 전사)와 translate(영어로 번역) 두 가지 태스크를 지원한다. 이를 별도 클래스로 분리하여 각 엔드포인트의 요청/응답 타입을 명확하게 정의했다.

  3. 공통 기반 클래스: OpenAISpeechToText에 공통 로직을 모으고, Transcription과 Translation이 이를 상속한다. 오디오 전처리, 엔진 호출, 에러 핸들링 등이 중복되지 않는다.

  4. 스트리밍 아키텍처: 긴 오디오 파일의 경우 전체 결과를 기다리면 지연이 크다. 부분 결과를 스트리밍하여 사용자가 점진적으로 결과를 받을 수 있다. 실시간 자막 생성 같은 사용 사례에 적합하다.

참고 자료

댓글

관련 포스트

vLLM 의 다른글