Skip to content

Instantly share code, notes, and snippets.

@safa-dayo
Created August 3, 2026 09:27
Show Gist options
  • Select an option

  • Save safa-dayo/057bcf801828e149f0d9077b63b7aa6b to your computer and use it in GitHub Desktop.

Select an option

Save safa-dayo/057bcf801828e149f0d9077b63b7aa6b to your computer and use it in GitHub Desktop.
MiniMax H3をGoogle Colab上のComfyUIで試すためのColabノートブック
# #@title MiniMax H3 with ComfyUI
#@markdown MiniMax H3 を ComfyUI のネイティブノードで起動します。
#@markdown T2V / I2V / R2V と、映像・32kHzステレオ音声の同時生成に対応します。
#@markdown
#@markdown **重要:** 最小構成でも約42.5GB(T2V/I2VまたはR2Vの片方)のダウンロードが必要です。
#@markdown 一般的な Colab T4 / L4 環境では、RAM・VRAM不足や非常に長い生成時間が発生する可能性があります。
#@markdown A100 40GB 以上とハイメモリランタイムを推奨しますが、通常の Colab 環境での完走を保証するものではありません。
from pathlib import Path
import json
import os
import platform
import shutil
import socket
import subprocess
import sys
import threading
import time
import urllib.request
#@markdown **ライセンスについて:** MiniMax H3 のモデルを利用すると、提供元の
#@markdown [MiniMax H3 Community License](https://huggingface.co/MiniMaxAI/MiniMax-H3/blob/main/LICENSE)
#@markdown を確認し、同意したものとみなされます。実行前に必ず内容をご確認ください。
#@markdown なお、EU・英国・韓国・米国はライセンスの許諾地域から除外されています。
def run(command, cwd=None):
"""Run a command and stop the cell when it fails."""
print("+", " ".join(map(str, command)))
subprocess.run([str(part) for part in command], cwd=cwd, check=True)
#@markdown ---
#@markdown ## ComfyUI の設定
USE_GOOGLE_DRIVE = False #@param {type:"boolean"}
UPDATE_COMFY_UI = True #@param {type:"boolean"}
USE_COMFYUI_MANAGER = True #@param {type:"boolean"}
INSTALL_CUSTOM_NODES_DEPENDENCIES = False #@param {type:"boolean"}
if USE_GOOGLE_DRIVE:
from google.colab import drive
drive.mount("/content/drive")
COMFY_ROOT = Path("/content/drive/MyDrive/ComfyUI")
else:
COMFY_ROOT = Path("/content/ComfyUI")
if not COMFY_ROOT.exists():
COMFY_ROOT.parent.mkdir(parents=True, exist_ok=True)
run([
"git", "clone", "https://github.com/Comfy-Org/ComfyUI.git", str(COMFY_ROOT)
])
elif not (COMFY_ROOT / ".git").exists():
raise RuntimeError(f"既存の {COMFY_ROOT} は ComfyUI の Git リポジトリではありません。")
if UPDATE_COMFY_UI:
print("-= Updating ComfyUI =-")
run(["git", "pull", "--ff-only"], cwd=COMFY_ROOT)
print("-= Installing ComfyUI dependencies =-")
run([
sys.executable,
"-m",
"pip",
"install",
"-r",
str(COMFY_ROOT / "requirements.txt"),
])
if USE_COMFYUI_MANAGER:
manager_dir = COMFY_ROOT / "custom_nodes" / "ComfyUI-Manager"
manager_dir.parent.mkdir(parents=True, exist_ok=True)
if not manager_dir.exists():
run([
"git",
"clone",
"https://github.com/Comfy-Org/ComfyUI-Manager.git",
str(manager_dir),
])
else:
run(["git", "pull", "--ff-only"], cwd=manager_dir)
manager_requirements = manager_dir / "requirements.txt"
if manager_requirements.exists():
run([
sys.executable,
"-m",
"pip",
"install",
"-r",
str(manager_requirements),
])
if INSTALL_CUSTOM_NODES_DEPENDENCIES:
manager_cli = COMFY_ROOT / "custom_nodes" / "ComfyUI-Manager" / "cm-cli.py"
if not manager_cli.exists():
raise RuntimeError(
"依存関係の復元には ComfyUI-Manager が必要です。"
"USE_COMFYUI_MANAGER を有効にしてください。"
)
run([sys.executable, str(manager_cli), "restore-dependencies"], cwd=COMFY_ROOT)
#@markdown ---
#@markdown ## MiniMax H3 の設定
#@markdown
#@markdown 用途を選択してください。T2V と I2V は同じ FL2VA モデルを使用します。
#@markdown R2V は画像・動画・音声を参照できる別モデルです。
H3_TASK = "T2V / I2V(FL2VA)" #@param ["T2V / I2V(FL2VA)", "R2V(画像・動画・音声参照)", "T2V / I2V + R2V(両方)"]
#@markdown 精度を選択してください。公式ワークフローの初期設定は最小構成です。
MODEL_PROFILE = "最小構成(pruned INT8 + NVFP4、推奨)" #@param ["最小構成(pruned INT8 + NVFP4、推奨)", "INT8品質優先(INT8 ConvRot)", "BF16基準品質(通常Colab非推奨)"]
PROFILE_CONFIG = {
"最小構成(pruned INT8 + NVFP4、推奨)": {
"fl2va": "minimax_h3_fl2va_pruned_int8_convrot.safetensors",
"ref2va": "minimax_h3_ref2va_pruned_int8_convrot.safetensors",
"text_encoder": "qwen3vl_32b_minimax_h3_nvfp4_awq.safetensors",
},
"INT8品質優先(INT8 ConvRot)": {
"fl2va": "minimax_h3_fl2va_int8_convrot.safetensors",
"ref2va": "minimax_h3_ref2va_int8_convrot.safetensors",
"text_encoder": "qwen3vl_32b_minimax_h3_int8_convrot.safetensors",
},
"BF16基準品質(通常Colab非推奨)": {
"fl2va": "minimax_h3_fl2va_bf16.safetensors",
"ref2va": "minimax_h3_ref2va_bf16.safetensors",
"text_encoder": "qwen3vl_32b_minimax_h3_bf16.safetensors",
},
}
FILE_SIZES = {
"minimax_h3_fl2va_pruned_int8_convrot.safetensors": 20_970_379_616,
"minimax_h3_ref2va_pruned_int8_convrot.safetensors": 20_970_379_616,
"qwen3vl_32b_minimax_h3_nvfp4_awq.safetensors": 15_687_142_551,
"minimax_h3_fl2va_int8_convrot.safetensors": 34_038_892_334,
"minimax_h3_ref2va_int8_convrot.safetensors": 34_038_894_550,
"qwen3vl_32b_minimax_h3_int8_convrot.safetensors": 27_141_342_152,
"minimax_h3_fl2va_bf16.safetensors": 66_280_487_368,
"minimax_h3_ref2va_bf16.safetensors": 66_280_487_368,
"qwen3vl_32b_minimax_h3_bf16.safetensors": 51_506_295_256,
"minimax_h3_video_vae_fp16.safetensors": 5_207_808_496,
"minimax_h3_audio_vae_fp32.safetensors": 605_254_808,
}
profile = PROFILE_CONFIG[MODEL_PROFILE]
use_fl2va = H3_TASK in ("T2V / I2V(FL2VA)", "T2V / I2V + R2V(両方)")
use_ref2va = H3_TASK in ("R2V(画像・動画・音声参照)", "T2V / I2V + R2V(両方)")
model_dirs = {
"diffusion_models": COMFY_ROOT / "models" / "diffusion_models",
"text_encoders": COMFY_ROOT / "models" / "text_encoders",
"vae": COMFY_ROOT / "models" / "vae",
}
for model_dir in model_dirs.values():
model_dir.mkdir(parents=True, exist_ok=True)
downloads = []
if use_fl2va:
downloads.append(("diffusion_models", profile["fl2va"]))
if use_ref2va:
downloads.append(("diffusion_models", profile["ref2va"]))
downloads.extend([
("text_encoders", profile["text_encoder"]),
("vae", "minimax_h3_video_vae_fp16.safetensors"),
("vae", "minimax_h3_audio_vae_fp32.safetensors"),
])
def remaining_download_bytes(items):
remaining = 0
for category, filename in items:
target = model_dirs[category] / filename
current_size = target.stat().st_size if target.exists() else 0
expected_size = FILE_SIZES[filename]
if current_size > expected_size:
raise RuntimeError(f"ファイルサイズが不正です: {target}")
remaining += expected_size - current_size
return remaining
remaining_bytes = remaining_download_bytes(downloads)
free_bytes = shutil.disk_usage(COMFY_ROOT).free
reserve_bytes = 5 * 1024**3
print(f"追加ダウンロード: 約 {remaining_bytes / 1_000_000_000:.1f} GB")
print(f"空き容量: 約 {free_bytes / 1_000_000_000:.1f} GB")
if free_bytes < remaining_bytes + reserve_bytes:
raise RuntimeError(
"モデル取得に必要な空き容量が不足しています。"
"別のモデル構成を選ぶか、保存先の空き容量を増やしてください。"
)
HF_BASE = "https://huggingface.co/Comfy-Org/MiniMax-H3/resolve/main"
def download_model(category, filename):
target = model_dirs[category] / filename
expected_size = FILE_SIZES[filename]
if target.exists() and target.stat().st_size == expected_size:
print(f"Already exists: {target.name}")
return
url = f"{HF_BASE}/{category}/{filename}?download=true"
print(f"-= Downloading {filename} =-")
run(["wget", "-c", "--show-progress", url, "-O", str(target)])
actual_size = target.stat().st_size
if actual_size != expected_size:
raise RuntimeError(
f"ダウンロード後のサイズが一致しません: {filename} "
f"(expected={expected_size}, actual={actual_size})"
)
for download in downloads:
download_model(*download)
#@markdown ---
#@markdown ## 公式ワークフロー
#@markdown 選択した用途の ComfyUI 公式テンプレートを `user/default/workflows` へ保存します。
workflow_dir = COMFY_ROOT / "user" / "default" / "workflows"
workflow_dir.mkdir(parents=True, exist_ok=True)
workflow_names = []
if use_fl2va:
workflow_names.extend(["video_minimax_h3_t2v.json", "video_minimax_h3_i2v.json"])
if use_ref2va:
workflow_names.append("video_minimax_h3_r2v.json")
WORKFLOW_BASE = (
"https://raw.githubusercontent.com/Comfy-Org/workflow_templates/"
"main/templates"
)
DEFAULT_FL2VA = "minimax_h3_fl2va_pruned_int8_convrot.safetensors"
DEFAULT_REF2VA = "minimax_h3_ref2va_pruned_int8_convrot.safetensors"
DEFAULT_TEXT_ENCODER = "qwen3vl_32b_minimax_h3_nvfp4_awq.safetensors"
for workflow_name in workflow_names:
print(f"-= Downloading official workflow: {workflow_name} =-")
workflow_url = f"{WORKFLOW_BASE}/{workflow_name}"
with urllib.request.urlopen(workflow_url) as response:
workflow_text = response.read().decode("utf-8")
workflow_text = workflow_text.replace(DEFAULT_FL2VA, profile["fl2va"])
workflow_text = workflow_text.replace(DEFAULT_REF2VA, profile["ref2va"])
workflow_text = workflow_text.replace(DEFAULT_TEXT_ENCODER, profile["text_encoder"])
# JSONとして壊れていないことを確認してから保存する。
json.loads(workflow_text)
workflow_path = workflow_dir / workflow_name
workflow_path.write_text(workflow_text, encoding="utf-8")
print(f"Saved: {workflow_path}")
#@markdown ---
#@markdown ## ComfyUI を起動
#@markdown Cloudflare の一時URLが表示されたらブラウザで開いてください。
#@markdown URLを知っている人はアクセスできるため、共有しないでください。
try:
import torch
if torch.cuda.is_available():
gpu_name = torch.cuda.get_device_name(0)
gpu_vram = torch.cuda.get_device_properties(0).total_memory / 1024**3
print(f"GPU: {gpu_name} ({gpu_vram:.1f} GiB VRAM)")
if gpu_vram < 40:
print(
"警告: 40 GiB未満のVRAMでは大規模オフロードが必要になり、"
"OOMまたは極端な低速化が起きる可能性があります。"
)
else:
print("警告: CUDA GPUが見つかりません。H3の実行は現実的ではありません。")
except Exception as exc:
print(f"GPU情報を取得できませんでした: {exc}")
machine = platform.machine().lower()
cloudflared_arch = "arm64" if machine in ("aarch64", "arm64") else "amd64"
cloudflared_path = Path("/content/cloudflared")
if not cloudflared_path.exists():
cloudflared_url = (
"https://github.com/cloudflare/cloudflared/releases/latest/download/"
f"cloudflared-linux-{cloudflared_arch}"
)
run(["wget", "-q", cloudflared_url, "-O", str(cloudflared_path)])
cloudflared_path.chmod(0o755)
def wait_for_comfyui_and_start_tunnel(port):
while True:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
if sock.connect_ex(("127.0.0.1", port)) == 0:
break
time.sleep(0.5)
print("\nComfyUI started. Creating a temporary Cloudflare URL...\n")
process = subprocess.Popen(
[str(cloudflared_path), "tunnel", "--url", f"http://127.0.0.1:{port}"],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
bufsize=1,
)
for line in process.stdout:
if "trycloudflare.com" in line:
start = line.find("https://")
print("ComfyUI URL:", line[start:].strip() if start >= 0 else line.strip())
port = 8188
threading.Thread(
target=wait_for_comfyui_and_start_tunnel,
args=(port,),
daemon=True,
).start()
os.chdir(COMFY_ROOT)
run([
sys.executable,
"main.py",
"--port",
str(port),
"--dont-print-server",
"--preview-method",
"none",
])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment