Implement structured H3 reference objects
This commit is contained in:
@@ -154,9 +154,19 @@ class DumasH3LongVideosHelperTests(unittest.TestCase):
|
||||
self.assertGreaterEqual(fn.cache_info().hits, 2)
|
||||
|
||||
def test_resolve_tagged_refs_preserves_sparse_socket_numbers(self):
|
||||
refs = [None, "img2", None, None, None, None, "img7", None, "img9"]
|
||||
refs = [
|
||||
None,
|
||||
{"kind": "character", "image": "img2", "name": "Jon"},
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
{"kind": "character", "image": "img7", "name": "Mara"},
|
||||
None,
|
||||
{"kind": "location", "image": "img9", "name": "Watchtower"},
|
||||
]
|
||||
|
||||
text, images, dropped = self.module.resolve_tagged_refs(
|
||||
text, references, dropped = self.module.resolve_tagged_refs(
|
||||
"Mara <Picture 7> turns toward Jon <Picture 2> while <Picture 9> watches.",
|
||||
refs,
|
||||
)
|
||||
@@ -165,42 +175,61 @@ class DumasH3LongVideosHelperTests(unittest.TestCase):
|
||||
text,
|
||||
"Mara <Picture 2> turns toward Jon <Picture 1> while <Picture 3> watches.",
|
||||
)
|
||||
self.assertEqual(images, ["img2", "img7", "img9"])
|
||||
self.assertEqual(
|
||||
[self.module._reference_image(ref) for ref in references],
|
||||
["img2", "img7", "img9"],
|
||||
)
|
||||
self.assertEqual(dropped, [])
|
||||
|
||||
def test_resolve_tagged_refs_drops_unconnected_sparse_slots(self):
|
||||
refs = [None, "img2", None, None, None, None, "img7", None, None]
|
||||
refs = [
|
||||
None,
|
||||
{"kind": "character", "image": "img2", "name": "Jon"},
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
{"kind": "character", "image": "img7", "name": "Mara"},
|
||||
None,
|
||||
None,
|
||||
]
|
||||
|
||||
text, images, dropped = self.module.resolve_tagged_refs(
|
||||
text, references, dropped = self.module.resolve_tagged_refs(
|
||||
"Use <Picture 7>, skip <Picture 4>, keep <Picture 2>.",
|
||||
refs,
|
||||
)
|
||||
|
||||
self.assertEqual(text, "Use <Picture 2>, skip, keep <Picture 1>.")
|
||||
self.assertEqual(images, ["img2", "img7"])
|
||||
self.assertEqual(
|
||||
[self.module._reference_image(ref) for ref in references],
|
||||
["img2", "img7"],
|
||||
)
|
||||
self.assertEqual(dropped, [4])
|
||||
|
||||
def test_shot_references_uses_all_connected_sparse_slots(self):
|
||||
refs = [None, "img2", None, "img4", None, None, "img7", None, None]
|
||||
refs = [
|
||||
None,
|
||||
{"kind": "character", "image": "img2"},
|
||||
None,
|
||||
{"kind": "character", "image": "img4"},
|
||||
None,
|
||||
None,
|
||||
{"kind": "location", "image": "img7"},
|
||||
None,
|
||||
None,
|
||||
]
|
||||
|
||||
self.assertEqual(
|
||||
self.module.shot_references(refs, "auto ref2v", 0, None),
|
||||
["img2", "img4", "img7"],
|
||||
)
|
||||
self.assertEqual(
|
||||
self.module.shot_references(refs, "first shot", 0, None),
|
||||
["img2", "img4", "img7"],
|
||||
)
|
||||
self.assertEqual(
|
||||
self.module.shot_references(refs, "every shot", 3, None),
|
||||
["img2", "img4", "img7"],
|
||||
)
|
||||
for mode, shot_index in (("auto ref2v", 0), ("first shot", 0), ("every shot", 3)):
|
||||
self.assertEqual(
|
||||
[self.module._reference_image(ref) for ref in self.module.shot_references(refs, mode, shot_index, None)],
|
||||
["img2", "img4", "img7"],
|
||||
)
|
||||
|
||||
def test_input_types_expose_nine_ref_slots(self):
|
||||
optional = self.module.H3LongVideos.INPUT_TYPES()["optional"]
|
||||
|
||||
for index in range(1, 10):
|
||||
self.assertIn(f"ref_image_{index}", optional)
|
||||
self.assertIn(f"ref_{index}", optional)
|
||||
self.assertIn("plan", optional)
|
||||
self.assertIn("plan_scene_index", optional)
|
||||
|
||||
@@ -215,18 +244,64 @@ class DumasH3LongVideosHelperTests(unittest.TestCase):
|
||||
|
||||
self.assertEqual(len(refs), 9)
|
||||
self.assertIsNone(refs[0])
|
||||
self.assertIs(refs[1], image2)
|
||||
self.assertIs(refs[6], image7)
|
||||
self.assertIs(self.module._reference_image(refs[1]), image2)
|
||||
self.assertIs(self.module._reference_image(refs[6]), image7)
|
||||
|
||||
def test_merge_ref_slots_prefers_direct_refs_over_plan_refs(self):
|
||||
merged = self.module._merge_ref_slots(
|
||||
(None, "direct2", None, None, "direct5", None, None, None, None),
|
||||
("plan1", "plan2", "plan3", None, "plan5", None, "plan7", None, None),
|
||||
(
|
||||
None,
|
||||
{"kind": "character", "image": "direct2"},
|
||||
None,
|
||||
None,
|
||||
{"kind": "character", "image": "direct5"},
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
),
|
||||
(
|
||||
{"kind": "location", "image": "plan1"},
|
||||
{"kind": "location", "image": "plan2"},
|
||||
{"kind": "location", "image": "plan3"},
|
||||
None,
|
||||
{"kind": "location", "image": "plan5"},
|
||||
None,
|
||||
{"kind": "location", "image": "plan7"},
|
||||
None,
|
||||
None,
|
||||
),
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
merged,
|
||||
("plan1", "direct2", "plan3", None, "direct5", None, "plan7", None, None),
|
||||
[self.module._reference_image(ref) if ref is not None else None for ref in merged],
|
||||
["plan1", "direct2", "plan3", None, "direct5", None, "plan7", None, None],
|
||||
)
|
||||
|
||||
def test_reference_context_matches_character_names_and_location_tags(self):
|
||||
refs = [
|
||||
{"kind": "character", "image": "img1", "name": "Mara", "description": "silver hair", "wardrobe": "red jacket"},
|
||||
{"kind": "location", "image": "img2", "name": "Hangar", "description": "wet concrete floor"},
|
||||
]
|
||||
|
||||
context = self.module._reference_context_for_text(
|
||||
"[Generation 1] Mara crosses the room toward <Picture 2>.",
|
||||
refs,
|
||||
)
|
||||
|
||||
self.assertIn("Persistent appearance for Mara: silver hair.", context)
|
||||
self.assertIn("Persistent wardrobe/style for Mara: red jacket.", context)
|
||||
self.assertIn("Location context for Hangar: wet concrete floor.", context)
|
||||
|
||||
def test_reference_character_memory_uses_character_wardrobe_only(self):
|
||||
refs = [
|
||||
{"kind": "character", "image": "img1", "name": "Mara", "wardrobe": "red jacket, black boots"},
|
||||
{"kind": "location", "image": "img2", "name": "Hangar", "description": "wet concrete floor", "wardrobe": "should be ignored"},
|
||||
]
|
||||
|
||||
self.assertEqual(
|
||||
self.module._reference_character_memory(refs),
|
||||
"Mara = red jacket, black boots",
|
||||
)
|
||||
|
||||
def test_ref_mode_defaults_are_ref2v_biased(self):
|
||||
|
||||
@@ -239,16 +239,13 @@ class DumasImageNodeTests(unittest.TestCase):
|
||||
saved_path = FakePILImage.saved_paths[0][0]
|
||||
self.assertTrue(os.path.isdir(os.path.dirname(saved_path)))
|
||||
|
||||
def test_character_helper_passes_through_images_and_formats_text(self):
|
||||
node = self.image_nodes.DumasCharacterHelperNode()
|
||||
image1 = FakeTensorBatch()
|
||||
image2 = FakeTensorBatch()
|
||||
def test_character_reference_builds_structured_reference(self):
|
||||
node = self.image_nodes.DumasCharacterReferenceNode()
|
||||
image = FakeTensorBatch()
|
||||
|
||||
result = node.build_character_text(
|
||||
image1=image1,
|
||||
image2=image2,
|
||||
image1_picture_id="2",
|
||||
image2_picture_id="3",
|
||||
result = node.build_reference(
|
||||
image=image,
|
||||
picture_id="2",
|
||||
character_id="char_dave",
|
||||
name="Dave",
|
||||
alias="The Locksmith",
|
||||
@@ -259,37 +256,46 @@ class DumasImageNodeTests(unittest.TestCase):
|
||||
height_feet="6",
|
||||
height_inches="2",
|
||||
accent="English",
|
||||
description="Square jaw, tired eyes, cropped brown hair.",
|
||||
general="wears a long grey coat",
|
||||
wardrobe="weathered red flight jacket, grey cargo shorts, black boots",
|
||||
)
|
||||
|
||||
self.assertIs(result[0], image1)
|
||||
self.assertIs(result[1], image2)
|
||||
reference = result[0]
|
||||
self.assertIs(reference["image"], image)
|
||||
self.assertEqual(
|
||||
result[2],
|
||||
(
|
||||
"<Picture 2> and <Picture 3> reference the same character who is called Dave.\n"
|
||||
"<Picture 2> is the primary full-body reference for Dave.\n"
|
||||
"<Picture 3> is a frontal facial reference for Dave.\n"
|
||||
"Dave is also known as The Locksmith, is male, is 41 years old, is English, works as a detective, is 6 feet 2 inches tall, speaks in an English accent.\n"
|
||||
"wears a long grey coat."
|
||||
),
|
||||
)
|
||||
self.assertEqual(
|
||||
result[3],
|
||||
"Dave = weathered red flight jacket, grey cargo shorts, black boots",
|
||||
reference,
|
||||
{
|
||||
"kind": "character",
|
||||
"id": "char-dave",
|
||||
"name": "Dave",
|
||||
"aliases": ["The Locksmith"],
|
||||
"picture_id": 2,
|
||||
"picture_label": "<Picture 2>",
|
||||
"image": image,
|
||||
"summary": "Dave shown in <Picture 2>.",
|
||||
"description": "Square jaw, tired eyes, cropped brown hair.",
|
||||
"wardrobe": "weathered red flight jacket, grey cargo shorts, black boots",
|
||||
"general": "wears a long grey coat",
|
||||
"facts": {
|
||||
"gender": "male",
|
||||
"age": "41",
|
||||
"nationality": "English",
|
||||
"occupation": "a detective",
|
||||
"height_feet": "6",
|
||||
"height_inches": "2",
|
||||
"accent": "English",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
def test_character_helper_handles_missing_optional_fields(self):
|
||||
node = self.image_nodes.DumasCharacterHelperNode()
|
||||
image1 = FakeTensorBatch()
|
||||
image2 = FakeTensorBatch()
|
||||
def test_character_reference_handles_missing_optional_fields(self):
|
||||
node = self.image_nodes.DumasCharacterReferenceNode()
|
||||
image = FakeTensorBatch()
|
||||
|
||||
result = node.build_character_text(
|
||||
image1=image1,
|
||||
image2=image2,
|
||||
image1_picture_id="4",
|
||||
image2_picture_id="6",
|
||||
result = node.build_reference(
|
||||
image=image,
|
||||
picture_id="4",
|
||||
character_id="",
|
||||
name="",
|
||||
alias="",
|
||||
@@ -300,53 +306,50 @@ class DumasImageNodeTests(unittest.TestCase):
|
||||
height_feet="",
|
||||
height_inches="",
|
||||
accent="",
|
||||
description="",
|
||||
general="",
|
||||
wardrobe="",
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
result[2],
|
||||
(
|
||||
"<Picture 4> and <Picture 6> reference the same character.\n"
|
||||
"<Picture 4> is the primary full-body reference for the character.\n"
|
||||
"<Picture 6> is a frontal facial reference for the character."
|
||||
),
|
||||
)
|
||||
self.assertEqual(result[3], "")
|
||||
reference = result[0]
|
||||
self.assertEqual(reference["kind"], "character")
|
||||
self.assertEqual(reference["picture_id"], 4)
|
||||
self.assertEqual(reference["picture_label"], "<Picture 4>")
|
||||
self.assertEqual(reference["wardrobe"], "")
|
||||
self.assertEqual(reference["general"], "")
|
||||
self.assertEqual(reference["facts"]["age"], "")
|
||||
|
||||
def test_character_helper_allows_picture_one_and_preserves_full_sheet_wardrobe(self):
|
||||
node = self.image_nodes.DumasCharacterHelperNode()
|
||||
image1 = FakeTensorBatch()
|
||||
image2 = FakeTensorBatch()
|
||||
def test_location_reference_builds_structured_reference(self):
|
||||
node = self.image_nodes.DumasLocationReferenceNode()
|
||||
image = FakeTensorBatch()
|
||||
|
||||
result = node.build_character_text(
|
||||
image1=image1,
|
||||
image2=image2,
|
||||
image1_picture_id="1",
|
||||
image2_picture_id="9",
|
||||
character_id="char_kristy",
|
||||
name="Kristy",
|
||||
alias="",
|
||||
gender="",
|
||||
age="",
|
||||
nationality="",
|
||||
occupation="",
|
||||
height_feet="",
|
||||
height_inches="",
|
||||
accent="",
|
||||
general="",
|
||||
wardrobe="Kristy = black coat, silver boots",
|
||||
result = node.build_reference(
|
||||
image=image,
|
||||
picture_id="9",
|
||||
location_id="coffee-shop-01",
|
||||
name="Coffee Shop",
|
||||
alias="Cafe Interior",
|
||||
description="Warm tungsten lighting, narrow counter, rainy front window.",
|
||||
general="Evening ambience, cramped but cozy.",
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
result[2],
|
||||
(
|
||||
"<Picture 1> and <Picture 9> reference the same character who is called Kristy.\n"
|
||||
"<Picture 1> is the primary full-body reference for Kristy.\n"
|
||||
"<Picture 9> is a frontal facial reference for Kristy."
|
||||
),
|
||||
result[0],
|
||||
{
|
||||
"kind": "location",
|
||||
"id": "coffee-shop-01",
|
||||
"name": "Coffee Shop",
|
||||
"aliases": ["Cafe Interior"],
|
||||
"picture_id": 9,
|
||||
"picture_label": "<Picture 9>",
|
||||
"image": image,
|
||||
"summary": "Coffee Shop shown in <Picture 9>.",
|
||||
"description": "Warm tungsten lighting, narrow counter, rainy front window.",
|
||||
"wardrobe": "",
|
||||
"general": "Evening ambience, cramped but cozy.",
|
||||
"facts": {},
|
||||
},
|
||||
)
|
||||
self.assertEqual(result[3], "Kristy = black coat, silver boots")
|
||||
|
||||
def test_save_image_returns_ui_entries_for_output_folder(self):
|
||||
node = self.image_nodes.DumasSaveImageNode()
|
||||
|
||||
Reference in New Issue
Block a user