Revert "Revert long videos ref routing to release"

This reverts commit 00985b73b3.
This commit is contained in:
2026-08-30 21:37:15 +00:00
parent b7c3bba199
commit a2dabaab7e
2 changed files with 93 additions and 8 deletions
+28 -8
View File
@@ -4558,7 +4558,7 @@ def _connected_refs(ref_slots):
return [ref for ref in (ref_slots or []) if _reference_image(ref) is not None] return [ref for ref in (ref_slots or []) if _reference_image(ref) is not None]
def resolve_tagged_refs(text, ref_list): def resolve_tagged_refs(text, ref_list):
"""(rewritten text, images, dropped) for the <Picture N> tags in ONE shot. """(rewritten text, images, dropped) for the <Picture N> tags in ONE shot.
The tokenizer numbers references by their position in the list it is handed, so The tokenizer numbers references by their position in the list it is handed, so
@@ -4579,11 +4579,31 @@ def resolve_tagged_refs(text, ref_list):
return f"<Picture {renumber[n]}>" if n in renumber else "" return f"<Picture {renumber[n]}>" if n in renumber else ""
out = _PICTURE_TAG.sub(sub, text or "") out = _PICTURE_TAG.sub(sub, text or "")
if dropped: # tidy the gap a removed tag leaves behind if dropped: # tidy the gap a removed tag leaves behind
out = re.sub(r"\s+([,.;:])", r"\1", out) out = re.sub(r"\s+([,.;:])", r"\1", out)
out = re.sub(r"(,\s*){2,}", ", ", out) out = re.sub(r"(,\s*){2,}", ", ", out)
out = re.sub(r"\s{2,}", " ", out) out = re.sub(r"\s{2,}", " ", out)
return out.strip(), [ref_list[n - 1] for n in live], dropped return out.strip(), [ref_list[n - 1] for n in live], dropped
def resolve_prompt_refs(text, ref_list):
"""(rewritten text, refs, dropped) for the refs a shot actually carries.
Explicit <Picture N> tags still decide which slot numbers the prompt points at,
but named character matches must ride into the real ref-image list too. Without
that split, a shot could inherit the facts/context for "Mara" and "Jon" while
only carrying a tagged location image, which reads exactly like the names were
understood but the faces were ignored."""
rewritten, tagged_refs, dropped = resolve_tagged_refs(text, ref_list)
refs = list(tagged_refs)
seen = {id(ref) for ref in refs}
for _slot_number, ref in _named_character_refs_for_text(rewritten, ref_list):
marker = id(ref)
if marker in seen:
continue
seen.add(marker)
refs.append(ref)
return rewritten, refs, dropped
def shot_references(ref_list, ref_mode, shot_index, handoff): def shot_references(ref_list, ref_mode, shot_index, handoff):
@@ -6750,7 +6770,7 @@ class H3LongVideos:
for shot_index, gen in enumerate(gens): for shot_index, gen in enumerate(gens):
shot_mode = beat_ref_mode_directive(beats[shot_index] if shot_index < len(beats) else "") or ref_mode shot_mode = beat_ref_mode_directive(beats[shot_index] if shot_index < len(beats) else "") or ref_mode
if shot_mode in ("where tagged", "auto ref2v") and any_tags_anywhere: if shot_mode in ("where tagged", "auto ref2v") and any_tags_anywhere:
if resolve_tagged_refs(gen, ref_slots)[1]: if resolve_prompt_refs(gen, ref_slots)[1]:
on.append(shot_index + 1) on.append(shot_index + 1)
tagged_used = True tagged_used = True
else: else:
@@ -6845,7 +6865,7 @@ class H3LongVideos:
# The prompt itself says where each reference belongs: the shot whose # The prompt itself says where each reference belongs: the shot whose
# text names <Picture N> gets image N, renumbered to match what that # text names <Picture N> gets image N, renumbered to match what that
# shot actually carries. Every untagged shot keeps its handoff. # shot actually carries. Every untagged shot keeps its handoff.
gen_prompt, shot_refs, dropped = resolve_tagged_refs(gen_prompt, ref_slots) gen_prompt, shot_refs, dropped = resolve_prompt_refs(gen_prompt, ref_slots)
for n in dropped: for n in dropped:
if n not in ref_missing: if n not in ref_missing:
ref_missing.append(n) ref_missing.append(n)
+65
View File
@@ -423,6 +423,41 @@ class DumasH3LongVideosHelperTests(unittest.TestCase):
) )
self.assertEqual(dropped, [4]) self.assertEqual(dropped, [4])
def test_resolve_prompt_refs_keeps_named_character_images_alongside_tagged_location(self):
refs = [
{"kind": "character", "image": "img1", "name": "Mara"},
{"kind": "character", "image": "img2", "name": "Jon"},
{"kind": "location", "image": "img3", "name": "Hangar"},
]
text, references, dropped = self.module.resolve_prompt_refs(
"Mara and Jon argue inside <Picture 3>.",
refs,
)
self.assertEqual(text, "Mara and Jon argue inside <Picture 1>.")
self.assertEqual(
[self.module._reference_image(ref) for ref in references],
["img3", "img1", "img2"],
)
self.assertEqual(dropped, [])
def test_resolve_prompt_refs_where_tagged_mode_stays_tag_only(self):
refs = [
{"kind": "character", "image": "img1", "name": "Mara"},
{"kind": "location", "image": "img2", "name": "Hangar"},
]
text, references, dropped = self.module.resolve_prompt_refs(
"Mara waits in the hangar near <Picture 2>.",
refs,
include_named=False,
)
self.assertEqual(text, "Mara waits in the hangar near <Picture 1>.")
self.assertEqual([self.module._reference_image(ref) for ref in references], ["img2"])
self.assertEqual(dropped, [])
def test_resolve_shot_references_uses_named_characters_without_picture_tags(self): def test_resolve_shot_references_uses_named_characters_without_picture_tags(self):
refs = [ refs = [
{"kind": "character", "image": "img1", "name": "Mara"}, {"kind": "character", "image": "img1", "name": "Mara"},
@@ -444,6 +479,21 @@ class DumasH3LongVideosHelperTests(unittest.TestCase):
self.assertFalse(shot_tag_driven) self.assertFalse(shot_tag_driven)
self.assertEqual(mode_eff, "auto ref2v") self.assertEqual(mode_eff, "auto ref2v")
def test_resolve_prompt_refs_prioritizes_characters_before_locations(self):
refs = [
{"kind": "location", "image": "img1", "name": "Hangar"},
{"kind": "character", "image": "img2", "name": "Mara"},
]
text, references, dropped = self.module.resolve_prompt_refs(
"[Generation 1] Mara waits in the hangar.",
refs,
)
self.assertEqual(text, "[Generation 1] Mara waits in the hangar.")
self.assertEqual([self.module._reference_image(ref) for ref in references], ["img2", "img1"])
self.assertEqual(dropped, [])
def test_shot_references_uses_all_connected_sparse_slots(self): def test_shot_references_uses_all_connected_sparse_slots(self):
refs = [ refs = [
None, None,
@@ -697,6 +747,21 @@ class DumasH3LongVideosHelperTests(unittest.TestCase):
self.assertIn("Persistent appearance for Mara: silver hair.", context) self.assertIn("Persistent appearance for Mara: silver hair.", context)
self.assertNotIn("Persistent wardrobe/style for Mara: red jacket.", context) self.assertNotIn("Persistent wardrobe/style for Mara: red jacket.", context)
def test_resolve_prompt_refs_adds_named_location_refs(self):
refs = [
{"kind": "location", "image": "img2", "name": "Hangar", "description": "wet concrete floor"},
]
rewritten, matched, dropped = self.module.resolve_prompt_refs(
"[Generation 1] They wait in the hangar.",
refs,
)
self.assertEqual(rewritten, "[Generation 1] They wait in the hangar.")
self.assertEqual(dropped, [])
self.assertEqual(len(matched), 1)
self.assertEqual(matched[0]["name"], "Hangar")
def test_resolve_tagged_refs_drops_reference_without_image(self): def test_resolve_tagged_refs_drops_reference_without_image(self):
refs = [ refs = [
{"kind": "character", "image": None, "name": "Mara"}, {"kind": "character", "image": None, "name": "Mara"},