SSE API

重新翻譯 SSE

連線資訊

項目值
基礎路徑https://vas-poc.vurbo.ai/api/v1/sse
協定HTTP + Server-Sent Events (SSE)
資料格式text/event-stream
認證方式Header X-API-Key: {KEY},或 Query ?api_key={KEY}(兩者皆可,Query 優先)

注意:瀏覽器原生 EventSource API 不支援自訂 Header,需使用 fetch API 搭配 ReadableStream,或使用支援 Header 的 SSE 客戶端套件。


端點總覽

方法端點說明
GET/api/v1/sse/retranslate/{taskId}重新翻譯全文
GET/api/v1/sse/retranslate/summary/{taskId}重新翻譯摘要
GET/api/v1/sse/recordings/{taskId}/entries/{sid}/retranslate重新翻譯單一句子(編輯原文後使用)

三支端點共通:內容無法翻譯時回報 llm_content_filtered,其 severity 是 warning、context 是 translation, 與其餘翻譯失敗的 error / sse 不同。依 severity 過濾事件的客戶端請確認 warning 沒有被濾掉,否則這類失敗會整批消失。


GET /api/v1/sse/retranslate/{taskId}

功能說明

將指定任務的所有句子重新翻譯為目標語言。透過 SSE 串流逐條發送翻譯結果。

使用場景

  • 切換顯示語言
  • 更新翻譯內容

認證方式

Header:X-API-Key(詳見 認證機制)

請求參數

參數位置類型必填說明
taskIdpathstring是錄音 ID(UUID)
targetLangquerystring是目標語言代碼(如 en-US)
segmentedquerystring否帶 1 表示客戶端支援分段續翻,見下方 分段續翻(v1.18.0)
fromSidquerynumber否只能和 segmented=1 一起使用:從這一句(含)開始翻;不帶就從頭開始(v1.18.0)
expectedRevisionquerynumber否逐字稿版本(≥ 1)。和目前的版本不符時不翻譯、不扣點,回 transcript_revision_conflict;兩種模式都可以帶(v1.18.0)

分段續翻

很長的逐字稿一次重翻可能超過單次請求的處理時間。帶 segmented=1 時,每次請求只翻一段,再用 done 帶回的 nextSid 接著翻下一段。

沒帶 segmented帶 segmented=1
處理方式一次翻完整份,翻完才儲存、才扣點從請求開始約 230 秒,時間到就在這一段停下;已翻的內容照常儲存
逐字稿太長串流開始前回 HTTP 422 retranslate_segmentation_required,不扣點不會收到這個錯誤,一律分段
done欄位不變另帶 revision;這一段被截斷時再帶 truncated: true 與 nextSid
計費整份一次計費每一段各自儲存、各自扣點,只算這一段翻的內容

續翻流程

  1. 送出 ?targetLang=en-US&segmented=1。
  2. 收到 done:
    • 沒有 truncated:已經翻完。
    • 有 truncated: true:以 fromSid={nextSid} 送出下一次請求(可同時帶 expectedRevision={revision},確保這段期間逐字稿沒有被其他操作改動)。
  3. 重複到 done 不再帶 truncated 為止。

注意:參數名稱是 fromSid 與 expectedRevision;寫成 from_sid、expected_revision,或 fromSid 沒有搭配 segmented=1,都會被拒絕(見下方「參數驗證失敗」),不會翻譯、不扣點。

請求範例

curl -N "https://vas-poc.vurbo.ai/api/v1/sse/retranslate/550e8400-e29b-41d4-a716-446655440000?targetLang=en-US" \
  -H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW"
// 使用 fetch API(因 EventSource 不支援 Header)
async function retranslateSSE(taskId, targetLang, apiKey) {
  const response = await fetch(
    `https://vas-poc.vurbo.ai/api/v1/sse/retranslate/${taskId}?targetLang=${targetLang}`,
    {
      headers: {
        'X-API-Key': apiKey
      }
    }
  );
  const reader = response.body.getReader();
  // ... 處理 SSE 事件
}

