单文件同步解析:file_parse
mineru-kit file-parse-server 提供独立的同步 HTTP 包装服务:接收一个文件,创建 Tasks、上传、等待,完成后直接返回 ZIP、DOCX 或 HTML 附件。它连接本地 API、Router 或 SaaS;切换部署只修改上游地址和认证。
启动
mineru-kit file-parse-server --api-url http://127.0.0.1:8000
# 上游基地址可以带路径前缀;不要附加 /v1/tasks。
MINERU_API_URL=https://mineru.net/api MINERU_API_KEY=your-key \
mineru-kit file-parse-server
| 启动参数 | 默认值 | 含义 |
|---|---|---|
--api-url |
MINERU_API_URL,否则必填 |
上游统一 Tasks API 基地址。 |
--api-key |
MINERU_API_KEY,否则为空 |
上游 Bearer Token。 |
--host、--port |
127.0.0.1、8003 |
包装服务监听地址。 |
--state-dir |
${MINERU_HOME:-~/.mineru}/file-parse |
恢复记录及传输临时目录。 |
--retention-seconds |
86400 |
首次观察终态后保留恢复记录的秒数;0 禁用回收。 |
包装入口不提供认证,面向本机或可信网络;部署方可以在反向代理设置访问认证。--api-key 只用于连接上游。请求不能覆盖上游地址或认证,入站 Authorization 不会转发到上游。
服务只执行 HTTP 上传、查询、下载,不初始化解析模型。OpenAPI 位于 /openapi.json,交互文档位于 /docs。这些路由属于独立包装服务,不注册到原 API Server 或 Router。
上传与等待
POST /v1/file_parse 仅接受 multipart/form-data,业务参数放在表单字段中;不接受查询参数、未知字段或重复标量字段。
| 字段 | 默认值 | 说明 |
|---|---|---|
file |
必填 | 一个非空文件,保留扩展名;不接受多文件。 |
tier |
上游默认 | 可选,仅对 PDF/图片生效;flash/basic/standard/advanced。 |
ocr_mode |
auto |
auto/txt/ocr。 |
page_range |
整本 | 与 Tasks 一致,PDF 支持 1-5、r1、all;格式支持范围由上游校验。 |
output_formats |
default |
default/docx/html/latex;重复同名字段表达列表,重复值按首次出现去重。 |
wait_timeout |
3600 |
整数秒,范围 0–3600。 |
request_id |
自动生成 | 1–64 个字母、数字、下划线或短横线。提前指定并保存它,可在连接中断后查询。 |
未提供 tier 表单字段时,包装层在上游 Tasks 请求中省略该字段,由上游按 standard → basic → flash → advanced 选择默认档位;仅开放 advanced 时自动选择 advanced。
显式值原样传递,PDF/图片请求的档位不受上游支持时返回错误,不自动升降档。
所有正常可用服务都支持 Office、HTML 等现有原生格式,固定使用 flash,即使传入上游未开放的合法档位也不改变原生解析路径。
表单空字符串或字符串 "null" 不是省略字段,属于非法值;OpenAPI 将 tier 声明为可省略的字符串枚举,不声明可空值。完整规则见 Tasks tier 语义。
# 成功时 response.body 是附件;超时或错误时是 JSON,请同时检查 response.headers。
curl --max-time 7200 -D response.headers -o response.body \
http://127.0.0.1:8003/v1/file_parse \
-F 'file=@report.pdf' \
-F 'request_id=report-20261023-001' \
-F 'tier=flash' -F 'ocr_mode=txt' \
-F 'output_formats=default' -F 'output_formats=docx'
output_formats 的列表语义与 Tasks 一致;multipart 中不使用 JSON 字符串或逗号分隔字符串。
已删除 include_model_output;表单携带该字段时返回 400,不接受旧参数。
响应结构与格式
以下三个接口均属于独立 file_parse 包装服务。它们的 JSON 状态对象是下文的 StatusResponse,独立于 Tasks 的创建、详情和取消响应模型。
| 接口 | HTTP | Content-Type | 正文格式 |
|---|---|---|---|
POST /v1/file_parse,同步等待成功 |
200 | 随输出格式 | ZIP、DOCX 或 HTML 附件字节。 |
POST /v1/file_parse,等待到期或 wait_timeout=0 |
202 | application/json |
完整 StatusResponse,保存 status_url 后继续查询。 |
GET /v1/file_parse/{request_id},查询成功 |
200 | application/json |
完整 StatusResponse,即使已知任务状态为失败、取消或提交结果未知。 |
GET /v1/file_parse/{request_id}/result,下载成功 |
200 | 随输出格式 | 与同步成功时相同的附件字节。 |
| 上述接口的已知业务、存储或上游调用错误 | 4xx / 5xx | application/json |
包装服务错误对象;已有恢复记录时同时携带状态字段。 |
JSON 正文使用 UTF-8;可空字段输出 null,不以省略字段代替。下列示例的 ID、地址为示意值。
POST /v1/file_parse:200 附件响应
成功时直接返回文件内容,不包含 JSON 外层包装,也不把文件编码为 Base64。
文件名为 <输入文件主名>.<产物扩展名>,例如输入 report.pdf:
output_formats |
下载文件名 | Content-Type | 文件内容 |
|---|---|---|---|
["default"] |
report.zip |
application/zip |
markdown.md、middle_json.json、structured_content.json、images/;不包含模型输出。 |
["docx"] |
report.docx |
application/vnd.openxmlformats-officedocument.wordprocessingml.document |
图片内嵌的 DOCX。 |
["html"] |
report.html |
text/html; charset=utf-8 |
图片内嵌的 UTF-8 HTML。 |
["latex"] |
report.zip |
application/zip |
document.tex、images/。 |
| 多个不同格式 | report.zip |
application/zip |
各格式平铺在一个 ZIP 中,不嵌套 ZIP;DOCX/HTML 成员为 document.docx、document.html。 |
包装服务原样返回验证过的上游产物,不重新渲染或打包。完整成员规则、图片及公式行为沿用 Tasks 输出格式。
HTTP/1.1 200 OK
Content-Type: application/zip
Content-Disposition: attachment; filename="report.zip"
X-Request-ID: report-20261023-001
X-Task-ID: job_0123456789abcdef01234567
<ZIP 文件二进制内容>
| 响应头 | 类型 | 返回条件及含义 |
|---|---|---|
Content-Type |
string | 成功附件的 MIME 类型,按上表确定。 |
Content-Disposition |
string | attachment 及建议文件名;非 ASCII 文件名可能使用标准的 filename* 编码。 |
X-Request-ID |
string | 成功附件必带,值为包装服务的请求 ID。 |
X-Task-ID |
string | 成功附件必带,值为已确认的上游 Tasks ID。 |
StatusResponse:查询与 202 的完整 JSON 对象
以下七个顶层字段均会出现。状态对象不返回 Tasks 的 files/progress,不暴露上游上传链接或签名下载地址。
| 字段 | JSON 类型 | 可为 null | 含义 |
|---|---|---|---|
request_id |
string | 否 | 包装服务的恢复标识,来自请求或自动生成。 |
task_id |
string | 是 | 已确认的上游父任务 ID;尚未获得有效创建响应时为 null。 |
status |
string | 否 | 包装服务保存的单文件状态,具体取值见下表。 |
status_url |
string | 否 | 包装服务自身的完整状态查询 URL。 |
result_url |
string | 是 | completed 时指向包装服务自身的 /v1/file_parse/{request_id}/result,其他状态为 null。 |
output_formats |
array of string | 否 | 按首次出现去重后的输出格式。 |
error |
object | 是 | 保存的最近错误或上游逐文件错误;无错误时为 null,具体字段见“错误响应”。 |
status |
含义 |
|---|---|
prepared |
本地源文件和恢复记录已持久化,尚未开始提交。 |
submitting |
正在向上游创建任务,尚无确认后的 task_id。 |
waiting-file |
上游仍在接收、下载、校验或确认输入;上游省略 upload 不表示已经就绪。 |
pending |
已进入上游执行队列。 |
running |
上游正在解析或导出。 |
completed |
上游完整产物已生成,可通过包装服务下载。 |
failed |
已知失败,读取 error。 |
canceled |
上游单文件任务已被取消。 |
submission_unknown |
创建请求可能已被上游接受,但包装服务没有获得有效任务 ID,不自动重放创建请求。 |
包装服务仅处理一个输入,不使用父 Tasks 的 partial 聚合状态。completed 表示上游产物已就绪;实际下载仍可能因结果过期或传输失败返回错误。
POST /v1/file_parse:202 JSON 响应
等待预算耗尽时,返回 202 Accepted 和完整 StatusResponse。这表示上游继续执行,不是解析失败。
wait_timeout=0 在源文件确认就绪后直接返回 202;即使任务已经完成,也返回 JSON,此时 status 可以是 completed,result_url 已非空。
HTTP/1.1 202 Accepted
Content-Type: application/json
Location: http://127.0.0.1:8003/v1/file_parse/report-20261023-001
Retry-After: 2
X-Request-ID: report-20261023-001
X-Task-ID: job_0123456789abcdef01234567
| 响应头 | 返回条件及含义 |
|---|---|
Location |
202 必带,与正文 status_url 相同。 |
Retry-After |
202 必带,当前值为字符串 2,单位为秒,提示查询间隔。 |
X-Request-ID |
202 必带,与正文 request_id 相同。 |
X-Task-ID |
已确认上游任务 ID 时携带,与正文 task_id 相同。 |
完整 JSON 正文示例:
{
"request_id": "report-20261023-001",
"task_id": "job_0123456789abcdef01234567",
"status": "running",
"status_url": "http://127.0.0.1:8003/v1/file_parse/report-20261023-001",
"result_url": null,
"output_formats": [
"default"
],
"error": null
}
GET /v1/file_parse/{request_id}:200 状态查询
查询已有记录且上游状态刷新成功时,返回 200 OK、Content-Type: application/json 和完整 StatusResponse。
返回值可以是 running,也可以是 completed/failed/canceled/submission_unknown;HTTP 200 不表示解析成功。
当前状态查询不统一附加 X-Request-ID/X-Task-ID,从 JSON 字段读取这两个标识。
{
"request_id": "report-20261023-001",
"task_id": "job_0123456789abcdef01234567",
"status": "completed",
"status_url": "http://127.0.0.1:8003/v1/file_parse/report-20261023-001",
"result_url": "http://127.0.0.1:8003/v1/file_parse/report-20261023-001/result",
"output_formats": [
"default"
],
"error": null
}
若刷新上游时本次调用失败,则返回非 2xx 错误 JSON;原任务仍可能存在并继续执行,使用相同 request_id 继续查询。
GET /v1/file_parse/{request_id}/result:200 附件下载
成功响应的二进制格式、MIME、命名及 X-Request-ID/X-Task-ID 与同步 POST 的 200 附件相同。
调用者不需要上游 API Key。每次下载先查询原任务并获取当前下载链接;结果未就绪返回 409 result_not_ready,没有已确认上游任务时也可能返回 409 task_not_created。
下载中断可以再次 GET。签名失效时重新查询原任务,最多尝试 3 次;上游结果已过期且无法恢复时返回错误,不自动重新解析。
错误响应
file_parse 使用自己的 code/message/upstream_status 错误结构;主 Tasks 接口的 ErrorDetail 为必填 code/message 和可选 param,不包含 type。
包装层直接返回的 error 包含以下三个字段,JSON 值中的 upstream_status 不省略:
| 字段 | JSON 类型 | 可为 null | 含义 |
|---|---|---|---|
error.code |
string | 否 | 包装层错误码或保留的上游错误码。 |
error.message |
string | 否 | 错误说明。 |
error.upstream_status |
integer | 是 | 已知上游 HTTP 错误状态码;本地错误、网络异常或通过任务状态得知的解析失败可以为 null。 |
没有恢复记录时
只返回顶层 error,没有 request_id/task_id/status_url 等状态字段。例如提交 JSON 而不是 multipart:
HTTP/1.1 415 Unsupported Media Type
Content-Type: application/json
{
"error": {
"code": "unsupported_media_type",
"message": "Expected multipart/form-data",
"upstream_status": null
}
}
已有恢复记录时
返回完整 StatusResponse 的七个顶层字段,但用本次操作的错误覆盖正文 error。status 仍表示该记录当前状态,不会因为本次 HTTP 失败就统一改成 failed。
例如查询尚未完成的结果,HTTP 为 409 Conflict,任务仍为 running:
{
"request_id": "report-20261023-001",
"task_id": "job_0123456789abcdef01234567",
"status": "running",
"status_url": "http://127.0.0.1:8003/v1/file_parse/report-20261023-001",
"result_url": null,
"output_formats": [
"default"
],
"error": {
"code": "result_not_ready",
"message": "The result is not ready",
"upstream_status": null
}
}
当前错误响应不保证附加 X-Request-ID/X-Task-ID 或 Location,恢复信息从 JSON 正文读取。只取得了客户端预先指定的 ID、但没有对应恢复记录时,仍使用前一种仅含 error 的结构。
状态查询成功响应中的 error 是持久化的错误快照:它可能是上述三字段结构并额外含 http_status,也可能保留上游逐文件错误的 code/message 和可选 param。历史恢复记录保持原样,可能仍含旧的 type 或其他字段;这不代表新 Tasks 协议接受旧字段。
其中 http_status 是包装层保存的错误响应状态,不是 upstream_status;客户端应容忍错误对象的扩展字段。
例如创建结果未知后,状态查询仍可返回 200 OK 和以下完整状态,不自动再次创建任务:
{
"request_id": "report-20261023-001",
"task_id": null,
"status": "submission_unknown",
"status_url": "http://127.0.0.1:8003/v1/file_parse/report-20261023-001",
"result_url": null,
"output_formats": [
"default"
],
"error": {
"code": "submission_unknown",
"message": "Task creation outcome is unknown; not resubmitting",
"upstream_status": null,
"http_status": 502
}
}
常见错误状态码
| HTTP | 常见 code | 含义 |
|---|---|---|
| 400 | invalid_request、upload_interrupted |
参数、多文件、空文件等校验失败,或完整接受前上传中断。 |
| 404 | request_not_found |
包装服务中没有记录,或恢复记录已过保留期。 |
| 408 | upload_receive_timeout |
接收入站请求体时发生传输空闲超时。 |
| 409 | request_id_conflict、result_not_ready、task_not_created 或取消错误码 |
ID 对应不同请求、结果未就绪、尚无确认任务或上游已取消。 |
| 410 | upload_expired |
包装层确认上游上传窗口已经过期。 |
| 415 | unsupported_media_type |
POST 不是 multipart/form-data。 |
| 500 | storage_error |
包装服务保存源文件、结果或访问本地存储失败。 |
| 502 | submission_unknown、upstream_unreachable、upstream_protocol_error、invalid_result、下载错误码或上游错误码 |
上游创建、查询、传输或解析失败,或结果校验失败。 |
上游认证失败以包装层的 502 返回,例如 error.code="invalid_api_key"、error.upstream_status=401;包装入口本身没有 API Key 认证,不把上游配置错误伪报为入口鉴权失败。
上游明确拒绝档位请求时也沿用包装层 502:显式 tier 不受支持保留 error.code="invalid_request"、error.upstream_status=400;没有可用默认档位的标准 Tasks 错误响应保留 error.code="quality_tier_unavailable"、error.upstream_status=503。两者记录为 failed,task_id=null,不标记为提交结果未知。
创建阶段的 5xx 响应中,仅符合共享错误结构的 503 quality_tier_unavailable 可确认为档位拒单;其余 5xx、408、畸形响应或网络异常仍按 submission_unknown 处理。明确拒单和提交结果未知都不会自动重放创建,保留期内相同 request_id 的重复请求和重启恢复沿用原记录。
submission_unknown 首次创建失败通常返回 502;后续请求仍可能得到 409/502,应查询原恢复记录,不自动重放创建。
等待、超时与断连后查询
每次调用在确认上游文件就绪后开始计时,等待包含排队、解析与导出。接收文件、上游上传、文件下载与校验准备及结果下载不占此预算,独立使用传输空闲超时。缓存命中跳过上传;只有进入 pending/running/completed 或失败/取消终态后,才清理本地保留的源文件。
- 正常轮询间隔为 2 秒。
- 连接、元数据请求、传输空闲超时分别为 10、30、600 秒。
- HTTP 客户端和反向代理的读取超时应覆盖上传、同步等待和结果传输;例如客户端配置 7200 秒,代理按业务文件大小配置相应超时。
curl http://127.0.0.1:8003/v1/file_parse/report-20261023-001
curl --fail-with-body -o report.zip \
http://127.0.0.1:8003/v1/file_parse/report-20261023-001/result
完整接收后的客户端断连不取消已托管的上传或解析。上传失败先查询状态,确认已经就绪便不重传;若保留的源文件仍可用,使用相同请求 ID 和相同输入可继续原任务。
包装服务当前不提供独立的取消接口。等待超时、客户端断连或包装服务关闭都不会自动向上游发送取消请求。
已取得非空 task_id 且有权使用对应上游身份时,可向配置的上游 API 调用 POST /v1/tasks/{task_id}/cancel;不要使用包装服务的基址,也不要把 request_id 当作上游任务 ID。
上游取消成功后,再查询 /v1/file_parse/{request_id} 可观察 canceled 状态。取消能力沿用上游 Tasks 契约;file_parse 自身的 status_url、响应字段和 null 规则保持不变。
去重与持久化边界
完整接收并持久化源文件后,包装服务才接受请求并托管上游上传。此后连接中断不取消上传或解析;文件尚未完整接收时中断不会创建任务。
同一 request_id、相同文件内容、逻辑文件名及归一化解析参数复用原任务;不同输入返回 409 request_id_conflict。wait_timeout 不参与身份比较。没有预先提供 ID 且尚未收到响应的调用者,可能无法自行找回任务。
请求映射使用独立 SQLite 数据库,按上游地址和认证隔离。数据库不保存上游 API Key。单个状态目录只允许一个包装服务进程;服务重启后恢复已知任务,尚待上传且链接有效时继续上传。上游任务记录如果因重启或过期而消失,不会自动重建。
源文件在上游确认就绪后删除,终结失败也会清理源文件。结果只临时下载,发送完成或中断即删除;重启清理遗留传输文件。恢复记录的保留期限不延长上游结果有效期,查询也不会为上游上传链接续期。结果及请求过期后应使用新的请求 ID 发起新的解析。
Python HTTP 示例
仓库提供 scripts/http_file_parse_example.py,依赖 httpx,提前打印并保存请求 ID;成功时原子保存附件,202 时打印查询信息并以 124 退出。
python scripts/http_file_parse_example.py report.pdf --output results/report.docx \
--output-format docx --request-id report-20261023-002
python scripts/http_file_parse_example.py report.pdf --wait-timeout 0 \
--request-id report-20261023-003
后续查询和下载仍使用上面的包装接口,不需要安装 MinerU SDK 或重建 ParseResult。
升级与恢复记录
本次协议升级要求 API Server、Router、SDK、Gradio 与包装服务同步更新,先停收请求并排空旧任务。
恢复记录目录使用 tasks-v5- 加既有上游地址与认证摘要,不加载、不迁移也不自动删除旧 tasks-v4-、tasks-v3-、tasks-v2- 或无前缀目录。包装服务自身的 status_url 与 Location 头保持不变,上游 Tasks 查询改用固定路径。旧 request_id 不再恢复;新空间内继续支持幂等和进程重启恢复。
上游 POST 只返回创建回执。包装服务先持久化服务端生成的 task_id/file_id 和原始请求上下文,再 GET 查询详情;查询失败或重启仍恢复同一任务,不重放创建。只有 Tasks 成功响应省略 null,本文的 StatusResponse、status_url 和错误响应保持原规则。