본문으로 건너뛰기

Speech to Text Streaming API

Streaming API는 WebSocket으로 오디오 chunk를 보내고 transcript delta와 완료 이벤트를 받습니다. 이벤트 이름은 OpenAI Realtime transcription core와 호환되는 형태를 사용하지만 연결 경로는 Kitsch 전용입니다.

연결 정보

wss://api.kitschlabs.com/v1/speech-to-text/stream

서버 환경에서 WebSocket handshake에 아래 인증 헤더 중 하나를 포함하세요.

Authorization: Bearer <API_KEY>

xi-api-key도 사용할 수 있습니다. 일반 브라우저 WebSocket API는 임의의 인증 헤더를 설정할 수 없으므로 API 키를 브라우저에 넣지 말고 서버에서 연결하세요.

연결이 승인되면 서버가 먼저 session.created를 보냅니다.

Session 설정

오디오를 보내기 전에 session.update를 한 번 보낼 수 있습니다.

{
"type": "session.update",
"session": {
"type": "transcription",
"audio": {
"input": {
"format": {"type": "audio/pcm", "rate": 24000},
"transcription": {
"model": "kitsch-stt-v1",
"language": "ko",
"prompt": "Kitsch Labs"
},
"turn_detection": {"type": "server_vad"}
}
}
}
}

서버는 적용된 설정을 session.updated로 반환합니다. 첫 audio chunk를 보낸 뒤에는 session을 변경할 수 없습니다.

지원 오디오 형식

형식입력 조건
audio/pcm24 kHz, mono, signed 16-bit little-endian PCM
audio/pcmu8 kHz, mono, G.711 μ-law
audio/pcma8 kHz, mono, G.711 A-law

기본값은 audio/pcm, 24 kHz입니다.

오디오를 어떻게 보내나요?

각 chunk를 base64로 인코딩해 보냅니다. 디코딩된 chunk 하나의 최대 크기는 256 KiB입니다.

{
"type": "input_audio_buffer.append",
"audio": "BASE64_AUDIO"
}

기본 server_vad는 발화 종료를 자동으로 감지합니다. 직접 발화를 끝내려면 turn_detectionnull로 설정하고 다음 event를 보냅니다.

{"type":"input_audio_buffer.commit"}

어떤 이벤트를 받나요?

이벤트설명
session.created연결과 transcription session 생성 완료
session.updatedsession 설정 반영 완료
input_audio_buffer.committed하나의 발화 입력이 확정됨
conversation.item.input_audio_transcription.delta확정된 transcript 조각
conversation.item.input_audio_transcription.completed발화의 전체 transcript와 사용량
error요청 또는 처리 오류

Delta event 예시:

{
"type": "conversation.item.input_audio_transcription.delta",
"event_id": "event_...",
"item_id": "item_...",
"content_index": 0,
"delta": "안녕하세요"
}

완료 event 예시:

{
"type": "conversation.item.input_audio_transcription.completed",
"event_id": "event_...",
"item_id": "item_...",
"content_index": 0,
"transcript": "안녕하세요.",
"usage": {"type": "duration", "seconds": 1.25}
}

item_idcontent_index를 기준으로 delta를 순서대로 이어 붙이고, completed event의 transcript를 해당 발화의 최종 결과로 사용하세요.

연결 예제

다음 예제는 24 kHz mono signed 16-bit little-endian raw PCM 파일을 보냅니다.

import asyncio
import base64
import json
import os

from websockets.asyncio.client import connect


async def transcribe() -> None:
headers = {"Authorization": f"Bearer {os.environ['KITSCH_API_KEY']}"}
async with connect(
"wss://api.kitschlabs.com/v1/speech-to-text/stream",
additional_headers=headers,
) as websocket:
print(await websocket.recv()) # session.created

await websocket.send(json.dumps({
"type": "session.update",
"session": {
"type": "transcription",
"audio": {
"input": {
"format": {"type": "audio/pcm", "rate": 24000},
"transcription": {"model": "kitsch-stt-v1", "language": "ko"},
"turn_detection": None,
}
},
},
}))

with open("speech.pcm", "rb") as audio:
while chunk := audio.read(64 * 1024):
await websocket.send(json.dumps({
"type": "input_audio_buffer.append",
"audio": base64.b64encode(chunk).decode("ascii"),
}))

await websocket.send(json.dumps({"type": "input_audio_buffer.commit"}))

async for raw_event in websocket:
event = json.loads(raw_event)
if event["type"] == "conversation.item.input_audio_transcription.delta":
print(event["delta"], end="", flush=True)
elif event["type"] == "conversation.item.input_audio_transcription.completed":
print()
break
elif event["type"] == "error":
raise RuntimeError(event["error"]["message"])


asyncio.run(transcribe())

제한과 과금

  • 연결 하나의 최대 session 길이는 30초입니다.
  • 성공한 연결 시간은 시작된 1초 단위로 올림해 초당 0.1크레딧을 계산합니다.
  • 한 연결에서 여러 발화를 처리할 수 있지만 30초 전에 새 session으로 연결하는 것이 좋습니다.
  • 일시적인 연결 오류 뒤에는 새 session으로 다시 연결하세요. 이전 session의 audio를 자동으로 재전송하지 마세요.

오류 처리

지원하지 않는 event나 잘못된 설정은 연결을 유지한 채 error event로 반환될 수 있습니다.

{
"type": "error",
"event_id": "event_...",
"error": {
"type": "invalid_request_error",
"code": "invalid_audio",
"message": "The audio field must be valid base64.",
"param": null,
"event_id": "<REQUEST_ID>"
}
}
Close code의미권장 행동
1000정상 완료 또는 session 제한 도달필요한 경우 새 session 연결
1008인증·권한·요청 정책 오류키, credit, 요청 설정 확인
1011일시적인 처리 오류제한적으로 재연결
1013동시 처리 용량 초과backoff 후 재연결