事件序列

0. connected      → 連線確認
1. translation    → 逐條發送翻譯結果(成功句,重複 N 次)
   error          → 句子翻譯失敗(per-sid,與 translation 交錯出現)
   error          → 逐字稿儲存失敗、同時有其他寫入,或 expectedRevision 不符(storage_upload_failed/transcript_revision_conflict,流程中止、**不會**送出 done)
2. done           → 翻譯完成(分段續翻時,也可能是這一段完成)

失敗句不發 translation,改發 event: error 帶 sid + error_code,失敗事件的格式與 WebSocket 即時翻譯一致,前端可共用同一套錯誤處理。

事件格式


translation

{
  "sid": 1,
  "text": "Hello",
  "is_final": true
}
欄位類型說明
sidnumber句子 ID
textstring翻譯結果
is_finalboolean是否為最終結果

error(per-sid 失敗)

當某句翻譯失敗時,不發 translation 而是發 error:

{
  "error_code": "sse_translation_failed",
  "severity": "error",
  "message": "SSE translation failed",
  "context": "sse",
  "sid": 5,
  "request_id": "req_abc123",
  "timestamp": "2026-04-26T10:30:45.123Z",
  "details": {
    "translation_language": "ja-JP",
    "original_error": "..."
  }
}
欄位類型說明
error_codestring錯誤碼:sse_translation_failed 或 llm_content_filtered
severitystring嚴重度。sse_translation_failed 為 error,llm_content_filtered 為 warning
messagestring人類可讀訊息
contextstring錯誤上下文。sse_translation_failed 為 sse,llm_content_filtered 為 translation
sidint失敗的句子編號
detailsobject含 translation_language、original_error 等 debug 資訊
request_idstring本次請求識別碼,回報問題時附上可加速定位
timestampstring事件發生時間(ISO 8601)

兩種失敗要分開處理:llm_content_filtered 代表該句內容無法翻譯,重試不會改變結果,請修改原文後再試; sse_translation_failed 代表翻譯當下未能完成,稍後重試通常可成功。 兩者的 severity 與 context 不同(見上表),若您依 severity 過濾事件,請確認 warning 沒有被濾掉。

失敗的句子會被儲存為翻譯錯誤記錄(見 history-playback),下次載入歷史時可看到失敗標記。 該記錄的值就是上述錯誤碼。失敗的語言不會寫入新的譯文:先前已有譯文的會維持舊譯文,先前沒有的則譯文中不會有這個語言。 因此不要只用「譯文欄位有沒有這個語言」判斷本次是否成功——那兩種情況分別會讓您誤判成成功與誤判成沒翻過。請一併檢查翻譯錯誤記錄。


done

{
  "totalUpdated": 10,
  "characters_billed": 12700,
  "charged": "6.4",
  "billed": true
}
欄位類型說明
totalUpdatednumber更新的句子總數(不含失敗句)
characters_billednumber本次計費依據的字元數
chargedstring本次操作依費率計算的消耗點數。此值反映用量:吃到飽方案已涵蓋的用量,此欄位仍回報消耗量
billedboolean本次是否產生消耗,恆為 true(未產生消耗時三個欄位皆不出現)
revisionnumber只在帶 segmented=1 時出現:這一段結束後的逐字稿版本,可作為下一段的 expectedRevision(v1.18.0)
truncatedboolean只在帶 segmented=1、而且這一段被截斷時出現,值一定是 true:還有句子沒翻(v1.18.0)
nextSidnumber與 truncated 一起出現:下一段請帶的 fromSid(v1.18.0)

分段續翻時被截斷的 done 範例:

{
  "totalUpdated": 640,
  "characters_billed": 25600,
  "charged": "12.8",
  "billed": true,
  "revision": 7,
  "truncated": true,
  "nextSid": 641
}

