附錄

Changelog

V1.5.7(2026-05-20)

文件更新(無 API 行為變更)

對外 API 行為完全不變,本版為文件補充與用詞調整版。

新增使用指南:摘要 Prompt 客製化

新增 摘要 Prompt 客製化指南,把原先散落在 6 份 reference 文件中的摘要客製化規格集中為單一 guide:

  • builtin / custom 兩種摘要模式的互斥規則與適用場景
  • REST POST /api/v1/summary、WebSocket start action、SSE regenerate/summary 三條入口的對應欄位
  • 逐字稿記錄欄位(含 summary_prompt_snapshot 審計欄位、summary_fallback_level / summary_dropped_segments 兩個 fallback 審計欄位)
  • 敏感詞與不雅字眼處理章節,整合三條路徑(客戶 prompt → 中性模式、逐字稿 → STT profanity_handling 遮罩、逐字稿 → 摘要層段落省略)並明示「API 層不會主動拒絕含敏感詞的請求
  • 內建安全防護機制(內容中性化指引、prompt injection 防護)與字元長度限制
  • Node.js / Python / WebSocket 三組完整範例

README.md 的「功能指南」表格新增此 guide 入口。

文件用詞調整

對外文件移除部分曝露後端結構的內部用詞,以更通用的描述取代:

  • 內部架構名稱(如「IPEVO 三層」、System Prompt + Template Prompt + Output Format)→ 改為「內建模板規則」、「模板完整內容」
  • LLM 供應商品牌名(AOAI / Azure OpenAI)→ 改為「LLM 服務」
  • 後端機制名(sandwich patternbinary search 定位觸發段)→ 改為「內建安全防護機制」、「自動定位並省略觸發段落」
  • Fallback chain 階層命名(L1 / L2 / L3)→ 對應改為「標準模式」/「中性模式」/「段落省略模式」(summary_fallback_level 欄位值維持 2 / 3 不變,僅文字描述調整)

本次調整僅限新指南 guides/summary-customization.md。其餘 reference 文件、changelog 歷史條目維持原貌,後續會在獨立版本中陸續對齊。

