Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd8ef84379 |
@@ -85,8 +85,9 @@
|
|||||||
|
|
||||||
- `Dumas Character Helper`
|
- `Dumas Character Helper`
|
||||||
- Inputs: `image1`, `image2`, picture IDs, character identity fields, `general`, `wardrobe`
|
- Inputs: `image1`, `image2`, picture IDs, character identity fields, `general`, `wardrobe`
|
||||||
- Outputs: `image1`, `image2`, `reference_prompt`, `wardrobe`
|
- Outputs: `image1`, `image2`, `reference_prompt`, `wardrobe`, `reference1`, `reference2`
|
||||||
- Restores the original general-purpose helper shape: pass two images through unchanged and emit prompt text/wardrobe text for manual wiring.
|
- Restores the original general-purpose helper shape while also emitting two structured `REFERENCE` objects for the prompt curator.
|
||||||
|
- The structured references carry the same character name, alias, age, height, gender, nationality, occupation, accent, wardrobe, and notes, so mentioning the character name in `Dumas H3 Prompt Curator` can include both helper images and the character facts automatically.
|
||||||
|
|
||||||
- `Dumas Location Helper`
|
- `Dumas Location Helper`
|
||||||
- Inputs: `image1`, `image2`, picture IDs, `location_id`, `name`, `alias`, `description`, `general`
|
- Inputs: `image1`, `image2`, picture IDs, `location_id`, `name`, `alias`, `description`, `general`
|
||||||
|
|||||||
+37
-3
@@ -1137,10 +1137,15 @@ def _reference_context(ref, compact_picture_number):
|
|||||||
|
|
||||||
def _subject_count_guard_text(selected_refs):
|
def _subject_count_guard_text(selected_refs):
|
||||||
character_labels = []
|
character_labels = []
|
||||||
|
seen_characters = set()
|
||||||
for picture_number, (_slot, ref) in enumerate(selected_refs or (), 1):
|
for picture_number, (_slot, ref) in enumerate(selected_refs or (), 1):
|
||||||
if ref.get("kind") != "character":
|
if ref.get("kind") != "character":
|
||||||
continue
|
continue
|
||||||
name = _reference_text(ref.get("name")) or _reference_text(ref.get("id"))
|
name = _reference_text(ref.get("name")) or _reference_text(ref.get("id"))
|
||||||
|
key = (_reference_text(ref.get("id")) or name or f"picture-{picture_number}").lower()
|
||||||
|
if key in seen_characters:
|
||||||
|
continue
|
||||||
|
seen_characters.add(key)
|
||||||
label = f"<Picture {picture_number}>"
|
label = f"<Picture {picture_number}>"
|
||||||
if name:
|
if name:
|
||||||
label = f"{label} {name}"
|
label = f"{label} {name}"
|
||||||
@@ -1960,8 +1965,8 @@ class DumasCharacterHelperNode:
|
|||||||
"IMAGE sockets plus simple identity fields, while passing both images "
|
"IMAGE sockets plus simple identity fields, while passing both images "
|
||||||
"through unchanged."
|
"through unchanged."
|
||||||
)
|
)
|
||||||
RETURN_TYPES = ("IMAGE", "IMAGE", "STRING", "STRING")
|
RETURN_TYPES = ("IMAGE", "IMAGE", "STRING", "STRING", _REFERENCE_TYPE, _REFERENCE_TYPE)
|
||||||
RETURN_NAMES = ("image1", "image2", "reference_prompt", "wardrobe")
|
RETURN_NAMES = ("image1", "image2", "reference_prompt", "wardrobe", "reference1", "reference2")
|
||||||
FUNCTION = "build_character_text"
|
FUNCTION = "build_character_text"
|
||||||
CATEGORY = "Dumas/MiniMax"
|
CATEGORY = "Dumas/MiniMax"
|
||||||
|
|
||||||
@@ -2122,7 +2127,36 @@ class DumasCharacterHelperNode:
|
|||||||
name,
|
name,
|
||||||
alias,
|
alias,
|
||||||
)
|
)
|
||||||
return (image1, image2, text, wardrobe_text)
|
facts = {
|
||||||
|
"gender": _normalize_free_text(gender),
|
||||||
|
"age": str(_parse_positive_int(age) or ""),
|
||||||
|
"nationality": _normalize_free_text(nationality),
|
||||||
|
"occupation": _normalize_free_text(occupation),
|
||||||
|
"height_feet": str(height_feet or "").strip(),
|
||||||
|
"height_inches": str(height_inches or "").strip(),
|
||||||
|
"accent": _normalize_free_text(accent),
|
||||||
|
}
|
||||||
|
common = {
|
||||||
|
"kind": "character",
|
||||||
|
"explicit_id": character_id,
|
||||||
|
"name": name,
|
||||||
|
"aliases": alias,
|
||||||
|
"description": general,
|
||||||
|
"wardrobe": wardrobe,
|
||||||
|
"general": general,
|
||||||
|
"facts": facts,
|
||||||
|
}
|
||||||
|
reference1 = make_reference(
|
||||||
|
image=image1,
|
||||||
|
summary="Primary full-body character reference.",
|
||||||
|
**common,
|
||||||
|
)
|
||||||
|
reference2 = make_reference(
|
||||||
|
image=image2,
|
||||||
|
summary="Secondary facial character reference.",
|
||||||
|
**common,
|
||||||
|
)
|
||||||
|
return (image1, image2, text, wardrobe_text, reference1, reference2)
|
||||||
|
|
||||||
|
|
||||||
class DumasLocationHelperNode:
|
class DumasLocationHelperNode:
|
||||||
|
|||||||
@@ -386,6 +386,17 @@ class DumasImageNodeTests(unittest.TestCase):
|
|||||||
self.assertIn("Dave is also known as The Locksmith", result[2])
|
self.assertIn("Dave is also known as The Locksmith", result[2])
|
||||||
self.assertIn("is 41 years old", result[2])
|
self.assertIn("is 41 years old", result[2])
|
||||||
self.assertEqual(result[3], "Dave = weathered red flight jacket, grey cargo shorts, black boots")
|
self.assertEqual(result[3], "Dave = weathered red flight jacket, grey cargo shorts, black boots")
|
||||||
|
self.assertIs(result[4]["image"], image1)
|
||||||
|
self.assertIs(result[5]["image"], image2)
|
||||||
|
self.assertEqual(result[4]["id"], "char-dave")
|
||||||
|
self.assertEqual(result[5]["id"], "char-dave")
|
||||||
|
self.assertEqual(result[4]["name"], "Dave")
|
||||||
|
self.assertEqual(result[4]["aliases"], ["The Locksmith"])
|
||||||
|
self.assertEqual(result[4]["facts"]["age"], "41")
|
||||||
|
self.assertEqual(result[4]["facts"]["height_feet"], "6")
|
||||||
|
self.assertEqual(result[4]["facts"]["height_inches"], "2")
|
||||||
|
self.assertEqual(result[4]["wardrobe"], "weathered red flight jacket, grey cargo shorts, black boots")
|
||||||
|
self.assertEqual(len(result), 6)
|
||||||
|
|
||||||
def test_location_helper_matches_character_helper_shape_without_wardrobe(self):
|
def test_location_helper_matches_character_helper_shape_without_wardrobe(self):
|
||||||
node = self.image_nodes.DumasLocationHelperNode()
|
node = self.image_nodes.DumasLocationHelperNode()
|
||||||
@@ -508,6 +519,47 @@ class DumasImageNodeTests(unittest.TestCase):
|
|||||||
self.assertIn("Only include the people explicitly described", result[0])
|
self.assertIn("Only include the people explicitly described", result[0])
|
||||||
self.assertEqual(result[10], 0)
|
self.assertEqual(result[10], 0)
|
||||||
|
|
||||||
|
def test_h3_prompt_curator_treats_helper_image_pair_as_one_character(self):
|
||||||
|
helper = self.image_nodes.DumasCharacterHelperNode()
|
||||||
|
curator = self.image_nodes.DumasH3PromptCuratorNode()
|
||||||
|
image1 = FakeTensorBatch()
|
||||||
|
image2 = FakeTensorBatch()
|
||||||
|
helper_result = helper.build_character_text(
|
||||||
|
image1=image1,
|
||||||
|
image2=image2,
|
||||||
|
image1_picture_id="1",
|
||||||
|
image2_picture_id="2",
|
||||||
|
character_id="char_dave",
|
||||||
|
name="Dave",
|
||||||
|
alias="The Locksmith",
|
||||||
|
gender="male",
|
||||||
|
age="41",
|
||||||
|
nationality="English",
|
||||||
|
occupation="detective",
|
||||||
|
height_feet="6",
|
||||||
|
height_inches="2",
|
||||||
|
accent="English",
|
||||||
|
general="Tired eyes, cropped brown hair",
|
||||||
|
wardrobe="weathered red flight jacket",
|
||||||
|
)
|
||||||
|
|
||||||
|
result = curator.curate_prompt(
|
||||||
|
action_prompt="Dave checks the locked door.",
|
||||||
|
anatomy_guard="on",
|
||||||
|
subject_count_guard="auto",
|
||||||
|
ref_1=helper_result[4],
|
||||||
|
ref_2=helper_result[5],
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertIs(result[1], image1)
|
||||||
|
self.assertIs(result[2], image2)
|
||||||
|
self.assertEqual(result[10], 2)
|
||||||
|
self.assertIn("Character facts for <Picture 1> Dave", result[0])
|
||||||
|
self.assertIn("41 years old", result[0])
|
||||||
|
self.assertIn("6 foot 2 tall", result[0])
|
||||||
|
self.assertIn("exactly one named character: <Picture 1> Dave", result[0])
|
||||||
|
self.assertNotIn("exactly 2 named characters", result[0])
|
||||||
|
|
||||||
def test_h3_prompt_curator_defaults_anatomy_guard_to_on(self):
|
def test_h3_prompt_curator_defaults_anatomy_guard_to_on(self):
|
||||||
required = self.image_nodes.DumasH3PromptCuratorNode.INPUT_TYPES()["required"]
|
required = self.image_nodes.DumasH3PromptCuratorNode.INPUT_TYPES()["required"]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user