From 8a670fdf601aace3ef4718037e31faa37fc36543 Mon Sep 17 00:00:00 2001 From: digital-garbage <133010426+digital-garbage@users.noreply.github.com> Date: Fri, 4 Sep 2026 18:18:41 +0300 Subject: [PATCH] fix: a prompt change was reported as "nothing changed" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The run fingerprint captured only scalar arguments, and the prompt arrives as conditioning tensors — so two runs of completely different text compared as a repeatability check, and the resulting structure drop (0.44-0.93) read as "generation is not reproducible here" when one row in the same batch showed a bit-identical pair (structure 1.000, detail x1.000, edge +0.000), proving it demonstrably is. Fingerprint now includes a conditioning stand-in: per-entry shape + mean + std, which differs between any two real prompts and costs two reductions on a tensor already resident. The row says "prompt/scenes changed" and refuses to call the pair a noise-floor reading. --- movie_editor/frontend/settings_window.js | 11 +++++++++-- samplers.py | 15 +++++++++++++++ tests/test_detail_probe.py | 12 ++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/movie_editor/frontend/settings_window.js b/movie_editor/frontend/settings_window.js index ce9ff6b5..e58e6d18 100644 --- a/movie_editor/frontend/settings_window.js +++ b/movie_editor/frontend/settings_window.js @@ -412,6 +412,9 @@ // so the seed decides. Different seeds = two different generations and the comparison // never was an A/B (a reference pins the subject, not the sample). Same seed = the // change moved the shot rather than its detail, which is a RESULT, not a bad setup. + if (r.changed && r.changed.conditioning) { + return "the prompt or scenes changed — a different generation, not an A/B"; + } const onlySeed = r.changed && !Object.keys(r.changed).filter((k) => k !== "seed").length; if (onlySeed && r.same_seed) { // Nothing differed at all: this is the instrument's noise floor for this setup. @@ -450,10 +453,14 @@ return r.same_seed ? "nothing changed — repeatability check" : "only the seed changed — a different sample"; } - return keys.slice(0, 3).map((k) => { + const rest = keys.filter((k) => k !== "conditioning"); + const promptMoved = keys.includes("conditioning") ? "prompt/scenes changed" : null; + const parts = rest.slice(0, 3).map((k) => { const show = (v) => (v === "" || v == null ? "—" : String(v)); return `${k}: ${show(ch[k][0])} → ${show(ch[k][1])}`; - }).join(" · ") + (keys.length > 3 ? ` · +${keys.length - 3} more` : ""); + }); + if (promptMoved) parts.unshift(promptMoved); + return parts.join(" · ") + (rest.length > 3 ? ` · +${rest.length - 3} more` : ""); } function detailProbeRows(key) { diff --git a/samplers.py b/samplers.py index 8dfe94ed..7a0229e5 100644 --- a/samplers.py +++ b/samplers.py @@ -7274,6 +7274,21 @@ class FunPackLTXAVSceneChainSampler: # name list would go stale the first time a widget is added. _run_settings = {k: v for k, v in locals().items() if isinstance(v, (bool, int, float, str)) and k != "self"} + # The prompt is NOT a scalar -- it arrives as conditioning tensors -- so scalars + # alone report "nothing changed" across two runs of completely different text, and a + # repeatability check that is really a prompt change reads as "generation is not + # reproducible". Cheap stand-in: per-entry shape + mean + std, which differs between + # any two real prompts and costs two reductions on an already-resident tensor. + try: + _fp = [] + for _e in (positive or [])[:8]: + _t = _e[0] if isinstance(_e, (list, tuple)) and _e else _e + if isinstance(_t, torch.Tensor) and _t.numel(): + _f = _t.detach().float() + _fp.append(f"{tuple(_t.shape)}:{float(_f.mean()):.5g}:{float(_f.std()):.5g}") + _run_settings["conditioning"] = "|".join(_fp) + except Exception: # noqa: BLE001 + pass # a fingerprint is a label, never a reason to fail a generation self._is_h3 = self._set_stream_axes(model) if self._is_h3: # H3's rating-driven mechanisms are h3_phrase_emphasis (Studio), h3_repr_steering, diff --git a/tests/test_detail_probe.py b/tests/test_detail_probe.py index 8d93a3cf..375c4df3 100644 --- a/tests/test_detail_probe.py +++ b/tests/test_detail_probe.py @@ -338,3 +338,15 @@ def test_no_settings_at_all_is_an_empty_diff_not_a_crash(): dp.record("k", _edged()) row = dp.record("k", _edged()) assert row["changed"] == {} + + +def test_a_prompt_change_is_reported_and_not_treated_as_a_repeatability_check(): + """The prompt is not a scalar -- it arrives as conditioning -- so scalars alone called two + runs of different text "nothing changed", and the resulting structure drop read as + "generation is not reproducible" rather than "you changed the prompt".""" + base = {"seed": 1, "cfg": 1.0, "conditioning": "(1,9,4096):0.01:0.5"} + dp.record("k", _edged(seed=0), seed=1, settings=base) + row = dp.record("k", _edged(seed=1), seed=1, + settings={**base, "conditioning": "(1,12,4096):0.03:0.7"}) + assert "conditioning" in row["changed"] + assert row["same_seed"] is True # same seed, but NOT the same generation