Changelog 內部編號清理

  • 移除 V1.5.4 章節標題的內部 task 編號((Task 15)
  • 移除 V1.5.4 內文的內部規範引用((安全紀律 §J3 #3)

參考文件


V1.5.6(2026-05-19)

文件對齊修正版(無 API 行為變更)

本版為文件校對版,對外 API 行為完全不變。下列項目若你曾依舊文件實作,請依當前規格調整。

Token 格式

  • broadcast_token:4 字元短碼(字符集 a-z0-9)
  • viewer_access_token:64 字元 alphanumeric 字串(非 JWT,無 payload 結構,請勿嘗試解析)

HTTP 狀態碼

  • sse_missing_target_lang / sse_unsupported_language:422
  • broadcast_token_invalid(viewer verify 端點):401

錯誤碼字串

  • POST /api/v1/imports 配額不足:stt_quota_exceeded
  • 廣播觀眾 SSE 找不到廣播:broadcast_session_not_found
  • 廣播觀眾 SSE 容量已滿:broadcast_capacity_exceeded
  • sse_translation_failed error 事件的 contextsse

WebSocket 事件命名

  • retranslate 成功事件:action: "translation"
  • 儲存層上傳失敗:以 type: "error" envelope 送出(error_codestorage_upload_failed / storage_connection_failed / storage_queue_full),無獨立 upload_error action

新列入文件的錯誤碼

端點 / Action錯誤碼說明
WebSocket set_nameset_name_empty / set_name_too_long / set_name_not_ready取代舊文件的 name_too_long
WebSocket audioaudio_process_failedSTT 寫入持續失敗(HTTP 500,建議重新連線)

參考文件


V1.5.5(2026-05-13)

Breaking Change:摘要 API 改為 mode-aware

V1.5.4 推出的「template + custom_prompt 共用」設計改為互斥:客戶必須在每次摘要請求中選擇 mode=builtin(套用 IPEVO 內建模板)或 mode=custom(客戶 prompt 完整取代三層)。

客戶端必須遷移:v1.5.4 客戶端不修改欄位將收到 422。

REST POST /api/v1/summary、SSE regenerate/summary、WebSocket start 三條入口統一新欄位

舊(v1.5.4)→ 新(v1.5.5)對照:

舊欄位新欄位備註
template / templateSlug / summary_template同名(僅 builtin mode)不變、但 custom mode 下禁帶
custom_prompt / customPrompt / summary_custom_promptprompt / summary_prompt(僅 custom mode)改名
custom_prompt_slug / customPromptSlug / summary_custom_prompt_slugprompt_slug / summary_prompt_slug(僅 custom mode)改名
persist_custom_prompt / persistCustomPrompt(移除)custom mode 強制 snapshot、無 opt-in
custom_instructions(移除)legacy 欄位、不再支援
(無)mode / summary_mode(required)新增必填、enum builtin / custom

互斥規則:

  • mode=builtintemplate 必填、prompt / prompt_slug 禁帶
  • mode=customprompt / prompt_slug 必填、template 禁帶
  • 違反 → 422 summary_mode_field_mismatch

GET /api/v1/tasks/ 回應欄位

data.tasks[] 中:

  • 新增 summary_modebuiltin / custom / null
  • summary_template 改為 effective slug(custom mode 下回傳客戶 slug,等同送出時的 prompt_slug
  • 移除 summary_custom_prompt_slug(合併至 summary_template

舊資料相容:未生成摘要的錄音 summary_modenull;既有 builtin 模式錄音的 summary_template 保留原值。

逐字稿記錄結構異動

新增 top-level 欄位(不是 nested 在 summary 物件下):

欄位說明
summary_modebuiltin / custom
summary_templateeffective slug — builtin → 內建 slug;custom → 客戶 slug
summary_plain_textbool
summary_prompt_snapshot僅 custom mode 出現,為客戶原樣傳入的 prompt 內容(builtin mode 不寫入)
summary_fallback_level僅 fallback 觸發時出現(值為 23),代表本次摘要實際走的 AOAI 內容過濾 fallback 路徑。L1 直接成功則 omit
summary_dropped_segments僅 fallback_level=3 時出現,為被剝除的逐字稿段 indices(原序整數陣列)

GET /api/v1/sse/history/transcribe/{taskId}init_summary 事件除既有 text 外,新增 mode / template / plain_text / prompt_snapshot(custom mode 才有值)讓客戶端追溯,以及 fallback_level / dropped_segments(fallback 觸發時才有值)。

WebSocket 新增 outbound event

  • summary_done:摘要生成完成(含 summary_mode / summary_template (effective) / summary_plain_text / tokens_used / summary_fallback_level / summary_dropped_segments不含 final_content
  • summary_error:摘要生成失敗(含 error_code / message

客戶端不再需要輪詢逐字稿記錄判斷摘要是否完成。

AOAI 內容過濾自動降級(fallback chain)

Custom mode 的客戶 prompt 或逐字稿內容若觸發 Azure OpenAI 的內容過濾(finish_reason=content_filter),系統會自動依下列順序降級而非直接回失敗:

階段動作UI 提示建議
L1(預設)用 sandwich pattern 保護的 customer prompt 直接呼叫不顯示提示
L2剝掉 customer prompt、改用中性 fallback prompt 重打「您的自訂指令含 Azure 內容過濾無法處理的詞彙,已使用中性模式產生摘要」
L3逐字稿按段切片、binary search 定位觸發段、剝除後重打「逐字稿含 N 段無法處理,已省略相關內容後產生摘要」(N = summary_dropped_segments 長度)
終點L3 也失敗 → emit summary_error with error_code=llm_content_filtered「本段內容無法產生摘要(內容過濾規則限制)」

L3 binary search 預算上限為 5 次 AOAI 呼叫(hard cap,避免 token 失控)。最壞情況單次摘要會耗用 L1 + L2 + L3 共 7 次 AOAI 呼叫的 token,所有呼叫都會進 usage_logs 計費。

客戶端對應實作:依 summary_fallback_level 顯示 UI 提示(見上表),summary_dropped_segments 可用於告知用戶實際省略了哪些區段。

規格範圍:本版 fallback chain 對 WebSocket realtime 摘要(錄音結束自動生成)與 檔案匯入摘要 兩條路徑生效。SSE regenerate/summary 端點的 fallback 整合屬 follow-up,當前版本被擋下時仍回 llm_content_filtered

Custom mode prompt 安全規則(v1.5.5 新增規則 4)

Custom mode 的 system prompt 安全 wrapper(sandwich pattern)的 header 與 footer 各新增一條規則,要求 LLM「對於原文中可能出現的口語化、情緒性或敏感詞彙,請以中性、客觀的語言概括其意旨,避免逐字引用或重複」。此規則由後端強制注入,不曝露給客戶端設定,目的是降低 L1 階段觸發內容過濾的機率。

wrapper 內容永遠不會寫入後端儲存或 log(已有跨語言 sha256 雜湊回歸測試保證)。客戶 prompt 原文仍透過 summary_prompt_snapshot 欄位儲存作為審計依據,與 summary_fallback_level 互補:

  • summary_prompt_snapshot = 客戶意圖(原 prompt 內容)
  • summary_fallback_level = 實際執行路徑(L1 直接 / L2 中性 / L3 丟段)

Custom mode prompt injection 防護

Custom mode 下,後端對客戶提供的 prompt 加上 prompt injection 防護,避免客戶 prompt 中的指令覆蓋系統規則。客戶仍應注意不要把不可信的終端用戶輸入直接拼進 prompt

新增錯誤碼

錯誤碼HTTP觸發條件
summary_invalid_mode422(SSE)/ 400(其他)mode 不是 builtin / custom
summary_mode_field_mismatch422 / 400mode 與欄位組合不符(必填缺漏 / 禁帶被帶入)
summary_prompt_too_long422 / 400prompt 超過 2000 字元
summary_prompt_slug_too_long422 / 400prompt_slug 超過 64 字元
summary_prompt_slug_invalid422 / 400prompt_slug 含控制字元(\n / \r / \t / \0 等)

客戶端建議

  1. 新增 mode 必填欄位 — 既有呼叫 templateSlug=meeting 的請求改為 mode=builtin&template=meeting
  2. 欄位重新命名customPromptpromptcustomPromptSlugpromptSlug;且這兩欄只在 mode=custom 下使用
  3. 移除 persistCustomPrompt — custom mode 自動保留 prompt 內容
  4. templateSlug 改為 template — 並且僅限 mode=builtin 使用
  5. 逐字稿記錄改為 top-level 欄位 — 不再嵌套在 summary 物件下
  6. 客戶端可從 done event / summary_done event 判斷是否已儲存 — 看 persisted: true/false,不需要再依 HTTP method 推斷

參考文件


V1.5.4(2026-05-12)

新增功能:摘要 Prompt 客戶客製化

企業客戶現在可以在不改動 IPEVO 內建模板的前提下,為摘要 API 加入自家規則。本版新增三個正交的客戶端參數,並把摘要重生成端點拆成「預覽」與「存檔」兩個動詞,避免 HTTP GET 帶副作用的設計斷層。

完全向後相容 — 不傳新欄位 = 行為與舊版一致。

POST /api/v1/summary 新增欄位

欄位類型限制說明
custom_promptstring≤2000 字元Layer 4 客戶自訂指示(append 在內建三層之後)
custom_prompt_slugstring≤64 字元、Unicode、禁控制字元客戶端自訂模板識別碼(pass-through)
plain_textbool預設 false要求純文字輸出(後端做 Markdown 後處理)
persist_custom_promptbool預設 falseopt-in:done event 是否回顯 custom_prompt 內容

SSE start / done event 也增補對應欄位(custom_prompt_slugplain_textfinal_contentcustom_prompt_snapshot),詳見 reference/rest/summary.md

/api/v1/sse/regenerate/summary/{taskId} 拆兩個端點

方法用途寫 DB儲存逐字稿計費
GET預覽(試跑、比較不同 prompt 結果)
POST存檔(正式儲存)✅ + bump revision

客戶端建議:若你的整合方原本依賴「打 GET 後後端記錄自動更新」,請改打 POST。GET 改為純預覽,不再寫入任何後端狀態。

done event 新增 persisted: bool 欄位,客戶端可直接從 payload 判斷此次是否已儲存,不需要再依靠 HTTP method 推斷。

WebSocket start action 新增 4 欄位

summary_custom_prompt / summary_custom_prompt_slug / summary_plain_text / summary_persist_custom_prompt,與 REST 端點欄位一一對應,限制相同。

新增端點:GET /api/v1/summary-templates/{slug}

曝露 IPEVO 內建模板的三層 Prompt 完整原始文字(system_prompt / template_prompt / output_format),供企業客戶整合時參考既有基礎,再決定 custom_prompt 該補什麼。

GET /api/v1/summary-templates 同時新增 ?category=summary|medical|legal|all 篩選與回應 data[].category 欄位(預設 summary,向後相容)。

新增錯誤碼

錯誤碼HTTP觸發條件
custom_prompt_too_long400custom_prompt 超過 2000 字元
custom_prompt_slug_too_long400custom_prompt_slug 超過 64 字元
custom_prompt_slug_invalid400custom_prompt_slug 含控制字元
template_not_found404指定 slug 的模板不存在或已停用
invalid_category400?category= 不在白名單內

行為變更

  • summary_text_empty / summary_text_too_long HTTP 狀態碼修正:原本因後端錯誤碼未明確映射而 fall through 到 500,本版修為符合語意的 400。
  • POST /api/v1/summary 錯誤事件 details 不再包含 LLM raw error:raw error 僅進 server log,回給客戶端的 details 只保留 provider 標示。
  • GET preview 仍會計費:LLM 真實消耗 token,不能讓 GET 端點白嫖。重複呼叫 GET 會重複計費,但不會改變 DB / Blob 狀態。

路徑與欄位命名規範

  • customPromptSlug客戶自訂 pass-through 識別碼(與既有 templateSlug exists 校驗的語意不同)。命名上前者為「客戶端追溯用」,後者為「VAS 內建模板查表用」。
  • summary_custom_prompt_slug 寫入 recordings 表的 VARCHAR(64) 欄位,方便事後查詢「這份摘要對應哪個客戶模板」。
  • custom_prompt_snapshot(opt-in)只在客戶設定 persist_custom_prompt=true 時才儲存於逐字稿記錄,永遠不進 DB

安全控管

  • 所有端點需 API Key 認證
  • VAS server log 不會 log custom_prompt 或逐字稿全文(僅 log 長度與 slug)
  • LLM 錯誤訊息會被 sanitize(不曝露 raw error 給客戶端)
  • custom_prompt 跨租戶完全隔離(Session-scope,無記憶持久化)

Bug 修正與內部改善

  • WebSocket start action 的 recording_id 欄位 deprecation 預告版號統一為 V2.0.0(events.md 原寫 V1.6.0,與程式碼註解不一致)
  • SSE sse-api.md 修正失效的 TOC anchor(指向 audio 段落但本文已遷移至獨立 reference/sse/audio.md
  • 改善文字清理:CJK 字元與 Emoji(含 ZWJ 序列)不再被誤切或誤拒
  • 摘要重生成的 fullText 加入 100,000 字元上限

參考文件


V1.5.3(2026-05-07)

Breaking Change:speaker_id 命名翻轉

V1.3.12 為了支援語者編輯,新增 original_speaker_id 欄位來保留原始 ID,但留下「同一名稱在不同階段語意不同」的設計斷層:WebSocket 即時錄音的 speaker_id 是原始 ID(如 Guest-1),SSE 歷史音檔載入後 speaker_id 變成顯示名(如 王經理,已套 alias)。前端常因此拿錯欄位丟進 PATCH /speakers/reassign

本版做一次性翻轉,不向下相容

舊名新名語意
speaker_id(顯示名)speaker_label顯示標籤(套 alias 後;可變、人類可讀)
original_speaker_id(原始 ID)speaker_id原始說話者 ID(不可變、永遠穩定)

翻轉後,speaker_id 在所有上下文(WebSocket / SSE / REST / blob / log)一致指向原始 ID;新增 speaker_label 表示套 alias 後的顯示標籤。語者編輯(rename / reassign / merge)一律以 speaker_id 為定位 key。

REST API 欄位變更

PATCH /api/v1/tasks/{taskId}/speakers/rename

位置舊欄位新欄位
Request bodyoriginal_namespeaker_id(最大 100 字元)
Request bodynew_namenew_label(最大 100 字元,禁控制字元 \x00-\x1F / \x7F 與換行
Response dataoriginal_namespeaker_id
Response datanew_namenew_label

speaker_id 仍可同時接受顯示標籤做連續改名(如先把 Guest-1 改名為「王經理」,再用「王經理」改為「王總」);解析後的 response speaker_id 永遠是原始 ID。

PATCH /api/v1/tasks/{taskId}/speakers/reassign

位置舊欄位新欄位
Request bodytarget_speaker_id不變(語意已對齊原始 ID)
Response datanew_speaker_namenew_speaker_label

target_speaker_id 必須是原始 ID(取自 init_sentence.speaker_id);reassign 不接受顯示標籤。

PATCH /api/v1/tasks/{taskId}/speakers/merge

位置舊欄位新欄位
Request bodysource_speaker_id / target_speaker_id不變(仍接受原始 ID 或當前顯示標籤)
Response datatarget_speaker_nametarget_speaker_label

WebSocket 事件變更

事件舊欄位新欄位
rename_speaker action bodyoriginal_name / new_namespeaker_id / new_label
result event 的 origin / translations[lang]speaker_id(混用顯示名)speaker_id(原始 ID)+ speaker_label(顯示標籤)
speaker_renamed eventoriginal_name / new_namespeaker_id / new_label
speaker_reassigned eventnew_speaker_namenew_speaker_label
speakers_merged event(缺 target 標籤)新增 target_speaker_label

SSE 事件變更

事件舊欄位新欄位
init_sentencespeaker_id(顯示名)+ original_speaker_id(原始 ID)speaker_id(原始 ID)+ speaker_label(顯示標籤)
Broadcast viewer origin / translationspeaker_id(混用)speaker_id + speaker_label
Broadcast viewer speaker_renamed / speaker_reassigned / speakers_merged同 WebSocket 對應事件同上

init_metadata.speaker_aliases(「原始 ID → 顯示標籤」映射)行為與欄位不變

客戶端建議

  • 使用 WebSocket 即時錄音的客戶:升版前同步 result.origin.speaker_id 與新增 result.origin.speaker_label 的處理;rename body 改送 { "speaker_id": "...", "new_label": "..." }
  • 使用 SSE 歷史音檔的客戶init_sentence.speaker_id 現在是原始 ID(過去是顯示名),改用 speaker_label 顯示
  • 做語者編輯(rename / reassign / merge)的客戶
    • rename → 用 speaker_id(原始 ID 或當前顯示標籤皆可)+ new_label
    • reassign → target_speaker_id 必須是原始 ID(取自 init_sentence.speaker_id,不能送顯示標籤)
    • merge → source_speaker_id / target_speaker_id 仍可送原始 ID 或當前顯示標籤
  • 做 TXT/SRT/CSV 匯出整合的客戶new_label 新增控制字元/換行驗證;若先前送過含換行的標籤會收到 422,請改為單行內容
  • 不做語者編輯、僅消費 transcript 文字的客戶:影響極小,唯一行為差異是若有舊代碼把 speaker_id 當顯示名直接渲染,需改用 speaker_label

資料相容性

  • 不做向下相容:舊 transcript blob(V1.3.12 ~ V1.5.1,含 speaker + original_speaker_id)需經 migration 才能在新版讀取;POC 階段沒有跨版本資料保留承諾
  • 新錄音不受影響:V1.5.3 以後新建的 transcript blob 直接使用新欄位

文件更新

參考文件


V1.5.1(2026-05-07)

Bug 修正:POST /api/v1/imports 補上術語 / 校正欄位的長度驗證

文件多處承諾的長度限制(如 term 最大 100 字元)過去在檔案匯入路徑沒有真正生效,超長內容會被悄悄接受。本版補回,行為與文件承諾對齊。

行為變更(與文件承諾對齊)

POST /api/v1/imports 對下列欄位新增 422 拒絕條件(先前會被接受):

欄位限制
terminology.<lang>陣列,最多 500 個術語(每語言)
terminology.<lang>[].termstring,最大 100 字元
terminology.<lang>[].boostnumeric,0.5–5.0(可省略,預設 1.0)
fuzzy_correction.<lang>[].correctstring,最大 200 字元
fuzzy_correction.<lang>[].incorrect[]string,最大 200 字元

限制值與 WebSocket config action 一致;先前只有 WebSocket 路徑會擋,本版把檔案匯入路徑補齊。

客戶端建議

若先前曾透過 POST /api/v1/imports 送過超長 term(>100 字元),現在會收到 422。前端應在送出前自行檢查長度並提示使用者。WebSocket 路徑無變化。


V1.5.0(2026-05-07)

內部命名統一(無對外 API 變更)

延續 V1.4.1 啟動的 task_id 命名統一,本版完成內部協議層的過渡。

對外公開 API(WebSocket、REST、Webhook、SSE)完全不變,客戶無需任何動作。

舊命名(recording_id)將於 V1.6.0 全面移除,相關客戶端遷移指引請見 V1.4.1 客戶端建議


V1.4.3(2026-05-07)

內部觀測層命名統一(無對外 API 變更)

承接 V1.4.1,本版針對 log 與監控層做命名統一過渡。

對外公開 API 完全不變,客戶無需任何動作。


V1.4.2(2026-05-07)

內部程式碼命名統一(無對外 API 變更)

承接 V1.4.1,本版把 task_id 命名推進到後端程式碼層級。

對外公開 API 完全不變,客戶無需任何動作。


V1.4.1(2026-05-06)

命名統一:以 task_id 為跨介面任務識別碼

過去同一筆任務在不同介面有不同欄位名(WebSocket 用 recording_id、Webhook 用 task_id、部分 REST path 變數混用 {recordingId} / {taskId}),整合方需自行對齊三邊命名。本版啟動命名統一週期,新整合請統一使用 task_id

WebSocket 變更(向後相容)

  • session_started 事件 payload 同時帶 task_idrecording_id兩者值完全相同(同一筆錄音的 UUID)
  • recording_id 欄位標記為 Deprecated,仍正常送出;預計 V1.6.0 移除
  • 文件補強:session_id 為 WS 連線層級識別碼(連線結束即失效),與 task_id(任務識別碼)為不同層級

REST API 變更(向後相容)

新增 /api/v1/tasks/{taskId}/... alias 路徑,與既有 /api/v1/recordings/{recordingId}/... 行為完全相同:

推薦(V1.4.1 起)Deprecated(V1.6.0 移除)
PATCH /api/v1/tasks/{taskId}/speakers/renamePATCH /api/v1/recordings/{recordingId}/speakers/rename
PATCH /api/v1/tasks/{taskId}/speakers/reassignPATCH /api/v1/recordings/{recordingId}/speakers/reassign
PATCH /api/v1/tasks/{taskId}/entries/{sid}PATCH /api/v1/recordings/{recordingId}/entries/{sid}

客戶端建議

  • 新整合:統一使用 task_id 欄位與 /api/v1/tasks/{taskId}/... 路徑,避免後續再次遷移
  • 既有整合:無需立即修改。recording_id/api/v1/recordings/... 在 V1.x 期間持續可用,建議在排程內遷移,最遲於 V1.6.0 釋出前完成
  • ID 對齊邏輯:若同時依賴 WS 與 Webhook,可直接以 WS 的 task_id(或舊名 recording_id)對齊 Webhook data.task_id,三者為同一 UUID
  • session_id 不要用於對齊session_id 僅在 WS 連線生命週期內有意義,不會出現在 Webhook 與 REST

移除時程預告(V1.6.0)

V1.6.0 將移除 WS payload 中的 recording_id 欄位,並移除 /api/v1/recordings/{recordingId}/... 路徑。詳細時程將於 V1.6.0 釋出前另行公布。

不變項

  • Webhook payload:data.task_id 既有命名不變
  • 既有 /api/v1/tasks/{taskId}/... 端點:保持不變

V1.4.0(2026-05-06)

新功能:歷史錄音原文編輯 + 自動重翻

使用者可修正 STT 辨識錯誤後重新生成翻譯,工作流見 Entries API 典型工作流

歷史紀錄 SSE 暴露編輯標記

historyTranscribe init_sentence 事件在被編輯句子上會帶 original_text_raw(STT 原始)與 original_text_edited_at,前端可顯示「已編輯」標記與「還原原文」功能。

安全性修補

  • retranslate / retranslateSummary 加入使用者過濾:兩個既有 SSE 端點先前存在水平權限漏洞(IDOR),允許讀取他人錄音。本版補上權限檢查,他人錄音回 recording_not_found
  • 重翻 / 重生摘要要求錄音已完成retranslate / retranslateSummary / retranslateEntry / regenerateSummary 四個端點要求 processing_status === completed,避免與處理中流程競爭。未完成時回 recording_not_completed

新增錯誤碼

錯誤碼HTTP說明
recording_not_completed422錄音尚未完成處理,不允許重翻 / 編輯 / 重生摘要
entry_not_found404找不到指定的句子
entry_text_empty422句子原文為空
entry_text_too_long422句子原文超過 2000 字元上限
transcript_revision_conflict409逐字稿已被其他請求修改(樂觀鎖衝突)

詳見 error-codes.md

客戶端建議

  • 編輯 STT 原文後:建議呼叫 PATCH 後立即觸發單句重翻 SSE,並帶上 PATCH 回應的 revisionexpectedRevision,避免併發覆寫
  • 顯示編輯標記:以 init_sentence 事件內 original_text_raw 欄位存在性'original_text_raw' in data)判斷是否被編輯過,不要用文字比對(使用者可能編輯後又改回原值)
  • 錄音狀態:對未完成的錄音呼叫重翻 / 編輯 / 重生摘要會回 recording_not_completed,前端應在 UI 阻擋這些操作直到 processing_status === completed

V1.3.13(2026-05-06)

行為變更(Breaking Changes)

  • WebSocket audio_format 鎖定為 pcmwebm:原本接受 5 種格式(pcm / webm / mp3 / wav / m4a)收斂為僅接受 pcmwebm,與文件 reference/websocket/voice-translation.md 既有規格一致。客戶若送 mp3 / wav / m4a 將收到 audio_format_unsupported(過去會被悄悄解碼成功,屬未文件化的隱性行為)。檔案匯入仍走 POST /api/v1/imports,不受影響。

文件更新

  • 音檔下載 Content-Type 一律 audio/mp4:rest-api / SSE audio / tasks export / history playback / curl / javascript 多處文件統一為「所有錄音音檔一律以 M4A 容器(AAC 編碼)回傳」,移除原本「動態決定」的循環敘述。
  • 檔案匯入支援格式收斂為 mp3 / wav / m4a:移除文件中對 mp4webm 的提及,以對齊實際接受的格式(guides/file-import.mdreference/rest/imports.md)。

客戶端建議

  • 使用 WebSocket start action 的客戶:請務必明確指定 audio_formatpcmwebm;若先前曾依賴未文件化的 mp3 / wav / m4a 隱性支援(極少數情境),請改走 檔案匯入 API
  • 下載錄音音檔的客戶所有新錄音 Content-Type 固定為 audio/mp4、副檔名 .m4a。若儲存中仍存有舊錄音,下載仍可能收到 audio/webm,建議保留對舊副檔名的處理分支以涵蓋歷史資料。

參考文件


V1.3.12(2026-05-04)

⚠️ 已於 V1.5.3 翻轉:本版引入的 original_speaker_id 欄位與「speaker_id 為顯示名」的設計已被 V1.5.3 的命名翻轉取代。本段保留作為歷史紀錄;新整合請直接參考 V1.5.3 規範,不需再實作此版的客戶端建議。

新增功能

  • History SSE 補欄位以對齊 Transcribe 語者編輯 UX:歷史紀錄的 init_metadatainit_sentence 事件各補一個欄位,讓前端能完整重用即時錄音頁的說話者編輯選單(單句重新指派 + 全域重命名)。
    • init_metadata 新增 speaker_aliases(object):「原始說話者 ID → 顯示名」映射。無別名時為 {}空物件,非空陣列)。供前端在送 PATCH /speakers/rename 前做撞名預檢,可涵蓋「在後端存在但因被 rename 過而畫面上沒出現的原始 ID」這類隱性衝突。
    • init_sentence 新增 original_speaker_id(string|null):未經 alias 替換的原始說話者識別碼,提供給 PATCH /speakers/reassign 作為 target_speaker_id 來源。
    • 舊資料 fallback:pre-v2.24.0 的舊版逐字稿記錄若無 original_speaker_id,SSE 出口會自動退回 speaker_id,避免新欄位帶 null 對舊錄音造成編輯入口失效。

行為變更

  • 無 breaking change。兩個欄位皆為純新增;既有 SSE client 用 Zod z.object(預設 strip extra)不會解析失敗,無須版本協商。

文件更新

客戶端建議

  • History 詳情頁要做語者編輯的客戶:請從 init_sentence.original_speaker_id 取得 reassign 用的原始 ID(不要用 speaker_id,那是已套 alias 的顯示名);用 init_metadata.speaker_aliases 做 rename 前的撞名預檢。
  • 不做語者編輯的客戶:可忽略新欄位,現有解析行為不受影響。

參考文件


V1.3.11(2026-05-04)

行為變更(Breaking Changes)

  • STT 拒絕 bare en 代碼(V1.3.10 changelog 原宣稱已移除但實際未生效):客戶若送 en 將收到 422 invalid_transcription_language,請改用 en-US / en-GB 等完整 BCP 47 代碼。
  • TTS 移除 4 個供應商不支援的 localeit-CHar-ILar-PSen-GH。語音服務供應商的 TTS 從未支援這 4 個 locale,過去客戶若請求其 voice 會在供應商端 runtime 失敗。STT 仍支援這 4 個 locale。

新增功能

  • TTS 補齊至供應商完整真值(154 種語言、325 個 voice):與語音服務供應商的 Monolingual Neural Voice 清單完全對齊(含 GA + Preview)
    • 中國方言(4 個新增)zh-CN-henanzh-CN-guangxizh-CN-liaoningzh-CN-shaanxi
    • 南亞語系(5 個新增)bn-BD 孟加拉語(孟加拉)、ta-LK 坦米爾語(斯里蘭卡)、ta-MY 坦米爾語(馬來西亞)、ta-SG 坦米爾語(新加坡)、ur-PK 烏爾都語(巴基斯坦)
    • 東南亞語系(1 個新增)su-ID 巽他語(印尼)
    • 東歐語系(1 個新增)sr-Latn-RS 塞爾維亞語(拉丁字母)
    • 北美原住民語系(2 個新增)iu-Cans-CA 因紐特語(加拿大音節)、iu-Latn-CA 因紐特語(加拿大拉丁字母)

文件更新

  • languages.md TTS 區段重寫,明確標示:
    • 145 個 STT locale 中有 141 個 STT/TTS 雙端皆支援;4 個(it-CHar-ILar-PSen-GH)僅 STT 支援
    • 154 個 TTS locale 中有 13 個僅 TTS 支援(4 個 zh-CN 方言 + 9 個其他語言)
  • guides/tts.md 數字更新(142→154 種語言、304→325 個 voice)
  • README.md TTS 描述更新

驗證結果

對比語音服務供應商官方的 STT 與 TTS voice 清單,VAS 完全對齊:

來源STTTTS localeTTS voiceDiarization
供應商官方14515432531
VAS14515432531

客戶端建議

  • 使用 en 簡碼的客戶:請改為 en-US 或其他完整 BCP 47 代碼。
  • 使用 it-CH/ar-IL/ar-PS/en-GH 做 TTS 的客戶:原本就會在 Azure 端失敗,請改用同語系的其他 locale(例:it-CHit-ITar-ILar-SAen-GHen-NG)。STT 不受影響。
  • 想用新增的 13 個 TTS-only locale 的客戶:可直接呼叫 GET /api/v1/tts/voices?language=zh-CN-henan 等取得 voice 列表。

參考文件


V1.3.10(2026-04-30)

文件更新

  • languages.md 數字修正
    • 語音辨識語言總數 119 種145 種(與 Azure STT 主表對齊)
    • 語音翻譯支援 117 種143 種(145 扣除 jv-ID 爪哇語、wuu-CN 吳語)

本版有遺留問題,請見 V1.3.11:本版原宣稱「已移除 en、語言數完全一致為 145」,但實際並未移除 "en"(仍為 146),亦未處理 TTS 端的 it-CH/ar-IL/ar-PS/en-GH(供應商 TTS 不支援)與 13 個 TTS-only locale 的缺漏。完整對齊修正於 V1.3.11 完成。

客戶端建議

  • 本版僅文件層數字修正,不影響運行中的整合。

參考文件


V1.3.9(2026-04-29)

新增功能

  • Webhook Secret Bootstrap 流程:解決客戶端首次整合 webhook 時無法取得 secret 的矛盾。Dashboard 新增「產生 Webhook Secret」按鈕(Lazy 產生),讓使用者先取得 secret 設定到接收端,再回頭設定 webhook URL。設定 URL 時的 probe 將以雙方一致的 secret 簽名,可一次通過。對齊 Stripe / Shopify 業界主流模式。
    • 新增端點POST /dashboard/api-keys/{id}/webhook/regenerate-secret(Dashboard 限定,沿用 webhook-update rate limiter,10/min/user)
    • 行為:產 64 字元隨機 secret 寫入 DB,不送 probe、不動 webhook URL,flash session 一次性回傳明文供 Dashboard 顯示
    • 重生影響:執行後舊 secret 立即失效,既有接收端會收到簽章不符的 webhook 直到接收端切換新 secret

行為變更

  • 清除 Webhook URL 不再清除 SecretPATCH /dashboard/api-keys/{id}/webhookwebhook_url 設為 null 時,webhook_secret 將保留不動。Secret 與 URL 改為各自獨立 lifecycle。客戶可先產 secret、稍後再設 URL,中途送空 URL 不會弄丟 secret。
  • Dashboard 不再以明文回傳 webhook_secretGET /dashboard/api-keys/{id} 回傳改為 webhook_secret_masked(前綴遮罩 + 後 4 碼)與 has_webhook_secret 布林。明文僅在剛產生時透過 flash session 顯示一次(對齊 Stripe)。

文件更新

  • guides/webhook.md:「方式二:API Key 級 webhook_url」改寫為兩步流程(產生 secret → 設定 URL),新增 Webhook Secret 生命週期章節,安全驗證章節加 Bootstrap callout。

客戶端建議

  • 首次整合:在 Dashboard 點「產生 Webhook Secret」、複製到接收端 .env、啟用 HMAC 驗證並重啟服務後,再回 Dashboard 填入 webhook URL。
  • 既有客戶:完全相容,無需任何改動。既有 webhook_url 與 webhook_secret 行為不變。
  • Secret rotation:建議在接收端短暫接受新舊兩把 secret,dashboard 重生後 in-flight webhook 處理完畢再下架舊 secret。

V1.3.8(2026-04-27)

新增功能

  • 翻譯服務不可用偵測(session-level):新增錯誤碼 translation_service_unavailable。當 LLM 翻譯服務連續失敗達閾值,後端會發出一次 session-level 錯誤事件,讓前端能顯示全域「翻譯暫不可用」提示,避免使用者只看到滿頁失敗的個別句子灰字。
    • 觸發條件
      • llm_timeout / llm_provider_error / llm_rate_limit / llm_request_failed 連續失敗 5 次升級
      • llm_auth_failed / llm_deployment_not_found / llm_quota_exceeded 1 次就立即升級(設定/帳務問題)
      • llm_content_filtered 不計入(內容問題,非服務問題)
    • 去重:每個 session 只通知一次;任一句翻譯成功則重置計數,可再次觸發
    • payloadtype: "error"severity: "error"不是 fatal — 不應斷線),不帶 siddetailsproviderlast_error_codefail_count
    • 觀眾通知:廣播模式下,所有觀眾(不限語言)也會收到此事件(透過 SSE event: error 通道)

文件更新(規範同步)

延續 V1.3.7+ 前端反應的規範盲點,本次完成:

  • error-codes.md — 句子級錯誤判斷規則:在「嚴重程度說明」表下方新增 sid 規則段落,明示**「當錯誤帶 sid 時,無論 severity 為何都應視為 sentence-level 錯誤,不應斷線」**。fatal + sid 的組合僅代表該句嚴重失敗,session 整體仍可繼續。
  • error-codes.mdtranslation_service_unavailable 錯誤碼註冊:在「翻譯服務錯誤」段落新增此錯誤碼及完整觸發規則說明
  • websocket-api.md:「錯誤訊息格式」段落新增 session-level translation 錯誤範例(不帶 sid,severity error)
  • sse-api.md — retranslate 段補 per-sid error 規則:明確列出「失敗句改發 event: errorsid + error_code,與 translation 交錯出現」的規範與 payload 格式(V1.3.7 已實作但只在 reference 子目錄記載)
  • reference/sse/broadcast-viewer.md:補 translation_service_unavailable 範例與特有錯誤碼條目
  • reference/websocket/events.md:清掉 translation_error dead spec(程式碼從未發出此 action 事件,實際走的是 type: "error" 通道)

客戶端建議

  • 既有的句子級錯誤處理(type: "error"sid無需任何改動
  • 若想顯示「翻譯服務不可用」全域提示,新增監聽:當收到 error_code === "translation_service_unavailable"(不帶 sid)時顯示 banner / toast;待後續任一句翻譯成功(再收到 translation 事件)即可解除。
  • 切勿把 translation_service_unavailable 視為斷線訊號 — STT(原文)仍會持續運作。

參考文件:


V1.3.7(2026-04-24)

行為變更

  • 即時錄音:靜音任務統一走正常完成流程:即時錄音(WebSocket)過程全程靜音、雜訊或無法辨識出任何句子時,現在仍會產生空逐字稿entries: []),並以 task_complete 事件結束。此行為與 V1.3.5 的檔案匯入流程對齊,realtime 與 import 兩條來源現在共用「零辨識結果視為合法 completed」語意。
  • SSE 歷史紀錄:不再回 sse_transcript_not_found 於靜音情境GET /api/v1/sse/history/transcribe/{taskId} 對靜音任務不再回傳 sse_transcript_not_found 錯誤,而是發送完整事件序列(init_metadata → init_summary(text='') → init_done(totalSentences=0))。客戶端應以 totalSentences === 0 判斷並顯示「無語音內容」空狀態。

Bug 修正

  • 修正 History 頁面靜音錄音卡在「處理中」:先前即時錄音若全程靜音,後端因 segmentsCount == 0 條件跳過 transcript 上傳,但 task_complete 仍送出 task_id,導致前端載入歷史紀錄時收到 sse_transcript_not_found(語意上為「尚未處理完成」),使 UI 永久停在 loading。修正後 realtime 路徑與 import 路徑一致,永遠上傳 transcript(空 entries 亦然)。

客戶端建議

  • 若先前對 sse_transcript_not_found 有「待重試 / 輪詢中」的處理邏輯,可保留作為防禦性 fallback(例如 blob 上傳延遲),但不應再用於判斷「任務無語音」——應改以 init_done.totalSentences === 0 判斷。
  • 建議 UI 在 totalSentences === 0 時提示可能原因(音量過小、全程靜音、辨識語言與音檔不符),與 V1.3.5 匯入場景的提示文案一致。

文件更新

  • 歷史紀錄 SSE 新增「邊界情境:無語音內容」章節,並訂正 sse_transcript_not_found 的處理建議描述

參考文件:


V1.3.6(2026-04-23)

新增功能

  • Tasks API:新增 POST /api/v1/tasks/{taskId}/force-fail:將卡在非終態(recording / importing / uploading / pending / processing)的任務強制標記為失敗
    • Body 可選 reason(最長 500 字元)
    • 觸發 recording.failed webhook,payload.failure_sourceuser_forced
    • 已是終態的任務會得到 invalid_processing_status(422)
  • Tasks API:新增 POST /api/v1/tasks/{taskId}/retry:將 failed 狀態的任務重新排入處理佇列
    • 前置條件:processing_status = failedaudio_status = successtranscript_status = success
    • 使用 ProcessRecordingJob::dispatch()->afterCommit() 確保 queue worker 不會在 DB 交易 commit 前讀到舊值
    • 不符前置條件回 invalid_processing_status(422),details 欄位會帶 audio_status / transcript_status 幫助定位

行為變更

  • 內部 API PATCH /api/v1/internal/recordings/{id}/status 加入終態保護:對已處於 completed / failed 的錄音若嘗試寫入不同狀態會回 409 Conflict(payload 帶 current_statusattempted_status)。幂等放行:推相同終態視為 no-op 回 200。此變更配合 force-fail 端點,避免 Go WebSocket 仍在運行時把使用者手動操作的結果覆蓋回去。
  • 錯誤碼 invalid_processing_status(422)擴充適用範圍:新增為 force-failretry 的通用回應;details 會帶 current_statusretry 場景額外帶 audio_statustranscript_status

文件更新

  • Tasks API 新增 force-failretry 兩支端點說明
  • 錯誤碼對照表 新增 invalid_processing_status 條目與「處理狀態不符」子章節

參考文件:


V1.3.5(2026-04-22)

行為優化

  • 音檔匯入:空白辨識結果過濾:當音檔因整段靜音、音量過小、雜訊或辨識語言與音檔不符而導致語音辨識結果為空時,後端現會統一過濾空白 phrase,不再產生大量 00:00 / 空文字的佔位片段
  • 零辨識結果為合法 completed 狀態:此情境下匯入任務仍以 status: completed 結束(不是 failed),task_id 正常產出,但後續載入的逐字稿 entries 為空陣列、segments_count0
  • 預算依實際時長扣除:無法辨識的音檔仍依音檔時長扣除月度預算(不退還)

客戶端建議

  • 載入逐字稿(SSE /api/v1/sse/history/transcribe/{taskId})後,若累計句子數為 0,顯示「此音檔未辨識出語音內容」空狀態
  • 不應將零辨識結果視為錯誤分支,應走完成分支後以句子數判斷
  • 建議 UI 同時提示可能原因(音量過小、全程靜音、辨識語言與音檔不符)

文件更新

參考文件:


V1.3.4(2026-04-22)

新增功能

  • Tasks API:新增 GET /api/v1/tasks/{taskId}/transcript/export:下載任務逐字稿,支援 五種格式txtsrtsbvvttcsv
    • 輸出內容包含原文與所有翻譯語言
    • CSV 以 UTF-8 BOM 開頭、欄位 index,start,end,speaker,text,<每翻譯語言一欄>、時間 HH:MM:SS(無毫秒)
    • SRT 時間 HH:MM:SS,mmm;SBV 時間 H:MM:SS.mmm,原文與翻譯以 | 串為單行;VTT 使用 WEBVTT 表頭
    • 檔名採用 {錄音名稱}-transcript.{ext}(RFC 5987 UTF-8 編碼)
    • 新增錯誤碼 recording_transcript_not_ready(422)

行為變更(Breaking)

  • 語者分離與多語言互斥改為硬性拒絕recognition_mode: multi_speaker 搭配多個 transcription_languages 時,原本會發出警告並自動截斷到第一個語言,現改為直接回傳 diarization_multilang_conflict 錯誤並拒絕開始
    • 錯誤 severity 從 warning 調整為 error
    • 前端需在使用者送出 start 前即限制「語者分離」與「多語言」二擇一,或處理此錯誤並引導使用者調整設定
    • 影響端點:WebSocket voice-translation / start

文件更新

參考文件:


V1.3.3(2026-04-21)

新增文件

  • Tasks API:補上 GET /api/v1/tasks/{taskId}/audio/export 端點的完整文件(原先實作存在但文件遺漏),包含參數、動態 Content-Type、錯誤碼及前端下載範例
  • 說明該端點與 SSE /api/v1/sse/audio/{taskId} 的差異:前者用於離線下載(Content-Disposition: attachment),後者用於播放(支援 Range Request)

文件修正

  • 修正 Voice Translation Actions 互譯模式 speakers 欄位說明表格:欄位名從 speaker 更正為 id,與 JSON 範例及實際服務行為一致
  • 修正 README API 參考表端點計數:Tasks 從 7 改為 8(新增 audio/export)、Broadcasts 從 9 改為 6(原本計數錯誤)

參考文件:


V1.3.2(2026-04-07)

文件結構調整

  • 移除 3 個已棄用的舊版文件(error-codes.md V0.6、languages.md V0.1、authentication.md V0.1)
  • appendix/error-codes.mdappendix/languages.md 移至根目錄,取代棄用版本
  • 更新所有交叉引用連結

V1.3.1(2026-03-26)

批次任務管理

  • 新增 PUT /api/v1/tasks/batch/pin:批次更新釘選狀態,單次最多 100 筆
  • 新增 DELETE /api/v1/tasks/batch:批次刪除任務,單次最多 100 筆
  • 兩個端點皆僅影響屬於當前用戶的任務,回應包含 affected_count

批次廣播撤銷

  • 新增 DELETE /api/v1/broadcasts/batch:批次撤銷 PENDING 狀態的廣播,單次最多 100 筆
  • 非 PENDING 狀態的 ID 會被忽略,回應包含 affected_count

參考文件:


版本:V1.5.7 最後更新:2026-05-20

Copyright © 2026