計費欄位:characters_billed、charged、billed 三欄僅在本次實際產生消耗時出現。 未產生消耗時(例如生成失敗)三欄皆不出現。判斷是否計費請以 billed 為準 (billed 為 true 才是計費)——此判準適用所有帶計費欄位的端點,不需為個別端點寫例外。 代呼叫並向終端用戶計價的整合方,可直接採用 charged 而不必自行推算。

特有錯誤碼

錯誤碼HTTP 狀態碼說明處理建議
sse_translation_failed500翻譯失敗(per-sid)失敗的單句仍透過 event: error 通知,整體流程不中斷
llm_content_filtered400該句內容無法翻譯(per-sid)重試無效;請修改該句原文後再試。該句不計入 totalUpdated,也不產生消耗
recording_not_found404找不到指定的錄音確認 taskId 正確
recording_not_completed422錄音尚未完成處理等待錄音完成後重試
sse_transcript_not_found404找不到逐字稿錄音可能尚未處理完成
auth_insufficient_credit402點數不足這是串流開始前的真 HTTP 402 JSON 回應,不是 SSE 事件;儲值後再試
stt_quota_exceeded402可用點數不足以支付本次預估消耗同為串流開始前的 JSON 回應;儲值後再試
record_translation_not_allowed400純錄音(record)類型不支援翻譯同為串流開始前的 JSON 回應;改用 transcribe 類型的錄音
storage_upload_failed500逐字稿儲存失敗本次重翻全部作廢、不計費;稍後重試。收到此碼後不會再收到 done
transcript_revision_conflict409同一份逐字稿正有其他寫入在進行,或帶了 expectedRevision 但與目前的版本不符(details 帶 expected_revision 與 actual_revision)本次重翻全部作廢、不計費。版本不符時請重新載入逐字稿取得最新版本;其他情況稍後重試即可。收到此碼後不會再收到 done
retranslate_segmentation_required422沒帶 segmented=1,而逐字稿太長、一次請求翻不完(details.sentenceCount 為要翻的句數,details.maxSentences 為上限)(v1.18.0)這是串流開始前的 JSON 回應,不扣點;改帶 segmented=1 分段續翻

參數驗證失敗沒有錯誤碼:targetLang 缺漏或不是支援的語言,或 segmented、fromSid、expectedRevision 的值與用法不合規則時,回的是 HTTP 200 + event: error,data 只有 message 一個欄位 —— 沒有 error_code、severity、context、request_id、timestamp。請以 message 判讀,並注意這與本頁其他錯誤的格式不同。

前端範例

async function retranslate(taskId, targetLang, apiKey) {
  const response = await fetch(
    `https://vas-poc.vurbo.ai/api/v1/sse/retranslate/${taskId}?targetLang=${targetLang}`,
    {
      headers: {
        'X-API-Key': apiKey
      }
    }
  );

  const reader = response.body.getReader();
  const decoder = new TextDecoder();

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    const events = parseSSE(decoder.decode(value));
    for (const event of events) {
      if (event.type === 'translation') {
        console.log(`句子 ${event.data.sid}: ${event.data.text}`);
      } else if (event.type === 'done') {
        console.log(`完成,共更新 ${event.data.totalUpdated} 句`);
      }
    }
  }
}

GET /api/v1/sse/retranslate/summary/{taskId}

功能說明

將指定任務的摘要重新翻譯為目標語言。透過 SSE 串流逐段發送翻譯結果。

重新翻譯的結果不會儲存:已儲存的摘要與其語言都不變,重新載入歷史紀錄時仍是原本的摘要。要把摘要換成其他語言並保存,請使用 重新生成摘要 的儲存端點(POST,會計費)。

  • 原文語言以摘要本身的語言為準。例如摘要已經重新生成為英文,就會當成英文來翻譯。
  • 摘要較長時,串流常會一次送出一大段,兩則之間也可能停頓數秒,但每一則仍是累積全文。
  • 處理時間與逾時的規則和 摘要翻譯 相同:一次請求上限約 230 秒,途中停頓超過 60 秒會送 error。
  • 要翻譯服務端沒有的摘要內容(例如合併後或編輯過的摘要),請使用 摘要翻譯。

