Compare commits

...
1 Commits
Author SHA1 Message Date
chris.dumas a51141cb26 Add location helper reference outputs 2026-09-07 07:46:24 +00:00
3 changed files with 71 additions and 7 deletions
+4 -3
View File
@@ -91,8 +91,9 @@
- `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`
- Outputs: `image1`, `image2`, `reference_prompt` - Outputs: `image1`, `image2`, `reference_prompt`, `reference1`, `reference2`
- Matching general-purpose helper for environments/locations: pass two images through unchanged and emit location reference prompt text. - Matching general-purpose helper for environments/locations: pass two images through unchanged, emit location reference prompt text, and provide two structured `REFERENCE` objects for the prompt curator.
- The structured references carry the same location name, alias, description, and notes, so mentioning the location name in `Dumas H3 Prompt Curator` can include both helper images and the location context automatically.
- `Dumas Anchor Style` - `Dumas Anchor Style`
- Inputs: `anchor_style`, `style_description` - Inputs: `anchor_style`, `style_description`
@@ -262,7 +263,7 @@ decr -> use index - 1
`Dumas H3 Plan Attach Scene Images` and `Dumas H3 Plan Extract Scene Images` are a companion pair for `ComfyUI-MiniMaxH3-Contex-Loop` and the local `ref2v` lane. The upstream H3 plan node cannot dynamically grow nine new image sockets for every JSON-defined scene, so Dumas stores scene image bindings beside the plan using a lightweight token and an in-memory registry. That keeps `plan.json` archiving intact while still letting you wire up nine IMAGE sockets per scene through chained helper nodes. `Dumas H3 Plan Attach Scene Images` and `Dumas H3 Plan Extract Scene Images` are a companion pair for `ComfyUI-MiniMaxH3-Contex-Loop` and the local `ref2v` lane. The upstream H3 plan node cannot dynamically grow nine new image sockets for every JSON-defined scene, so Dumas stores scene image bindings beside the plan using a lightweight token and an in-memory registry. That keeps `plan.json` archiving intact while still letting you wire up nine IMAGE sockets per scene through chained helper nodes.
`Dumas Character Helper` is the restored two-image/text helper for general H3 workflows, and `Dumas Location Helper` mirrors it for scene/environment references. The structured `Dumas Character Reference` and `Dumas Location Reference` nodes remain available separately for workflows that want a single `REFERENCE` socket. `Dumas H3 Prompt Curator` consumes those structured references, assigns the final `<Picture N>` numbering, and outputs only the compacted images the prompt actually mentions. `Dumas Character Helper` is the restored two-image/text helper for general H3 workflows, and `Dumas Location Helper` mirrors it for scene/environment references. Both helpers also emit structured `REFERENCE` sockets for the curator. The structured `Dumas Character Reference` and `Dumas Location Reference` nodes remain available separately for workflows that want a single `REFERENCE` socket. `Dumas H3 Prompt Curator` consumes those structured references, assigns the final `<Picture N>` numbering, and outputs only the compacted images the prompt actually mentions.
`Dumas Strip Iteration Suffix` keeps the part before the first underscore and drops the rest. Names like `char123_pose_final.png` become `char123.png`, while names with no underscore such as `char123.png` are left untouched. `Dumas Strip Iteration Suffix` keeps the part before the first underscore and drops the rest. Names like `char123_pose_final.png` become `char123.png`, while names with no underscore such as `char123.png` are left untouched.
+22 -3
View File
@@ -2164,8 +2164,8 @@ class DumasLocationHelperNode:
"Build a general location reference prompt from two IMAGE sockets plus " "Build a general location reference prompt from two IMAGE sockets plus "
"simple environment fields, while passing both images through unchanged." "simple environment fields, while passing both images through unchanged."
) )
RETURN_TYPES = ("IMAGE", "IMAGE", "STRING") RETURN_TYPES = ("IMAGE", "IMAGE", "STRING", _REFERENCE_TYPE, _REFERENCE_TYPE)
RETURN_NAMES = ("image1", "image2", "reference_prompt") RETURN_NAMES = ("image1", "image2", "reference_prompt", "reference1", "reference2")
FUNCTION = "build_location_text" FUNCTION = "build_location_text"
CATEGORY = "Dumas/MiniMax" CATEGORY = "Dumas/MiniMax"
@@ -2253,7 +2253,26 @@ class DumasLocationHelperNode:
description, description,
general, general,
) )
return (image1, image2, text) common = {
"kind": "location",
"explicit_id": location_id,
"name": name,
"aliases": alias,
"description": description,
"general": general,
"facts": {},
}
reference1 = make_reference(
image=image1,
summary="Primary location reference.",
**common,
)
reference2 = make_reference(
image=image2,
summary="Secondary location reference.",
**common,
)
return (image1, image2, text, reference1, reference2)
class DumasCharacterReferenceNode: class DumasCharacterReferenceNode:
+45 -1
View File
@@ -421,7 +421,17 @@ class DumasImageNodeTests(unittest.TestCase):
self.assertIn("Coffee Shop is also known as Cafe Interior", result[2]) self.assertIn("Coffee Shop is also known as Cafe Interior", result[2])
self.assertIn("Warm tungsten lighting, narrow counter, rainy front window.", result[2]) self.assertIn("Warm tungsten lighting, narrow counter, rainy front window.", result[2])
self.assertIn("Evening ambience, cramped but cozy.", result[2]) self.assertIn("Evening ambience, cramped but cozy.", result[2])
self.assertEqual(len(result), 3) self.assertIs(result[3]["image"], image1)
self.assertIs(result[4]["image"], image2)
self.assertEqual(result[3]["kind"], "location")
self.assertEqual(result[4]["kind"], "location")
self.assertEqual(result[3]["id"], "coffee-shop-01")
self.assertEqual(result[4]["id"], "coffee-shop-01")
self.assertEqual(result[3]["name"], "Coffee Shop")
self.assertEqual(result[3]["aliases"], ["Cafe Interior"])
self.assertEqual(result[3]["description"], "Warm tungsten lighting, narrow counter, rainy front window")
self.assertEqual(result[3]["general"], "Evening ambience, cramped but cozy")
self.assertEqual(len(result), 5)
def test_soundscape_helper_defaults_to_selected_preset_description(self): def test_soundscape_helper_defaults_to_selected_preset_description(self):
node = self.image_nodes.DumasSoundscapeHelperNode() node = self.image_nodes.DumasSoundscapeHelperNode()
@@ -560,6 +570,40 @@ class DumasImageNodeTests(unittest.TestCase):
self.assertIn("exactly one named character: <Picture 1> Dave", result[0]) self.assertIn("exactly one named character: <Picture 1> Dave", result[0])
self.assertNotIn("exactly 2 named characters", result[0]) self.assertNotIn("exactly 2 named characters", result[0])
def test_h3_prompt_curator_uses_location_helper_references_by_name(self):
helper = self.image_nodes.DumasLocationHelperNode()
curator = self.image_nodes.DumasH3PromptCuratorNode()
image1 = FakeTensorBatch()
image2 = FakeTensorBatch()
helper_result = helper.build_location_text(
image1=image1,
image2=image2,
image1_picture_id="1",
image2_picture_id="2",
location_id="coffee_shop",
name="Coffee Shop",
alias="Cafe Interior",
description="Warm tungsten lighting, narrow counter, rainy front window",
general="Evening ambience, cramped but cozy",
)
result = curator.curate_prompt(
action_prompt="A slow push through the Coffee Shop as rain streaks the windows.",
anatomy_guard="on",
subject_count_guard="auto",
ref_1=helper_result[3],
ref_2=helper_result[4],
)
self.assertIs(result[1], image1)
self.assertIs(result[2], image2)
self.assertEqual(result[10], 2)
self.assertIn("<Picture 1> Coffee Shop", result[0])
self.assertIn("<Picture 2> Coffee Shop", result[0])
self.assertIn("Location context for <Picture 1> Coffee Shop", result[0])
self.assertIn("Warm tungsten lighting", result[0])
self.assertNotIn("Subject count guard:", 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"]