Speech to Text Streaming API
Streaming APIは、WebSocketで音声chunkを受信し、transcript deltaと完了eventを 返します。event名はOpenAI Realtime transcription互換の形式を採用していますが、 接続先はKitsch Labs専用です。
接続情報
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を1回送信できます。
{
"type": "session.update",
"session": {
"type": "transcription",
"audio": {
"input": {
"format": {"type": "audio/pcm", "rate": 24000},
"transcription": {
"model": "kitsch-stt-v1",
"language": "ja",
"prompt": "Kitsch Labs"
},
"turn_detection": {"type": "server_vad"}
}
}
}
}
サーバーは適用した設定をsession.updatedで返します。最初の音声chunkを送信した
後はsessionを変更できません。
対応する音声形式
| 形式 | 入力条件 |
|---|---|
audio/pcm | 24 kHz、mono、signed 16-bit little-endian PCM |
audio/pcmu | 8 kHz、mono、G.711 μ-law |
audio/pcma | 8 kHz、mono、G.711 A-law |
デフォルトはaudio/pcm、24 kHzです。
音声の送信方法
各chunkをbase64でエンコードして送信します。デコード後のchunk 1つの最大サイズは 256 KiBです。
{"type":"input_audio_buffer.append","audio":"BASE64_AUDIO"}
デフォルトのserver_vadは発話終了を自動検出します。手動で終了する場合は
turn_detectionをnullに設定し、次のeventを送信します。
{"type":"input_audio_buffer.commit"}
受信するイベント
| イベント | 説明 |
|---|---|
session.created | transcription sessionの作成完了 |
session.updated | session設定の反映完了 |
input_audio_buffer.committed | 1つの発話入力が確定 |
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_idとcontent_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": "ja"},
"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())
制限と課金
- 1接続の最大session時間は30秒です。
- 接続時間を開始済みの1秒単位に切り上げ、1秒あたり0.1 creditで計算します。
- 1接続で複数の発話を処理できますが、30秒に達する前に新しいsessionへ接続する ことを推奨します。
- 一時的な接続エラーの後は、新しいsessionとして再接続してください。以前の sessionの音声を自動再送しないでください。
エラー処理
未対応の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後に再接続 |