SSE API

Import Progress

連線資訊

項目
基礎路徑https://vas-poc.vurbo.ai/api/v1/sse
協定HTTP + Server-Sent Events (SSE)
資料格式text/event-stream
認證方式Header X-API-Key: {KEY}

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


端點總覽

方法端點說明
GET/api/v1/sse/imports/{importId}/progress即時追蹤音檔匯入進度

GET /api/v1/sse/imports/{importId}/progress

功能說明

即時追蹤音檔匯入的處理進度。連線後透過 SSE 串流持續推送進度更新,直到匯入完成、失敗或連線超時。

使用場景

  • 上傳音檔後即時顯示處理進度條
  • 追蹤音檔轉換、轉錄、翻譯、摘要等各階段進展

認證方式

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

請求參數

參數位置類型必填說明
importIdpathstring匯入任務 ID(UUID)

請求範例

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

事件序列

情境一:匯入尚在處理中
┌────────────────────────────────────────────────────┐
│ 1. connected       → 連線確認                      │
│ 2. progress        → 發送目前進度                   │
│ 3. progress ×N     → 進度有變化時持續推送            │
│    heartbeat ×N    → 每 15 秒無進度變化時發送心跳     │
│ 4. completed       → 匯入成功,連線結束              │
│    或 failed       → 匯入失敗,連線結束              │
│    或 timeout      → 超過 15 分鐘,連線結束          │
└────────────────────────────────────────────────────┘

情境二:匯入已完成(終態)
┌────────────────────────────────────────────────────┐
│ 1. connected       → 連線確認                      │
│ 2. progress        → 發送最終進度                   │
│ 3. completed       → 直接發送完成事件並結束          │
│    或 failed       → 直接發送失敗事件並結束          │
└────────────────────────────────────────────────────┘

事件格式


connected

連線成功確認。

event: connected
data: {"message":"匯入進度服務已連線 (importId: 550e8400-e29b-41d4-a716-446655440000)"}
{
  "message": "匯入進度服務已連線 (importId: xxx)"
}
欄位類型說明
messagestring連線確認訊息

progress

處理進度更新。當進度百分比有變化時發送。

event: progress
data: {"import_id":"550e8400-...","status":"processing","stage":"transcribing","progress":45,"message":"轉錄中..."}
{
  "import_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "processing",
  "stage": "transcribing",
  "progress": 45,
  "message": "轉錄中..."
}
欄位類型說明
import_idstring匯入任務 ID(UUID)
statusstring匯入狀態,見下方「狀態與階段」
stagestring | null目前處理階段,見下方「狀態與階段」
progressinteger進度百分比(0-100)
messagestring可讀的進度訊息

狀態(status)值:

說明
pending等待處理
processing處理中
completed已完成
failed已失敗

階段(stage)值與對應進度範圍:

說明進度範圍
converting音檔格式轉換0% - 10%
transcribing語音轉文字10% - 60%
translating文字翻譯60% - 85%
summarizing產生摘要85% - 100%
null尚未開始(pending 狀態)

completed

匯入處理完成。收到此事件後連線將自動結束。

event: completed
data: {"import_id":"550e8400-...","status":"completed","task_id":"abc123-...","message":"處理完成"}
{
  "import_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "completed",
  "task_id": "abc123-e29b-41d4-a716-446655440000",
  "message": "處理完成"
}
欄位類型說明
import_idstring匯入任務 ID(UUID)
statusstring固定為 completed
task_idstring產生的錄音 ID(recording_id),可用於後續查詢
messagestring固定為 處理完成

邊界情境(v1.3.5):若音檔因靜音、音量過小、雜訊或辨識語言與音檔不符導致無法辨識出任何語音內容,仍會以 completed 事件結束(不是 failed),task_id 正常產出,但後續透過 GET /api/v1/sse/history/transcribe/{taskId} 載入的逐字稿句子數為 0。客戶端應以句子數判斷是否顯示「無語音內容」空狀態,而非將其視為錯誤。詳見 音檔匯入指南 – 音檔無法辨識時的行為


failed

匯入處理失敗。收到此事件後連線將自動結束。

event: failed
data: {"import_id":"550e8400-...","status":"failed","error_code":"import_invalid_format","error_message":"不支援的音檔格式"}
{
  "import_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "failed",
  "error_code": "import_invalid_format",
  "error_message": "不支援的音檔格式"
}
欄位類型說明
import_idstring匯入任務 ID(UUID)
statusstring固定為 failed
error_codestring錯誤代碼
error_messagestring可讀的錯誤訊息

heartbeat

心跳事件,每 15 秒在進度無變化時發送,用於保持連線活躍。

event: heartbeat
data: {"timestamp":1708761600}
{
  "timestamp": 1708761600
}
欄位類型說明
timestampintegerUnix 時間戳

timeout

連線超時事件。超過 15 分鐘未完成時發送,連線將自動結束。

event: timeout
data: {"message":"連線超時"}
{
  "message": "連線超時"
}
欄位類型說明
messagestring固定為 連線超時

特有錯誤碼

連線建立後,若匯入任務不存在,會透過 error 事件回傳:

錯誤碼說明處理建議
import_not_found找不到指定的匯入任務確認 importId 正確

前端範例

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

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

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

    buffer += decoder.decode(value, { stream: true });

    // 解析 SSE 格式:event: xxx\ndata: {...}\n\n
    const events = buffer.split('\n\n');
    buffer = events.pop(); // 保留未完成的部分

    for (const eventStr of events) {
      if (!eventStr.trim()) continue;

      const lines = eventStr.split('\n');
      let eventType = '';
      let eventData = '';

      for (const line of lines) {
        if (line.startsWith('event: ')) {
          eventType = line.slice(7);
        } else if (line.startsWith('data: ')) {
          eventData = line.slice(6);
        }
      }

      if (!eventType || !eventData) continue;

      const data = JSON.parse(eventData);

      switch (eventType) {
        case 'connected':
          console.log('已連線:', data.message);
          break;

        case 'progress':
          console.log(`[${data.stage}] ${data.progress}% - ${data.message}`);
          // 更新 UI 進度條
          updateProgressBar(data.progress, data.stage, data.message);
          break;

        case 'completed':
          console.log('匯入完成! 錄音 ID:', data.task_id);
          // 導向錄音詳情頁
          navigateToRecording(data.task_id);
          break;

        case 'failed':
          console.error('匯入失敗:', data.error_code, data.error_message);
          // 顯示錯誤訊息
          showError(data.error_message);
          break;

        case 'heartbeat':
          console.log('心跳:', new Date(data.timestamp * 1000));
          break;

        case 'timeout':
          console.warn('連線超時:', data.message);
          break;

        case 'error':
          console.error('錯誤:', data);
          break;
      }
    }
  }
}

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

Copyright © 2026