H3 Console
给 minimax-h3/ 的四后端 API(gateway.py + 4× api.py)加一层 Web 管理台:
左侧任务记录列表 + 右侧新建/详情,上传首尾帧/参考图 → 提交生成 → 点开任务看状态和结果。
不重做调度,只加 gateway.py 没有的东西(Web UI、文件上传、任务持久化、按需 fl2va/ref2va 弹性分卡)。
需求/架构决策见 <code>DESIGN.md</code>——先读那份,尤其「四点五、弹性 partition 调度器」。
页面结构
左侧栏固定:顶部一条新建任务(选中态=当前在填新表单),下面是任务记录列表,按提交时间倒序, 点哪条就直接跳到那条的详情。右侧是新建表单(三个 tab:文生/首尾帧/参考图)或任务详情二选一。
URL 是真实可分享的路径,不是纯前端状态:
/newtask.html(或裸/)— 新建任务页/task.html/<任务id>— 某个任务的详情页,刷新、直接打开、发链接给别人都能定位到同一条
这三条路径背后是同一个 static/index.html,server.py 里 /task.html/{tid} 和 /newtask.html
两条路由原样返回这份文件,靠前端 JS 读 location.pathname 决定渲染哪个视图——不是三个页面文件,
省得同一份侧边栏/样式在多个 html 里重复维护。提交完新任务会自动跳到 /task.html/<新id>。
状态(2026-08-20)
已部署到服务器,端口 8080,跑在 /data/models/minimax-h3/console/,弹性调度器全链路验证通过。
本地冒烟测试:上传图片 → 拒绝非图片文件 → 提交生成任务 → 轮询到 done → 下载代理拿到合法 mp4 →
sqlite 影子记录核对无误 → 浏览器打开首页、三个 tab 切换、任务列表内联播放,均正常。
服务器上用真实 GPU 做的弹性调度全链路验证(这部分本地测不了,见 DESIGN.md「四点五」):
上传首帧图 → 提交 fl2va 任务(当时四卡全 ref2va)→ console 正确返回 queued(不是 503)→
supervisor 30 秒后选中一个空闲 ref2va 后端 flip 成 fl2va → reconciler 在新后端 ready 后自动把
排队的任务提交上去 → 任务 running → done → 通过 console 下载代理拿到合法 mp4。全程无人工干预。
部署过程中揪出的两个真实 bug(修完才让上面这条链路跑通)
_flip_backend里的 PID 提取从来没匹配上过:ss -lptn的输出里 pid 不是独立的空格分隔 token,是嵌在users:(("python",pid=NNNN,fd=14))里面的——tok.startswith("pid=")永远不命中,kill从来没真的执行。后果是"flip"只是在已被占用的端口上起了一个新 uvicorn 进程,因端口冲突 立刻退出,旧进程全程没受影响——不是数据损坏,但功能是假的。改用re.findall(r"pid=(\d+)", ...)。- 弹性调度器不知道"已经在切换中",会对同一个需求连续 flip 好几张卡:api.py 冷启动要
13-20 秒,跨了不止一个 10 秒的 tick。中间几轮 tick 看到目标分区的 ready 数还是 0,
误判成需求没满足,接着把第二张、第三张卡也切了过去——一个
pending=1的请求切了三张卡。 加了flipping: dict[port, (目标分区, 发起时间)]记账,效果数(ready + 正在切换中的) 满足需求就不再触发新的 flip,60 秒还没起来算超时放弃。 pkill -f "<pattern>"杀掉了自己所在的 SSH 会话:这条不是 console 代码的 bug,是我操作 服务器时踩的坑——start-all.sh注释里其实早就警告过这个("pgrep -f 会匹配到自己那条命令行"), 这次是真的踩了一遍:pkill -f "uvicorn server:app.*8080"这条命令的完整命令行本身就包含 被搜索的那串文本,pkill -f把自己所在的 shell 一起杀了,SSH 连接直接断,命令链后半截全没执行。 之后全程改用ss -lptn "sport = :PORT" | grep -o "pid=..."按端口精确定位再 kill,不再用pkill -f。
怎么起(本地开发)
cd h3-console
./run.sh # 默认 H3_GATEWAY_URL=http://192.168.110.67:8000,端口 8090
浏览器打开 http://localhost:8090。三个 tab(文生视频/首尾帧/参考图)+ 任务列表都在首页。
⚠️ 本地开发时上传功能不完整:H3_CONSOLE_PUBLIC_BASE 没配的话 /v1/assets 会直接报错——
因为上传图片的 URL 最终要被服务器上的 api.py worker 反向拉取,本地 Mac 猜不出自己对服务器
可达的地址。要在本地测完整的首尾帧/参考图流程,配成 Mac 在局域网里的地址:
H3_CONSOLE_PUBLIC_BASE=http://<你的 Mac 局域网 IP>:8090 ./run.sh
前提是服务器能反向连回你的 Mac(同一局域网通常没问题;接了会劫持路由的 VPN 就不行,
HANDOFF.md 里记过一次这个坑:nc -z 会给假阳性,出问题用 ping+route -n get 确认路径)。
不配这个变量时,文生视频(不需要上传)那个 tab 可以正常测。
部署到服务器(已完成,2026-08-20)
rsync -av --exclude='.venv' --exclude='__pycache__' --exclude='console.sqlite3' --exclude='assets/*' \
h3-console/ admin@192.168.110.67:/data/models/minimax-h3/console/
ssh admin@192.168.110.67
cd /data/models/minimax-h3/console
H3_GATEWAY_URL=http://127.0.0.1:8000 \
H3_CONSOLE_PUBLIC_BASE=http://192.168.110.67:8080 \
H3_ENABLE_SUPERVISOR=1 \
PORT=8080 \
setsid nohup ./run.sh > console.log 2>&1 < /dev/null &
对外访问用现成的 FRP 隧道 gateway.wudaima.com(配置已存在、一直空挂,见 DESIGN.md 问题2)——
这一步还没验证公网访问,只验证过局域网内 http://192.168.110.67:8080 直连。
重启/重新部署时不要用 pkill -f(见上面「揪出的坑」第3条,会误杀自己的 SSH 会话),改用:
PID=$(ss -lptn "sport = :8080" | grep -o "pid=[0-9]*" | cut -d= -f2 | sort -u)
[ -n "$PID" ] && kill $PID
部署后验过:curl http://192.168.110.67:8080/v1/backends 看到 4/4 ready;提交一个 fl2va
首尾帧任务,console.log 里能看到 [supervisor] flip port=... ref2va -> fl2va,30 秒后(空闲
判定)触发一次、只触发一次,新后端 ready 后任务自动提交并跑完,下载代理拿到合法 mp4。
回滚
console 是独立进程,不碰 gateway.py/api.py 一行代码,出问题直接 kill console 进程即可,
四个 H3 后端和 gateway 不受影响(不像 deploy-scripts/ 那套需要镜像级回滚)。
已知限制
- 鉴权:暂未加(
DESIGN.md问题3,跑通了再加)。部署到公网前必须补,否则和现有服务一样是 无鉴权可直调,风险更高——console 多了写文件(上传)的能力。 - 任务持久化是 console 自己的 sqlite,和
api.py内存态之间有轮询间隔的短暂不一致(几秒级), 不追求强一致。 - 磁盘:
/只剩 29G,/data83% 满,上传接口目前只做单文件 20MB 上限校验,没做磁盘余量检查, 真到磁盘紧张时会和其他生成任务一样直接写失败,没有额外保护——按 DESIGN.md「开放风险」记录的, 真出问题再加。