只需要最终结果时,轮询 GET /transforms/{uuid} 即可。展示处理进度或预览内容时,使用 observation 接口。
Observation 响应
GET /transforms/{uuid}/observation?preview_limit=100
{ "status": "running", "progress": { "queued": false, "stage": "converting", "completed_words": 1250, "total_words": 8000 }, "preview": { "cursor": "opaque-cursor", "updated": [ { "file": "converting/body", "url": "https://storage.example/body.jsonl?signed", "range": {"start": 0, "end": 240} } ] }}示例地址与偏移仅说明响应结构;实际读取时使用任务返回的完整 URL 和范围。
progress 只可能在任务运行时返回。queued 为 true 表示仍在排队,此时无法计算进度百分比。执行阶段包括 preparing、extracting、converting、packaging、uploading。
页数由 completed_pages 和 total_pages 表示,原文处理量由 completed_words 和 total_words 表示。这些字段可能缺失,不能按零处理。只有总量大于零时,才能计算百分比。
进度对应当前操作,切换阶段后总量可能变化。这些数字不用于表示费用。
处理量可能包含回退处理的内容,不能据此判断翻译是否完整。任务完成以 status: "succeeded" 为准。
读取更新并推进 cursor
每个任务需要分别保存 preview.cursor。下次请求 observation 时,将它通过 URLSearchParams 编码,作为 cursor 参数传入,不要修改其内容。
响应始终包含 preview.cursor;没有新内容时,可能不返回 updated。
- 请求
updated中的每个 URL,无需传入 API Key,也不要缓存响应。 - 有
range时发送Range: bytes=start-(end-1)。偏移按 UTF-8 字节计算,包含 start、不包含 end。 - 要求响应为
206,完整读取字节后解码 UTF-8,再解析完整 JSONL 记录。元数据更新没有 range,读取为完整 JSON。 - 所有更新都读取并处理成功后,再保存新的 cursor。任何一步失败都保留旧值,稍后重试。重试可能读到相同内容,可按任务、内容流和字节范围去重,避免重复添加。
以下函数读取指定字节范围内的 JSONL 记录:
async function readPreviewRange(update) { const { start, end } = update.range; const response = await fetch(update.url, { headers: { Range: `bytes=${start}-${end - 1}` }, cache: "no-store", }); if (response.status !== 206) throw new Error(`Preview HTTP ${response.status}`); const bytes = await response.arrayBuffer(); if (bytes.byteLength !== end - start) throw new Error("Incomplete preview range"); const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes); if (!text.endsWith("\n")) throw new Error("Incomplete JSONL record"); return text.slice(0, -1).split("\n").map(line => JSON.parse(line));}轮询、元数据读取和状态更新需由调用方处理。预览请求不携带 API Key;展示文档内容时需防止脚本注入。
预览范围
preview_limit 控制每个 JSONL 流返回的记录数,默认为 500,传入更大的值也按 500 处理。首次请求返回最近的记录;如果读取速度跟不上处理速度,可能会跳过较早的内容。
因此,不能通过 cursor 遍历整份文档。完整内容请从转换结果中下载。
任务结束时,请处理完最后一批更新再停止查询。成功后,响应的 download_urls 中会包含 {get_url, head_url},下载时使用默认文件名。如需指定名称,请另行请求 /download-urls?filename=...。
失败的任务也可能留下部分预览,但内容并不完整。
内容流与资源
| 内容流 | 内容 |
|---|---|
extraction/metadata、converting/metadata | 完整 {metadata, original?} JSON;替换旧视图,不作增量合并 |
extraction/body、extraction/footnotes | PDF 提取记录,包含 content 树和可选原文区域 |
PDF 的 converting/body、converting/footnotes | 自然段记录,含 text、可选 original 和原文 regions |
EPUB 的 converting/body | 纯文本片段,含 original、translated 和可选 epubcfi |
PDF 提取预览可能与最终章节结构不同。EPUB 没有 extraction 或独立脚注流。记录可能带可选的 emitted_at;应兼容没有该字段的旧记录,并忽略未知的新增字段。
预览内容中的图片和公式用资源哈希标识。将哈希填入 GET /transforms/{uuid}/preview-assets/{extraction|converting}/{hash}.{png|pdf},即可获取文件地址。
读取文件时无需传入 API Key。链接过期后,重新调用该接口获取即可。