跳转到内容

SSE 实时进度

通过 SSE(Server-Sent Events)实时接收文件翻译任务(/jobs)的进度。SSE 未开放时连接返回 404,请改为轮询任务状态。

浏览器的 EventSource 不支持自定义 Header,因此采用两步凭据方式:

  1. 用 API Key 换取 ticket(30 秒内有效,只能使用一次)
  2. 用 ticket 建立 SSE 连接
POST /api/v1/sse/ticket/{job_id} → {"ticket": "..."}
GET /api/v1/sse/jobs/{job_id}?ticket=...
// 1. 换取 ticket(普通 API Key 认证)
const { ticket } = await fetch(
`https://omnidoc.top/api/v1/sse/ticket/${jobId}`,
{ method: 'POST', headers: { 'X-API-Key': API_KEY } }
).then(r => r.json());
// 2. 用 ticket 连接 SSE
const source = new EventSource(
`https://omnidoc.top/api/v1/sse/jobs/${jobId}?ticket=${ticket}`
);
// 事件是命名事件,用 addEventListener(onmessage 收不到)
source.addEventListener('job_update', (event) => {
const data = JSON.parse(event.data);
console.log(`状态: ${data.status}, 阶段: ${data.stage}, 进度: ${data.stage_progress}%`);
});
source.addEventListener('job_done', (event) => {
const data = JSON.parse(event.data);
console.log(`结束: ${data.status}`);
source.close();
});
事件说明
job_update状态/阶段变化(连接后先推一次当前快照)
job_done任务结束(completed 或 failed),此后可关闭连接
timeout流达到最长时限,需重新连接
error服务端异常,稍后重新换取 ticket 连接
event: job_update
data: {"status":"processing","stage":"translating","stage_progress":45,"stage_message":"正在翻译..."}
event: job_done
data: {"status":"completed","stage":"completed","stage_progress":100,"stage_message":""}

任务完成后通过 GET /api/v1/artifacts?job_id=... 获取产物列表。

import json
import requests
import sseclient
ticket = requests.post(
f"{BASE}/sse/ticket/{job_id}", headers=HEADERS
).json()["ticket"]
response = requests.get(
f"{BASE}/sse/jobs/{job_id}", params={"ticket": ticket}, stream=True
)
client = sseclient.SSEClient(response)
for event in client.events():
data = json.loads(event.data)
print(f"[{event.event}] {data.get('stage')} {data.get('stage_progress', 0)}%")
if event.event == "job_done":
break
  • ticket 一次性有效,重连前需重新获取
  • 长时间没有任务事件时,服务端会发送 timeout 事件;需重新换取 ticket 并连接
  • 连接闲置期间服务端会发送心跳注释,客户端应忽略注释行
  • 如果连接断开,可用轮询 GET /jobs/{id} 作为兜底