mortred_model_server

Changelog

All notable changes to this project are documented here. The format follows Keep a Changelog; versions follow Semantic Versioning. 每个版本的条目保留英文原文,中文说明 以引用块附于同版本之下。

[Unreleased]

Added

pack 占用在 supervisor spawn 和 mortredctl doctor --strict 上 fail-closed。 已启用 pack 里的 TensorRT id 必须有 calibrate --write-pack 写入的 gpu_mem_mib(以及 gpu_mem_at_workers 与 [pack] GPU 指纹)。缺 stamp、 worker_nums 过期、联合预算超限都会拒绝 spawn,错误里是下一条命令,不进 crash-loop。occupancy_policy=off / MORTRED_OCCUPANCY_ENFORCE=0 跳过 spawn 闸(unsafe),--strict 仍失败。gpu_mem_limit_mb 仍然只约束 ORT CUDA EP。

Added

增加 cpu 源码树最短成功路径文档 docs/shortest-path-cpu.md(README Quick Start 已链入),以及 SME-02 evidence(docs/evidence/)。

Changed

Fixed

install_deps.sh 的 toml++ 改为限时下载到暂存文件、拒绝残片(<17000 行)、 始终保证 TOML_EXCEPTIONS=0,不对半截下载盖 stamp。SME-01 在 docs/evidence/ 归档了 Linux tests-only 53/53(d51b0287),并加入活清单 docs/sme-oob-checklist.md。

release 工作流把 github.repository 转成小写再推 GHCR,与文档中的 ghcr.io/maybeshewill-cv/mortred_model_server 一致(大写 owner 非法)。 bootstrap 在已发布的 .sha256 与包内容不匹配时拒绝安装;缺少校验文件仍 WARN 后继续。

install_deps.sh --workflow 也会安装 vendored 的 libssl/libcrypto(与 --all 同路径)。有 workflow 时 CMake 硬要 vendored::crypto;sanitizer CI 只跑 --workflow 会在 configure 阶段挂。--check 现在断言 libcrypto.so.3 或 libcrypto.so.1.1。

install_deps.sh 把 ORT 头与 pin 的库版本强绑定:wipe/重装会整目录替换 3rd_party/include/onnxruntime;stamp_fresh 在 ORT_API_VERSION 不匹配(gpu 下缺 struct CUDAProviderOptions)时失效;--check 用同一套门闩 fail-closed。 避免本地 full-GPU 用残留的 ORT 1.18 头编译 ort_session.cpp CUDA EP、却链接 1.29。

同步推理在 model_run_timeout 到期时回包,不再把已完成项丢掉。HTTP series 上是唯一回复的 counter;逐项计算是脱离 series 的 go 链,外加一个请求级 timer。results[] 长度恒为 N:没有任何项发布时 504 / status 4;至少一项 完成时 200 / status 68 / partial=true。凑批走异步 BatchCollector::submit, 与非凑批共用请求级 timer:回包对已发布 slot 做 acquire 快照(未发布 → TIMEOUT); 唯一回复之后迟到的 write_slot 会被丢弃。

发布 .sha256 只写 tarball 文件名,curl -fLO 后 sha256sum -c 才能 打出 OK(deployment §6.1)。原先 sha256sum /绝对路径 会把 runner 路径写进 校验文件。bootstrap.sh 先解析 latest tag,再下 mortred_model_server-<tag>-<profile>-linux-x64.tar.gz(没有 ...-latest-... 资产)。gpu staging 与 cpu 一样落到 opt/mortred/(cp -a src/. dest/, 不是 cp -a src dest/opt/)。

