wsUrl へ開くWebSocket接続のルールを扱います。セッションの作成、取得、一覧、終了のREST APIはAPIリファレンスにあります。
セッションと接続はどう違いますか?
セッションはRESTで作成する文字起こしの単位で、接続はそのセッションにつながる1本のWebSocketです。セッションの状態はOPEN と ENDED の2つだけです。接続は、セッションが OPEN の間であれば切れても再びつなげられます。
- セッションが
ENDEDになると元に戻せません。文字起こしを続けるには新しいセッションを作成してください。 - 接続が切れても、セッションは
OPENのまま残ります。同じwsUrlとstreamTokenで再接続してください。 - セッションが終了した理由は
endReasonで受け取ります。値はsession.endedにまとめています。
接続
POST /v1/external/realtime-transcriptions のレスポンスにある wsUrl に、Authorization: Bearer <streamToken> ヘッダーを付けて接続します。
wsUrlは受け取った値をそのまま使ってください。現在はwss://<host>/v1/external/realtime-transcriptions/{id}/streamの形ですが、ホストとパスは変わる可能性があります。- トークンは
Authorizationヘッダーでのみ受け付けます。クエリ文字列、Cookie、APIキーでは接続できません。そのため、ヘッダーを設定できないブラウザのWebSocket APIでは接続できません。 - Originヘッダーは検査しません。
streamTokenは1つのセッションでのみ使え、セッションがOPENの間は再接続にも同じトークンを使います。トークンはセッション作成のレスポンスでしか受け取れず、再発行するAPIはありません。トークンを失くした場合は、セッションを閉じて新しいセッションを作成してください。
error メッセージを送ってから接続を閉じます。HTTPステータスで拒否するのは次の3つの場合だけです。
接続を受け付けると、Tiroはトークン、セッションの状態、トークンを発行したAPIキー、ワークスペースのポリシーを確認します。セッション中にポリシーが無効になったりAPIキーが削除されたりしても、Tiroは開いている接続を切らず、次の接続から拒否します。
音声フレーム
音声はバイナリフレームで、制御メッセージはテキストフレームで送ります。テキストフレーム1つにはUTF-8のJSONオブジェクトを1つ入れます。session.started を受け取る前に音声を送ってもかまいません。Tiroは文字起こしの準備が整う前に届いた音声を保持し、続けて処理します。
音声の先行量はどう計算しますか?
音声の先行量は、送った音声の長さが実際の経過時間よりどれだけ先行しているかを表します。Tiroはこの値を接続ごとに測り、60秒を超えると接続を閉じます。- 何も送らない間に余裕はたまりません。1分間何も送らずに2分ぶんの音声を一度に送っても60秒を超えます。
- 再接続すると直前の接続の先行量を引き継ぎます。再接続で上限がリセットされることはありません。
- 60秒を超えると、
error(audio_rate_exceeded)のretryAfterMsに超過分が入ります。その時間以上待ってから再接続し、実時間の速度で送ってください。
メッセージ
すべてのテキストメッセージにtype フィールドがあります。長さとオフセットは名前が Ms で終わる整数ミリ秒、時刻は名前が At で終わるISO-8601 UTC文字列です。ロケールは ko_KR のような対応ロケールの表記を使います。
順序。 同じ
segmentId の transcript と、同じ translationId の translation は送られた順に届きます。isFinal: false が0回以上届いた後、isFinal: true が1回届きます。異なるsegmentの間、そして transcript と translation の間の順序は保証しません。
互換性。 Tiroはメッセージの種類とフィールドを追加することがあります。フィールドの削除や、名前、型、意味の変更はしません。クライアントは、未知の type、フィールド、reason、endReason、status の値を無視するか、既定の処理をしてください。
session.end
クライアントがセッションを終了するときに送ります。複数回送っても2回目以降は無視します。その後の手順は終了をご覧ください。
session.started
接続ごとの最初のメッセージです。通常は1秒前後で届き、まれに数十秒かかります。
transcript
文字起こしの結果です。
- 同じ
segmentIdのisFinal: falseメッセージが届いたら、そのsegmentのテキストを丸ごと置き換えてください。 isFinal: trueはsegmentごとにちょうど1回届きます。その後、そのsegmentのメッセージは届きません。isFinal: falseなしでisFinal: trueだけが届くこともあります。- segmentの大きさはTiroが決め、保証しません。現在は発話の終わりで区切られます。
translation
翻訳の結果です。セッションで translationLocale を指定した場合にだけ届きます。
- 置き換えのルールは
transcriptと同じです。同じtranslationIdのisFinal: falseは以前のテキストを丸ごと置き換え、isFinal: trueは1回だけ届きます。 - 翻訳の断片は原文のsegmentと1対1で対応しません。1つの断片が複数のsegmentにまたがったり、1つのsegmentが複数の断片に分かれたりします。
- 確定した断片の
textを届いた順に区切り文字なしでつなげると、翻訳文になります。断片は単語の途中で分かれることがあるため、断片の間に空白を入れないでください。空白だけの確定断片は単語の区切りなので、そのままつなげてください。 - 翻訳は原文の確定より遅れて届くことも、原文の
isFinal: falseより先に届くこともあります。 - 自動検出のセッションでは、
transcript.localeがtranslationLocaleと同じsegmentは翻訳しません。言語を指定したセッションでも、翻訳先の言語で話した部分は翻訳されないことがあります。
session.ended
セッションが終了したことを知らせるメッセージです。セッションの最後のメッセージで、Tiroはこのメッセージの後にclose 1000 で接続を閉じます。このメッセージを受け取ったら再接続しないでください。
接続がないときに終了したセッションは、このメッセージの送り先がありません。その場合は
GET /v1/external/realtime-transcriptions/{id} で状態を確認してください。終了したセッションに接続すると error(session_ended)が届きます。
error
エラーを知らせるメッセージです。
終了
最後の結果まですべて受け取るにはsession.end を送ってください。
1
session.end を送る
最後の音声フレームを送った後、
{"type": "session.end"} を送ります。その後に送った音声は無視します。2
残りの結果を受け取る
Tiroが残りの
transcript と translation をすべて isFinal: true で送ります。まだ確定していないsegmentと翻訳の断片は、最後のテキストで確定して送ります。3
session.ended を受け取る
Tiroが
session.ended を送り、close 1000 で接続を閉じます。session.ended を受け取るまで接続を維持してください。残りの結果を送る時間は翻訳を含めて最大30秒です。30秒以内に終わらない場合、Tiroはそれまでに送った結果で session.ended を送り、incomplete: true を付けます。
RESTでセッションを閉じた場合やタイムアウトに達した場合も、接続があれば同じ順序で終了します。このとき、Tiroに届いたもののまだ文字起こしを始めていない音声は、結果に含まれないことがあります。
WebSocket接続を閉じるだけでは、セッションは OPEN のまま残り、ワークスペースの同時セッションの枠を1つ占有します。接続が単に切れた場合、その時点で確定していない結果も受け取れません。
タイムアウト
- テキストメッセージは音声なしのタイマーをリセットしません。
- ミュート中は接続を維持したまま送信を止めてもかまいません。15分以内に再び送ればセッションは続きます。
- Tiroはタイムアウトの時点より遅れてセッションを終了させることがあります。接続のあるセッションは最大1分、接続のないセッションは最大3分ほど遅れます。
再接続
セッションがOPEN なら、接続が切れても同じ wsUrl と streamToken で再接続して続けられます。再接続するかどうかは最後に受け取ったメッセージで決め、close codeで分岐しないでください。
session.endedを受け取った場合、セッションは終了しています。再接続しないでください。- 接続を閉じた
error(fatal: true)がretryable: falseなら、再接続しないでください。接続が維持されるエラー(fatal: false、例:invalid_message)はこの判断に含めないでください。 - それ以外の場合は再接続してください。
retryable: trueのerrorを受け取った場合や、メッセージなしで切れた場合です。close1012はサーバーの再起動なのですぐに再接続し、それ以外はバックオフしてください。
retryAfterMs があれば、その時間以上待ってください。セッションが OPEN の間は再接続の回数に上限はありません。
再接続に成功すると、session.started が resumed: true で届きます。セッションがすでに終了していれば、error(session_ended、retryable: false)が届きます。
切断中の音声はどう再送しますか?
Tiroは音声の再送を求めません。送れなかった音声はセッションに残らないため、クライアントが自分でためておいて再送する必要があります。- 切断中は直近の音声を最大60秒までためておいてください。それより古い部分は捨ててください。
- 再接続したら
session.startedのaudioOffsetMsを確認してください。この値はTiroが処理を終えた音声の長さなので、直前の接続で実際に送った長さより短いことがあります。Tiroが直前の接続の切断に気づく前に新しい接続が既存の接続を置き換えると、0になることもあります。 - セッションのタイムラインで
audioOffsetMs以降にあたる音声をバッファーから先に送り、続けてリアルタイムの音声を送ってください。こうしても音声の先行量60秒の範囲内に収まります。 - Tiroは再送された音声がすでに受け取った音声かどうかを判定しません。
- close
1012はメッセージなしで閉じるため、閉じる直前に送ったフレームの一部がTiroに届いていない可能性があります。この場合もaudioOffsetMs以降の音声を再送してください。 - 切断の直前まで
isFinal: falseだったsegmentと翻訳の断片は捨ててください。Tiroは新しい接続でそれらを再送しません。
同じセッションに2回接続するとどうなりますか?
1つのセッションに接続は1つだけです。同じトークンで新しい接続が来ると、Tiroは新しい接続を使い、既存の接続を閉じます。- 既存の接続は
error(connection_replaced、retryable: false)を受け取り、close4409で閉じます。既存の接続のクライアントは再接続しないでください。 - 新しい接続は
session.startedをresumed: trueで受け取り、セッションを続けます。
WebSocketエラー
reason は増えることがあります。未知の reason を受け取っても、fatal と retryable の値に従ってください。
close code
次の表はログを見るときの参考にしてください。再接続するかどうかは再接続のルールで決め、close codeで分岐しないでください。close codeは増えることがあります。RESTエラー
セッションのREST APIは、エラー処理の共通形式でエラーを返します。分岐にはdetail.reason を使ってください。detail.reason がないレスポンスはHTTPステータスで分岐してください。エンドポイントごとのエラーはAPIリファレンスにあります。