使用場景

  • 切換摘要顯示語言
  • 獲取不同語言的摘要

認證方式

Header:X-API-Key(詳見 認證機制)

請求參數

參數位置類型必填說明
taskIdpathstring是錄音 ID(UUID)
targetLangquerystring是目標語言代碼(如 en-US)

請求範例

curl -N "https://vas-poc.vurbo.ai/api/v1/sse/retranslate/summary/550e8400-e29b-41d4-a716-446655440000?targetLang=en-US" \
  -H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW"
// 使用 fetch API(因 EventSource 不支援 Header)
async function retranslateSummarySSE(taskId, targetLang, apiKey) {
  const response = await fetch(
    `https://vas-poc.vurbo.ai/api/v1/sse/retranslate/summary/${taskId}?targetLang=${targetLang}`,
    {
      headers: {
        'X-API-Key': apiKey
      }
    }
  );
  const reader = response.body.getReader();
  // ... 處理 SSE 事件
}

事件序列

0. connected              → 連線確認
1. summary_translation    → 逐段發送摘要翻譯(重複 N 次)
   error                  → 翻譯失敗(流程中止,**不會**送出 done)
2. done                   → 翻譯完成

事件格式


summary_translation

{
  "text": "累積翻譯結果...",
  "is_final": false
}
欄位類型說明
textstring累積的翻譯結果(串流式,逐漸增長)
is_finalboolean是否為最終結果(最後一筆為 true)

done

{
  "totalUpdated": 1
}
欄位類型說明
totalUpdatednumber固定為 1,表示已完成一份摘要的翻譯;不代表已儲存
truncatedboolean只在譯文不完整時出現,值一定是 true;譯文完整時不會有這個欄位。會出現的情況有兩種:翻譯達到處理時間或長度上限而被截斷,或譯文明顯少於原文(v1.17.0 新增)

本端點不計費,done 不含 characters_billed / charged / billed 欄位。 僅重新翻譯既有摘要時不另行計費;全文重翻(本頁上一節)才計費。

特有錯誤碼

錯誤碼HTTP 狀態碼說明處理建議
sse_summary_not_found404找不到摘要該錄音沒有摘要
sse_summary_translation_failed500摘要翻譯失敗。details.original_error 為 Translation timed out 時表示逾時(等待回應逾時、途中停頓超過 60 秒,或時間到時還沒有任何譯文)稍後重試
llm_content_filtered400摘要內容無法翻譯重試無效;請調整摘要內容後再試
recording_not_found404找不到指定的錄音確認 taskId 正確
recording_not_completed422錄音尚未完成處理等待錄音完成後重試
sse_transcript_not_found404找不到逐字稿錄音可能尚未處理完成
auth_insufficient_credit402點數不足這是串流開始前的真 HTTP 402 JSON 回應,不是 SSE 事件;儲值後再試
record_translation_not_allowed400純錄音(record)類型不支援翻譯同為串流開始前的 JSON 回應;改用 transcribe 類型的錄音

GET /api/v1/sse/recordings/{taskId}/entries/{sid}/retranslate

功能說明

重新翻譯單一句子。最常見場景:使用者透過 PATCH /api/v1/tasks/{id}/entries/{sid} 編輯原文後,呼叫此端點將該句的所有翻譯重做。

與全文重翻 (/retranslate/{taskId}) 的差異:

  • 全文重翻:翻譯所有句子到指定語言(單一目標語言)
  • 單句重翻:只翻譯一個句子,但可同時翻該句曾翻譯過或曾翻譯失敗過的所有語言

使用場景

  • 使用者編輯 STT 原文後自動觸發
  • 個別句子翻譯失敗的補救

認證方式

