Files
exportAnything-ComfyUI-Diff…/tests/test_ltx25_camera_motion_contract.py
danrh 8f23c95546 Add synchronized music-video production planning
Add measured-audio, timed-lyrics, H3 lane planning, identity relay, upload-song routing, model-specific camera contracts, LTX long-horizon planning, workflow migrations, the validated V6 example, regression coverage, and an Unreleased changelog.
2026-08-23 08:03:39 -05:00

790 lines
36 KiB
Python

from __future__ import annotations
import importlib.util
import unittest
import uuid
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
def load_contract_module():
"""Load the pure contract without importing ComfyUI, Torch, or nodes.py."""
module_name = f"diffusiongemma_ltx25_camera_contract_{uuid.uuid4().hex}"
spec = importlib.util.spec_from_file_location(module_name, ROOT / "ltx25_contract.py")
if spec is None or spec.loader is None:
raise RuntimeError("could not load the LTX-2.5 contract module")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
class Ltx25CameraMotionContractTests(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls.contract = load_contract_module()
@staticmethod
def _conditioned_metadata(mode: str) -> dict:
first_last = mode == "first_last_frame"
return {
"source": "image",
"ltx25_context_schema": "dg-ltx25-contract/1",
"ltx_first_frame_attached": True,
"ltx_last_frame_attached": first_last,
"ltx_frame_pair_attached": first_last,
}
def _validate_conditioned(self, prompt: str, mode: str) -> dict:
metadata = self._conditioned_metadata(mode)
mode_report = self.contract.resolve_ltx25_generation_mode(mode, metadata, 2 if mode == "first_last_frame" else 1)
return self.contract.validate_ltx25_prompt(prompt, mode_report, metadata, 15.0)
def test_i2v_and_flf_still_hard_block_a_cut(self) -> None:
prompt = (
"A static medium shot at eye level holds on the subject. "
"A hard cut to a locked wide shot at eye level reveals the street."
)
for mode in ("image_to_video", "first_last_frame"):
with self.subTest(mode=mode):
report = self._validate_conditioned(prompt, mode)
self.assertFalse(report["ready"])
self.assertIn(
"ltx_conditioned_mode_requires_single_continuous_take",
report["reasons"],
)
self.assertGreater(report["complexity"]["observed"]["cut_count"], 0)
def test_compatible_camera_phases_do_not_block_conditioned_readiness(self) -> None:
prompt = (
"A medium shot at eye level follows one continuous take. The camera slowly pans right "
"to follow the subject, smoothly dollies forward as the subject approaches a doorway, "
"and gently tilts upward to finish on the sign above it."
)
for mode in ("image_to_video", "first_last_frame"):
with self.subTest(mode=mode):
report = self._validate_conditioned(prompt, mode)
self.assertTrue(report["ready"], report["reasons"])
self.assertNotIn(
"ltx_camera_complexity_exceeds_duration_budget",
report["reasons"],
)
def test_brisk_compound_i2v_camera_path_is_one_valid_continuous_take(self) -> None:
prompt = (
"From the exact supplied first-frame viewpoint, a medium shot follows the cyclist in "
"one unbroken take. The camera rapidly trucks right beside her; then the camera "
"orbits energetically as she rounds the corner; finally the camera cranes upward to finish in a "
"high-angle wide composition over the same alley, with no cut and no added actor."
)
report = self._validate_conditioned(prompt, "image_to_video")
self.assertTrue(report["ready"], report["reasons"])
observed = report["complexity"]["observed"]
self.assertEqual(observed["estimated_shots"], 1)
self.assertEqual(observed["cut_count"], 0)
self.assertGreaterEqual(observed["camera_motion_phase_count"], 3)
self.assertTrue(
{"track", "orbit", "vertical"}.issubset(
{match["family"] for match in observed["camera_motion_matches"]}
)
)
self.assertNotIn(
"ltx_conditioned_mode_requires_single_continuous_take",
report["reasons"],
)
self.assertNotIn(
"ltx_camera_complexity_exceeds_duration_budget",
report["reasons"],
)
def test_negated_camera_movements_count_as_zero_positive_moves(self) -> None:
prompt = (
"A medium shot at eye level remains static, with no pan, tilt, zoom, or dolly "
"throughout the continuous take."
)
report = self.contract.analyze_ltx25_prompt_complexity(prompt, "image_to_video", 15.0)
self.assertEqual(report["observed"]["distinct_camera_moves"], 0)
self.assertEqual(report["observed"]["camera_motion_matches"], [])
self.assertEqual(report["observed"]["negated_camera_move_count"], 4)
self.assertNotIn("ltx_camera_complexity_exceeds_duration_budget", report["reasons"])
def test_contracted_and_plain_negations_do_not_become_positive_motion(self) -> None:
prompt = (
"A medium shot at eye level remains locked off. The camera isn't panning, doesn't "
"tilt, and cannot zoom during the continuous take."
)
report = self.contract.analyze_ltx25_prompt_complexity(prompt, "image_to_video", 15.0)
self.assertEqual(report["observed"]["distinct_camera_moves"], 0)
self.assertEqual(report["observed"]["camera_motion_matches"], [])
self.assertGreaterEqual(report["observed"]["negated_camera_move_count"], 3)
def test_subject_actions_and_scene_nouns_are_not_camera_movements(self) -> None:
prompt = (
"A medium shot at eye level stays steady as the astronaut tilts his head while a candy "
"truck rolls past and a construction crane rotates overhead."
)
report = self.contract.analyze_ltx25_prompt_complexity(prompt, "image_to_video", 15.0)
self.assertEqual(report["observed"]["distinct_camera_moves"], 0)
self.assertEqual(report["observed"]["camera_motion_matches"], [])
self.assertNotIn("ltx_camera_complexity_exceeds_duration_budget", report["reasons"])
def test_pan_inflections_canonicalize_to_one_motion_family(self) -> None:
prompt = (
"A medium shot at eye level begins as the camera pans right, continues panning across "
"the street, and then pans toward the doorway in one unbroken take."
)
report = self.contract.analyze_ltx25_prompt_complexity(prompt, "image_to_video", 15.0)
matches = report["observed"]["camera_motion_matches"]
self.assertEqual(report["observed"]["distinct_camera_moves"], 1)
self.assertTrue(matches)
self.assertEqual({match["family"] for match in matches}, {"pan"})
def test_camera_support_systems_are_not_movement_families(self) -> None:
prompt = (
"A medium shot at eye level uses gimbal and Steadicam support while the camera remains "
"steady throughout the continuous take."
)
report = self.contract.analyze_ltx25_prompt_complexity(prompt, "image_to_video", 15.0)
self.assertEqual(report["observed"]["distinct_camera_moves"], 0)
self.assertEqual(report["observed"]["camera_motion_matches"], [])
self.assertNotIn("ltx_camera_complexity_exceeds_duration_budget", report["reasons"])
support_only = self.contract.analyze_ltx25_prompt_complexity(
"A medium shot at eye level uses gimbal and Steadicam support throughout.",
"image_to_video",
15.0,
)
self.assertIn("ltx_camera_state_missing", support_only["reasons"])
def test_tracking_shot_and_generic_camera_move_are_recognized(self) -> None:
prompt = (
"A medium shot at eye level begins as a tracking shot, and then the camera moves "
"forward toward the doorway in one continuous take."
)
report = self.contract.analyze_ltx25_prompt_complexity(prompt, "image_to_video", 15.0)
self.assertEqual(
{match["family"] for match in report["observed"]["camera_motion_matches"]},
{"track", "move"},
)
self.assertNotIn("ltx_camera_state_missing", report["reasons"])
def test_nominal_tracking_arc_is_a_continuous_camera_path(self) -> None:
prompt = (
"A medium shot starts from the exact supplied first-frame viewpoint. "
"The camera begins a slow, handheld-style tracking arc around her, gaining kinetic "
"energy as she starts to dance. As the camera decelerates, it settles into a tight "
"close-up and holds."
)
report = self.contract.analyze_ltx25_prompt_complexity(
prompt,
"image_to_video",
25.0,
"auto",
)
self.assertIn(
"tracking arc",
{match["text"].casefold() for match in report["observed"]["camera_motion_matches"]},
)
self.assertNotIn("ltx_camera_state_missing", report["reasons"])
def test_replayed_25_second_candidate_passes_camera_state_validation(self) -> None:
prompt = (
"A medium shot captures a woman with curly blonde hair, wearing a green floral print "
"bikini, holding a green game controller aloft in a dimly lit, crowded party setting "
"with harsh flash lighting. Starting from this anchored frame, she lets out a joyful "
"shout as the muffled thumping of an alt-pop beat and ambient crowd chatter fills the "
"room. The camera begins a slow, handheld-style tracking arc around her, gaining "
"kinetic energy as she starts to dance rhythmically. Through most of the shot, she "
"spins slowly, the game controller held steady in her raised hand while the background "
"figures of partygoers blur in a bokeh of movement and light. The direct flash creates "
"glints on her skin and deep shadows behind her, emphasizing the late90s aesthetic. "
"As the camera decelerates, it settles into a tight close-up on her expressive face as "
"she laughs and looks directly into the lens. The clip ends holding on this vibrant, "
"high-energy expression as the music reaches a brief crescendo and the sound of a "
"cheering crowd fades into a rhythmic hum."
)
metadata = self._conditioned_metadata("image_to_video")
mode_report = self.contract.resolve_ltx25_generation_mode(
"image_to_video",
metadata,
1,
)
report = self.contract.validate_ltx25_prompt(
prompt,
mode_report,
metadata,
25.0,
"auto",
)
self.assertTrue(report["ready"], report["reasons"])
self.assertNotIn("ltx_camera_state_missing", report["reasons"])
def test_camera_pronoun_carries_across_a_compound_path(self) -> None:
prompt = (
"A medium shot at eye level follows the camera as it pans right, then tilts up, "
"and slowly pulls back in one continuous take."
)
report = self.contract.analyze_ltx25_prompt_complexity(prompt, "image_to_video", 15.0)
self.assertEqual(
{match["family"] for match in report["observed"]["camera_motion_matches"]},
{"pan", "tilt", "dolly"},
)
self.assertNotIn("ltx_camera_state_missing", report["reasons"])
def test_diagnostics_expose_each_positive_match_family_and_source_span(self) -> None:
prompt = (
"A medium shot at eye level begins as the camera pans right and then smoothly dollies "
"forward in one continuous take."
)
report = self.contract.analyze_ltx25_prompt_complexity(prompt, "image_to_video", 15.0)
matches = report["observed"]["camera_motion_matches"]
self.assertEqual({match["family"] for match in matches}, {"pan", "dolly"})
for match in matches:
self.assertIsInstance(match["text"], str)
self.assertTrue(match["text"])
self.assertEqual(len(match["span"]), 2)
start, end = match["span"]
self.assertIsInstance(start, int)
self.assertIsInstance(end, int)
self.assertLess(start, end)
self.assertEqual(prompt[start:end], match["text"])
def test_three_positive_phases_never_emit_the_obsolete_three_vs_one_hard_reason(self) -> None:
prompt = (
"A medium shot at eye level uses one continuous camera path: the camera pans right, "
"dollies forward, and tilts upward without any cut."
)
report = self.contract.analyze_ltx25_prompt_complexity(prompt, "image_to_video", 15.0)
self.assertEqual(report["observed"]["distinct_camera_moves"], 3)
self.assertNotIn(
"ltx_camera_complexity_exceeds_duration_budget",
report["reasons"],
)
def test_equipment_and_capture_nouns_are_not_motion_or_camera_state(self) -> None:
equipment_prompts = (
"The camera uses rolling shutter capture for a medium shot at eye level.",
"The camera uses a tracking marker for a medium shot at eye level.",
"The camera uses a zoom lens for a medium shot at eye level.",
"The camera uses a boom arm for a medium shot at eye level.",
"The camera uses a dolly track for a medium shot at eye level.",
)
for prompt in equipment_prompts:
with self.subTest(prompt=prompt):
report = self.contract.analyze_ltx25_prompt_complexity(
prompt, "image_to_video", 15.0
)
self.assertEqual(report["observed"]["distinct_camera_moves"], 0)
self.assertEqual(report["observed"]["distinct_camera_states"], 0)
self.assertEqual(report["observed"]["camera_motion_matches"], [])
self.assertIn("ltx_camera_state_missing", report["reasons"])
def test_common_positive_camera_phrasings_satisfy_camera_state(self) -> None:
motion_phrases = (
"A gentle pan right follows the subject",
"A slow push-in approaches the subject",
"A panning camera frames the subject",
"The camera follows the subject",
"The camera glides beside the subject",
)
for motion_phrase in motion_phrases:
with self.subTest(motion_phrase=motion_phrase):
prompt = f"A medium shot at eye level holds the composition. {motion_phrase}."
report = self.contract.analyze_ltx25_prompt_complexity(
prompt, "image_to_video", 15.0
)
self.assertGreater(report["observed"]["distinct_camera_moves"], 0)
self.assertTrue(report["observed"]["camera_motion_matches"])
self.assertNotIn("ltx_camera_state_missing", report["reasons"])
def test_negated_static_states_do_not_satisfy_camera_requirement(self) -> None:
negated_states = (
"The camera is not static",
"There is no static camera",
"The shot proceeds without a locked-off camera",
"The camera never stays steady",
)
for state_phrase in negated_states:
with self.subTest(state_phrase=state_phrase):
prompt = f"A medium shot at eye level shows the subject. {state_phrase}."
report = self.contract.analyze_ltx25_prompt_complexity(
prompt, "image_to_video", 15.0
)
self.assertEqual(report["observed"]["distinct_camera_states"], 0)
self.assertIn("ltx_camera_state_missing", report["reasons"])
def test_another_subject_stays_steady_does_not_bind_to_the_camera(self) -> None:
prompt = (
"A medium shot at eye level shows a cyclist crossing the frame while another subject "
"stays steady beside the road."
)
report = self.contract.analyze_ltx25_prompt_complexity(
prompt, "image_to_video", 15.0
)
self.assertEqual(report["observed"]["distinct_camera_states"], 0)
self.assertIn("ltx_camera_state_missing", report["reasons"])
def test_camera_directions_do_not_bleed_and_zoom_in_reports_direction(self) -> None:
prompt = (
"A medium shot at eye level uses one continuous path as the camera pans left, then "
"dollies forward and zooms in."
)
report = self.contract.analyze_ltx25_prompt_complexity(
prompt, "image_to_video", 15.0
)
matches = report["observed"]["camera_motion_matches"]
by_family = {match["family"]: match for match in matches}
self.assertEqual(by_family["pan"]["direction"], "left")
self.assertEqual(by_family["dolly"]["direction"], "forward")
self.assertIn(by_family["zoom"]["direction"], {"in", "forward"})
def test_conditioned_modes_block_transitions_that_create_a_new_shot(self) -> None:
transition_phrases = (
"A cut reveals",
"After a cut, the image reveals",
"A cross-dissolve reveals",
"A wipe transitions to",
"The image fades into a new",
)
for mode in ("image_to_video", "first_last_frame"):
for transition_phrase in transition_phrases:
with self.subTest(mode=mode, transition_phrase=transition_phrase):
prompt = (
"A static medium shot at eye level holds on the subject. "
f"{transition_phrase} locked wide shot at eye level showing the street."
)
report = self._validate_conditioned(prompt, mode)
self.assertFalse(report["ready"])
self.assertGreater(report["complexity"]["observed"]["cut_count"], 0)
self.assertIn(
"ltx_conditioned_mode_requires_single_continuous_take",
report["reasons"],
)
def test_conditioned_modes_allow_terminal_fade_or_cut_to_black(self) -> None:
terminal_transitions = (
"then the image fades to black",
"then the image cuts to black",
)
for mode in ("image_to_video", "first_last_frame"):
for transition in terminal_transitions:
with self.subTest(mode=mode, transition=transition):
prompt = (
"A static medium shot at eye level holds on the subject, "
f"{transition}."
)
report = self._validate_conditioned(prompt, mode)
self.assertTrue(report["ready"], report["reasons"])
self.assertEqual(report["complexity"]["observed"]["cut_count"], 0)
self.assertNotIn(
"ltx_conditioned_mode_requires_single_continuous_take",
report["reasons"],
)
def test_negated_editing_instructions_do_not_create_phantom_cuts(self) -> None:
negated_edits = (
"with no cuts, wipes, or dissolves",
"with no hard cuts",
"and the image does not cut to another shot",
)
for mode in ("image_to_video", "first_last_frame"):
for instruction in negated_edits:
with self.subTest(mode=mode, instruction=instruction):
prompt = (
"A static medium shot at eye level holds on the subject "
f"{instruction}."
)
report = self._validate_conditioned(prompt, mode)
self.assertEqual(report["complexity"]["observed"]["cut_count"], 0)
self.assertNotIn(
"ltx_conditioned_mode_requires_single_continuous_take",
report["reasons"],
)
def test_modified_negated_camera_motion_has_no_active_matches(self) -> None:
negated_motion_phrases = (
"The camera never slowly pans",
"The camera doesn't gently tilt",
"The shot proceeds without a slow pan",
"The camera does not use a slow pan",
)
for motion_phrase in negated_motion_phrases:
with self.subTest(motion_phrase=motion_phrase):
prompt = f"A medium shot at eye level shows the subject. {motion_phrase}."
report = self.contract.analyze_ltx25_prompt_complexity(
prompt, "image_to_video", 15.0
)
self.assertEqual(report["observed"]["distinct_camera_moves"], 0)
self.assertEqual(report["observed"]["camera_motion_matches"], [])
def test_physical_rig_and_optical_actions_are_not_camera_motion(self) -> None:
prompts = (
"A medium shot at eye level uses a moving platform beneath the actor.",
"The camera lens uses a rotating polarizer for a medium shot at eye level.",
)
for prompt in prompts:
with self.subTest(prompt=prompt):
report = self.contract.analyze_ltx25_prompt_complexity(
prompt, "image_to_video", 15.0
)
self.assertEqual(report["observed"]["distinct_camera_moves"], 0)
self.assertEqual(report["observed"]["camera_motion_matches"], [])
self.assertIn("ltx_camera_state_missing", report["reasons"])
def test_camera_uses_motion_grammar_remains_recognized(self) -> None:
cases = (
("The camera uses a gentle pan right", "pan"),
("The camera uses a tracking shot beside the subject", "track"),
)
for motion_phrase, family in cases:
with self.subTest(motion_phrase=motion_phrase):
prompt = f"A medium shot at eye level shows the subject. {motion_phrase}."
report = self.contract.analyze_ltx25_prompt_complexity(
prompt, "image_to_video", 15.0
)
matches = report["observed"]["camera_motion_matches"]
self.assertIn(family, {match["family"] for match in matches})
self.assertNotIn("ltx_camera_state_missing", report["reasons"])
def test_explicit_no_camera_motion_counts_as_a_static_state(self) -> None:
static_phrases = (
"There is no camera movement",
"There is no camera motion",
"The camera does not move",
)
for static_phrase in static_phrases:
with self.subTest(static_phrase=static_phrase):
prompt = f"A medium shot at eye level shows the subject. {static_phrase}."
report = self.contract.analyze_ltx25_prompt_complexity(
prompt, "image_to_video", 15.0
)
self.assertEqual(report["observed"]["distinct_camera_moves"], 0)
self.assertGreater(report["observed"]["distinct_camera_states"], 0)
self.assertNotIn("ltx_camera_state_missing", report["reasons"])
def test_physical_cut_language_is_not_an_edit(self) -> None:
physical_cut_phrases = (
"The cut reveals blood beneath the skin",
"A cut shows pale wood beneath the paint",
"A bandage covers the cut to his hand",
"After a cut blood beads on the skin",
"Another cut reveals hidden wire in the cable",
"She cuts to the left side of the fabric",
)
for physical_phrase in physical_cut_phrases:
with self.subTest(physical_phrase=physical_phrase):
prompt = (
"A static medium shot at eye level holds on the subject. "
f"{physical_phrase}."
)
report = self.contract.analyze_ltx25_prompt_complexity(
prompt, "image_to_video", 15.0
)
self.assertEqual(report["observed"]["cut_count"], 0)
self.assertNotIn(
"ltx_conditioned_mode_requires_single_continuous_take",
report["reasons"],
)
def test_shot_bearing_cut_language_remains_an_edit(self) -> None:
editorial_prompts = (
(
"A static medium shot at eye level holds on the subject. "
"A cut reveals a locked wide shot at eye level showing the street."
),
(
"A static medium shot at eye level holds on the subject. "
"After a cut, a wide shot at eye level shows the street with a locked camera."
),
)
for mode in ("image_to_video", "first_last_frame"):
for prompt in editorial_prompts:
with self.subTest(mode=mode, prompt=prompt):
report = self._validate_conditioned(prompt, mode)
self.assertGreater(report["complexity"]["observed"]["cut_count"], 0)
self.assertIn(
"ltx_conditioned_mode_requires_single_continuous_take",
report["reasons"],
)
def test_editorial_cut_imperatives_block_conditioned_modes(self) -> None:
editorial_prompts = (
"A static medium shot at eye level holds on her. Then, cut to the street.",
"A static medium shot at eye level holds on the street. Cut back to her.",
(
"A static medium shot at eye level holds on the subject. "
"Cutting between the street and the room."
),
)
for mode in ("image_to_video", "first_last_frame"):
for prompt in editorial_prompts:
with self.subTest(mode=mode, prompt=prompt):
report = self._validate_conditioned(prompt, mode)
self.assertGreater(report["complexity"]["observed"]["cut_count"], 0)
self.assertIn(
"ltx_conditioned_mode_requires_single_continuous_take",
report["reasons"],
)
def test_physical_cut_subjects_do_not_become_editorial_commands(self) -> None:
physical_prompts = (
(
"A static medium shot at eye level shows the tailor as she cuts to the left side "
"of fabric."
),
(
"A static medium shot at eye level shows scissors cut between two layers of "
"fabric."
),
)
for prompt in physical_prompts:
with self.subTest(prompt=prompt):
report = self.contract.analyze_ltx25_prompt_complexity(
prompt, "image_to_video", 15.0
)
self.assertEqual(report["observed"]["cut_count"], 0)
self.assertNotIn(
"ltx_conditioned_mode_requires_single_continuous_take",
report["reasons"],
)
def test_camera_control_verbs_do_not_bind_equipment_actions_as_motion(self) -> None:
equipment_actions = (
"The camera makes a moving platform wobble",
"The camera executes a tracking calibration",
"The camera performs a rotating filter calibration",
)
for action in equipment_actions:
with self.subTest(action=action):
prompt = f"A medium shot at eye level shows the subject. {action}."
report = self.contract.analyze_ltx25_prompt_complexity(
prompt, "image_to_video", 15.0
)
self.assertEqual(report["observed"]["distinct_camera_moves"], 0)
self.assertEqual(report["observed"]["camera_motion_matches"], [])
self.assertIn("ltx_camera_state_missing", report["reasons"])
def test_camera_control_verbs_preserve_real_motion(self) -> None:
cases = (
("The camera performs a gentle pan right", "pan"),
("The camera executes a slow orbit around the subject", "orbit"),
("The camera makes a dolly-in toward the subject", "dolly"),
("A slowly rotating camera frames the subject", "roll"),
)
for action, family in cases:
with self.subTest(action=action):
prompt = f"A medium shot at eye level shows the subject. {action}."
report = self.contract.analyze_ltx25_prompt_complexity(
prompt, "image_to_video", 15.0
)
matches = report["observed"]["camera_motion_matches"]
self.assertIn(family, {match["family"] for match in matches})
self.assertNotIn("ltx_camera_state_missing", report["reasons"])
def test_not_only_coordination_keeps_both_camera_moves_positive(self) -> None:
prompt = (
"A medium shot at eye level shows the subject while the camera is not only panning "
"but also tilting."
)
report = self.contract.analyze_ltx25_prompt_complexity(
prompt, "image_to_video", 15.0
)
self.assertEqual(
{match["family"] for match in report["observed"]["camera_motion_matches"]},
{"pan", "tilt"},
)
self.assertEqual(
report["observed"]["camera_diagnostics"]["negated_motion_mentions"],
[],
)
def test_contrastive_negation_only_negates_the_first_camera_move(self) -> None:
prompt = (
"A medium shot at eye level shows the subject while the camera does not pan but instead "
"tilts upward."
)
report = self.contract.analyze_ltx25_prompt_complexity(
prompt, "image_to_video", 15.0
)
active_families = {
match["family"] for match in report["observed"]["camera_motion_matches"]
}
negated_families = {
match["family"]
for match in report["observed"]["camera_diagnostics"]["negated_motion_mentions"]
}
self.assertEqual(active_families, {"tilt"})
self.assertEqual(negated_families, {"pan"})
self.assertNotIn("ltx_camera_state_missing", report["reasons"])
def test_gerund_bearing_camera_negations_have_no_active_motion(self) -> None:
negated_actions = (
"The shot proceeds without using a slow pan",
"The camera avoids using a gentle tilt",
"The shot continues without executing a slow orbit",
"The shot continues without making a slow camera move",
)
for action in negated_actions:
with self.subTest(action=action):
prompt = f"A medium shot at eye level shows the subject. {action}."
report = self.contract.analyze_ltx25_prompt_complexity(
prompt, "image_to_video", 15.0
)
self.assertEqual(report["observed"]["distinct_camera_moves"], 0)
self.assertEqual(report["observed"]["camera_motion_matches"], [])
def test_article_bearing_camera_negations_have_no_active_motion(self) -> None:
negated_actions = (
"The shot proceeds without the camera slowly panning",
"The shot proceeds without any camera gently tilting",
"The shot proceeds without a camera slowly panning",
"The shot proceeds without the camera performing a slow pan",
"The shot proceeds without the camera using a slow pan",
)
for action in negated_actions:
with self.subTest(action=action):
prompt = f"A medium shot at eye level shows the subject. {action}."
report = self.contract.analyze_ltx25_prompt_complexity(
prompt, "image_to_video", 15.0
)
self.assertEqual(report["observed"]["distinct_camera_moves"], 0)
self.assertEqual(report["observed"]["camera_motion_matches"], [])
def test_control_verb_camera_negations_are_classified_as_negated(self) -> None:
negated_actions = (
"The director does not allow the camera to pan",
"The director does not let the camera pan",
"The director never permits the camera to pan",
"Do not let the camera pan",
"The director forbids the camera from panning",
)
for action in negated_actions:
with self.subTest(action=action):
prompt = f"A medium shot at eye level shows the subject. {action}."
report = self.contract.analyze_ltx25_prompt_complexity(
prompt, "image_to_video", 15.0
)
self.assertEqual(report["observed"]["distinct_camera_moves"], 0)
self.assertEqual(report["observed"]["camera_motion_matches"], [])
self.assertGreater(report["observed"]["negated_camera_move_count"], 0)
self.assertEqual(
{
match["family"]
for match in report["observed"]["camera_diagnostics"][
"negated_motion_mentions"
]
},
{"pan"},
)
def test_modified_control_verb_negations_keep_the_motion_negated(self) -> None:
cases = (
("The director does not allow camera to slowly pan", "pan"),
("The director does not let camera slowly pan", "pan"),
("The director never permits camera to gently tilt", "tilt"),
("The shot proceeds without allowing camera to gently tilt", "tilt"),
)
for action, family in cases:
with self.subTest(action=action):
prompt = f"A medium shot at eye level shows the subject. {action}."
report = self.contract.analyze_ltx25_prompt_complexity(
prompt, "image_to_video", 15.0
)
self.assertEqual(report["observed"]["distinct_camera_moves"], 0)
self.assertEqual(report["observed"]["camera_motion_matches"], [])
self.assertEqual(
{
match["family"]
for match in report["observed"]["camera_diagnostics"][
"negated_motion_mentions"
]
},
{family},
)
def test_gerund_bearing_cut_negations_do_not_create_edits(self) -> None:
negated_edits = (
"without making a cut",
"without using a hard cut",
"and avoids making any cuts",
"and does not make a cut",
)
for mode in ("image_to_video", "first_last_frame"):
for instruction in negated_edits:
with self.subTest(mode=mode, instruction=instruction):
prompt = (
"A static medium shot at eye level holds on the subject "
f"{instruction}."
)
report = self._validate_conditioned(prompt, mode)
self.assertEqual(report["complexity"]["observed"]["cut_count"], 0)
self.assertNotIn(
"ltx_conditioned_mode_requires_single_continuous_take",
report["reasons"],
)
def test_yet_coordination_only_negates_the_first_camera_move(self) -> None:
prompt = (
"A medium shot at eye level shows the subject while the camera does not pan, yet tilts "
"upward."
)
report = self.contract.analyze_ltx25_prompt_complexity(
prompt, "image_to_video", 15.0
)
active_families = {
match["family"] for match in report["observed"]["camera_motion_matches"]
}
negated_families = {
match["family"]
for match in report["observed"]["camera_diagnostics"]["negated_motion_mentions"]
}
self.assertEqual(active_families, {"tilt"})
self.assertEqual(negated_families, {"pan"})
self.assertNotIn("ltx_camera_state_missing", report["reasons"])
if __name__ == "__main__":
unittest.main()