[V2] vLLM 스트리밍 응답 to WebSocket - 100-hours-a-week/6-nemo-ai GitHub Wiki

(SSE → WebSocket Proxy)

📌 왜 WebSocket으로 직접 스트리밍이 불가능한가?

  • vLLM은 기본적으로 HTTP 기반 SSE(Server-Sent Events) 로 스트리밍 응답을 제공합니다.
  • 즉, stream: true 옵션을 사용하면 data: 형식의 SSE 응답을 HTTP로 전달합니다.
  • 그러나 WebSocket은 전혀 다른 프로토콜로, vLLM은 이를 직접 지원하지 않습니다.
  • 따라서 FastAPI 등에서 vLLM과 직접 WebSocket 연결은 불가능합니다.

❗ WebSocket으로 클라이언트에 스트리밍하려면, 중간에서 SSE를 수신하고 WebSocket으로 전달하는 프록시(Proxy) 구조가 필요합니다.


✅ SSE → WebSocket 프록시가 필요한 이유

  • 일부 클라이언트(예: 웹 UI, 채팅 앱)는 SSE를 지원하지 않고 WebSocket만 사용합니다.
  • 이런 환경에서 FastAPI가 중간에서 vLLM의 SSE 응답을 받아 WebSocket으로 중계해야 합니다.
  • 이를 통해 클라이언트는 WebSocket으로 실시간 응답을 받을 수 있습니다.

🔧 FastAPI에서 SSE → WebSocket 프록시 구현 개요

  1. 클라이언트 → FastAPI

    • 클라이언트가 WebSocket 연결을 엽니다. 예: ws://yourserver.com/stream
  2. FastAPI → vLLM

    • FastAPI가 httpx.AsyncClient()로 vLLM에 POST 요청

      {
        "prompt": "...",
        "stream": true
      }
      
  3. FastAPI가 vLLM의 SSE 응답 수신

    • 응답 스트림은 data: {json} 형태의 텍스트
    • [DONE]이 오면 종료
  4. FastAPI가 SSE 데이터를 WebSocket으로 전송

    if line.startswith("data: "):
        data = line[6:]
        if data.strip() == "[DONE]":
            break
        token = json.loads(data)["choices"][0]["delta"]["content"]
        await websocket.send_text(token)
    
  5. SSE 종료 또는 에러 발생 시 WebSocket도 종료


⚠️ 요약: 왜 직접 WebSocket 연결이 안 되는가?

  • vLLM은 WebSocket을 전혀 지원하지 않음
  • HTTP 방식의 SSE만 지원하며, WebSocket 요청은 거절됨
  • 따라서 WebSocket 기반 클라이언트를 지원하려면:
    • FastAPI 서버가 SSE를 수신 → WebSocket으로 중계해야 함

📎 장점과 주의사항

✅ 장점

  • WebSocket만 사용하는 클라이언트도 지원 가능
  • vLLM의 빠른 토큰 스트리밍을 그대로 전달
  • FastAPI의 비동기 처리와 자연스럽게 통합

⚠️ 주의사항

  • SSE 응답은 httpxaiohttp 같은 비동기 HTTP 클라이언트로 처리
  • WebSocket 연결 종료, 에러 핸들링 필수
  • data: [DONE] 등의 종료 신호를 정확히 처리해야 스트림이 정상 종료됨