fix: skip Studio's own prompt enhancement when nothing reads its conditioning

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
digital-garbage
2026-09-23 23:45:15 +03:00
co-authored by Claude Opus 5.5
parent 85e5173c7c
commit a44074fe52
5 changed files with 67 additions and 6 deletions
+11 -4
View File
@@ -9920,7 +9920,7 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
prompt_enhance_temperature=0.7, prompt_enhance_top_p=0.92,
prompt_enhance_max_length=400, prompt_enhance_thinking=False,
prompt_enhance_image=None, prompt_enhance_sampling=None,
prompt_enhance_output=False,
prompt_enhance_output=False, prompt_enhance_scenes=True,
_seed=None, _seed_source="fresh seed", _scene_seeds=None, _velocity_keys=None,
batch_variants=1, guess_mode=False, guess_direction="up", guess_range=1.0,
guess_freeze_seed=True, movie_editor_scene_ratings=None, scene_segments=None,
@@ -10571,11 +10571,17 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
# When the run splits into scenes, the per-scene conditionings REPLACE this base entry
# — so enhancing the base too would be a second full generation whose result is then
# thrown away. One generation per prompt that is actually encoded, never two.
_enhance_base = prompt_enhance and not split_by_transitions
# Off when nothing reads Studio's own conditioning (the Editor checks the wiring):
# rewriting a prompt whose encoding is discarded is a generation for nothing.
_enhance_own = prompt_enhance and prompt_enhance_scenes
if prompt_enhance and not prompt_enhance_scenes:
print("[FunPackVideoRefinerV2] Prompt enhancer: Studio's own prompt skipped — "
"nothing reads its conditioning; only the enhanced_prompt output is written.")
_enhance_base = _enhance_own and not split_by_transitions
# top_k / min_p / penalties / greedy; `seed` 0 = follow the generation seed.
_enhance_sampling = dict(prompt_enhance_sampling or {})
_enhance_seed = int(_enhance_sampling.pop("seed", 0) or 0) or seed
if prompt_enhance and not _enhance_base:
if _enhance_own and not _enhance_base:
print("[FunPackVideoRefinerV2] Prompt enhancer: base prompt skipped — this run "
"splits into scenes and each scene is enhanced instead.")
if _enhance_base:
@@ -10845,7 +10851,7 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
# authoritative on count, so rewriting the scenes as a single paragraph
# and re-splitting could return a different number of scenes and desync
# every clip from its anchor. One call per distinct text, cached.
if prompt_enhance and split_scene_texts:
if _enhance_own and split_scene_texts:
_enhanced_texts = []
for _i, t in enumerate(split_scene_texts):
_after, _st = self._v2_enhance_prompt(
@@ -13845,6 +13851,7 @@ class FunPackStudio:
h3_phrase_variability=float(rf.get("h3_phrase_variability", 0.0) or 0.0),
prompt_enhance=bool(rf.get("prompt_enhance", False)),
prompt_enhance_output=bool(rf.get("prompt_enhance_output", False)),
prompt_enhance_scenes=bool(rf.get("prompt_enhance_scenes", True)),
prompt_enhance_system=str(rf.get("prompt_enhance_system", "") or ""),
prompt_enhance_temperature=float(rf.get("prompt_enhance_temperature", 0.7)),
prompt_enhance_top_p=float(rf.get("prompt_enhance_top_p", 0.92)),
+30
View File
@@ -1052,6 +1052,20 @@ def build(object_info: dict, models_config: dict, params: dict, media: dict | No
_break_cycles(graph, report, protected_edges,
_node_labels(slots, slot_node_id, object_info, core=CORE))
# Nothing reads Studio's own conditioning (e.g. H3's reference-to-video node feeding the
# sampler directly): Studio's per-scene prompt enhancement would be thrown away.
if "studio" in graph and "sampler" in graph and \
not _feeds_from(graph, graph["sampler"]["inputs"].get("positive"), ("studio", 1)):
try:
_ss_p = json.loads(str(graph["studio"]["inputs"].get("studio_settings") or "{}"))
except ValueError:
_ss_p = {}
if isinstance(_ss_p, dict):
_rf_p = _ss_p.get("refiner") if isinstance(_ss_p.get("refiner"), dict) else {}
_rf_p["prompt_enhance_scenes"] = False
_ss_p["refiner"] = _rf_p
graph["studio"]["inputs"]["studio_settings"] = json.dumps(_ss_p)
for msg in pipeline_wiring.validate_models_wiring(models_config):
report["blocking"].append(msg)
report["unsatisfied"].append(msg)
@@ -1373,6 +1387,22 @@ def _reaches_upstream(graph, start, goal):
return False
def _feeds_from(graph, value, source):
"""Whether the link `value` is, or is built upstream from, output `source` (node, idx)."""
stack, seen = [value], set()
while stack:
v = stack.pop()
if not (isinstance(v, list) and len(v) == 2 and isinstance(v[0], str)):
continue
if (v[0], v[1]) == tuple(source):
return True
if v[0] in seen or v[0] not in graph:
continue
seen.add(v[0])
stack.extend((graph[v[0]].get("inputs") or {}).values())
return False
def _edge_input(graph, dst, up):
"""The input name on `dst` that consumes `up`'s output (or None)."""
for inp, val in (graph.get(dst, {}).get("inputs", {}) or {}).items():
+16
View File
@@ -1597,3 +1597,19 @@ def test_enhanced_prompt_link_wires_to_studios_output():
assert oi["FunPackStudio"]["output_name"][builder.STUDIO_ENHANCED_PROMPT_OUT] == "enhanced_prompt"
rf = json.loads(graph["studio"]["inputs"]["studio_settings"])["refiner"]
assert rf["prompt_enhance_output"] is True
def test_studio_skips_its_own_enhancement_when_nothing_reads_its_conditioning():
import json
oi = {**OI, "R2V": {"input": {"required": {"prompt": ["STRING", {"default": ""}]}},
"output": ["CONDITIONING"], "output_name": ["CONDITIONING"]}}
def rf_of(models):
graph, _ = builder.build(oi, models, PARAMS)
return json.loads(graph["studio"]["inputs"]["studio_settings"]).get("refiner", {})
assert "prompt_enhance_scenes" not in rf_of({"slots": []}) # default: Studio feeds it
bypassed = {"full_control": True,
"slots": [{"id": "r2v", "node_class": "R2V", "inputs": {}, "wires": {}}],
"core_overrides": {"sampler": {"positive": "out:r2v:CONDITIONING"}}}
assert rf_of(bypassed)["prompt_enhance_scenes"] is False
+2 -2
View File
@@ -345,7 +345,7 @@ def _refine_source():
def test_the_base_prompt_is_not_enhanced_when_the_run_splits():
src = _refine_source()
assert "_enhance_base = prompt_enhance and not split_by_transitions" in src
assert "_enhance_base = _enhance_own and not split_by_transitions" in src
def test_the_base_enhancement_is_gated_on_that_flag():
@@ -356,7 +356,7 @@ def test_the_base_enhancement_is_gated_on_that_flag():
def test_the_scene_path_is_still_enhanced_when_splitting():
src = _refine_source()
assert "if prompt_enhance and split_scene_texts:" in src
assert "if _enhance_own and split_scene_texts:" in src
def test_the_skip_is_reported_not_silent():
+8
View File
@@ -2321,3 +2321,11 @@ def test_enhanced_prompt_output_costs_nothing_when_unlinked(tmp_path):
out, clip = _enhanced_output(tmp_path, prompt_enhance=True)
assert out is None
assert not any(t.endswith("a cat, soft light") for t, _ in clip.tokenize_calls)
def test_studio_skips_its_own_enhancement_when_told_nothing_reads_it(tmp_path):
out, clip = _enhanced_output(tmp_path, prompt_enhance=True, prompt_enhance_scenes=False)
assert out is None and clip.generate_kwargs == {}
out, clip = _enhanced_output(tmp_path, prompt_enhance=True, prompt_enhance_scenes=False,
prompt_enhance_output=True)
assert out == "a vivid cat on a sunlit sill"