write_prometheus_credentials.sh 把 scrape 密钥 chown 给真正读它的进程: compose 里 prom/prometheus 的 uid 65534,或 apt 单元的 prometheus 用户(/etc/prometheus/*)。权限仍是 600。compose 监控通过 prometheus.compose.yml 刮 host.docker.internal:8080(容器内 localhost 是 Prometheus 自己)。deployment 补了属主步骤和该 Docker 主机名。

Changed

GPU 线改为 CUDA 12 + TensorRT 10.3 + cuDNN 9 + MNN 3.6.1 + ORT 1.29 cuda12。 install_deps 的 wipe/重装会清掉 CUDA 11 / TensorRT 8 / ORT 1.18 库以及 与 pin 不一致的 ORT 头;--check 校验 ORT_API_VERSION。--cuda-version 11 失败。引擎必须用匹配的 trtexec 重建。full GPU CMake 拒绝 AddressSanitizer。

入口一无 Docker 按 GitHub latest tag 下 mortred_model_server-<version>-<profile>-linux-x64.tar.gz。Release 不挂 ...-latest-... tarball 文件名(GHCR :latest-cpu / :latest-gpu 仍是镜像 tag)。README / deployment / bootstrap.sh 不再写成「无 Docker 一定能装上」: 还没有 Release 时 WARN 并落到源码构建。verify_deployment.sh --basic 禁止 打包或下载 mortred_model_server-latest-。

YOLOv5 / v6 / v7 / v8 的 min_box_area_px 统一在解码坐标系(letterbox 网络像素)里、unmap 之前过滤。原先 v5/v6 在 unmap 后的源图像像素上比 面积,v8 完全不读该键。合成解码单测覆盖这条共用阈值。

checker 按 HTTP catalog 的 [*.backend] type="tensorrt" 对照 trt_engines.json(不再找已删除的 *_TRT)。脚手架(TODO(new_model) / 不在 HTTP catalog)跳过。cpu 权重集合不再含 catalog 不服务、且不在 HF 上的 hrnet ONNX。

发布 tarball 按平铺写进文档:包根就是 install.sh、opt/mortred/、 deploy/(打包脚本和 gpu release job 本来就是这样打的)。README / 部署 / 安装脚本注释改为解到空目录,不再 cd 一个打包器不会生成的 wrapper 目录。

入口二/三与 bootstrap 改为三个 token(init-trust / supervisor.env), 与 compose 已有的 :? scrape 必填对齐。可选监控 compose 挂载 scrape credentials 文件(不由应用 compose 或发布 tarball 发明 token),并 provision Grafana 的 Prometheus 数据源。

人读文档与统一信封对齐:请求 images[],响应 {status, status_str, task_id, results[]}。api-contract、api-keys 与任务教程 不再把 img_data 或 {code, msg, data} 写成成功示例。中英监控/部署均写明 网关 /metrics 含环回也要 MORTRED_METRICS_TOKEN。README Model Zoo 区分 HTTP catalog 与 bench-only。

Fixed

ci_container_boot.sh 按统一信封断言推理结果(status / results[],含 results[0].status),不再读已删除的 code。payload.get("code", 0) 会永远 通过,HTTP 200 且 status != 0 仍会打印成功。

HTTP 扩散工人在 init 时从模型 TOML 种入采样模板(sample_size / 默认步数 / channels)。缺或空 sample_size 会 init 失败,而不再每请求 MODEL_EMPTY_INPUT_IMAGE。异步冒烟与 DDPM 示例改用 params.timesteps (不是根上 timestep)和统一结果信封。少步 CPU 出图证明: model_golden.ddpm_celeba_hq_fewstep + conf/ci/ddpm_onnx_fewstep.toml (不进 hosted;ONNX 约 143MiB)。该用例断言 HTTP adapter 走出 128x128 PNG, 不钉像素(每次 random_device 仍重新播种)。异步冒烟会设 MORTRED_AUTH_TOKEN 并在 /jobs 带 Authorization: Bearer(空 token 是 401;/healthz 仍公开)。

Supervisor 在磁盘上已有 mortred-gateway.out 时,若 scrape token 未设置或与 推理/管理 token 相同则拒绝 listen;spawn __gateway 使用同一谓词,失败记为 permanent 而非 backoff,避免「健康横幅 + 网关崩溃循环」。/api/v1/keys 仍是 404。

BaseAiModel::run(以及 packed BackendCvModel::run_batch)接住 run_impl / OpenCV 的抛出并返回 MODEL_RUN_SESSION_FAILED,避免 workflow go 线程 std::terminate 整进程。packed run_batch 在 catch 后还会把该错误码盖到 每一个 item_status(run_image_batch 会先全部标 OK;不盖的话 HTTP 会谎报成功)。 扩散 DDPM/DDIM/cls-cond 后处理不再对 1/4 通道 convertTo(CV_8UC3) + COLOR_RGB2BGR(LDM latent channels=4 且 save_raw_output);出图按通道转换,只要 raw 时跳过。

do_work 在把 worker 还回队列之前写入推理 metrics 和运行时间 EWMA (与 process_batch 相同)。析构 drain 把「worker 已入队」当作可以销毁 这些成员;enqueue 后再写会与超时请求的析构竞态。

YOLO v5/v6/v7/v8 预处理改为 Ultralytics 导出同款中心 letterbox(等比、 填充 114),框反变换用同一套 pad/scale。NanoDet / CenterFace / LibFace 仍走拉伸 GeometryScale。Fork CI 用仅 CI 的 ONNX overlay (conf/ci/yolov8_onnx_hosted.toml,yolov8s.onnx)证明 YOLOv8 decode+NMS+unmap;出厂 yolov8_config.toml 仍是 TensorRT。

Supervisor 优雅关停不再挂死。WFServerBase::stop() 本身就是阻塞的 shutdown() + wait_finish();旧代码又补了一次 wait_finish(),第二次 永久阻塞。生产上一直被 systemd TimeoutStopSec 的 SIGKILL 掩盖—— mortred-supervisor 此前从未真正优雅退出过。由本次重构的进程内 SupervisorApp 关停路径暴露。

Changed

控制面去全局化(内部重构,无行为变化):网关/supervisor 的文件级全局 状态移除,GatewayApp / SupervisorApp 持有各自状态;run() 将进程 环境映射为显式的 *InitOptions,app 对象移入新的依赖 workflow 的 control_workflow 库以便测试进程内链接。验收:两个网关与两个 supervisor 实例(不同 root/token/catalog)可同进程并存 (gateway_multiinstance_test、supervisor_multiinstance_test)。

Fixed

网关现在向模型服务器转发 raw-body 控制头 X-Mortred-Params / X-Mortred-Options / X-Request-ID,并在自身错误回复中回显 X-Request-ID。此前 raw-body 请求经网关代理后参数与关联 id 被静默丢弃 (JSON envelope 路径不受影响)。其余客户端头仍然丢弃——默认拒绝的转发 白名单就是防头注入的屏障。

Supervisor 的 process() 现在与网关/模型服务器一样把空 method 折叠为 ""。此前畸形请求行(workflow 返回 null method)会从 nullptr 构造 std::string——未定义行为,通常表现为管理面崩溃。

异步任务回复现在计入 mortred_http_requests_total:此前 202 提交回复、 200 状态/等待/结果回复以及异步错误码(404/405/409/429)在模型服务器监控 上不可见。异步回复刻意不产生 mortred_http_request_duration_ms 样本——该 直方图观测的是推理耗时,异步 HTTP 路径上没有这一耗时。

模型进程与网关的 main 在 SIGINT/SIGTERM 时会 done() WaitGroup,从而走到 stop() 和析构 drain;不再被默认信号直接杀死。Supervisor 本身原本就是这样。

Changed

POST /jobs 在准入时立即返回 202,不再等推理结束。GET …/wait 在终态或 wait 预算耗尽时返回(单位毫秒)。客户逐步验收见 async-jobs-customer-test.zh-cn.md。

Removed

Changed

Changed

Fixed

Added

Removed

Changed

Fixed

Added

[0.1.0] - 2026-08-23

Added

Fixed

中文摘要:新增部署 Profile 体系(cpu/gpu 单一事实源贯穿构建、依赖、目录、 权重四层)、双轨分发(Docker 双 target + compose profiles;版本化 tarball