Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d4232a142 | ||
|
|
d68d04254f | ||
|
|
22398c5213 | ||
|
|
1a4cfbfd7e | ||
|
|
eac4e73b47 |
@@ -58,10 +58,10 @@
|
|||||||
|
|
||||||
- `Dumas H3 Prompt Curator`
|
- `Dumas H3 Prompt Curator`
|
||||||
- Inputs: `action_prompt`, `anatomy_guard`, `subject_count_guard`, optional `anchor`, optional `soundscape`, optional `bgm`, optional `ref_1` through `ref_9`
|
- Inputs: `action_prompt`, `anatomy_guard`, `subject_count_guard`, optional `anchor`, optional `soundscape`, optional `bgm`, optional `ref_1` through `ref_9`
|
||||||
- Outputs: `prompt`, `ref_image_1` through `ref_image_9`, `reference_count`, `debug`, `anchor`, `sounds`, `bgm`, `original_ref_1` through `original_ref_9`, `reference_description`
|
- 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.
|
- 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.
|
- 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, selected structured references in compacted order, and the compiled reference-description block used inside the prompt.
|
- Extra component outputs expose the cleaned anchor, sounds, BGM, and each selected original reference image plus its compiled reference description in compacted order.
|
||||||
- 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.
|
- 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`
|
- `Dumas H3 Shot Length`
|
||||||
|
|||||||
+20
-9
@@ -1253,11 +1253,13 @@ def curate_h3_prompt(
|
|||||||
soundscape_text = _reference_text(soundscape)
|
soundscape_text = _reference_text(soundscape)
|
||||||
bgm_text = _reference_text(bgm)
|
bgm_text = _reference_text(bgm)
|
||||||
reference_description = ""
|
reference_description = ""
|
||||||
|
individual_reference_descriptions = []
|
||||||
if selected:
|
if selected:
|
||||||
reference_description = " ".join(
|
individual_reference_descriptions = [
|
||||||
_reference_context(ref, picture_number)
|
_reference_context(ref, picture_number)
|
||||||
for picture_number, (_slot, ref) in enumerate(selected, 1)
|
for picture_number, (_slot, ref) in enumerate(selected, 1)
|
||||||
)
|
]
|
||||||
|
reference_description = " ".join(individual_reference_descriptions)
|
||||||
|
|
||||||
prompt_parts = []
|
prompt_parts = []
|
||||||
_append_prompt_section(prompt_parts, "Scene anchor", anchor_text)
|
_append_prompt_section(prompt_parts, "Scene anchor", anchor_text)
|
||||||
@@ -1277,8 +1279,9 @@ def curate_h3_prompt(
|
|||||||
prompt = prompt[: _H3_PROMPT_MAX_CHARS - 3].rstrip() + "..."
|
prompt = prompt[: _H3_PROMPT_MAX_CHARS - 3].rstrip() + "..."
|
||||||
images = [_reference_image(ref) for _slot, ref in selected]
|
images = [_reference_image(ref) for _slot, ref in selected]
|
||||||
images.extend([None] * (_H3_PROMPT_REF_SLOTS - len(images)))
|
images.extend([None] * (_H3_PROMPT_REF_SLOTS - len(images)))
|
||||||
selected_refs = [ref for _slot, ref in selected]
|
individual_reference_descriptions.extend(
|
||||||
selected_refs.extend([None] * (_H3_PROMPT_REF_SLOTS - len(selected_refs)))
|
[""] * (_H3_PROMPT_REF_SLOTS - len(individual_reference_descriptions))
|
||||||
|
)
|
||||||
debug = (
|
debug = (
|
||||||
f"Selected {len(selected)} reference(s): "
|
f"Selected {len(selected)} reference(s): "
|
||||||
+ ", ".join(
|
+ ", ".join(
|
||||||
@@ -1296,8 +1299,8 @@ def curate_h3_prompt(
|
|||||||
anchor_text,
|
anchor_text,
|
||||||
soundscape_text,
|
soundscape_text,
|
||||||
bgm_text,
|
bgm_text,
|
||||||
*selected_refs[:_H3_PROMPT_REF_SLOTS],
|
*images[:_H3_PROMPT_REF_SLOTS],
|
||||||
reference_description,
|
*individual_reference_descriptions[:_H3_PROMPT_REF_SLOTS],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -2689,8 +2692,8 @@ class DumasH3PromptCuratorNode:
|
|||||||
("STRING",)
|
("STRING",)
|
||||||
+ ("IMAGE",) * _H3_PROMPT_REF_SLOTS
|
+ ("IMAGE",) * _H3_PROMPT_REF_SLOTS
|
||||||
+ ("INT", "STRING", "STRING", "STRING", "STRING")
|
+ ("INT", "STRING", "STRING", "STRING", "STRING")
|
||||||
+ (_REFERENCE_TYPE,) * _H3_PROMPT_REF_SLOTS
|
+ ("IMAGE",) * _H3_PROMPT_REF_SLOTS
|
||||||
+ ("STRING",)
|
+ ("STRING",) * _H3_PROMPT_REF_SLOTS
|
||||||
)
|
)
|
||||||
RETURN_NAMES = (
|
RETURN_NAMES = (
|
||||||
"prompt",
|
"prompt",
|
||||||
@@ -2717,7 +2720,15 @@ class DumasH3PromptCuratorNode:
|
|||||||
"original_ref_7",
|
"original_ref_7",
|
||||||
"original_ref_8",
|
"original_ref_8",
|
||||||
"original_ref_9",
|
"original_ref_9",
|
||||||
"reference_description",
|
"compiled_ref_description_1",
|
||||||
|
"compiled_ref_description_2",
|
||||||
|
"compiled_ref_description_3",
|
||||||
|
"compiled_ref_description_4",
|
||||||
|
"compiled_ref_description_5",
|
||||||
|
"compiled_ref_description_6",
|
||||||
|
"compiled_ref_description_7",
|
||||||
|
"compiled_ref_description_8",
|
||||||
|
"compiled_ref_description_9",
|
||||||
)
|
)
|
||||||
FUNCTION = "curate_prompt"
|
FUNCTION = "curate_prompt"
|
||||||
CATEGORY = "Dumas/MiniMax"
|
CATEGORY = "Dumas/MiniMax"
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import { app } from "/scripts/app.js";
|
||||||
|
|
||||||
|
const NODE_NAME = "DumasH3PromptCurator";
|
||||||
|
const EXPECTED_OUTPUTS = [
|
||||||
|
"prompt",
|
||||||
|
"ref_image_1",
|
||||||
|
"ref_image_2",
|
||||||
|
"ref_image_3",
|
||||||
|
"ref_image_4",
|
||||||
|
"ref_image_5",
|
||||||
|
"ref_image_6",
|
||||||
|
"ref_image_7",
|
||||||
|
"ref_image_8",
|
||||||
|
"ref_image_9",
|
||||||
|
"reference_count",
|
||||||
|
"debug",
|
||||||
|
"anchor",
|
||||||
|
"sounds",
|
||||||
|
"bgm",
|
||||||
|
"original_ref_1",
|
||||||
|
"original_ref_2",
|
||||||
|
"original_ref_3",
|
||||||
|
"original_ref_4",
|
||||||
|
"original_ref_5",
|
||||||
|
"original_ref_6",
|
||||||
|
"original_ref_7",
|
||||||
|
"original_ref_8",
|
||||||
|
"original_ref_9",
|
||||||
|
"compiled_ref_description_1",
|
||||||
|
"compiled_ref_description_2",
|
||||||
|
"compiled_ref_description_3",
|
||||||
|
"compiled_ref_description_4",
|
||||||
|
"compiled_ref_description_5",
|
||||||
|
"compiled_ref_description_6",
|
||||||
|
"compiled_ref_description_7",
|
||||||
|
"compiled_ref_description_8",
|
||||||
|
"compiled_ref_description_9",
|
||||||
|
];
|
||||||
|
const EXPECTED_NAMES = new Set(EXPECTED_OUTPUTS);
|
||||||
|
|
||||||
|
function pruneStaleOutputs(node) {
|
||||||
|
if (!Array.isArray(node.outputs)) return;
|
||||||
|
|
||||||
|
const byName = new Map();
|
||||||
|
for (const output of node.outputs) {
|
||||||
|
if (!output?.name || !EXPECTED_NAMES.has(output.name) || byName.has(output.name)) continue;
|
||||||
|
byName.set(output.name, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextOutputs = [];
|
||||||
|
for (const name of EXPECTED_OUTPUTS) {
|
||||||
|
const existing = byName.get(name);
|
||||||
|
if (existing) {
|
||||||
|
nextOutputs.push(existing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextOutputs.length && nextOutputs.length !== node.outputs.length) {
|
||||||
|
node.outputs = nextOutputs;
|
||||||
|
node.size = node.computeSize?.() || node.size;
|
||||||
|
node.setDirtyCanvas?.(true, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.registerExtension({
|
||||||
|
name: "Dumas.H3PromptCuratorOutputs",
|
||||||
|
async beforeRegisterNodeDef(nodeType, nodeData) {
|
||||||
|
if (nodeData?.name !== NODE_NAME) return;
|
||||||
|
|
||||||
|
const originalOnNodeCreated = nodeType.prototype.onNodeCreated;
|
||||||
|
const originalOnConfigure = nodeType.prototype.onConfigure;
|
||||||
|
|
||||||
|
nodeType.prototype.onNodeCreated = function onNodeCreated() {
|
||||||
|
const created = originalOnNodeCreated?.apply(this, arguments);
|
||||||
|
pruneStaleOutputs(this);
|
||||||
|
return created;
|
||||||
|
};
|
||||||
|
|
||||||
|
nodeType.prototype.onConfigure = function onConfigure() {
|
||||||
|
const configured = originalOnConfigure?.apply(this, arguments);
|
||||||
|
pruneStaleOutputs(this);
|
||||||
|
return configured;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -502,14 +502,14 @@ class DumasImageNodeTests(unittest.TestCase):
|
|||||||
self.assertEqual(result[12], "grounded handheld thriller")
|
self.assertEqual(result[12], "grounded handheld thriller")
|
||||||
self.assertEqual(result[13], "steady rain")
|
self.assertEqual(result[13], "steady rain")
|
||||||
self.assertEqual(result[14], "low suspense music")
|
self.assertEqual(result[14], "low suspense music")
|
||||||
self.assertEqual(result[15]["name"], "Dave")
|
self.assertIs(result[15], dave_image)
|
||||||
self.assertIs(result[15]["image"], dave_image)
|
self.assertIs(result[16], cafe_image)
|
||||||
self.assertEqual(result[16]["name"], "Coffee Shop")
|
|
||||||
self.assertIs(result[16]["image"], cafe_image)
|
|
||||||
self.assertIsNone(result[17])
|
self.assertIsNone(result[17])
|
||||||
self.assertIn("<Picture 1> Dave", result[24])
|
self.assertIn("<Picture 1> Dave", result[24])
|
||||||
self.assertIn("<Picture 2> Coffee Shop", result[24])
|
self.assertNotIn("<Picture 2> Coffee Shop", result[24])
|
||||||
self.assertIn("Location context for <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], "")
|
||||||
|
|
||||||
def test_h3_prompt_curator_renumbers_explicit_reference_tags(self):
|
def test_h3_prompt_curator_renumbers_explicit_reference_tags(self):
|
||||||
node = self.image_nodes.DumasH3PromptCuratorNode()
|
node = self.image_nodes.DumasH3PromptCuratorNode()
|
||||||
@@ -647,11 +647,14 @@ class DumasImageNodeTests(unittest.TestCase):
|
|||||||
|
|
||||||
def test_h3_prompt_curator_uses_documented_reference_limits(self):
|
def test_h3_prompt_curator_uses_documented_reference_limits(self):
|
||||||
node = self.image_nodes.DumasH3PromptCuratorNode()
|
node = self.image_nodes.DumasH3PromptCuratorNode()
|
||||||
self.assertEqual(len(node.RETURN_TYPES), 25)
|
self.assertEqual(len(node.RETURN_TYPES), 33)
|
||||||
self.assertEqual(node.RETURN_NAMES[1:10], tuple(f"ref_image_{i}" for i in range(1, 10)))
|
self.assertEqual(node.RETURN_NAMES[1:10], tuple(f"ref_image_{i}" for i in range(1, 10)))
|
||||||
self.assertEqual(node.RETURN_NAMES[12:15], ("anchor", "sounds", "bgm"))
|
self.assertEqual(node.RETURN_NAMES[12:15], ("anchor", "sounds", "bgm"))
|
||||||
self.assertEqual(node.RETURN_NAMES[15:24], tuple(f"original_ref_{i}" for i in range(1, 10)))
|
self.assertEqual(node.RETURN_NAMES[15:24], tuple(f"original_ref_{i}" for i in range(1, 10)))
|
||||||
self.assertEqual(node.RETURN_NAMES[24], "reference_description")
|
self.assertEqual(
|
||||||
|
node.RETURN_NAMES[24:33],
|
||||||
|
tuple(f"compiled_ref_description_{i}" for i in range(1, 10)),
|
||||||
|
)
|
||||||
|
|
||||||
def test_normalize_reference_upgrades_generic_summary_with_socket_picture_id(self):
|
def test_normalize_reference_upgrades_generic_summary_with_socket_picture_id(self):
|
||||||
image = FakeTensorBatch()
|
image = FakeTensorBatch()
|
||||||
|
|||||||
Reference in New Issue
Block a user