预制服务
minimax-h3 turbo + RTX VSR 合并镜像使用说明
一个镜像 =minimax-H3 生视频服务(ComfyUI:8188)+ 视频超分服务(VSR API:8001),单张 RTX 5090(32GB)跑通「768p 生成 → 2K 超分」全链路,产物带音轨
一、快速开始:5 步跑通全链路
Step 1 — 探活(两个服务各一次)
curl -s $COMFY/system_stats # 返回 200 + JSON 即活(ComfyUI 无 /health)
curl -s $VSR/healthz # {"status":"ok","service":"rtxvsr",...}
Step 2 — 提交一条 H3 生成任务(768p / 5 秒 / 4 步 turbo)
# workflow JSON 太长,完整版见第四节,这里示意提交方式:
curl -s -X POST $COMFY/prompt \
-H "Content-Type: application/json" \
-d '{"prompt": {…workflow…}, "client_id": "my-app-1"}'
# → {"prompt_id": "7498f8fe-…", "number": 1, "node_errors": {}}
⚠️ node_errors 非空 = 工作流有错,根本没入队
Step 3 — 轮询到 success,下载 768p 产物
curl -s $COMFY/history/7498f8fe-… | jq '.[].status.status_str' # "success"
# 从 outputs 里拿 filename,然后:
curl -s -o h3_raw.mp4 "$COMFY/view?filename=<文件名>&subfolder=&type=output"
Step 4 — 提交超分任务(2 倍放大 → 2688×1536)
curl -s -X POST $VSR/api/v1/upscale/file \
-F "file=@h3_raw.mp4" -F "scale=2.0" -F "quality=HIGH" \
-F "vcodec=libx264"
# → {"task_id":"d1247542e667","status":"queued","poll":"/api/v1/tasks/…"}
⚠️ 共绩平台必须用 vcodec=libx264(节点 NVENC 当前不可用,详见 5.4 节)。本地/自建机器可用默认 h264_nvenc。
Step 5 — 轮询 done,下载 2K 产物
curl -s $VSR/api/v1/tasks/d1247542e667 # {"status":"done","meta":{"total_s":30.9,…}}
curl -s -o h3_2k.mp4 $VSR/api/v1/tasks/d1247542e667/result
⚠️ /result 返回的是 video/mp4 二进制流(带 Content-Disposition 头),不是 JSON——直接当文件下载。
二、镜像概览与启动模式
| 项 | 内容 |
|---|---|
| 基础镜像 | minimax-h3 turbo 官方预制(ComfyUI 0.31.0 + int8 DiT×2 + turbo 4-step LoRA + qwen3vl 文本编码器 + 双 VAE) |
| 附加层 | RTX VSR(nvidia-vfx/Maxine VFX)FastAPI 服务、cupy-cuda12x 运行栈、python-multipart |
| 显存 | 生成峰值 ~27.8GB;超分峰值 ~6-10.5GB(随分辨率) |
| 启动配置 | ENTRYPOINT=/entrypoint-merge.sh,CMD=both |
启动模式(run 参数选一个)
| 模式 | 行为 | 什么时候用 |
|---|---|---|
both(默认) | ComfyUI(8188) + VSR(8001) 双进程 | 一体机(注意显存叠加,见下) |
comfyui | 仅 ComfyUI | 纯生成节点 |
vsr | 仅 VSR API | 纯超分节点(可部署在便宜机器上) |
selftest | 依次自检 cupy→nvvfx→VSR→NVENC 后退出 | 部署后冒烟验证(一次性容器) |
显存布局(both 模式,同卡)
ComfyUI(H3): 峰值 ~27.8 GiB(模型 int8 ~20G + 文本编码器 nvfp4 ~15G 走 CPU offload + KV)
VSR: 逐任务 ~6-10.5 GiB
三、部署
3.1 预制镜像
3.2 共绩 openapi 部署
① 查资源拿 mark(mark 是资源标识,随库存变动,每次部署前查):
curl -s "https://openapi.suanli.cn/api/deployment/resource/search" \
-H "token: <你的API密钥>" -H "version: 1.0.0" -H "timestamp: <毫秒时间戳>"
# 找到 "device_name":"5090" + "region":"tj1q"(天津一区)的那条,
# 记下 results[i].regions[0].mark(一长串 base64)和 resource 对象
② 创建任务:
{
"task_name": "h3vsr_merge_014",
"points": 1,
"repository_username": "bala",
"repository_password": "<bala账号密码>",
"resources": [{
"mark": "<第①步查到的mark>",
"resource": {"device_name": "5090", "region": "tj1q", "gpu_name": "5090",
"gpu_count": 1, "gpu_memory": 32600, "memory": 64512, "cpu_cores": 16},
"region_name": "天津一区",
"weight": null
}],
"services": [{
"service_name": "h3vsr-svc",
"service_image": "harbor.suanleme.cn/bala/h3vsr-merge:0.1.4-5090",
"env": [{"name": "NVIDIA_DRIVER_CAPABILITIES", "value": "compute,utility,video"}],
"remote_ports": [{"service_port": 8188}, {"service_port": 8001}],
"start_script_v2": {"command": null, "args": []},
"resource_weight": {"cpu_weight": 1, "mem_weight": 1, "gpu_weight": 1}
}]
}
curl -s -X POST "https://openapi.suanli.cn/api/task/deployment/create" \
-H "token: <密钥>" -H "version: 1.0.0" -H "timestamp: <毫秒>" \
-H "Content-Type: application/json" --data-binary @payload.json
# → {"code":"0000","data":{"task_id":3402316}}
③ 轮询到 Running,拿公网 URL:
curl -s "https://openapi.suanli.cn/api/task/deployment/detail?task_id=3402316" \
-H "token: …" -H "version: 1.0.0" -H "timestamp: …"
# data.status == "Running",data.services[0].remote_ports[].url 就是两个公网地址
四、服务 A:H3 文生视频
4.1 探活
ComfyUI 没有 /health 端点,探活用 GET /system_stats(HTTP 200 即活)。刚启动 1~3 分钟内可能拒绝连接(模型链加载中),重试即可。
4.2 T2V 工作流(权威版,逐节点注释)
这是实测跑通的 turbo 4-step 工作流。为什么是这五个采样节点:H3 的 turbo LoRA 要求 CFG=1.0,此时不需要 negative 分支,用 SamplerCustomAdvanced 五节点链(RandomNoise→BasicGuider→KSamplerSelect→BasicScheduler→SamplerCustomAdvanced)替代普通 KSampler——普通 KSampler 也能跑但这套是官方 turbo 工作流的原版结构,参数语义最清晰。
{
"10": {"class_type": "UNETLoader", "inputs": {
"unet_name": "minimax_h3_fl2va_pruned_int8_convrot.safetensors",
"weight_dtype": "default"}},
"11": {"class_type": "LoraLoaderModelOnly", "inputs": {
"model": ["10", 0],
"lora_name": "minimax_h3_fl2v_turbo_4step_v1.0_768p_comfyui_bf16.safetensors",
"strength_model": 1.0}},
"12": {"class_type": "MiniMaxH3SigmaShift", "inputs": {
"model": ["11", 0], "shift_video": 12.0, "shift_audio": 3.0}},
"13": {"class_type": "CLIPLoader", "inputs": {
"clip_name": "qwen3vl_32b_minimax_h3_nvfp4_awq.safetensors",
"type": "minimax", "device": "default"}},
"14": {"class_type": "VAELoader", "inputs": {"vae_name": "minimax_h3_video_vae_fp16.safetensors"}},
"15": {"class_type": "VAELoader", "inputs": {"vae_name": "minimax_h3_audio_vae_fp32.safetensors"}},
"16": {"class_type": "MiniMaxH3ImageToVideo", "inputs": {
"clip": ["13", 0], "vae": ["14", 0],
"prompt": "Cinematic anime style, a red sports car drifting through a neon-lit Tokyo street at night, rain-slick asphalt reflections, motion blur. Audio: engine roar, tire screech.",
"width": 1344, "height": 768, "length": 124}},
"20": {"class_type": "RandomNoise", "inputs": {"noise_seed": 123456}},
"21": {"class_type": "BasicGuider", "inputs": {"model": ["12", 0], "conditioning": ["16", 0]}},
"22": {"class_type": "KSamplerSelect", "inputs": {"sampler_name": "euler"}},
"23": {"class_type": "BasicScheduler", "inputs": {
"model": ["12", 0], "scheduler": "simple", "steps": 4, "denoise": 1.0}},
"24": {"class_type": "SamplerCustomAdvanced", "inputs": {
"noise": ["20", 0], "guider": ["21", 0], "sampler": ["22", 0],
"sigmas": ["23", 0], "latent_image": ["16", 1]}},
"30": {"class_type": "VAEDecode", "inputs": {"samples": ["24", 0], "vae": ["14", 0]}},
"31": {"class_type": "VAEDecodeAudio", "inputs": {"samples": ["24", 0], "vae": ["15", 0]}},
"32": {"class_type": "CreateVideo", "inputs": {"images": ["30", 0], "audio": ["31", 0], "fps": 24.0}},
"33": {"class_type": "SaveVideo", "inputs": {
"video": ["32", 0], "filename_prefix": "H3VSR", "format": "auto", "codec": "auto"}}
}
节点说明:
10-15加载器链:DiT(int8) → turbo LoRA → sigma shift(turbo 专用,shift 参数勿乱动)→ 文本编码器 → 双 VAE(视频/音频分开)16生成参数全在这里(见下表);注意它输出两个值:conditioning(接 21)和 latent(接 24)20-24采样链:4 步 euler + CFG 隐含 1.0(BasicGuider 只接 positive 就是 CFG=1)30-33解码成片:视频 VAE + 音频 VAE 分别解码 → 合成 mp4(fps=24,H3 原生帧率)
4.3 生成参数表
| 参数 | 含义 | 可选值与约束 |
|---|---|---|
width/height | 分辨率 | 必须 32 的倍数。常用:864×480(480p)/ 1280×720(720p)/ 1344×768(768p,推荐) / 1920×1080(1080p,32G 卡 10s/15s 会 OOM) |
length | 帧数(24fps) | 必须落在 17k+5 网格:5s=124、10s=243、15s=362。校验式:(length-5) % 17 == 0 且 124 ≤ length ≤ 362 |
prompt | 提示词 | 同时描述画面和音轨(“Audio: …”),H3 生成同步音频 |
⚠️ 帧数网格是最容易踩的坑:提交非法值(如 245)不会报错,节点静默向上取整(245→260 帧),多花时间且超出训练范围质量下降。实测记录:提交 245 实际生成 260 帧、365→379 帧。
⚠️ 产物帧数 = 请求 +3(H3 帧数补偿):请求 124 实际 127 帧,属正常,不是 bug。
⚠️ 防缓存:同参数同 seed 第二次提交会命中执行缓存秒回旧结果。批量跑每条要随机 seed(改节点 20 的 noise_seed)。
4.4 提交、轮询、下载
POST /prompt body: {"prompt": <workflow>, "client_id": "<任意ID>"}
→ {"prompt_id": "...", "node_errors": {}} # node_errors 非空=工作流有错没入队
GET /history/{prompt_id}
→ status.status_str: "success" / "error"
→ outputs.{node_id}.gifs[] / videos[]: [{filename, subfolder, type}]
GET /view?filename=<名>&subfolder=<子目录>&type=output → 文件流
错误详情:status_str=error 时看 status.messages 里的 execution_error,有节点名和异常栈。
性能基准(5090 单卡 turbo 4-step,实测 P50):
| 分辨率 | 5s | 10s | 15s |
|---|---|---|---|
| 480p | 38s | 65s | 117s |
| 720p | 75s | 168s | 375s |
| 768p | 81~138s | 243s | 452s(接近 OOM 边界) |
(768p/5s 的区间:本地热态 80.1s、本地冷启动 110.2s、共绩远程冷启动 138.2s)
4.5 参考生视频(R2V)
T2V 换三处成 R2V 链,其余不变:
| 节点 | T2V(默认) | R2V |
|---|---|---|
| 10 unet_name | fl2va | minimax_h3_ref2va_pruned_int8_convrot.safetensors |
| 11 lora_name | fl2v_turbo…v1.0 | minimax_h3_ref2v_turbo_4step_v0.1_comfyui_bf16.safetensors |
| 16 class_type | MiniMaxH3ImageToVideo | MiniMaxH3ReferenceToVideo(多必填 audio_vae: ["15",0]) |
R2V 关键点(实测):
- 参考图上传:
POST /upload/image(multipart,字段名image),客户端超时设 ≥600s(35MB 大图用默认 120s 会 write timeout) - ref_images 接线:API 里是列表:
"ref_images": [["201",0], ["202",0]];写成ref_image_1会报TypeError: unexpected keyword argument - 提示词前部用
<Picture 1>、<Picture 2>引用参考图 - fl2va ↔ ref2va 模型切换有 ~124s 冷加载(同进程内来回切很亏,尽量按模型分组批量跑)
五、服务 B:VSR 视频超分
超分 API 文档位置:部署链接+ /docs
5.1 接口一览
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /healthz | 健康检查 |
| POST | /api/v1/upscale/file | multipart 直传视频 |
| GET | /api/v1/tasks/{id} | 状态轮询 |
| GET | /api/v1/tasks/{id}/result | 下载产物 |
5.2 参数详解
curl -X POST $VSR/api/v1/upscale/file \
-F "file=@input.mp4" \ # 必填:输入视频(mp4/h264 最佳;带音轨会原样保留)
-F "scale=2.0" \ # 放大倍数,支持小数(1.5 实测 OK);768p→2K 用 2.0
-F "quality=HIGH" \ # 质量档,见下表
-F "vcodec=libx264" \ # 编码器,见 5.4 节(平台差异!)
-F "bitrate=50M" # 输出码率,2K 建议 50M
quality 枚举(非法值返回 400 “bad quality”):
| 值 | 速度 | 用途 |
|---|---|---|
| BICUBIC | 最快 | 不跑模型,纯插值(自检链路用) |
| LOW / MEDIUM | 快 | 预览 |
| HIGH | 中 | 768p AI 生成源推荐 |
| ULTRA | 略慢 | 与 HIGH 差距很小(13.5s vs 10.1s),可选 |
| HIGHBITRATE_* | - | 高码率源专用 |
status 轮询响应:
{"task_id":"…","status":"done","progress":1.0,"error":null,
"meta":{"total_s":30.9,"size_mb":54.3,"quality":"HIGH","scale":2.0}}
status 取值:queued → running → done | error。error 时 error 字段是完整异常文本。
5.3 错误码实测
| 请求 | 响应 |
|---|---|
| scale=“abc” | 422 + {"detail":[{"loc":["body","scale"],"msg":"Input should be a valid number…"}]} |
| 缺 file 字段 | 422 |
| quality=“NOT_A_LEVEL” | 400 “bad quality”(worker 层校验,提交时是 200 queued,稍后 error)⚠️ 即:quality 错误是异步暴露的,轮询时才能看到 |
| task_id 不存在 | 404 |
5.4 并发
/healthz 的 concurrency.max=3,但同卡 both 模式下受显存约束实际安全并发=1(生成空闲时)。queue_visible 是「已提交未开始」数,不是队列深度,别当监控用。
六、全链路整合:完整可运行客户端
单文件、纯标准库、带超时与错误处理。覆盖典型场景:探活 → 生成(校验帧数网格)→ 下载 → 超分(自动选编码器)→ 下载 → 校验。。
#!/usr/bin/env python3
"""h3vsr-merge 全链路客户端:H3 生成 768p -> VSR 2x 超分 -> 2K mp4
用法: python h3vsr_client.py "提示词" [输出目录]
依赖: 仅 Python 标准库
"""
import json, time, random, sys, os, urllib.request, urllib.parse, uuid
# ========== 配置(按你的部署改这两行) ==========
COMFY = "https://deployment-452-xxxx-8188.550w.link"
VSR = "https://deployment-452-xxxx-8001.550w.link"
VCODEC = "libx264" # 共绩平台用 libx264;本地机器可改回 h264_nvenc
# ================================================
# --- 帧数网格校验(H3 硬约束,防静默取整多耗时间)---
def valid_length(n):
return (n - 5) % 17 == 0 and 124 <= n <= 362
def build_workflow(prompt, width=1344, height=768, length=124, seed=None):
"""构造 T2V 工作流。分辨率须 32 倍数,帧数须 17k+5 网格(124/243/362)。"""
assert width % 32 == 0 and height % 32 == 0, "宽高必须是 32 的倍数"
assert valid_length(length), "length 必须是 124/243/362(17k+5 网格)"
return {
"10": {"class_type": "UNETLoader", "inputs": {
"unet_name": "minimax_h3_fl2va_pruned_int8_convrot.safetensors", "weight_dtype": "default"}},
"11": {"class_type": "LoraLoaderModelOnly", "inputs": {
"model": ["10", 0],
"lora_name": "minimax_h3_fl2v_turbo_4step_v1.0_768p_comfyui_bf16.safetensors",
"strength_model": 1.0}},
"12": {"class_type": "MiniMaxH3SigmaShift", "inputs": {
"model": ["11", 0], "shift_video": 12.0, "shift_audio": 3.0}},
"13": {"class_type": "CLIPLoader", "inputs": {
"clip_name": "qwen3vl_32b_minimax_h3_nvfp4_awq.safetensors",
"type": "minimax", "device": "default"}},
"14": {"class_type": "VAELoader", "inputs": {"vae_name": "minimax_h3_video_vae_fp16.safetensors"}},
"15": {"class_type": "VAELoader", "inputs": {"vae_name": "minimax_h3_audio_vae_fp32.safetensors"}},
"16": {"class_type": "MiniMaxH3ImageToVideo", "inputs": {
"clip": ["13", 0], "vae": ["14", 0], "prompt": prompt,
"width": width, "height": height, "length": length}},
"20": {"class_type": "RandomNoise", "inputs": {
"noise_seed": seed if seed is not None else random.randint(1, 2**48)}}, # 每条随机,防缓存秒回
"21": {"class_type": "BasicGuider", "inputs": {"model": ["12", 0], "conditioning": ["16", 0]}},
"22": {"class_type": "KSamplerSelect", "inputs": {"sampler_name": "euler"}},
"23": {"class_type": "BasicScheduler", "inputs": {
"model": ["12", 0], "scheduler": "simple", "steps": 4, "denoise": 1.0}},
"24": {"class_type": "SamplerCustomAdvanced", "inputs": {
"noise": ["20", 0], "guider": ["21", 0], "sampler": ["22", 0],
"sigmas": ["23", 0], "latent_image": ["16", 1]}},
"30": {"class_type": "VAEDecode", "inputs": {"samples": ["24", 0], "vae": ["14", 0]}},
"31": {"class_type": "VAEDecodeAudio", "inputs": {"samples": ["24", 0], "vae": ["15", 0]}},
"32": {"class_type": "CreateVideo", "inputs": {"images": ["30", 0], "audio": ["31", 0], "fps": 24.0}},
"33": {"class_type": "SaveVideo", "inputs": {
"video": ["32", 0], "filename_prefix": "h3vsr_client", "format": "auto", "codec": "auto"}},
}
def http_json(url, data=None, method=None, timeout=120):
req = urllib.request.Request(url, method=method)
if data is not None:
req.add_header("Content-Type", "application/json")
req.data = json.dumps(data).encode()
with urllib.request.urlopen(req, timeout=timeout) as r:
return json.loads(r.read())
def download(url, path, timeout=1800):
with urllib.request.urlopen(url, timeout=timeout) as r, open(path, "wb") as f:
while True:
chunk = r.read(1 << 20)
if not chunk: break
f.write(chunk)
def wait_comfy(pid, poll_s=15, limit_s=3000):
"""轮询 ComfyUI 到 success/error。5s 片约 80-140s,15s 片留足 50 分钟。"""
t0 = time.time()
while time.time() - t0 < limit_s:
time.sleep(poll_s)
hist = http_json(f"{COMFY}/history/{pid}", timeout=60)
if pid in hist:
st = hist[pid]["status"]["status_str"]
if st == "error":
msgs = hist[pid]["status"].get("messages", [])
raise RuntimeError(f"H3 生成失败: {json.dumps(msgs, ensure_ascii=False)[:800]}")
if st == "success":
return hist[pid]
print(f" ...生成中 {int(time.time()-t0)}s", flush=True)
raise TimeoutError("H3 生成超时")
def comfy_output(entry):
"""从 history 里抠出产物文件(优先 mp4)。"""
files = []
for node_out in entry.get("outputs", {}).values():
for key in ("gifs", "videos", "images", "audio"):
files.extend(node_out.get(key, []))
mp4 = next((f for f in files if f["filename"].endswith(".mp4")), None)
if not mp4:
raise RuntimeError(f"产物里没有 mp4: {files}")
return mp4
def vsr_upscale(src_path, scale=2.0, quality="HIGH"):
"""multipart 提交超分并轮询到 done,返回 task_id。"""
boundary = uuid.uuid4().hex
with open(src_path, "rb") as f: filedata = f.read()
parts = [f'--{boundary}\r\nContent-Disposition: form-data; name="file"; filename="{os.path.basename(src_path)}"\r\nContent-Type: video/mp4\r\n\r\n'.encode()
+ filedata + b"\r\n"]
for k, v in [("scale", str(scale)), ("quality", quality), ("vcodec", VCODEC)]:
parts.append(f'--{boundary}\r\nContent-Disposition: form-data; name="{k}"\r\n\r\n{v}\r\n'.encode())
parts.append(f"--{boundary}--\r\n".encode())
req = urllib.request.Request(f"{VSR}/api/v1/upscale/file", data=b"".join(parts), method="POST")
req.add_header("Content-Type", f"multipart/form-data; boundary={boundary}")
with urllib.request.urlopen(req, timeout=600) as r:
up = json.loads(r.read())
tid = up["task_id"]
t0 = time.time()
while time.time() - t0 < 1800: # 软编忙时可达 100s/5s片,15s 片留 30 分钟
time.sleep(8)
st = http_json(f"{VSR}/api/v1/tasks/{tid}", timeout=60)
if st["status"] == "error":
raise RuntimeError(f"超分失败: {st.get('error','')[:500]}")
if st["status"] == "done":
print(f" 超分完成: {st['meta']}", flush=True)
return tid
print(f" ...超分 {st['status']} {int(time.time()-t0)}s", flush=True)
raise TimeoutError("超分超时")
def main():
prompt = sys.argv[1] if len(sys.argv) > 1 else "Cinematic anime style, a red sports car drifting through neon-lit Tokyo street at night. Audio: engine roar, rain ambience."
outdir = sys.argv[2] if len(sys.argv) > 2 else "."
os.makedirs(outdir, exist_ok=True)
# 0) 探活(ComfyUI 冷启动 1-3 分钟,重试 5 次)
for i in range(5):
try:
http_json(f"{COMFY}/system_stats", timeout=30); break
except Exception:
if i == 4: raise
print("ComfyUI 未就绪,30s 后重试…", flush=True); time.sleep(30)
http_json(f"{VSR}/healthz", timeout=30)
# 1) 生成(产物帧数 = 请求 +3 属正常)
t0 = time.time()
resp = http_json(f"{COMFY}/prompt", {"prompt": build_workflow(prompt), "client_id": "h3vsr-client"})
if resp.get("node_errors"):
raise RuntimeError(f"工作流校验失败: {json.dumps(resp['node_errors'], ensure_ascii=False)[:500]}")
entry = wait_comfy(resp["prompt_id"])
gen_s = time.time() - t0
f = comfy_output(entry)
q = urllib.parse.urlencode({"filename": f["filename"], "subfolder": f.get("subfolder",""), "type": f.get("type","output")})
raw = os.path.join(outdir, "h3_raw.mp4")
download(f"{COMFY}/view?{q}", raw)
print(f"✅ 生成 {gen_s:.0f}s → {raw} ({os.path.getsize(raw)/1e6:.1f}MB)")
# 2) 超分(串行:生成完才提交,单卡显存安全)
t1 = time.time()
tid = vsr_upscale(raw)
dst = os.path.join(outdir, "h3_2k.mp4")
download(f"{VSR}/api/v1/tasks/{tid}/result", dst) # /result 是 mp4 流
print(f"✅ 超分 {time.time()-t1:.0f}s → {dst} ({os.path.getsize(dst)/1e6:.1f}MB)")
print(f"🎉 全链路 {time.time()-t0:.0f}s")
if __name__ == "__main__":
main()
八、故障排查速查表
| 症状 | 原因 | 解法 |
|---|---|---|
C001 body is error | payload 结构错 / 客户端重编码 null | 按 3.1 模板;curl —data-binary @file |
A001 must need token… | header 缺失或时间戳过期 | 三件套(token/version/timestamp)每次现取 |
ComfyUI 提交即 node_errors 非空 | 工作流节点名/参数错 | 对照 4.2 权威版;最常见是 length 不在网格、模型文件名抄错(带 comfyui_bf16 后缀勿截断) |
| 生成帧数比请求多 3 帧 | H3 帧数补偿 | 正常 |
| 生成秒回但结果是旧片 | 执行缓存命中(同 seed) | 每条随机 seed |
VSR 报 OpenEncodeSessionEx failed | 平台节点 NVENC 不可用 | vcodec=libx264(共绩);本地不受影响 |
| VSR JSON 模式任务永久 pending | video_url 拉取无超时卡死 worker | 一律用 multipart /upscale/file |
| quality 明明错了却不报 422 | 校验在 worker 层,异步暴露 | 轮询 status==error 看 error 字段 |
| 生成中提交超分 → OOM | 单卡显存叠加(27.8+10.5>32G) | 串行:生成完再超分(第六节客户端已处理) |
/result 解析 JSON 报错 | 它返回的是 mp4 二进制流 | 按文件流下载 |
九、附录
A. 内置模型清单
/comfyui/ComfyUI/models/diffusion_models/
minimax_h3_fl2va_pruned_int8_convrot.safetensors # T2V DiT (int8, ~21GB)
minimax_h3_ref2va_pruned_int8_convrot.safetensors # R2V DiT (int8, ~21GB)
/comfyui/ComfyUI/models/loras/
minimax_h3_fl2v_turbo_4step_v1.0_768p_comfyui_bf16.safetensors
minimax_h3_ref2v_turbo_4step_v0.1_comfyui_bf16.safetensors
/comfyui/ComfyUI/models/vae/
minimax_h3_video_vae_fp16.safetensors # 视频 VAE
minimax_h3_audio_vae_fp32.safetensors # 音频 VAE
/comfyui/ComfyUI/models/clip/
qwen3vl_32b_minimax_h3_nvfp4_awq.safetensors # 文本编码器 (nvfp4, ~15.7GB)