Files
rookiestar28-ComfyUI-Sigmax/scripts/run_minimax_h3_diffusers_parity.py
rookiestar28 5939330cda feat(h3): add MiniMax H3 schedule and parity lanes
Implement explicit Base FL2VA and Ref2VA profiles, sigma node and native workflow contracts; add Diffusers and ComfyUI-native parity runners, golden evidence, CI jobs, compatibility contracts, and package/release updates.

Progress: tracked index and reviewed .secrets.baseline reconciled across 56 public paths; performance matrix regenerated; local Diffusers and native ComfyUI H3 parity PASS; wheel audit PASS (102 files, zero mandatory runtime dependencies). Linux full gate passed pre-pytest stages and reached 2603 passed, 3 skipped, 2 failed.

Blockers: the remaining pytest failures are stale native-Windows receipt/matrix fingerprints; this WSL environment cannot execute powershell.exe, cmd.exe, wsl.exe, or PE32+ Windows Python. Hosted Actions evidence is unavailable because GH_TOKEN is invalid. Canonical pinned H1/H2 was not launched because reference/ComfyUI requires comfy-aimdo==0.4.11 while the selected executable environment has 0.4.10.

Handoff: regenerate tests/compatibility/fixtures/windows_py313_v1.json natively, regenerate/check the derived compatibility matrix, rerun the Linux full gate, publish the candidate through authenticated hosted CI, then run scripts/run_comfyui_e2e.py --minimax-h3-only against ComfyUI 14b05228 with comfy-aimdo==0.4.11. Keep M6-05 IN_PROGRESS until all gates pass. Detailed local-only records remain in ignored .planning/ and reference/; no weights or credentials are included.
2026-08-05 14:11:20 +08:00

155 lines
5.9 KiB
Python

"""Run isolated MiniMax H3 Diffusers parity against the pinned unreleased branch."""
from __future__ import annotations
import argparse
import importlib
import importlib.metadata
import json
import sys
from collections.abc import Sequence
from pathlib import Path
from typing import Any, cast
from scripts.parity.minimax_h3_diffusers_report import (
NUMPY_VERSION,
REQUIRED_GRID_POINTS,
TORCH_VERSION,
build_parity_report,
canonical_json,
)
from scripts.parity.minimax_h3_official import DIFFUSERS_REVISION
def _require_distribution_version(distribution: str, expected: str) -> str:
try:
actual = importlib.metadata.version(distribution)
except importlib.metadata.PackageNotFoundError as error:
raise RuntimeError(
f"missing parity dependency {distribution}=={expected}; "
"install requirements/parity-minimax-h3-diffusers.txt in an isolated environment"
) from error
cpu_build = distribution == "torch" and actual == f"{expected}+cpu"
if actual != expected and not cpu_build:
raise RuntimeError(f"parity dependency {distribution} must be {expected}, found {actual}")
return expected
def _require_diffusers_revision() -> str:
"""Require a VCS-installed Diffusers distribution at the frozen H3 commit."""
try:
distribution = importlib.metadata.distribution("diffusers")
except importlib.metadata.PackageNotFoundError as error:
raise RuntimeError(
"Diffusers is not installed in the isolated parity environment"
) from error
raw_direct_url = distribution.read_text("direct_url.json")
if not raw_direct_url:
raise RuntimeError("Diffusers direct_url.json is missing; a VCS commit cannot be proven")
try:
direct_url = json.loads(raw_direct_url)
except json.JSONDecodeError as error:
raise RuntimeError("Diffusers direct_url.json is malformed") from error
vcs_info = direct_url.get("vcs_info")
if not isinstance(vcs_info, dict) or vcs_info.get("commit_id") != DIFFUSERS_REVISION:
raise RuntimeError("Diffusers is not installed from the pinned MiniMax H3 commit")
return DIFFUSERS_REVISION
def _tensor_values(tensor: object) -> tuple[float, ...]:
try:
detached = tensor.detach() # type: ignore[attr-defined]
cpu = detached.cpu()
values = cpu.tolist()
except (AttributeError, TypeError, ValueError, RuntimeError) as error:
raise RuntimeError("Diffusers parity tensor cannot be read as CPU values") from error
if not isinstance(values, list):
raise RuntimeError("Diffusers parity tensor must flatten to a list")
return tuple(float(value) for value in values)
def _load_diffusers_cases() -> tuple[dict[int, dict[str, tuple[float, ...]]], dict[str, str]]:
diffusers_revision = _require_diffusers_revision()
versions = {
"device": "cpu",
"diffusers": importlib.metadata.version("diffusers"),
"diffusers_revision": diffusers_revision,
"numpy": _require_distribution_version("numpy", NUMPY_VERSION),
"python": f"{sys.version_info.major}.{sys.version_info.minor}",
"torch": _require_distribution_version("torch", TORCH_VERSION),
}
torch = importlib.import_module("torch")
scheduler_module = importlib.import_module("diffusers.schedulers.scheduling_minimax_h3")
scheduler_type = cast(Any, scheduler_module.MiniMaxH3Scheduler)
cases: dict[int, dict[str, tuple[float, ...]]] = {}
with torch.no_grad():
for points in REQUIRED_GRID_POINTS:
video_scheduler = scheduler_type(shift=12.0)
video_scheduler.set_timesteps(num_inference_steps=points, device="cpu")
audio_scheduler = scheduler_type(shift=3.0)
audio_scheduler.set_timesteps(num_inference_steps=points, device="cpu")
cases[points] = {
"audio": _tensor_values(audio_scheduler.sigmas),
"video": _tensor_values(video_scheduler.sigmas),
}
return cases, versions
def _load_velocity_probe() -> dict[str, object]:
torch = importlib.import_module("torch")
scheduler_module = importlib.import_module("diffusers.schedulers.scheduling_minimax_h3")
scheduler_type = cast(Any, scheduler_module.MiniMaxH3Scheduler)
scheduler = scheduler_type(shift=12.0)
scheduler.set_timesteps(num_inference_steps=4, device="cpu")
sample = torch.tensor((0.25, -0.5), dtype=torch.float32, device="cpu")
velocity = torch.tensor((0.125, -0.25), dtype=torch.float32, device="cpu")
timestep = scheduler.timesteps[0]
with torch.no_grad():
result = scheduler.step(velocity, timestep, sample)
return {
"reference": _tensor_values(result.prev_sample),
"sample": _tensor_values(sample),
"sigma": float(scheduler.sigmas[0].item()),
"sigma_next": float(scheduler.sigmas[1].item()),
"timestep": float(timestep.item()),
"velocity": _tensor_values(velocity),
}
def main(arguments: Sequence[str] | None = None) -> int:
"""Generate a complete report or fail without creating partial evidence."""
parser = argparse.ArgumentParser()
parser.add_argument("--output", type=Path, required=True)
parsed = parser.parse_args(arguments)
try:
cases, environment = _load_diffusers_cases()
report = build_parity_report(
cases,
velocity_reference=_load_velocity_probe(),
environment=environment,
)
encoded = canonical_json(report)
except (
AttributeError,
ImportError,
KeyError,
ModuleNotFoundError,
RuntimeError,
TypeError,
ValueError,
) as error:
print(f"PARITY=FAIL\n{error}", file=sys.stderr)
return 2
parsed.output.parent.mkdir(parents=True, exist_ok=True)
parsed.output.write_text(encoded, encoding="utf-8", newline="\n")
print(f"PARITY=PASS\nREPORT={parsed.output}")
return 0
if __name__ == "__main__":
raise SystemExit(main())