Show anatomy guard in H3 script output

This commit is contained in:
2026-08-27 07:29:02 +00:00
parent 89225c94bd
commit 57541ee847
2 changed files with 29 additions and 7 deletions
+28 -7
View File
@@ -3225,7 +3225,7 @@ def continuity_warnings(gens):
return out return out
def dialogue_filler_warnings(beats, seconds_per_shot): def dialogue_filler_warnings(beats, seconds_per_shot):
"""Dialogue shots with far more time than their line, which H3 fills with speech. """Dialogue shots with far more time than their line, which H3 fills with speech.
dialogue_fit_warnings covers the opposite error -- a line too long for its shot, dialogue_fit_warnings covers the opposite error -- a line too long for its shot,
@@ -3249,10 +3249,31 @@ def dialogue_filler_warnings(beats, seconds_per_shot):
if gap >= 3.0 and sec > spoken * 2: if gap >= 3.0 and sec > spoken * 2:
out.append(f"shot {i}: {spoken:.1f}s of dialogue in a {sec:.1f}s shot -- {gap:.1f}s of " out.append(f"shot {i}: {spoken:.1f}s of dialogue in a {sec:.1f}s shot -- {gap:.1f}s of "
f"unscripted audio the model will fill with more speech") f"unscripted audio the model will fill with more speech")
return out return out
def speech_flags(beats): def annotate_script_guards(gens, anatomy_shots, anatomy_mode):
"""Human-readable guard report prepended to the script socket.
The `script` output is for inspection, not for feeding back into the node, so
a compact report there is the quickest way to see whether a guard actually made
it into each shot's prompt text.
"""
lines = []
if anatomy_mode == "off":
lines.append("# anatomy_guard: off")
elif anatomy_shots:
lines.append(
"# anatomy_guard: injected on shot(s) "
+ ",".join(str(n) for n in anatomy_shots)
)
else:
lines.append("# anatomy_guard: no shots matched the auto gate")
body = "\n---\n".join(gens)
return "\n".join(lines + ["", body]) if body else "\n".join(lines)
def speech_flags(beats):
"""Per-beat: does it contain scripted (quoted) dialogue? Same rule the prompt """Per-beat: does it contain scripted (quoted) dialogue? Same rule the prompt
builder uses to decide silencing, exposed so the renderer can also MUTE the builder uses to decide silencing, exposed so the renderer can also MUTE the
audio of non-speech shots -- a deterministic fix when H3 vocalizes anyway.""" audio of non-speech shots -- a deterministic fix when H3 vocalizes anyway."""
@@ -6433,7 +6454,7 @@ class H3LongVideos:
# correctly-SHAPED empty one rather than None: a downstream LATENT input # correctly-SHAPED empty one rather than None: a downstream LATENT input
# would choke on None, and this keeps the preview wireable exactly like # would choke on None, and this keeps the preview wireable exactly like
# a real run. # a real run.
return (ph_img, ph_audio, plan, "\n---\n".join(gens), max(plan_lens), return (ph_img, ph_audio, plan, annotate_script_guards(gens, anatomy_shots, anatomy_mode), max(plan_lens),
sum(plan_lens), shots, total, float(fps), int(fps), sum(plan_lens), shots, total, float(fps), int(fps),
_empty_av_latent(w, h, 5, fps)[0], global_soundscape, [], []) _empty_av_latent(w, h, 5, fps)[0], global_soundscape, [], [])
@@ -6740,7 +6761,7 @@ class H3LongVideos:
watermark_opacity, watermark_margin, intro_text, intro_seconds, watermark_opacity, watermark_margin, intro_text, intro_seconds,
intro_fade, intro_size, intro_position, overlay_font, overlay_stroke) intro_fade, intro_size, intro_position, overlay_font, overlay_stroke)
script = "\n---\n".join(gens) script = annotate_script_guards(gens, anatomy_shots, anatomy_mode)
actual = all_frames.shape[0] / fps actual = all_frames.shape[0] / fps
uniform_len = len(set(shot_lens)) == 1 uniform_len = len(set(shot_lens)) == 1
shape_str = (f"{len(gens)} shot(s) x {shot_lens[0]}f (~{shot_lens[0] / fps:.1f}s each) " shape_str = (f"{len(gens)} shot(s) x {shot_lens[0]}f (~{shot_lens[0] / fps:.1f}s each) "
+1
View File
@@ -459,6 +459,7 @@ class DumasH3LongVideosHelperTests(unittest.TestCase):
plan_only=True, plan_only=True,
) )
self.assertIn("# anatomy_guard: injected on shot(s) 2", result[3])
self.assertIn( self.assertIn(
"[Generation 1] editorial room, soft practical lighting. Francine stands alone.", "[Generation 1] editorial room, soft practical lighting. Francine stands alone.",
result[3], result[3],