From b2791f7af0f23ec9d900faca00a34e039fc99c40 Mon Sep 17 00:00:00 2001 From: Isulion Date: Tue, 20 Oct 2026 20:38:45 +0200 Subject: [PATCH] feat(megaprompt-v4): register IsulionMegaPromptV4 node (single prompt output) Co-Authored-By: Claude Opus 5.5 --- Core_Nodes/mega_prompt_v4.py | 24 ++++++++++++++++++++++++ __init__.py | 3 +++ tests/fixtures/expected_node_keys.json | 1 + tests/fixtures/node_contracts_v2.json | 10 ++++++++++ tests/test_mega_prompt_v4.py | 26 ++++++++++++++++++++++++++ 5 files changed, 64 insertions(+) diff --git a/Core_Nodes/mega_prompt_v4.py b/Core_Nodes/mega_prompt_v4.py index 669edd8..105895a 100644 --- a/Core_Nodes/mega_prompt_v4.py +++ b/Core_Nodes/mega_prompt_v4.py @@ -222,3 +222,27 @@ def roll(state, reels, seed, registry=None): prompt, segments = assemble_prompt(state) return _result(state, prompt, segments, rolled + to_roll, seed, registry) + + +class IsulionMegaPromptV4: + """Slot-machine prompt generator: every execution re-rolls the unlocked reels.""" + + TITLE = "đŸ•šī¸ Mega Prompt V4" + + @classmethod + def INPUT_TYPES(cls): + return {"required": {"machine_state": ("STRING", {"default": DEFAULT_STATE})}} + + RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("prompt",) + FUNCTION = "generate" + CATEGORY = "Isulion/Core" + OUTPUT_NODE = True + + @classmethod + def IS_CHANGED(cls, machine_state): + return float("nan") # the machine spins on every run + + def generate(self, machine_state): + result = roll(machine_state, REEL_NAMES, new_seed()) + return {"ui": {"draw": [result]}, "result": (result["prompt"],)} diff --git a/__init__.py b/__init__.py index 8bf1fab..b946ad4 100644 --- a/__init__.py +++ b/__init__.py @@ -9,6 +9,7 @@ __description__ = "Advanced prompt generation nodes for ComfyUI with multiple th # Import all node classes from .Core_Nodes.mega_prompt_V3 import IsulionMegaPromptV3 +from .Core_Nodes.mega_prompt_v4 import IsulionMegaPromptV4 from .Core_Nodes.mega_prompt_all_themes import IsulionMultiplePromptGenerator from .Core_Nodes.isucollage_node import IsuCollageNode from .Core_Nodes.load_images_node import IsulionLoadImagesNode @@ -86,6 +87,7 @@ NODE_CLASS_MAPPINGS = { "IsulionCivitaiTrending": IsulionCivitaiTrending, "IsulionCivitaiImageDisplay": IsulionCivitaiImageDisplay, "IsulionMegaPromptV3": IsulionMegaPromptV3, + "IsulionMegaPromptV4": IsulionMegaPromptV4, "IsulionMultiplePromptGenerator": IsulionMultiplePromptGenerator, "IsuCollage_Node": IsuCollageNode, "IsulionLoadImagesNode": IsulionLoadImagesNode, @@ -100,6 +102,7 @@ NODE_CLASS_MAPPINGS = { # Display name mappings NODE_DISPLAY_NAME_MAPPINGS = { "IsulionMegaPromptV3": "🚀 Isulion Mega Prompt V3", + "IsulionMegaPromptV4": "đŸ•šī¸ Isulion Mega Prompt V4", "IsulionVideoPromptGenerator": "đŸŽĨ Isulion Video Prompt Generator", "IsulionMultiplePromptGenerator": "🔄 Isulion Multiple Prompt Generator", "IsulionAnimalRandom": "đŸĻ Isulion Animal Selector", diff --git a/tests/fixtures/expected_node_keys.json b/tests/fixtures/expected_node_keys.json index f2d222a..6533fda 100644 --- a/tests/fixtures/expected_node_keys.json +++ b/tests/fixtures/expected_node_keys.json @@ -21,6 +21,7 @@ "IsulionLoadImagesNode", "IsulionMagicalEffectGenerator", "IsulionMegaPromptV3", + "IsulionMegaPromptV4", "IsulionMultiplePromptGenerator", "IsulionMythicalLocationGenerator", "IsulionNegativePromptGenerator", diff --git a/tests/fixtures/node_contracts_v2.json b/tests/fixtures/node_contracts_v2.json index 3c6bbd2..90f55ba 100644 --- a/tests/fixtures/node_contracts_v2.json +++ b/tests/fixtures/node_contracts_v2.json @@ -3643,5 +3643,15 @@ } ] } + }, + "IsulionMegaPromptV4": { + "required": { + "machine_state": [ + "STRING", + { + "default": "{\"version\": 1}" + } + ] + } } } diff --git a/tests/test_mega_prompt_v4.py b/tests/test_mega_prompt_v4.py index 9100d19..10998da 100644 --- a/tests/test_mega_prompt_v4.py +++ b/tests/test_mega_prompt_v4.py @@ -232,3 +232,29 @@ def test_roll_uses_the_shared_generation_lock(): assert RecordingLock.entries == 1 finally: mp4.GENERATION_LOCK = original + + +import math + + +def test_node_contract_and_registration(): + pkg = import_package() + cls = pkg.NODE_CLASS_MAPPINGS["IsulionMegaPromptV4"] + assert pkg.NODE_DISPLAY_NAME_MAPPINGS["IsulionMegaPromptV4"] == "đŸ•šī¸ Isulion Mega Prompt V4" + assert cls.INPUT_TYPES() == {"required": {"machine_state": ("STRING", {"default": '{"version": 1}'})}} + assert (cls.RETURN_TYPES, cls.RETURN_NAMES, cls.CATEGORY, cls.OUTPUT_NODE) == (("STRING",), ("prompt",), "Isulion/Core", True) + assert math.isnan(cls.IS_CHANGED('{"version": 1}')) + + +def test_node_generate_rolls_everything_and_reports_ui(): + mp4 = _mp4() + original, mp4.new_seed = mp4.new_seed, lambda: 5 + try: + node = mp4.IsulionMegaPromptV4() + out = node.generate('{"version": 1}') + draw = out["ui"]["draw"] + assert len(draw) == 1 and draw[0]["seed"] == 5 and out["result"] == (draw[0]["prompt"],) + assert draw[0]["prompt"] and draw[0]["state"]["theme"]["name"] in mp4.get_theme_registry().handlers + assert node.generate("garbage")["result"] == out["result"] # same seed, default state => same prompt + finally: + mp4.new_seed = original