Created
August 3, 2026 09:27
-
-
Save safa-dayo/057bcf801828e149f0d9077b63b7aa6b to your computer and use it in GitHub Desktop.
MiniMax H3をGoogle Colab上のComfyUIで試すためのColabノートブック
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # #@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