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:
co-authored by
Claude Opus 5.5
parent
85e5173c7c
commit
a44074fe52
+11
-4
@@ -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)),
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user