机器可读契约是 docs/openapi.json(每个模型进程的 GET /openapi.json 也提供)。
本页是人读摘要。不要再复制已删除的 {req_id, code, msg, data} 信封或 img_data
字段——那些请求会返回 422。
生产流量统一经由 mortred-gateway(默认 :8080)。网关先按 catalog
id 匹配 /v1/models/{id}/…,再按遗留 server_uri 精确匹配,转发到
仅监听环回地址的模型服务器。网关负责外部 Bearer Token 鉴权
(MORTRED_GATEWAY_AUTH_TOKEN 或 MORTRED_API_TOKEN),将上游不可达映射为
503、传输失败映射为 502;下文所有模型服务器状态码均原样透传。网关的
GET /healthz 为公开端点。GET /metrics 含环回在内一律需要
MORTRED_METRICS_TOKEN。网关拒绝在缺少独立 scrape Bearer(不得与推理
token 相同)时启动。模型端口仅绑定环回地址、不得对外暴露。Mortred 自身是
明文 HTTP;对外服务必须由主机网络上的 Nginx 终结 TLS
(mortredctl init-edge)。fail-closed 拒绝无鉴权监听、缺少 metrics token、
以及未设 MORTRED_EXPOSE=docker 或 unsafe 的通配绑定。
监督器(supervisor,:8787)在 /api/v1/ 下提供管理 REST API
(health/catalog/status/生命周期/日志/metrics)与内嵌 Web UI;
mortredctl 是它的命令行客户端。推理冒烟(控制台发送按钮和
mortredctl infer)把数据面信封 POST 到网关的
/v1/models/{id}/infer,Bearer 与管理 API 相同(MORTRED_API_TOKEN)。
监督进程只做管理(catalog / 启停 / 日志 / UI)。推理和异步 jobs
走网关。:8080 上的遗留 {server_uri} 仍然可用。
状态码、端点或响应结构变更必须同时更新 docs/openapi.json
(用 python scripts/gen_openapi.py 重新生成)。
当服务器配置了 auth_token 时,模型推理端点要求携带 Authorization
请求头:
Authorization: Bearer <token>
401 + WWW-Authenticate: Bearer realm="Mortred"。/healthz、/ready、/openapi.json)公开。
监督器 GET /api/v1/metrics 需要管理 token。网关 GET /metrics 含环回
一律要 MORTRED_METRICS_TOKEN。模型进程的 GET /metrics 需要进程鉴权
token(监督器拉起的子进程总有 MORTRED_AUTH_TOKEN);空 token 返回 401,
不是公开刮取。/healthz 仍公开。不要把推理 token 当作 scrape 密钥。auth_token 为空时,模型推理和 /metrics 返回 401。健康/元数据仍公开。
服务器仍拒绝无 token 的非环回监听,以及未设 MORTRED_EXPOSE=docker|unsafe
的通配绑定。空配置的 token 不会授权任何请求。POST;其他方法返回 405 并携带 Allow: POST。Content-Type 必须为 application/json(允许 ; charset= 参数);
缺失或为其他媒体类型返回 415。request_size_limit MB;显式 Content-Length 超限返回
413。req_id、images、params、options。未知键和已删除的
img_data 返回 422。{
"status": 0,
"status_str": "OK",
"task_id": "客户端提供或服务器生成",
"model": { "name": "MOBILENETV2", "version": "" },
"results": [
{
"status": 0,
"data": {
"class_id": 123,
"category": "tabby cat",
"scores": [0.1, 0.8, 0.1]
}
}
],
"server_time_ms": 41.2,
"partial": false
}
契约校验失败时(HTTP 422):
{
"status": 66,
"status_str": "invalid request parameter",
"task_id": "",
"results": [],
"server_time_ms": 0.0,
"partial": false,
"errors": [
{
"pointer": "/img_data",
"message": "field 'img_data' was removed; use images: [\"<base64>\"] (migration: img_data -> images[0])"
}
]
}
先看 HTTP 状态,再看顶层 status,再逐项看 results[i].status。该项失败时
results[i].data 为 null。忽略未知响应字段。各任务载荷在 results[].data
下,定义见 docs/openapi.json components.schemas
(src/server/response_serializers.h)。
JSON 字段名是 status(不是 code)。映射见 src/server/http_status.h。
status |
含义 | HTTP |
|---|---|---|
| 0 | 成功 | 200 |
| 68 | 截止超时,部分结果 | 200 |
| 50 | JSON 解析错误 | 400 |
| 3 | 输入图片为空 | 400 |
| 66 | 非法请求(img_data、未知键、错误 params) |
422 |
| 60 | 不支持的媒体类型 | 415 |
| 61 | 请求实体过大 | 413 |
| 67 | 单请求条目过多 | 413 |
| 62 | 方法不允许 | 405 |
| 63 | 未找到 | 404 |
| 65 | 服务未就绪 | 503 |
| 4 | 模型运行超时 | 504 |
| 6 | 模型输出契约失败 | 500 |
| 401 | 未授权 | 401 |
| 429 | 限流或等待队列已满(携带 Retry-After) |
429 |
| 其他 | 服务器错误 | 500 |
Content-Type: application/json; charset=utf-8
X-Request-ID: <task_id>
Cache-Control: no-store
| 端点 | 方法 | 说明 |
|---|---|---|
/healthz |
GET | 存活探针 |
/ready |
GET | 就绪探针 |
/metrics |
GET | Prometheus 指标(鉴权见上) |
/openapi.json |
GET | OpenAPI 文档(内嵌副本) |
未知路径(含已删除的 /welcome、/hello_world HTML 探活)返回 404 与
进程级 UnifiedResponse。
{
"req_id": "可选",
"images": ["base64 编码的图片"],
"params": {},
"options": {}
}
images 必填且恒为数组(≥1)。params / options 为可选对象。阈值、
DDPM timesteps 等模型参数放在 params 里,不要放在根上。生成式模型同样
需要 images[];像素会被忽略,dummy base64 即可。
img_data(HTTP 422)下面不是成功请求。即使同时带了 images 也会被拒绝:
{
"req_id": "legacy",
"img_data": "base64 编码的图片"
}
当 max_queue_depth > 0 且等待队列已满时,模型服务器立即以 429
拒绝,并携带 Retry-After 响应头(依据队列深度、运行时长 EWMA 与
worker 数量估算排水时间,钳制在 1-60 秒)。网关将两者原样转发。
新增可选服务端配置键:max_queue_depth(0 = 不限制)、max_batch_size
(默认 1;大于 1 时在 max_batch_delay_ms 窗口内凑批),以及
mortred_queue_rejected_total / mortred_batch_size /
mortred_batch_window_wait_ms 指标。
批内逐条失败隔离:一条失败(坏图、解码错误)只返回它自己的错误 status, 同批其他条目照常得到结果;仅会话级失败(引擎错误)才会使全部参与 条目失败。
服务端开启 async_enabled 后,长耗时推理可异步提交。模型端口上的路径不变
(POST /jobs、GET /jobs/{id} 等)。经 网关 时同一套处理带 catalog id
前缀;网关无状态,并把 Location / poll_url / result_url 改写成该前缀:
| 网关 | 方法 | 模型端口上游 |
|---|---|---|
/v1/models/{id}/infer |
POST | {server_uri} |
/v1/models/{id}/jobs |
POST | /jobs |
/v1/models/{id}/jobs/{job} |
GET | /jobs/{job} |
/v1/models/{id}/jobs/{job}/wait |
GET | /jobs/{job}/wait + query |
/v1/models/{id}/jobs/{job}/result |
GET | /jobs/{job}/result |
{server_uri} |
POST | {server_uri}(遗留) |
GET /v1/models/{id}/infer 与 GET {server_uri} 返回 405。未知 {id} 与未知
server_uri 使用同一 404 信封。模型未开异步时,上游 404 原样透传。
| 端点(模型端口) | 成功 | 错误 |
|---|---|---|
POST /jobs |
准入时 202,返回 job_id、state: pending、poll_url、result_url |
准入队列满时 429 |
GET /jobs/{id} |
200,返回 state(pending/running/done/failed/timeout) |
未知 id 404 |
GET /jobs/{id}/wait?timeout=N |
job 进入终态或 wait 预算耗尽时 200(timeout 单位毫秒;默认 30000,上限 300000)。预算耗尽时 state 仍可能是 pending/running |
未知 id 404 |
GET /jobs/{id}/result |
200 标准响应封装(可重复读取) |
未知 id 404,未完成 409(含 pending/running/failed/timeout) |
202 表示服务器接受了该 job,不表示推理已经完成。正确的客户端不要把
POST /jobs 当成阻塞的 /infer。提交体与 /infer 相同(images[])。
逐步验收步骤见
async-jobs-customer-test.zh-cn.md。
任务账本保存在内存中(重启即失)。组件设计、并发契约与验证门禁见 async-job-table.zh-cn.md。