Files
Ethanfel 011c915fa7 release: ship H3 correctness fixes and research tools in 1.8.5
Publish the existing H3 merge/export and autotuner correctness fixes, offline character and AV calibration tools, provenance audits, and regression coverage. Add a dictionary-preserving storage hook for offline mapped TIES exports.

Keep scoring weights and defaults unchanged, retain NP/CT as opt-in, and exclude the rejected F08 search-admission prototype. Validate the exact staged tree: 1052 Python tests, 69 subtests, 3 JS tests, and real ComfyUI CPU export round trips pass; 3 CUDA-only tests skipped.
2026-09-09 12:10:02 +02:00

100 lines
5.1 KiB
Python

"""Reconstruct Synchformer model inputs from original clips without GPU inference."""
from __future__ import annotations
import argparse
import hashlib
from pathlib import Path
try:
from .h3_synchformer import (CFG, CKPT, POLICY, REPO, ROOT, audio_control, control_gates,
decode_audio, decode_video, digest, isolate, now, read_json, save_new, tensor_record)
except ImportError:
from h3_synchformer import (CFG, CKPT, POLICY, REPO, ROOT, audio_control, control_gates,
decode_audio, decode_video, digest, isolate, now, read_json, save_new, tensor_record)
def audit(name):
import numpy as np
import torch
import torchaudio
if not name or Path(name).name != name or name in (".", ".."):
raise ValueError("Simple run name required")
isolate()
torch.set_num_threads(4)
run = ROOT / "runs" / name
start, complete = read_json(run / "start.json"), read_json(run / "complete.json")
policy = read_json(POLICY)
manifest_path = REPO / policy["manifest"]
manifest = read_json(manifest_path)
pins = {str(POLICY): start["policy_sha256"], str(manifest_path): start["manifest_sha256"],
str(ROOT / "setup-receipt.json"): start["receipt_sha256"],
str(REPO / "scripts/h3_synchformer.py"): start["runner_sha256"],
str(REPO / "scripts/h3_local_av_evaluator.py"): start["audio_helper_sha256"]}
for path, sha in pins.items():
if digest(path) != sha:
raise ValueError(f"Changed pinned artifact: {path}")
receipt = read_json(ROOT / "setup-receipt.json")
pins.update({str(CFG): receipt["config_sha256"], str(CKPT): receipt["checkpoint_sha256"]})
for path in (CFG, CKPT):
if digest(path) != pins[str(path)]:
raise ValueError("Changed model asset")
mel = torchaudio.transforms.MelSpectrogram(sample_rate=16000, win_length=400,
hop_length=160, n_fft=1024, n_mels=128)
by_case = {c["case_id"]: c for c in manifest["cases"]}
reconstructed = {}
rows = []
for case_id, condition, repeat in start["jobs"]:
case = by_case[case_id]
if case_id not in reconstructed:
if digest(case["path"]) != case["sha256"]:
raise ValueError("Original media changed")
pins[case["path"]] = case["sha256"]
video, vi = decode_video(case["path"])
if vi["source_frame_indices"] != [round(i * 24 / 25) for i in range(125)]:
raise ValueError("PTS mapping does not match independently calculated 24-to-25-fps mapping")
audio, ai = decode_audio(case["path"])
reconstructed[case_id] = video, vi, audio, ai
video, vi, audio, ai = reconstructed[case_id]
# Reconstruct from declared ranges, without invoking Synchformer's Compose.
controlled = audio_control(audio, condition)
v = torch.stack([video[2 + i * 8:18 + i * 8, :, 16:240, 101:325] for i in range(14)])
v = ((v.half().div(255.) - .5) / .5).unsqueeze(0)
a = torch.from_numpy(np.stack([controlled[1280 + i * 5120:11520 + i * 5120] for i in range(14)]))
a = torch.log(mel(a) + 1e-6)
if a.shape != (14, 128, 65):
raise ValueError("Unexpected raw spectrogram shape")
a = torch.nn.functional.pad(a, (0, 1), value=0.)
a = ((a + 4.2677393) / (2 * 4.5689974)).unsqueeze(0).unsqueeze(2)
stem = f"{case_id}-{condition}-r{repeat}"
path, ipath = run / (stem + ".json"), run / (stem + ".input.json")
row, before = read_json(path), read_json(ipath)
if any(row[k] != value for k, value in before.items()):
raise ValueError("Input record altered after inference")
info = row["input"]
if (info["video_tensor"] != tensor_record(v) or info["audio_tensor"] != tensor_record(a)
or info["waveform_sha256"] != hashlib.sha256(controlled.tobytes()).hexdigest()
or info["audio_peak"] != float(np.max(np.abs(controlled)))
or info["video"] != vi or info["audio"] != ai
or info["segment_video_ranges"] != [[2 + i * 8, 18 + i * 8] for i in range(14)]
or info["segment_audio_ranges"] != [[1280 + i * 5120, 11520 + i * 5120] for i in range(14)]):
raise ValueError(f"Reconstructed model input mismatch: {stem}")
pins[str(path)] = digest(path)
pins[str(ipath)] = digest(ipath)
rows.append(row)
gates = control_gates(rows)
if gates != complete["gates"] or len(rows) != complete["cases"]:
raise ValueError("Completion summary does not match raw evidence")
for path in (run / "start.json", run / "complete.json", Path(__file__).resolve()):
pins[str(path)] = digest(path)
report = {"completed_at_utc": now(), "cases": len(rows), "gates": gates,
"independently_reconstructed_model_inputs": True, "gpu_inference_rerun": False,
"original_media_unchanged": True, "artifact_sha256": pins}
save_new(run / "control-audit.json", report)
print({k: v for k, v in report.items() if k != "artifact_sha256"})
if __name__ == "__main__":
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("run_name")
audit(parser.parse_args().run_name)