Keep curator auxiliary refs input-aligned

This commit is contained in:
2026-09-17 16:02:27 +00:00
parent bfaa5fa733
commit b5eb941c4b
3 changed files with 59 additions and 15 deletions
+2 -2
View File
@@ -58,7 +58,7 @@
- Outputs: `prompt`, `ref_image_1` through `ref_image_9`, `reference_count`, `debug`, `anchor`, `sounds`, `bgm`, `original_ref_1` through `original_ref_9`, `compiled_ref_description_1` through `compiled_ref_description_9`
- Builds one standalone MiniMax H3 prompt from your final action text plus structured character/location references.
- The action text can mention references by character/location name, alias, `<Picture N>`, or `<refN>`. Only mentioned references are emitted, and the output images are compacted/renumbered so skipped inputs do not leave gaps.
- Extra component outputs expose the cleaned anchor, sounds, BGM, and each selected original reference image plus its compiled reference description in compacted order.
- Extra component outputs expose the cleaned anchor, sounds, BGM, and each connected input reference image plus its compiled reference description in original socket order. These inspection/reuse outputs are not trimmed by the action text; only the final prompt and `ref_image_*` H3 reference outputs are trimmed.
- Adds curated reference context, anatomy guard text, optional subject-count guard text, anchor/style text, `overall_soundscape:` text, and `background_music:` text while respecting MiniMax H3's reference-generation shape: one prompt plus up to nine reference images.
- `Dumas H3 Shot Length`
@@ -266,7 +266,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 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 plus optional anchor, soundscape, and BGM strings, 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 plus optional anchor, soundscape, and BGM strings, assigns the final `<Picture N>` numbering, and outputs compacted `ref_image_*` sockets for only the references the prompt actually mentions. Its `original_ref_*` and `compiled_ref_description_*` outputs mirror the connected input sockets for reuse/debugging even when a reference is not mentioned in the final prompt.
`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.
+15 -8
View File
@@ -1253,13 +1253,13 @@ def curate_h3_prompt(
soundscape_text = _reference_text(soundscape)
bgm_text = _reference_text(bgm)
reference_description = ""
individual_reference_descriptions = []
selected_reference_descriptions = []
if selected:
individual_reference_descriptions = [
selected_reference_descriptions = [
_reference_context(ref, picture_number)
for picture_number, (_slot, ref) in enumerate(selected, 1)
]
reference_description = " ".join(individual_reference_descriptions)
reference_description = " ".join(selected_reference_descriptions)
prompt_parts = []
_append_prompt_section(prompt_parts, "Scene anchor", anchor_text)
@@ -1279,9 +1279,16 @@ def curate_h3_prompt(
prompt = prompt[: _H3_PROMPT_MAX_CHARS - 3].rstrip() + "..."
images = [_reference_image(ref) for _slot, ref in selected]
images.extend([None] * (_H3_PROMPT_REF_SLOTS - len(images)))
individual_reference_descriptions.extend(
[""] * (_H3_PROMPT_REF_SLOTS - len(individual_reference_descriptions))
)
original_images = [
_reference_image(ref) if ref is not None else None
for ref in normalized_refs
]
original_images.extend([None] * (_H3_PROMPT_REF_SLOTS - len(original_images)))
input_reference_descriptions = [
_reference_context(ref, slot_number) if ref is not None else ""
for slot_number, ref in enumerate(normalized_refs, 1)
]
input_reference_descriptions.extend([""] * (_H3_PROMPT_REF_SLOTS - len(input_reference_descriptions)))
debug = (
f"Selected {len(selected)} reference(s): "
+ ", ".join(
@@ -1299,8 +1306,8 @@ def curate_h3_prompt(
anchor_text,
soundscape_text,
bgm_text,
*images[:_H3_PROMPT_REF_SLOTS],
*individual_reference_descriptions[:_H3_PROMPT_REF_SLOTS],
*original_images[:_H3_PROMPT_REF_SLOTS],
*input_reference_descriptions[:_H3_PROMPT_REF_SLOTS],
)
+42 -5
View File
@@ -503,13 +503,50 @@ class DumasImageNodeTests(unittest.TestCase):
self.assertEqual(result[13], "steady rain")
self.assertEqual(result[14], "low suspense music")
self.assertIs(result[15], dave_image)
self.assertIs(result[16], cafe_image)
self.assertIsNone(result[17])
self.assertIs(result[16], van_image)
self.assertIs(result[17], cafe_image)
self.assertIn("<Picture 1> Dave", result[24])
self.assertNotIn("<Picture 2> Coffee Shop", result[24])
self.assertIn("<Picture 2> Coffee Shop", result[25])
self.assertIn("Location context for <Picture 2> Coffee Shop", result[25])
self.assertEqual(result[26], "")
self.assertIn("<Picture 2> Blue Van", result[25])
self.assertIn("scuffed blue delivery van", result[25])
self.assertIn("<Picture 3> Coffee Shop", result[26])
self.assertIn("Location context for <Picture 3> Coffee Shop", result[26])
self.assertNotIn("Blue Van", prompt)
def test_h3_prompt_curator_aux_reference_outputs_follow_input_sockets(self):
node = self.image_nodes.DumasH3PromptCuratorNode()
used_image = FakeTensorBatch()
unused_image = FakeTensorBatch()
used = self.image_nodes.make_reference(
kind="character",
image=used_image,
name="Maya",
description="short silver hair",
)
unused = self.image_nodes.make_reference(
kind="location",
image=unused_image,
name="Unused Warehouse",
description="rusted loading bay doors",
)
result = node.curate_prompt(
action_prompt="Maya waits in the rain.",
anatomy_guard="off",
subject_count_guard="off",
ref_1=used,
ref_2=unused,
)
self.assertIs(result[1], used_image)
self.assertIsNone(result[2])
self.assertEqual(result[10], 1)
self.assertNotIn("Unused Warehouse", result[0])
self.assertIs(result[15], used_image)
self.assertIs(result[16], unused_image)
self.assertIn("<Picture 1> Maya", result[24])
self.assertIn("<Picture 2> Unused Warehouse", result[25])
self.assertIn("rusted loading bay doors", result[25])
def test_h3_prompt_curator_renumbers_explicit_reference_tags(self):
node = self.image_nodes.DumasH3PromptCuratorNode()