스트리밍 출력 — SSE로 토큰 단위 응답 받기

스트리밍 출력 — SSE로 토큰 단위 응답 받기

LLM 채팅을 만들다 보면, 전체 응답이 끝날 때까지 가만히 기다리기보다 글자가 만들어지는 대로 받아 보고 싶어요. Replicate는 server-sent events(SSE) 기반의 스트리밍 출력을 지원해서, 모델이 입력을 처리하는 동안 실시간으로 진행되는 결과를 받아볼 수 있어요. 이 글에서 스트리밍을 요청하고 소비하는 방법을 정리해요.

출처: Replicate Docs — Streaming output

스트리밍 출력이란

스트리밍 출력은 모델이 입력을 처리하는 동안 실시간 점진적 업데이트를 받아볼 수 있게 해 줘요. 전체 예측이 끝날 때까지 기다리는 대신, 결과가 생성되는 즉시 접근할 수 있어서 즉각 응답이 필요한 채팅 봇 같은 애플리케이션에 잘 맞아요.

큰 그림은 이렇게 동작해요.

  1. stream 옵션을 켜고 예측을 만든다.
  2. Replicate가 스트리밍 출력을 받을 URL이 담긴 예측을 돌려준다.
  3. 그 URL에 연결해 업데이트 스트림을 받는다.

어떤 모델이 스트리밍을 지원하나요

스트리밍은 Llama 3의 여러 변형을 포함해 다수의 언어 모델이 지원해요.

  • meta/meta-llama-3.1-405b-instruct: 4050억 파라미터 모델로 채팅 완성용 파인튜닝. 최고 정확도의 채팅 봇을 만들고 싶다면 이 모델을 써요.
  • meta/meta-llama-3-70b-instruct: 700억 파라미터, 채팅 완성용 파인튜닝. 정확도와 비용의 균형이 좋아요.
  • meta/meta-llama-3-70b: 700억 파라미터 베이스 모델. 사용자가 쓰다 만 글을 이어주는 것 같은 다른 종류의 언어 완성에 쓰기 좋아요.
  • meta/meta/meta-llama-3-8b-instruct: 80억 파라미터, 채팅 완성용 파인튜닝. 정확도를 조금 희생하더라도 더 빠르고 저렴하게 채팅 봇을 만들고 싶을 때 써요.
  • meta/meta/meta-llama-3-8b: 80억 파라미터 베이스 모델. 다양한 자연어 생성 작업에 쓸 수 있어요.

스트리밍을 지원하는 전체 모델 목록은 streaming language models 컬렉션에서 확인할 수 있어요.

스트리밍 요청하기

API로 예측을 만들면 응답에 urls 객체가 담겨요. 여기 예측과 상호작용할 여러 URL이 들어 있는데, 요청한 모델 버전이 스트리밍을 지원하면 urlsstream URL이 포함돼요.

{
  "web": "https://replicate.com/p/cky59275mdrm80cpw83rcn3ej0",
  "get": "https://api.replicate.com/v1/predictions/cky59275mdrm80cpw83rcn3ej0",
  "stream": "https://stream.replicate.com/v1/files/bcwr-3afcgaxf5opqtgeq5ababozl3erroi6ody73lpkwklvnu7bwtmrq",
  "cancel": "https://api.replicate.com/v1/predictions/cky59275mdrm80cpw83rcn3ej0/cancel"
}

urls.stream URL을 server-sent events(SSE)로 스트리밍 출력을 받는 데 쓸 수 있어요.

EventSource는 서버 이벤트를 받는 표준 웹 브라우저 API예요. WebSocket 같은 완전한 양방향 연결 없이도 서버가 실시간 업데이트를 브라우저로 밀어줄 수 있게 해 주죠.

cURL로 stream: true를 켜고 예측을 만드는 예시입니다.

curl -X POST -H "Authorization: Bearer $REPLICATE_API_TOKEN" \
      -d '{"input": {"prompt": "Tell me a story"}, "stream": true}' \
      "https://api.replicate.com/v1/models/meta/meta-llama-3-70b-instruct/predictions"

Python 클라이언트는 이렇게 간단해요.

import replicate

stream = replicate.stream("meta/meta-llama-3-70b-instruct", {
  prompt: "Tell me a story",
})

이 방식의 모델 페이지는 https://replicate.com/meta/llama-3-70b-instruct 에서 볼 수 있어요.

스트리밍 출력 받기

urls.stream URL에 Accept: text/event-stream을 붙여 GET 요청을 보내면 스트리밍 이벤트가 와요.

curl -X GET -H "Authorization: Bearer $REPLICATE_API_TOKEN" \
      -H "Accept: text/event-stream" \
      "https://streaming.api.replicate.com/v1/predictions/fuwwvjtbdmroc4xifxdcwqtdfq"

스트리밍 이벤트는 이렇게 생겼어요.

event: output
id: 1690212292:0
data: Once upon a time...

JavaScript 클라이언트에서는 예측의 stream URL로 EventSource를 구성해 받을 수 있어요. eventoutput일 때마다 data를 모으면 전체 텍스트가 완성돼요.

const output = [];

for await (const { event, data } of stream) {
  if (event === "output") {
    output.push(data);
  }
}
console.log(output.join(""));

예측의 이벤트 스트림은 다음 세 가지 이벤트 타입으로 구성돼요.

이벤트 형식 설명
output plain text 예측이 새 출력을 반환할 때 방출
error JSON 예측이 에러를 반환할 때 방출
done JSON 예측이 끝났을 때 방출

done 이벤트는 예측이 성공적으로 끝나거나, 취소되거나, 에러가 났을 때 방출돼요.

예측이 성공하면 빈 JSON 페이로드와 함께 done 이벤트를 받아요.

event: output
id: 1690212292:0
data: Once upon a time...

event: output
id: 1690212293:0
data: The End.

event: done
data: {}

예측이 취소되면 {"reason": "canceled"} JSON 페이로드와 함께 done 이벤트를 받아요.

event: output
id: 1690212292:0
data: Once upon a time...

event: done
data: {"reason": "canceled"}

예측이 에러를 내면 에러 JSON 페이로드가 담긴 error 이벤트에 이어 {"reason": "error"} 페이로드의 done 이벤트를 받아요.

event: output
id: 1690212292:0
data: Once upon a time...

event: error
data: {"detail": "Something went wrong"}

event: done
data: {"reason": "error"}

408 Request timeout

이벤트 스트림 엔드포인트에는 30초 타임아웃이 있어요. 타임아웃에 닿으면 408: 408 Request Timeout이라는 텍스트를 담은 빈 이벤트가 스트림으로 내려와요.

:408: 408 Request Timeout

이런 경우는 보통 예측이 삭제된 뒤에 스트림에 연결하려고 하거나(API 예측은 1시간 후 만료), 클라이언트가 done 이벤트를 처리하고 연결을 닫는 데 실패했을 때 생겨요.

더 알아보기