mortred_model_server

HTTP API 契约

| English | 中文 | |—|—|

机器可读契约是 docs/openapi.json(每个模型进程的 GET /openapi.json 也提供)。 本页是人读摘要。不要再复制已删除的 {req_id, code, msg, data} 信封或 img_data 字段——那些请求会返回 422。

拓扑说明:mortred-gateway

生产流量统一经由 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 POST /api/v1/servers/{id}/start|stop|restart 成功返回 200 + {"ok":true},失败返回 500 + {"ok":false,"error":...}(与 graceful_restart 一致)。客户端可依赖 HTTP 状态或 body 的 ok。 (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>

请求规则

通用响应封装

{
  "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)。

HTTP 状态码映射

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

已准入的模型请求 results[] 长度恒为 N = images[]。凑批路径(max_batch_size > 1,SME-09)与非批共享同一请求 deadline / HTTP timer;已过期的条目直接 TIMEOUT,不再 checkout worker(已开始的 run_batch 不会中途取消)。HTTP 504(status 4) 表示截止时还没有发布任何完成项,每项都是超时且 data: null。HTTP 200 + status 68 + partial: true 表示至少一项已按时完成——客户端不要把这种 200 当 5xx 整单重试。拒绝信封(401/422/…)仍可用空 results[]。

通用响应头

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 (产品 server 写 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-jobs-customer-test.zh-cn.md。