From 643ca447c09529ddf8bf0bb8ad70764684de3024 Mon Sep 17 00:00:00 2001 From: digital-garbage <133010426+digital-garbage@users.noreply.github.com> Date: Sun, 14 Jun 2026 18:58:27 +0300 Subject: [PATCH] Run VF ascend+search once on the merged scene, not per key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per-scene multi-key steering ran the value-function ascend+search inside the per-key loop, then averaged. That injected independent search noise per key and let a poorly-fit fresh key's VF ascend toward a bad optimum, which the averaging baked in — a body-horror path. Now each key contributes only its learned directions (per-prompt conditioning memory, already delta-capped); the merge is formed and magnitude-disciplined, then a single ascend+search runs on the merged result using the always-on project default key (the most-trained, most stable VF). Keyed single-key scenes also now ascend with the default VF rather than that one key's, for the same stability reason. Co-Authored-By: Claude Opus 4.8 --- conditioning.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/conditioning.py b/conditioning.py index 17d59dec..8f2a3151 100644 --- a/conditioning.py +++ b/conditioning.py @@ -13221,14 +13221,16 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner): # default-key path for this scene (per-entry so it matches no-key behaviour) out.append(self._v2_ascend_conditioning([entry], default_key, apply=value_guidance)[0]) continue + # Each key contributes only its learned DIRECTIONS here (per-prompt + # conditioning memory). The value-function ascend+search runs ONCE on the + # merged result below — not per key — because per-key search injected + # independent noise and a poorly-fit fresh key's VF could ascend toward a + # bad optimum, which the averaging then baked in (a body-horror path). steered = [] for key in sorted(keys): try: c, _ = self._v2_apply_conditioning_memory(cond.clone(), _key_global(key), profile) - if not isinstance(c, torch.Tensor): - c = cond.clone() - c = self._v2_ascend_conditioning([(c, meta)], key, apply=value_guidance)[0][0] - steered.append(c) + steered.append(c if isinstance(c, torch.Tensor) else cond.clone()) except Exception as error: print(f"[FunPackStudio] Scene key '{key}' steering failed: {error}") if not steered: @@ -13240,8 +13242,7 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner): merged = torch.stack(steered, dim=0).mean(dim=0) # Averaging per-key tensors that point different ways shrinks the # per-token magnitude (and collapses it toward zero when keys - # disagree), and ascend/search already broke the per-key renorm — - # so a raw mean can land far from the scene encode at the wrong + # disagree), leaving the merge far from the scene encode at the wrong # magnitude, which free-runs into warping on simple prompts. Re-impose # the single-key discipline: cap the delta vs the raw encode, then # renormalize to its per-token norm (same tail as @@ -13254,6 +13255,13 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner): merged = cond + delta * scale merged = torch.clamp(merged, min=-60.0, max=60.0) merged = merged / merged.norm(dim=-1, keepdim=True).clamp_min(1e-8) * original_norm + # Single VF ascend+search on the merged conditioning, using the always-on + # project default key (the most-trained, most stable VF) rather than any + # one scene key. + try: + merged = self._v2_ascend_conditioning([(merged, meta)], default_key, apply=value_guidance)[0][0] + except Exception as error: + print(f"[FunPackStudio] Merged scene ascend failed: {error}") merged = protect_audio_channels(merged, cond) out.append((merged, meta)) merged_scenes.append((scene_index, sorted(keys)))