Header X-API-Key 或 Query api_key(兩者皆可)。瀏覽器原生 EventSource 不支援自訂 Header,這種情況用 Query 參數即可。詳見 認證機制。

請求參數

參數位置類型必填說明
taskIdpathstring是錄音 ID(UUID)
sidpathnumber是句子 ID(1-based)
targetLangquerystring否目標語言代碼。省略時會重翻該句所有「曾翻譯過或曾翻譯失敗過」的語言,也就是譯文與翻譯錯誤記錄兩者的聯集
expectedRevisionquerynumber否樂觀鎖:當前 transcript revision;不符會回 transcript_revision_conflict
api_keyquerystring條件API Key。未帶 X-API-Key Header 時必填

請求範例

# 重翻所有已存在語言
curl -N "https://vas-poc.vurbo.ai/api/v1/sse/recordings/{taskId}/entries/5/retranslate?api_key=vas_xxx"

# 只重翻 en-US,並要求 revision 必須是 3
curl -N "https://vas-poc.vurbo.ai/api/v1/sse/recordings/{taskId}/entries/5/retranslate?targetLang=en-US&expectedRevision=3&api_key=vas_xxx"

事件序列

1. connected   → 連線確認
2. progress    → 開始翻譯某語言(每語言 1 次)
3. translated  → 該語言翻譯完成(每語言 1 次)
   或 error    → 該語言翻譯失敗
4. done        → 全部完成

事件格式

progress

{ "sid": 5, "lang": "en-US", "status": "translating" }

translated

{
  "sid": 5,
  "lang": "en-US",
  "text": "Hello world",
  "tokens_used": 25
}

error(單語言失敗)

錯誤碼與全文重翻同一組:內容無法翻譯時為 llm_content_filtered,其餘失敗為 sse_translation_failed。 下方範例只列關鍵欄位;實際事件的欄位與全文重翻的 error 完全相同(見上一節的欄位表)。

{
  "error_code": "sse_translation_failed",
  "sid": 5,
  "details": { "translation_language": "ja-JP", "original_error": "..." }
}

done

{
  "sid": 5,
  "revision": 6,
  "original_text_edited_at": "2026-05-06T10:30:00.000000Z",
  "languages_translated": ["en-US"],
  "languages_failed": ["ja-JP"]
}
欄位類型說明
sidnumber句子 ID
revisionnumber寫入後的新 revision(用於下次樂觀鎖)
original_text_edited_atstring|null原文編輯時間(若該句被編輯過)
languages_translatedarray翻譯成功的語言代碼
languages_failedarray翻譯失敗的語言代碼

特有錯誤碼

錯誤碼HTTP 狀態碼說明處理建議
recording_not_found404錄音不存在或不屬於該使用者確認 taskId 正確
recording_not_completed422錄音尚未完成處理等待錄音完成後重試
entry_not_found404找不到指定的句子確認 sid 正確
entry_text_empty422該句原文為空(只有空白字元也算)先透過 PATCH 編輯原文
sse_translation_failed500某個目標語言翻譯失敗(per-lang)該語言會出現在 done 的 languages_failed,其餘語言不受影響;稍後重試
llm_content_filtered400某個目標語言的內容無法翻譯(per-lang)該語言會出現在 done 的 languages_failed;重試無效,請修改該句原文後再試
auth_insufficient_credit402點數不足這是串流開始前的真 HTTP 402 JSON 回應,不是 SSE 事件;儲值後再試
record_translation_not_allowed400純錄音(record)類型不支援翻譯同為串流開始前的 JSON 回應;改用 transcribe 類型的錄音
transcript_revision_conflict409revision 不符,或同一份逐字稿正有其他寫入在進行重新載入 transcript 取得最新 revision 後重試
storage_upload_failed500逐字稿儲存失敗本次寫入全部未生效;稍後重試。收到此碼後不會再收到 done

版本:V1.24.1 最後更新:2026-09-28

Copyright © 2026