inferencekit-cli 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- inferencekit/__init__.py +9 -0
- inferencekit/__main__.py +5 -0
- inferencekit/analysis/__init__.py +1 -0
- inferencekit/analysis/ncu_csv.py +284 -0
- inferencekit/analysis/nsys_sqlite.py +518 -0
- inferencekit/canonical.py +104 -0
- inferencekit/checkpoint.py +396 -0
- inferencekit/cli.py +274 -0
- inferencekit/cuda/bw_peak.cu +252 -0
- inferencekit/cuda/torch_bw.py +60 -0
- inferencekit/engines/__init__.py +7 -0
- inferencekit/engines/adapters.py +190 -0
- inferencekit/engines/clip/__init__.py +13 -0
- inferencekit/engines/clip/comfyui.py +749 -0
- inferencekit/engines/clip/diffusers_ltx.py +601 -0
- inferencekit/engines/clip/noise.py +46 -0
- inferencekit/engines/clip/official_ltx.py +316 -0
- inferencekit/engines/clip/server.py +542 -0
- inferencekit/engines/clip/stepdump.py +182 -0
- inferencekit/engines/gates.py +94 -0
- inferencekit/engines/sglang_worker.py +86 -0
- inferencekit/engines/vllm_worker.py +129 -0
- inferencekit/evidence.py +427 -0
- inferencekit/gpuinfo.py +93 -0
- inferencekit/hardware.py +135 -0
- inferencekit/mcp_server.py +178 -0
- inferencekit/ops/__init__.py +30 -0
- inferencekit/ops/bench.py +184 -0
- inferencekit/ops/bench_audio.py +384 -0
- inferencekit/ops/bench_clip.py +819 -0
- inferencekit/ops/bench_image.py +393 -0
- inferencekit/ops/bench_rounds.py +297 -0
- inferencekit/ops/bench_serve.py +866 -0
- inferencekit/ops/ceiling.py +135 -0
- inferencekit/ops/compare.py +378 -0
- inferencekit/ops/correctness.py +153 -0
- inferencekit/ops/doctor.py +486 -0
- inferencekit/ops/gates.py +1772 -0
- inferencekit/ops/gates_clip.py +930 -0
- inferencekit/ops/intake.py +1321 -0
- inferencekit/ops/ledger.py +749 -0
- inferencekit/ops/manifest.py +309 -0
- inferencekit/ops/ncu.py +183 -0
- inferencekit/ops/nsys.py +224 -0
- inferencekit/ops/prompts.py +316 -0
- inferencekit/ops/quality.py +2161 -0
- inferencekit/ops/quality_clip.py +690 -0
- inferencekit/ops/quality_clip_steps.py +341 -0
- inferencekit/ops/report.py +317 -0
- inferencekit/ops/review.py +119 -0
- inferencekit/ops/rocprof.py +89 -0
- inferencekit/ops/roofline.py +392 -0
- inferencekit/ops/store.py +61 -0
- inferencekit/proc.py +172 -0
- inferencekit/prompts/ATTRIBUTION.md +56 -0
- inferencekit/prompts/__init__.py +167 -0
- inferencekit/prompts/build.py +518 -0
- inferencekit/prompts/dev-load.jsonl.gz +0 -0
- inferencekit/prompts/dev-load.manifest.json +212 -0
- inferencekit/prompts/dev.jsonl.gz +0 -0
- inferencekit/prompts/dev.manifest.json +214 -0
- inferencekit/prompts/exclusions.json +54 -0
- inferencekit/quality/__init__.py +23 -0
- inferencekit/quality/client.py +66 -0
- inferencekit/quality/data/gsm8k_LICENSE.txt +21 -0
- inferencekit/quality/fake.py +329 -0
- inferencekit/quality/gsm8k.py +224 -0
- inferencekit/quality/logprobs.py +106 -0
- inferencekit/quality/metrics.py +235 -0
- inferencekit/quality/niah.py +63 -0
- inferencekit/quality/parquet.py +296 -0
- inferencekit/quality/sampling.py +262 -0
- inferencekit/quality/step_replay.py +217 -0
- inferencekit/quality/video_metrics.py +304 -0
- inferencekit/registry.py +206 -0
- inferencekit/review/__init__.py +6 -0
- inferencekit/review/diff.py +163 -0
- inferencekit/review/media.py +181 -0
- inferencekit/review/page.py +920 -0
- inferencekit/review/rundir.py +844 -0
- inferencekit/roofline_headers.py +504 -0
- inferencekit/schemas/evidence.v1.json +130 -0
- inferencekit/schemas/evidence.v2.json +499 -0
- inferencekit/schemas/intake.v1.json +276 -0
- inferencekit/schemas/run-manifest.v1.json +118 -0
- inferencekit/serve/__init__.py +8 -0
- inferencekit/serve/audio_client.py +330 -0
- inferencekit/serve/client.py +446 -0
- inferencekit/serve/clip_client.py +351 -0
- inferencekit/serve/clip_rounds.py +429 -0
- inferencekit/serve/image_client.py +373 -0
- inferencekit/serve/loadgen.py +136 -0
- inferencekit/serve/metrics.py +203 -0
- inferencekit/serve/profiles.py +105 -0
- inferencekit/serve/prompts_io.py +125 -0
- inferencekit/serve/rounds.py +1111 -0
- inferencekit/serve/servers.py +897 -0
- inferencekit/stats.py +249 -0
- inferencekit_cli-0.1.0.dist-info/METADATA +233 -0
- inferencekit_cli-0.1.0.dist-info/RECORD +104 -0
- inferencekit_cli-0.1.0.dist-info/WHEEL +4 -0
- inferencekit_cli-0.1.0.dist-info/entry_points.txt +2 -0
- inferencekit_cli-0.1.0.dist-info/licenses/LICENSE +202 -0
- inferencekit_cli-0.1.0.dist-info/licenses/NOTICE +9 -0
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
"""quality clip-steps: is each denoising step of an engine's clip what the reference computes from the same latent?
|
|
2
|
+
|
|
3
|
+
The final-clip comparison (quality clip) needs dozens of requests on a video model, because tiny exact-kernel
|
|
4
|
+
differences grow over the steps (on LTX-2.5 an exact other attention kernel already moves a clip to ~25 dB
|
|
5
|
+
PSNR). This check removes the growth: the engine dumps every step of every clip it makes (the contract in
|
|
6
|
+
engines/clip/stepdump.py: x_t, the guided output its sampler took, x_{t-1}, the final latent, under
|
|
7
|
+
$CLIP_STEP_DUMP_DIR/<clip sha256>.safetensors), and the BF16 reference recomputes each step from the engine's
|
|
8
|
+
own x_t (teacher forcing; its own text conditioning from the prompt). So a step's error is that step's alone.
|
|
9
|
+
|
|
10
|
+
1. After the clips are made, ``--requests`` of the engine's clips (default 2; bench clip outputs with their
|
|
11
|
+
request bodies) are drawn with ``--audit-seed`` (drawn and recorded when not given). Every step is audited:
|
|
12
|
+
a cache or a skipped guidance pass on any step shows there.
|
|
13
|
+
2. The replay worker (quality/step_replay.py) runs in the reference pipeline's Python (``--python``) and loads
|
|
14
|
+
the reference once (``--pipeline``, ``--pipeline-model``, ``--pipeline-arg``: the reference's launch
|
|
15
|
+
arguments, as the clip server takes them). Per step and latent stream: e = |out_eng - out_ref| / |out_ref|
|
|
16
|
+
and the cosine; then the floor f, the same replay with ``--alt-attention`` (an exact but different
|
|
17
|
+
kernel) on the same inputs: f = |out_alt - out_ref| / |out_ref|.
|
|
18
|
+
3. PASS needs, on every audited request: e <= factor * f + slack at every step and stream (``--factor``
|
|
19
|
+
2, ``--slack`` calibrated on the GPU, below), and every guard:
|
|
20
|
+
dump present for the clip, named by its sha256, its request equal to bench clip's request body
|
|
21
|
+
layout streams, shapes and keys of the request's latents; N steps = the steps asked
|
|
22
|
+
schedule the dump's sigmas equal the reference's (|d| <= ``--sigma-tol``)
|
|
23
|
+
dtypes each dumped tensor has the reference's dtype for it (no lower-precision latents or outputs)
|
|
24
|
+
chain x_{t-1} = the reference scheduler's update of (x_t, out_eng) within ``--chain-tol`` (relative
|
|
25
|
+
L2), each step's x_{t-1} is the next step's x_t and the final latent the last x_{t-1}
|
|
26
|
+
(exactly), and the first x_t is the reference's own initial noise for the request
|
|
27
|
+
decode the reference's decoder makes, from the dump's final latent, the clip that was delivered: same
|
|
28
|
+
frame count and size, mean PSNR >= ``--decode-psnr-db``
|
|
29
|
+
A dump that fails a guard is not judged step by step (FAIL). An alternative kernel identical to the
|
|
30
|
+
reference's (f = 0 everywhere) cannot calibrate the ratio: UNPROVEN unless the engine is exact (e = 0).
|
|
31
|
+
The final-clip PSNR of quality clip stays a loose gross-failure gate beside this one.
|
|
32
|
+
|
|
33
|
+
Calibration (one RTX PRO 6000, LTX-2.5 t2v 768x512x121 with audio, 30 steps, 2 audited clips, 16 Oct 2026):
|
|
34
|
+
* floor (the reference's flash SDPA vs ``_native_efficient`` on the same x_t): 1.1e-2 to 1.5e-1 per step and
|
|
35
|
+
stream, rising towards the last step [measured]; the guidance combination (CFG 3, STG 1, modality 3, rescale
|
|
36
|
+
0.7) amplifies each pass's kernel noise.
|
|
37
|
+
* an exact engine with another kernel (``_native_cudnn``): e / floor 0.81-1.12 over 120 step outputs, PASS
|
|
38
|
+
[measured]. The slack 1e-3 adds at most 5% to the smallest limit (2 x 1.08e-2) [derived].
|
|
39
|
+
* decode check: honest clips decode at 35.1-39.3 dB against the delivered mp4 (H.264 loss) [measured]; clips
|
|
40
|
+
of another exact kernel's trajectory sit at ~25 dB [measured earlier, quality clip floor]: minimum 30 dB.
|
|
41
|
+
* one reused step (step 15 takes step 14's output): FAIL, e / floor 2.8 and 4.1 [measured]. STG dropped on the
|
|
42
|
+
last 3 steps: NOT caught at factor 2 (e / floor at most 1.65: the STG term there is ~9% of the output, the
|
|
43
|
+
floor 6-15%) [measured].
|
|
44
|
+
* time: about 8 min 15 s for 2 audited clips, model load 26 s included [measured].
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
from __future__ import annotations
|
|
48
|
+
|
|
49
|
+
import json
|
|
50
|
+
import os
|
|
51
|
+
import random
|
|
52
|
+
import secrets
|
|
53
|
+
import sys
|
|
54
|
+
from pathlib import Path
|
|
55
|
+
|
|
56
|
+
import inferencekit
|
|
57
|
+
from inferencekit import evidence as ev
|
|
58
|
+
from inferencekit.canonical import file_sha256
|
|
59
|
+
from inferencekit.ops.quality import _kind
|
|
60
|
+
from inferencekit.ops.quality_clip import clip_files, index_mismatch, requests_of
|
|
61
|
+
from inferencekit.proc import run
|
|
62
|
+
from inferencekit.registry import Arg, OpError, operation
|
|
63
|
+
|
|
64
|
+
WORKER = Path(__file__).resolve().parents[1] / "quality" / "step_replay.py"
|
|
65
|
+
MARK = "INFERKIT_RESULT "
|
|
66
|
+
FORMAT = "inferencekit.quality.clip_steps.v1"
|
|
67
|
+
SAFETENSORS_DTYPES = {"f64": "float64", "f32": "float32", "bf16": "bfloat16", "f16": "float16"}
|
|
68
|
+
DEFAULT_FACTOR = 2.0
|
|
69
|
+
DEFAULT_SLACK = 1e-3
|
|
70
|
+
DEFAULT_CHAIN_TOL = 1e-5
|
|
71
|
+
DEFAULT_SIGMA_TOL = 1e-4
|
|
72
|
+
DEFAULT_DECODE_PSNR_DB = 30.0
|
|
73
|
+
DEFAULTS_SOURCE = ("RTX PRO 6000, LTX-2.5 t2v 768x512x121, 30 steps, 16 Oct 2026: per-step floor 1.1e-2 to 1.5e-1, "
|
|
74
|
+
"exact cuDNN engine max e/floor 1.12, honest decode 35.1-39.3 dB, alt-kernel clips ~25 dB")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _pipeline_args(items) -> dict:
|
|
78
|
+
out = {}
|
|
79
|
+
for text in items or []:
|
|
80
|
+
key, sep, value = str(text).partition("=")
|
|
81
|
+
if not sep or not key:
|
|
82
|
+
raise OpError(f"--pipeline-arg must be KEY=VALUE, got {text!r}")
|
|
83
|
+
try:
|
|
84
|
+
out[key] = json.loads(value)
|
|
85
|
+
except ValueError:
|
|
86
|
+
out[key] = value
|
|
87
|
+
return out
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _shim(staging: Path) -> Path:
|
|
91
|
+
"""A directory holding only the inferencekit package (a link), for the worker's PYTHONPATH: the harness's
|
|
92
|
+
site-packages must not shadow the pipeline venv's packages."""
|
|
93
|
+
shim = staging / "shim"
|
|
94
|
+
shim.mkdir(parents=True, exist_ok=True)
|
|
95
|
+
link = shim / "inferencekit"
|
|
96
|
+
if not link.exists():
|
|
97
|
+
link.symlink_to(Path(inferencekit.__file__).resolve().parent, target_is_directory=True)
|
|
98
|
+
return shim
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def run_replay(python: str | None, cfg: dict, staging: Path, timeout_s: float) -> dict:
|
|
102
|
+
work = staging / "worker"
|
|
103
|
+
work.mkdir(parents=True, exist_ok=True)
|
|
104
|
+
cfg_path, out_path, log_path = work / "replay.config.json", work / "replay.results.json", work / "replay.log"
|
|
105
|
+
cfg_path.write_text(json.dumps({**cfg, "out": str(out_path)}))
|
|
106
|
+
env = dict(os.environ)
|
|
107
|
+
env["PYTHONPATH"] = os.pathsep.join([str(_shim(staging))] + [p for p in env.get("PYTHONPATH", "").split(
|
|
108
|
+
os.pathsep) if p])
|
|
109
|
+
try:
|
|
110
|
+
r = run([python or sys.executable, str(WORKER), str(cfg_path)], timeout=timeout_s, env=env,
|
|
111
|
+
stdout_path=work / "replay.stdout.txt", stderr_path=log_path)
|
|
112
|
+
finally:
|
|
113
|
+
cfg_path.unlink(missing_ok=True)
|
|
114
|
+
(work / "replay.stdout.txt").unlink(missing_ok=True)
|
|
115
|
+
lines = [ln for ln in r.stdout.splitlines() if ln.startswith(MARK)]
|
|
116
|
+
head = json.loads(lines[-1][len(MARK):]) if lines else None
|
|
117
|
+
if not head or not head.get("ok") or not out_path.is_file():
|
|
118
|
+
tail = log_path.read_text(errors="replace")[-1500:] if log_path.is_file() else r.stderr[-1500:]
|
|
119
|
+
why = (head or {}).get("error") or f"exit {r.returncode}"
|
|
120
|
+
raise OpError(f"step replay worker failed under {python} ({why}). It needs torch, safetensors, numpy, PyAV "
|
|
121
|
+
"and the reference pipeline: pass the reference's interpreter with --python." +
|
|
122
|
+
(f"\nlog tail:\n{tail}" if tail.strip() else ""))
|
|
123
|
+
doc = json.loads(out_path.read_text())
|
|
124
|
+
out_path.unlink()
|
|
125
|
+
return doc
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _check(ok: bool, reason: str) -> dict:
|
|
129
|
+
return {"verdict": "PASS" if ok else "FAIL", "reason": reason}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def judge(item: dict, steps: int, factor: float, slack: float, chain_tol: float, sigma_tol: float,
|
|
133
|
+
decode_db: float) -> dict:
|
|
134
|
+
"""{verdict, checks, worst} of one audited request's worker result."""
|
|
135
|
+
checks: dict[str, dict] = {}
|
|
136
|
+
if item.get("error"):
|
|
137
|
+
checks["dump"] = _check(False, item["error"])
|
|
138
|
+
return {"verdict": "FAIL", "checks": checks}
|
|
139
|
+
diff = item.get("request_diff") or []
|
|
140
|
+
checks["dump"] = _check(not diff, "request matches" if not diff else f"the dump's request differs in {diff}")
|
|
141
|
+
layout = item.get("layout") or []
|
|
142
|
+
checks["layout"] = _check(not layout, "; ".join(layout) or f"{item.get('n_steps')} steps, streams "
|
|
143
|
+
f"{item.get('streams')}")
|
|
144
|
+
sch = item.get("schedule") or {}
|
|
145
|
+
ref = sch.get("ref_sigmas") or []
|
|
146
|
+
sig, sig_n = sch.get("sigma") or [], sch.get("sigma_next") or []
|
|
147
|
+
if not ref:
|
|
148
|
+
checks["schedule"] = {"verdict": "NOT RUN", "reason": "not replayed"}
|
|
149
|
+
elif len(ref) - 1 != len(sig) or len(sig) != steps:
|
|
150
|
+
checks["schedule"] = _check(False, f"{len(sig)} steps dumped, the reference's schedule has {len(ref) - 1} "
|
|
151
|
+
f"for {steps} asked")
|
|
152
|
+
else:
|
|
153
|
+
d = max([abs(a - b) for a, b in zip(sig, ref[:-1])] + [abs(a - b) for a, b in zip(sig_n, ref[1:])] + [0.0])
|
|
154
|
+
checks["schedule"] = _check(len(sig_n) == len(sig) and d <= sigma_tol,
|
|
155
|
+
f"max |sigma - reference sigma| {d:.3g} (tolerance {sigma_tol:g})")
|
|
156
|
+
if "steps" not in item: # not replayed: a layout or schedule failure says why
|
|
157
|
+
checks.setdefault("schedule", {"verdict": "NOT RUN", "reason": "not replayed"})
|
|
158
|
+
v = "FAIL" if any(c["verdict"] == "FAIL" for c in checks.values()) else "UNPROVEN"
|
|
159
|
+
return {"verdict": v, "checks": checks}
|
|
160
|
+
bad_dt = []
|
|
161
|
+
for s, fields in (item.get("ref_dtypes") or {}).items():
|
|
162
|
+
for f, want in fields.items():
|
|
163
|
+
got = (item.get("dump_dtypes") or {}).get(s, {}).get(f)
|
|
164
|
+
if SAFETENSORS_DTYPES.get(str(got).lower(), got) != want:
|
|
165
|
+
bad_dt.append(f"{s}.{f} {SAFETENSORS_DTYPES.get(str(got).lower(), got)} (reference {want})")
|
|
166
|
+
checks["dtypes"] = _check(not bad_dt, "; ".join(bad_dt) or "as the reference's")
|
|
167
|
+
rows = item["steps"]
|
|
168
|
+
cont = [w for w, v in item.get("continuity") or [] if v != 0.0]
|
|
169
|
+
chain = max((r["chain"] for r in rows), default=0.0)
|
|
170
|
+
init = {s: v for s, v in (item.get("initial") or {}).items() if v > chain_tol}
|
|
171
|
+
why = []
|
|
172
|
+
if chain > chain_tol:
|
|
173
|
+
worst = max(rows, key=lambda r: r["chain"])
|
|
174
|
+
why.append(f"x_next is not the reference scheduler's update of the dumped output (max relative error "
|
|
175
|
+
f"{chain:.3g} at {worst['stream']} step {worst['i']}; tolerance {chain_tol:g})")
|
|
176
|
+
if cont:
|
|
177
|
+
why.append(f"{len(cont)} latent(s) differ from the next step's start (e.g. {cont[0]})")
|
|
178
|
+
if init:
|
|
179
|
+
why.append(f"the first latent is not the request's initial noise ({init})")
|
|
180
|
+
checks["chain"] = _check(not why, "; ".join(why) or f"max relative error {chain:.3g}")
|
|
181
|
+
dec = item.get("decode") or {}
|
|
182
|
+
if dec.get("psnr_db") is None:
|
|
183
|
+
checks["decode"] = _check(False, f"the delivered clip has {dec.get('frames_clip')} frames of "
|
|
184
|
+
f"{dec.get('size_clip')}, the reference's decode {dec.get('frames_ref')} of "
|
|
185
|
+
f"{dec.get('size_ref')}")
|
|
186
|
+
else:
|
|
187
|
+
same = dec.get("frames_clip") == dec.get("frames_ref")
|
|
188
|
+
checks["decode"] = _check(same and dec["psnr_db"] >= decode_db,
|
|
189
|
+
f"{dec.get('frames_clip')} vs {dec.get('frames_ref')} frames; mean PSNR "
|
|
190
|
+
f"{dec['psnr_db']:.2f} dB vs the reference's decode of the final latent "
|
|
191
|
+
f"(minimum {decode_db:g})")
|
|
192
|
+
over, worst, unfloored = [], None, 0
|
|
193
|
+
for r in rows:
|
|
194
|
+
f = r.get("floor")
|
|
195
|
+
if f is None:
|
|
196
|
+
unfloored += 1
|
|
197
|
+
continue
|
|
198
|
+
r["limit"] = factor * f + slack
|
|
199
|
+
r["ratio"] = r["e"] / f if f > 0 else (0.0 if r["e"] == 0 else None)
|
|
200
|
+
if r["e"] > r["limit"]:
|
|
201
|
+
over.append(r)
|
|
202
|
+
if worst is None or r["e"] - r["limit"] > worst["e"] - worst["limit"]:
|
|
203
|
+
worst = r
|
|
204
|
+
exact = all(r["e"] == 0 for r in rows)
|
|
205
|
+
floor_zero = rows and all((r.get("floor") or 0) == 0 for r in rows)
|
|
206
|
+
if unfloored:
|
|
207
|
+
checks["steps"] = {"verdict": "UNPROVEN", "reason": f"{unfloored} step(s) have no floor (alt replay failed)"}
|
|
208
|
+
elif over:
|
|
209
|
+
w = max(over, key=lambda r: r["e"] - r["limit"])
|
|
210
|
+
checks["steps"] = _check(False, f"{len(over)} of {len(rows)} step outputs beyond {factor:g} x floor + "
|
|
211
|
+
f"{slack:g} (worst {w['stream']} step {w['i']}: e {w['e']:.3g}, floor "
|
|
212
|
+
f"{w['floor']:.3g}, cosine {w['cos']})")
|
|
213
|
+
elif floor_zero and not exact:
|
|
214
|
+
checks["steps"] = {"verdict": "UNPROVEN", "reason": "the alternative kernel's outputs equal the reference's "
|
|
215
|
+
"(floor 0): it cannot calibrate the ratio (was --alt-attention applied?)"}
|
|
216
|
+
else:
|
|
217
|
+
checks["steps"] = _check(True, f"all {len(rows)} step outputs within {factor:g} x floor + {slack:g}" +
|
|
218
|
+
(" (bit-identical)" if exact else f"; max e {max(r['e'] for r in rows):.3g}"))
|
|
219
|
+
from inferencekit import stats
|
|
220
|
+
|
|
221
|
+
v = stats.combine_claims(c["verdict"] for c in checks.values())
|
|
222
|
+
return {"verdict": v, "checks": checks, "worst": worst}
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
@operation("quality_clip_steps", ("quality", "clip-steps"),
|
|
226
|
+
"Per-step check of a video engine against the BF16 reference: for audited clips drawn after the clips "
|
|
227
|
+
"are made, each denoising step's guided output (from the engine's per-step dump) against the reference's "
|
|
228
|
+
"output from the same latent, judged against an alternative exact attention kernel's floor, with "
|
|
229
|
+
"schedule, dtype, chain and decode guards.",
|
|
230
|
+
[Arg("outputs", "string", "The engine's clips: a bench clip outputs/<label> directory (its request bodies "
|
|
231
|
+
"in inputs/<label>/).", required=True),
|
|
232
|
+
Arg("dumps", "string", "The engine's dump directory (its CLIP_STEP_DUMP_DIR).", required=True),
|
|
233
|
+
Arg("pipeline", "string", "The reference clip pipeline, as the clip server names it (diffusers_ltx or "
|
|
234
|
+
"MODULE:FUNCTION).", required=True),
|
|
235
|
+
Arg("pipeline_model", "string", "The reference's --model (folder or hub id).", required=True),
|
|
236
|
+
Arg("pipeline_arg", "array", "The reference's --pipeline-arg KEY=VALUE (repeat; JSON values).",
|
|
237
|
+
items="string"),
|
|
238
|
+
Arg("python", "string", "Python of the reference pipeline's venv (torch, safetensors, numpy, PyAV); "
|
|
239
|
+
"default: this interpreter."),
|
|
240
|
+
Arg("task", "string", "The reference pipeline variant.", default="t2v", choices=("t2v", "i2v")),
|
|
241
|
+
Arg("device", "string", "The reference's --device.", default="cuda"),
|
|
242
|
+
Arg("dtype", "string", "The reference's --dtype.", default="bf16"),
|
|
243
|
+
Arg("offload", "string", "The reference's --offload.", default="none",
|
|
244
|
+
choices=("none", "model", "sequential")),
|
|
245
|
+
Arg("attention", "string", "The reference's --attention (reference mode sets its own)."),
|
|
246
|
+
Arg("alt_attention", "string", "The exact but different attention backend of the floor replay.",
|
|
247
|
+
default="_native_efficient"),
|
|
248
|
+
Arg("requests", "integer", "Clips to audit (every step of each).", default=2),
|
|
249
|
+
Arg("audit_seed", "integer", "Seed of the draw of audited clips (default: drawn now and recorded)."),
|
|
250
|
+
Arg("factor", "number", "Pass: e <= factor x floor + slack at every step.", default=DEFAULT_FACTOR),
|
|
251
|
+
Arg("slack", "number", "Absolute slack on the relative error e.", default=DEFAULT_SLACK),
|
|
252
|
+
Arg("chain_tol", "number", "Relative L2 tolerance of the chain check.", default=DEFAULT_CHAIN_TOL),
|
|
253
|
+
Arg("sigma_tol", "number", "Absolute tolerance on each sigma.", default=DEFAULT_SIGMA_TOL),
|
|
254
|
+
Arg("decode_psnr_db", "number", "Minimum mean PSNR of the delivered clip vs the reference's decode of the "
|
|
255
|
+
"final latent.", default=DEFAULT_DECODE_PSNR_DB),
|
|
256
|
+
Arg("model", "string", "Model id for the record's identity."),
|
|
257
|
+
Arg("run_dir", "string", "Run directory: the score also goes to <run-dir>/quality/clip_steps.json and "
|
|
258
|
+
"the record to <run-dir>/evidence/."),
|
|
259
|
+
Arg("timeout_s", "number", "Seconds the replay worker may take in all.", default=3600)])
|
|
260
|
+
def quality_clip_steps(call, outputs, dumps, pipeline, pipeline_model, pipeline_arg, python, task, device, dtype,
|
|
261
|
+
offload, attention, alt_attention, requests, audit_seed, factor, slack, chain_tol, sigma_tol,
|
|
262
|
+
decode_psnr_db, model, run_dir, timeout_s):
|
|
263
|
+
if requests < 1:
|
|
264
|
+
raise OpError("--requests must be >= 1")
|
|
265
|
+
out_dir, dump_dir = Path(outputs), Path(dumps)
|
|
266
|
+
if not dump_dir.is_dir():
|
|
267
|
+
raise OpError(f"--dumps {dump_dir}: not a directory")
|
|
268
|
+
files = clip_files(out_dir)
|
|
269
|
+
bodies = requests_of(out_dir)
|
|
270
|
+
if not bodies:
|
|
271
|
+
raise OpError(f"{out_dir}: no request bodies (bench clip keeps them in inputs/<label>/): the replay needs "
|
|
272
|
+
"each clip's prompt, shape, steps and seed")
|
|
273
|
+
not_timed = index_mismatch(out_dir, files)
|
|
274
|
+
candidates = sorted(k for k in files if k in bodies)
|
|
275
|
+
seed = audit_seed if audit_seed is not None else secrets.randbits(32)
|
|
276
|
+
picked = sorted(random.Random(seed).sample(candidates, min(requests, len(candidates))))
|
|
277
|
+
from inferencekit.engines.clip import stepdump
|
|
278
|
+
|
|
279
|
+
reqs = [{"key": k, "body": bodies[k], "clip": str(files[k].resolve()),
|
|
280
|
+
"dump": str(stepdump.dump_path(dump_dir, file_sha256(files[k])[0]).resolve())} for k in picked]
|
|
281
|
+
cfg = {"pipeline": {"name": pipeline, "model": pipeline_model, "task": task, "device": device, "dtype": dtype,
|
|
282
|
+
"offload": offload, "attention": attention, "extra": _pipeline_args(pipeline_arg)},
|
|
283
|
+
"alt_attention": alt_attention, "requests": reqs}
|
|
284
|
+
store = ev.Store()
|
|
285
|
+
with store.staging_dir() as staging:
|
|
286
|
+
doc = run_replay(python, cfg, staging, timeout_s)
|
|
287
|
+
audited = {}
|
|
288
|
+
for it in doc["items"]:
|
|
289
|
+
j = judge(it, int(bodies[it["key"]]["steps"]), factor, slack, chain_tol, sigma_tol, decode_psnr_db)
|
|
290
|
+
if it["key"] in not_timed:
|
|
291
|
+
j["checks"]["outputs_index"] = _check(False, not_timed[it["key"]])
|
|
292
|
+
j["verdict"] = "FAIL"
|
|
293
|
+
audited[it["key"]] = {**j, "decode": it.get("decode"), "seconds": it.get("seconds"),
|
|
294
|
+
"dump_bytes": it.get("dump_bytes"), "n_steps": it.get("n_steps")}
|
|
295
|
+
from inferencekit import stats
|
|
296
|
+
|
|
297
|
+
verdict = stats.combine_claims(a["verdict"] for a in audited.values())
|
|
298
|
+
caps = []
|
|
299
|
+
if len(picked) < requests:
|
|
300
|
+
caps.append(f"only {len(picked)} clip(s) with request bodies to audit, {requests} asked")
|
|
301
|
+
verdict = "UNPROVEN" if verdict == "PASS" else verdict
|
|
302
|
+
rows = [{"key": it["key"], **r} for it in doc["items"] for r in it.get("steps") or []]
|
|
303
|
+
jl = staging / "clip_steps.jsonl"
|
|
304
|
+
jl.write_text("".join(json.dumps(r) + "\n" for r in rows))
|
|
305
|
+
artifacts = [ev.artifact(jl, staging, "clip_steps")]
|
|
306
|
+
log = staging / "worker" / "replay.log"
|
|
307
|
+
if log.is_file() and log.stat().st_size:
|
|
308
|
+
log.write_text(ev.redact_text(log.read_text(errors="replace")))
|
|
309
|
+
artifacts.append(ev.artifact(log, staging, "worker_log"))
|
|
310
|
+
judged = [r for r in rows if r.get("floor") is not None]
|
|
311
|
+
ratios = [r["ratio"] for r in judged if r.get("ratio") is not None]
|
|
312
|
+
label = out_dir.name
|
|
313
|
+
result = {"verdict": verdict, "label": label, "audit_seed": seed, "audited": audited,
|
|
314
|
+
"candidates": len(candidates), "requests": requests, "caps": caps,
|
|
315
|
+
"rule": {"factor": factor, "slack": slack, "chain_tol": chain_tol, "sigma_tol": sigma_tol,
|
|
316
|
+
"decode_psnr_db": decode_psnr_db, "alt_attention": alt_attention,
|
|
317
|
+
"defaults_source": DEFAULTS_SOURCE},
|
|
318
|
+
"max_ratio": max(ratios) if ratios else None,
|
|
319
|
+
"max_e": max((r["e"] for r in rows), default=None),
|
|
320
|
+
"max_floor": max((r["floor"] for r in judged), default=None),
|
|
321
|
+
"worker": {"load_s": doc.get("load_s"), "total_s": doc.get("total_s"),
|
|
322
|
+
"describe": doc.get("describe")}}
|
|
323
|
+
mets = [ev.metric("step_error_max", result["max_e"], "relative_l2", "max", n=len(rows), subject=label),
|
|
324
|
+
ev.metric("step_floor_max", result["max_floor"], "relative_l2", "max", n=len(judged), subject=label),
|
|
325
|
+
ev.metric("step_error_over_floor_max", result["max_ratio"], "ratio", "max", n=len(ratios),
|
|
326
|
+
subject=label),
|
|
327
|
+
ev.metric("replay_seconds", doc.get("total_s"), "s", "total", n=len(picked))]
|
|
328
|
+
summary = (f"quality clip-steps {verdict}: {label}, {len(picked)} audited clip(s), {len(rows)} step outputs; "
|
|
329
|
+
f"max e/floor {result['max_ratio']:.3g}" if result["max_ratio"] is not None else
|
|
330
|
+
f"quality clip-steps {verdict}: {label}, {len(picked)} audited clip(s)")
|
|
331
|
+
kind, extra = _kind("clip_steps")
|
|
332
|
+
rec = ev.build(call, kind=kind, claim_class="observed", summary=summary,
|
|
333
|
+
identity_=ev.identity(engine={"name": label}, model={"id": model}),
|
|
334
|
+
metrics=mets, artifacts=artifacts, missing=[] if rows else ["no step was replayed"],
|
|
335
|
+
limitations=caps + ["audio and video latents are judged; the decoded audio is not compared"],
|
|
336
|
+
data={**extra, **result, "format": FORMAT})
|
|
337
|
+
if run_dir:
|
|
338
|
+
q = Path(run_dir) / "quality"
|
|
339
|
+
q.mkdir(parents=True, exist_ok=True)
|
|
340
|
+
(q / "clip_steps.json").write_text(json.dumps(result, indent=1) + "\n")
|
|
341
|
+
return store.save(rec, staging)
|