Files
IAMCCS-IAMCCS-nodes/tests/test_prompter_evolving.py

163 lines
7.9 KiB
Python

import json
import iamccs_prompter as prompter
def test_evolving_natural_language_infers_boundaries_and_frames():
beats = prompter.parse_evolving_timeline(
"0-5 seconds: walks through the city.\n"
"At 5 seconds she stops and looks up.\n"
"A 10 secondi inizia a correre.",
duration_seconds=20,
fps=24,
)
assert [(beat["start_frame"], beat["end_frame"]) for beat in beats] == [
(0, 120), (120, 240), (240, 480),
]
assert beats[2]["action"] == "inizia a correre"
def test_evolving_rejects_untimed_prose():
try:
prompter.parse_evolving_timeline("She walks and later starts running.", duration_seconds=20)
except ValueError as exc:
assert "second marker" in str(exc)
else:
raise AssertionError("untimed evolving prose must fail instead of silently becoming continuous")
def test_prompter_exports_evolving_policy_and_demo_text():
project = prompter.default_project()
project["task_mode"] = "fl2va"
project["sections"]["action"] = "The woman crosses the city in one uninterrupted take."
project["extended_conditioning_policy"] = "evolving"
project["evolving_timeline"] = prompter.EVOLVING_DEMO_TIMELINE
output = prompter.IAMCCS_Prompter().compose(
json.dumps(project), "fl2va", "global", "guided", "replace", 6800
)
result = output["result"]
linx = result[0]
request = linx["resources"]["iamccs_prompter_injection"]
report = json.loads(result[3])
assert request["extended_conditioning_policy"] == "evolving"
assert "At 10 seconds" in request["evolving_timeline"]
assert report["extended_conditioning"]["evolving_event_count"] == 4
assert report["extended_conditioning"]["required_shotboard_mode"] == "fl2va_extended_av"
def test_existing_projects_without_policy_migrate_to_default():
project = prompter._safe_project({"schema_version": 4, "sections": {"scene": "A street."}})
assert project["schema_version"] == 7
assert project["extended_conditioning_policy"] == "default"
assert project["evolving_timeline"] == ""
def test_fl2va_continuous_is_one_untimed_global_injection_without_local_prompt():
project = prompter.default_project()
project["task_mode"] = "fl2va"
project["extended_conditioning_policy"] = "continuous"
project["sections"].update({
"boundary_frames": "Picture 1 is the exact opening visual authority.",
"identity_continuity_locks": "Preserve the same woman and bicycle.",
"action": "0-8 seconds: the woman pedals steadily toward the bridge.",
"shot_list": "At 8 seconds she keeps pedaling steadily toward the bridge.",
"camera": "One steady backward tracking camera.",
})
output = prompter.IAMCCS_Prompter().compose(
json.dumps(project), "fl2va", "local_1", "guided", "replace", 6800
)
linx, final_prompt, _project_json, report_json, *_ = output["result"]
report = json.loads(report_json)
injections = linx["resources"]["iamccs_prompter_injections"]
assert [item["target"] for item in injections] == ["global"]
assert "continuous_action:" in final_prompt
assert "the woman pedals steadily toward the bridge" in final_prompt
assert "0-8 seconds" not in final_prompt
assert "At 8 seconds" not in final_prompt
assert linx["resources"]["iamccs_prompter_local_prompt"] == ""
assert report["local_characters"] == 0
def test_fl2va_default_keeps_normal_global_and_local_contract():
project = prompter.default_project()
project["task_mode"] = "fl2va"
project["sections"].update({
"identity_continuity_locks": "Preserve the same woman.",
"action": "The woman turns toward the window.",
})
output = prompter.IAMCCS_Prompter().compose(
json.dumps(project), "fl2va", "local_1", "guided", "replace", 6800
)
linx = output["result"][0]
assert project["extended_conditioning_policy"] == "default"
assert [item["target"] for item in linx["resources"]["iamccs_prompter_injections"]] == ["global", "local_1"]
def test_fl2va_backend_separates_static_global_from_action_local():
project = prompter.default_project()
project["task_mode"] = "fl2va"
project["sections"].update({
"boundary_frames": "Picture 1 opens the take and Picture 2 defines the final composition.",
"identity_continuity_locks": "Preserve the same soldier woman and army throughout.",
"action": "The soldier woman walks toward camera and raises her sword.",
"shot_list": "At 4 seconds she raises her sword.",
"camera": "One steady backward tracking shot; no cuts or camera reset.",
"production_sound": "Footsteps and one sword draw at 4 seconds.",
"non_diegetic_music": "No score.",
})
final_prompt, details = prompter._compose_prompt(project, "fl2va", "guided", "")
assert "raises her sword" not in final_prompt
assert "At 4 seconds" not in final_prompt
assert "no cuts" not in final_prompt.lower()
assert "Preserve the same soldier woman" in final_prompt
assert "raises her sword" in details["local_prompt"]
assert "At 4 seconds" in details["local_prompt"]
def test_final_prompt_override_is_preserved_by_backend():
project = prompter.default_project()
project["task_mode"] = "fl2va"
project["sections"]["identity_continuity_locks"] = "Generated identity continuity."
project["sections"]["action"] = "Generated local action."
project["final_prompt_override_enabled"] = True
project["final_prompt_override"] = "MANUALLY EDITED GLOBAL"
project["final_local_prompt_override_enabled"] = True
project["final_local_prompt_override"] = "MANUALLY EDITED LOCAL"
output = prompter.IAMCCS_Prompter().compose(json.dumps(project), "fl2va", "global", "guided", "replace", 6800)
linx, final_prompt, _project_json, report_json, *_ = output["result"]
report = json.loads(report_json)
assert final_prompt == "MANUALLY EDITED GLOBAL"
assert linx["resources"]["iamccs_prompter_local_prompt"] == "MANUALLY EDITED LOCAL"
assert report["final_prompt_override"] is True
assert report["final_local_prompt_override"] is True
def test_canonical_evolving_repairs_malformed_ai_timeline_before_queue():
raw = (
"0-12 seconds: the soldier woman takes a deep breath and walks toward the camera while the army follows behind her in the same formation and direction. "
"[ONSET_once] 12-22 seconds: the soldier woman gives one brief rage scream toward the camera and the soldiers immediately answer with one brief collective scream "
"[SUSTAIN] after both screams have finished, they continue advancing toward the camera in the same formation without repeating either scream before\n"
"22 seconds. 22-30 seconds: the soldier woman stops and starts to laugh while the army behind her starts to sing epic battle songs; continue this final performance naturally through the end"
)
canonical = prompter._validate_canonical_evolving_timeline(raw)
lines = canonical.splitlines()
assert len(lines) == 3
assert lines[0].startswith("0-12 seconds: [ONSET ONCE]")
assert "[THEN SUSTAIN] the soldier woman walks toward the camera" in lines[0]
assert lines[1].startswith("12-22 seconds: [ONSET ONCE]")
assert "[THEN SUSTAIN] they continue advancing toward the camera" in lines[1]
assert "after both screams" not in lines[1].lower()
assert "without repeating" not in lines[1].lower()
assert lines[2].startswith("22-30 seconds: [ONSET ONCE] the soldier woman stops")
assert "continues laughing" in lines[2]
assert "continues singing" in lines[2]
assert "[ONSET_once]" not in canonical
assert "[SUSTAIN]" not in canonical
def test_canonical_evolving_timestamp_precedes_all_tags():
raw = "[ONSET_once] 12-22 seconds: she shouts once [SUSTAIN] she keeps marching"
canonical = prompter._validate_canonical_evolving_timeline(raw)
assert canonical == "12-22 seconds: [ONSET ONCE] she shouts once; [THEN SUSTAIN] she keeps marching"