diff --git a/src/hyperloom/agents/framework/repo_map.py b/src/hyperloom/agents/framework/repo_map.py index ab3f7f1c5e..c2c31bc605 100644 --- a/src/hyperloom/agents/framework/repo_map.py +++ b/src/hyperloom/agents/framework/repo_map.py @@ -15,6 +15,7 @@ "vllm": "https://github.com/ROCm/vllm.git", "atom": "https://github.com/ROCm/ATOM.git", "xdit": "https://github.com/xdit-project/xDiT.git", + "worldmirror": "https://github.com/Tencent-Hunyuan/HY-World-2.0.git", } diff --git a/src/hyperloom/inference_optimizer/assets/configs/baseline_worldmirror.yaml b/src/hyperloom/inference_optimizer/assets/configs/baseline_worldmirror.yaml new file mode 100644 index 0000000000..70ae4450fc --- /dev/null +++ b/src/hyperloom/inference_optimizer/assets/configs/baseline_worldmirror.yaml @@ -0,0 +1,88 @@ +# Magpie baseline benchmark config for WorldMirror (HunyuanWorld-Mirror). +# Resolved by default_baseline_config() / _default_profile_config() when +# $FRAMEWORK=worldmirror. +# +# WorldMirror is a SCRIPTABLE (server-less) framework, like xDiT: there is no +# OpenAI server, no benchmark_serving.py client, and no server_lifecycle reuse. +# It is a ~1.2B feed-forward 3D reconstruction model (ViT backbone + DPT/GS +# heads) run as a single forward pass per scene. The wrapper +# worldmirror_{runner_type}.sh runs the benchmark once per variant, parses e2e +# latency, runs the quality gate (per-head relative-L1 on depth/pts3d/normals vs +# a fixed BF16 reference), and writes benchmark_report.json directly. Throughput +# is recon/s. +# +# Two fields are FALLBACK ONLY and overridden at run time by the executor: +# - `benchmark.model` <- --model / $MODEL_PATH (the HY-World checkout) +# - `benchmark.runner_type` <- --gpu-type / $GPU_TYPE / rocm-smi probe +# At materialize time Hyperloom pins benchmark_script to +# worldmirror_{runner_type}.sh, so it is intentionally NOT set here. +# +# The quality-gate reference paths (XDIT_QUALITY_REF for compare / +# XDIT_QUALITY_REF_WRITE for the baseline that establishes the reference) are +# injected by the shared scriptable choke point in _workload_envs (keyed on +# is_scriptable), so the XDIT_ prefix is reused here to match what Hyperloom +# injects. The pass/fail tolerance (WM_QUALITY_REL_MAX) and WorldMirror +# generation knobs use the WM_ prefix. + +benchmark: + framework: worldmirror + workload_kind: scriptable # drives server-less branching in the loop + # benchmark_script: worldmirror_mi355x.sh # uncomment to force a script + model: /path/to/HY-World-2.0 # OVERRIDDEN at runtime; see header. + precision: bf16 # LOCKED for a worldmirror session + run_mode: local + + envs: + # Single-GPU forward pass by default. SP (sequence-parallel) + FSDP layouts + # for multi-GPU are A/B'd as discoverable optimizations, not the baseline. + TP: 1 + WM_TARGET_SIZE: 518 # input image resize (model default) + WM_NUM_ITERATIONS: 5 # timed iterations per scene (steady-state) + WM_WARMUP_CALLS: 3 + WM_SCENES: "" # empty = sweep all bundled examples/ scenes + WM_COND_FLAGS: "0 0 0" # [depth, rays, camera] conditioning + # torch.compile knob (explorable). 0=eager (baseline). The optimizer can A/B + # WM_USE_TORCH_COMPILE=1 with WM_TORCH_COMPILE_MODE in + # {default, reduce-overhead, max-autotune} to attack small-GEMM/conv launch + # overhead via inductor fusion + CUDA graphs. reduce-overhead uses CUDA + # graphs (best for the ~34% host-sync/launch idle seen in roofline). + WM_USE_TORCH_COMPILE: 0 + WM_TORCH_COMPILE_MODE: default + # torch.compile dynamic-shape knob (explorable). {0=off/per-shape static + # (default, WORKS), auto, 1=on}. NOTE: on this model dynamic=auto/1 currently + # hits a TorchInductor/Triton codegen bug ("KeyError: (float32, float64)") on + # the second shape, so per-shape static (0) is the working path. With the + # rope.py fix, static compile gives ~-25 to -38% per scene (mode=default). + # Each of the ~10-15 distinct scene shapes compiles once (warmup-absorbed). + WM_TORCH_COMPILE_DYNAMIC: 0 + # aiter flash-attention knob (explorable). 0=native torch SDPA (baseline). + # WM_USE_AITER_ATTN=1 routes attention through AMD aiter's tuned MI355X + # (gfx950) flash-attention kernels; the model backbone attention is + # head_dim=64, non-causal, no mask -> aiter-friendly. Attacks the SDPA slice + # (~6.7% of compute). Falls back to native SDPA on any unsupported case. + WM_USE_AITER_ATTN: 0 + # Geometry-quality gate. The wrapper writes quality_gate.{passed,lpips,ssim,mse}; + # _accuracy_gate maps passed->1.0. Gate keys on per-head relative-L1 (scale- + # invariant across depth/pts3d/normals); MSE is reported as a diagnostic only. + XDIT_QUALITY_REF: "" # reference output dir; empty = gate skipped + WM_QUALITY_REL_MAX: 0.2 # max relative-L1 per head (2%); all heads must pass + # Optional per-head overrides, e.g. WM_QUALITY_REL_MAX_NORMALS: 0.01 + # /opt/rocm/bin is REQUIRED so AITER's runtime JIT can find `hipcc`. + PATH: "/venv/bin:/opt/venv/bin:/opt/rocm/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + # ROCR_VISIBLE_DEVICES is injected at runtime by the executor as 0..TP-1. + + gpu_selection: + auto: false # executor pins via ROCR_VISIBLE_DEVICES + + server_lifecycle: + enabled: false # server-less: no reuse + + timeout_seconds: 7200 # 120 min hard cap (load + warmup + iters) + + profiler: + torch_profiler: + enabled: false # baseline needs throughput + quality only + system_profiler: + enabled: false + tracelens: + enabled: false diff --git a/src/hyperloom/inference_optimizer/assets/configs/profile_worldmirror.yaml b/src/hyperloom/inference_optimizer/assets/configs/profile_worldmirror.yaml new file mode 100644 index 0000000000..49650e158e --- /dev/null +++ b/src/hyperloom/inference_optimizer/assets/configs/profile_worldmirror.yaml @@ -0,0 +1,61 @@ +# Magpie profile benchmark config for WorldMirror (HunyuanWorld-Mirror). +# Same contract as baseline_worldmirror.yaml but with torch_profiler enabled so +# the loop can attribute time across the ViT backbone GEMMs, the GS renderer, +# and the DPT/normal heads. Resolved by `_default_profile_config()` when +# $FRAMEWORK=worldmirror. +# +# WorldMirror is SCRIPTABLE (server-less), like xDiT: no OpenAI server, no +# benchmark_serving.py client, no server_lifecycle reuse. The wrapper +# worldmirror_{runner_type}.sh runs one forward pass per scene under the +# torch profiler and writes benchmark_report.json + the trace directly. +# +# Fallback-only fields (overridden at runtime by the executor): +# - `benchmark.model` <- --model / $MODEL_PATH (the HY-World checkout) +# - `benchmark.runner_type` <- --gpu-type / $GPU_TYPE / rocm-smi probe +# At materialize time Hyperloom pins benchmark_script to +# worldmirror_{runner_type}.sh, so it is intentionally NOT set here. + +benchmark: + framework: worldmirror + workload_kind: scriptable # drives server-less branching in the loop + # benchmark_script: worldmirror_mi355x.sh # uncomment to force a script + model: /path/to/HY-World-2.0 # OVERRIDDEN at runtime; see header. + precision: bf16 # LOCKED for a worldmirror session + run_mode: local + + envs: + TP: 1 + WM_TARGET_SIZE: 518 # input image resize (model default) + WM_NUM_ITERATIONS: 2 # fewer timed iters under the profiler + WM_WARMUP_CALLS: 3 + WM_SCENES: "" # empty = sweep all bundled examples/ scenes + WM_COND_FLAGS: "0 0 0" # [depth, rays, camera] conditioning + # torch.compile knob (explorable; see baseline_worldmirror.yaml). Profiling + # runs eager by default so the trace shows real per-kernel time; the loop + # flips WM_USE_TORCH_COMPILE=1 on the variants it wants to profile compiled. + WM_USE_TORCH_COMPILE: 0 + WM_TORCH_COMPILE_MODE: default + # Geometry-quality gate (same semantics as baseline_worldmirror.yaml). + XDIT_QUALITY_REF: "" # reference output dir; empty = gate skipped + WM_QUALITY_REL_MAX: 0.2 # max relative-L1 per head; all heads must pass + # PROFILE=1 makes worldmirror_{runner_type}.sh emit a torch trace into + # $VLLM_TORCH_PROFILER_DIR (injected by the profile executor). + PROFILE: 1 + # /opt/rocm/bin is REQUIRED so AITER's runtime JIT can find `hipcc`. + PATH: "/venv/bin:/opt/venv/bin:/opt/rocm/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + + gpu_selection: + auto: false # executor pins via ROCR_VISIBLE_DEVICES + + server_lifecycle: + enabled: false # server-less: no reuse + + timeout_seconds: 7200 # 120 min hard cap (load + warmup + iters) + + profiler: + torch_profiler: + enabled: true # collect *.pt.trace.json.gz for TraceLens + system_profiler: + enabled: false + tracelens: + enabled: true diff --git a/src/hyperloom/inference_optimizer/framework_registry.py b/src/hyperloom/inference_optimizer/framework_registry.py index c3515e6057..09bfa793f4 100644 --- a/src/hyperloom/inference_optimizer/framework_registry.py +++ b/src/hyperloom/inference_optimizer/framework_registry.py @@ -103,6 +103,21 @@ class FrameworkSpec: supports_server_reuse=False, throughput_unit="img/s", ), + # WorldMirror (HunyuanWorld-Mirror): a ~1.2B feed-forward 3D reconstruction + # model (ViT backbone + DPT/GS heads). It runs a single forward pass per + # scene, so it is a SCRIPTABLE (server-less) workload like xDiT: the wrapper + # worldmirror_{runner_type}.sh runs the benchmark once per variant, parses + # e2el latency, runs a quality gate (LPIPS/SSIM/MSE vs a BF16 reference), and + # writes benchmark_report.json. primary_metric_name() -> "e2el_mean_ms" is + # derived automatically from kind=SCRIPTABLE. + "worldmirror": FrameworkSpec( + name="worldmirror", + kind=SCRIPTABLE, + extra_args_env="EXTRA_WORLDMIRROR_ARGS", + repo_url="https://github.com/Tencent-Hunyuan/HY-World-2.0.git", + supports_server_reuse=False, + throughput_unit="recon/s", + ), } DEFAULT_FRAMEWORK = "sglang" diff --git a/src/hyperloom/orchestrator/actions/executors/_workload_envs.py b/src/hyperloom/orchestrator/actions/executors/_workload_envs.py index 4e44afb1a3..b2c16fb332 100644 --- a/src/hyperloom/orchestrator/actions/executors/_workload_envs.py +++ b/src/hyperloom/orchestrator/actions/executors/_workload_envs.py @@ -231,6 +231,7 @@ def _coerce_workload_int_env(env_key: str, raw: str) -> int: "vllm": Path("assets/configs/baseline_vllm.yaml"), "xdit": Path("assets/configs/baseline_xdit.yaml"), "hunyuan_image3": Path("assets/configs/baseline_hunyuan_image3.yaml"), + "worldmirror": Path("assets/configs/baseline_worldmirror.yaml"), } _DEFAULT_BASELINE_CONFIG = Path("assets/configs/baseline_sglang.yaml") diff --git a/src/hyperloom/orchestrator/actions/executors/profile.py b/src/hyperloom/orchestrator/actions/executors/profile.py index 6249f20a25..4734258093 100644 --- a/src/hyperloom/orchestrator/actions/executors/profile.py +++ b/src/hyperloom/orchestrator/actions/executors/profile.py @@ -484,6 +484,8 @@ def _default_profile_config() -> Path: name = "profile_vllm.yaml" elif fw == "xdit": name = "profile_xdit.yaml" + elif fw == "worldmirror": + name = "profile_worldmirror.yaml" elif fw == "hunyuan_image3": name = "profile_hunyuan_image3.yaml" else: diff --git a/src/hyperloom/orchestrator/phases/framework.py b/src/hyperloom/orchestrator/phases/framework.py index 47193a6141..754e2ac3d0 100644 --- a/src/hyperloom/orchestrator/phases/framework.py +++ b/src/hyperloom/orchestrator/phases/framework.py @@ -2958,6 +2958,8 @@ def _framework_agent_discover_repo_urls(self, framework: str) -> list[str]: Returns: An order-preserving, deduped list of repo URLs to query. """ + from hyperloom.inference_optimizer import framework_registry + from ..framework import client as _fa_client from ..specialists.domains import PR_QUERY_REPOS @@ -2976,11 +2978,17 @@ def _add(u: str) -> None: # Primary: the framework's own repo. _add(_fa_client.repo_url_for_framework(framework)) - # Global allowlist (owner/name -> URL). - for repo in PR_QUERY_REPOS: - repo = str(repo or "").strip() - if repo and "/" in repo: - _add(f"https://github.com/{repo}.git") + # Global allowlist (owner/name -> URL). This is a serving/infra PR set + # (sglang/vllm/pytorch/...) meant for cross-porting between serving + # frameworks. Scriptable frameworks (e.g. worldmirror, xDiT) can never + # git-apply a serving diff, so querying it only surfaces irrelevant + # candidates that burn FRAMEWORK-phase time — scout the framework's own + # repo only. + if not framework_registry.is_scriptable(framework): + for repo in PR_QUERY_REPOS: + repo = str(repo or "").strip() + if repo and "/" in repo: + _add(f"https://github.com/{repo}.git") if not urls: # Last-ditch: let phase_discover resolve from framework itself.