modelbeast/server/operators/hunyuan3d_mlx/run.py
m3ultra 573afa7b7a hunyuan3d_mlx operator (local MLX image→3D) + cluster docs
- server/operators/hunyuan3d_mlx: native-MLX Hunyuan3D 2.1, both stages, PBR.
  Verified end-to-end on M3 Ultra: 260s total, 40k-face GLB w/ 2048² PBR.
  Weights public (no HF login). Runs on M1 (MLX-native).
- scripts/install_hunyuan3d_mlx.sh: uv py3.11 venv + MLX-path deps.
- CLUSTER.md: local-first policy + M3/M1/M4 fleet roles + central-queue howto.
- BENCHMARKS/README/AGENTS: hunyuan3d_mlx rows + trellis head-to-head.
2026-07-16 10:10:43 +10:00

72 lines
2.4 KiB
Python

import argparse
import json
import os
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[3]
VENDOR = ROOT / "vendor" / "hunyuan3d-mlx"
ap = argparse.ArgumentParser()
ap.add_argument("--input", action="append", default=[])
ap.add_argument("--outdir", required=True)
ap.add_argument("--params", default="{}")
a = ap.parse_args()
p = json.loads(a.params)
if not a.input:
print("ERROR: no input image")
sys.exit(1)
if not (VENDOR / "generate_e2e.py").exists():
print(f"ERROR: hunyuan3d-mlx not installed at {VENDOR}. Run scripts/install_hunyuan3d_mlx.sh")
sys.exit(1)
outdir = Path(a.outdir)
out_stem = outdir / f"hunyuan3d_{Path(a.input[0]).stem}"
cmd = [
sys.executable, "generate_e2e.py", str(Path(a.input[0]).resolve()),
"--output", str(out_stem.resolve()),
"--steps", str(p.get("steps", 50)),
"--guidance", str(p.get("guidance_scale", 7.5)),
"--octree-resolution", str(p.get("octree_resolution", 256)),
"--texture-size", str(p.get("texture_size", 2048)),
"--max-num-view", str(p.get("max_num_view", 6)),
"--seed", str(p.get("seed", 42)),
]
if p.get("no_texture"):
cmd.append("--no-texture")
env = os.environ.copy()
# xet chunked downloader fails on some HF repos — force the reliable HTTP path
# (same fix as trellis_mac / flux_local).
env["HF_HUB_DISABLE_XET"] = "1"
# guard the torch + compiled-kernel OpenMP collision like sf3d / trellis_mac.
env["KMP_DUPLICATE_LIB_OK"] = "TRUE"
env.setdefault("OMP_NUM_THREADS", "1")
env.setdefault("MKL_NUM_THREADS", "1")
print("+", " ".join(cmd), flush=True)
print("(first run downloads ~15GB MLX weights: dgrauet/hunyuan3d-2.1-mlx)", flush=True)
res = subprocess.run(cmd, cwd=str(VENDOR), stdout=sys.stdout, stderr=subprocess.STDOUT, env=env)
if res.returncode != 0:
sys.exit(res.returncode)
# generate_e2e.py writes <stem>.glb (textured, or geometry if --no-texture) and
# optionally <stem>_shape.glb. Prefer the primary <stem>.glb.
final = out_stem.with_suffix(".glb")
if final.exists():
chosen = final
else:
glbs = sorted(outdir.rglob("*.glb"))
if not glbs:
print("ERROR: hunyuan3d_mlx produced no .glb")
sys.exit(1)
chosen = glbs[0]
outputs = [{"path": str(chosen), "name": f"hunyuan3d_{Path(a.input[0]).stem}.glb",
"meta": {"tool": "hunyuan3d_mlx"}}]
(outdir / "result.json").write_text(json.dumps({"outputs": outputs}))
print("done:", chosen, flush=True)