import random import re class MultilineRandomChanceNode: @classmethod def INPUT_TYPES(cls): return { "required": { "text": ( "STRING", { "multiline": True, "default": "A professional photo of a {female::5|male} mage, casting a {fire::5|ice::2|lightning} spell, cinematic lighting.", }, ), "seed": ("INT", {"default": 0, "min": 0, "max": 0xFFFFFFFFFFFFFFFF}), }, "optional": { "regex_pattern": ("STRING", {"default": ",\\s*,"}), "regex_replacement": ("STRING", {"default": ","}), }, } RETURN_TYPES = ("STRING",) RETURN_NAMES = ("selected_text",) FUNCTION = "process_random_text" CATEGORY = "TextRandomizer/Text" def _parse_weights(self, options_list, separator="::"): choices, weights = [], [] for opt in options_list: if separator in opt: parts = opt.rsplit(separator, 1) choice_text = parts[0].strip() try: weight = float(parts[1].strip()) except ValueError: weight = 1.0 else: choice_text = opt.strip() weight = 1.0 choices.append(choice_text) weights.append(weight) return choices, weights def _replace_brackets(self, match): content = match.group(1) raw_options = content.split("|") choices, weights = self._parse_weights(raw_options, separator="::") if choices: return random.choices(choices, weights=weights, k=1)[0] return "" def process_random_text(self, text, seed, regex_pattern="", regex_replacement=""): random.seed(seed) lines = [line.strip() for line in text.split("\n") if line.strip()] if not lines: return ("",) base_choices, base_weights = self._parse_weights(lines, separator="::") selected_line = random.choices(base_choices, weights=base_weights, k=1)[0] bracket_pattern = re.compile(r"\{([^{}]+)\}") loop_guard = 0 while bracket_pattern.search(selected_line) and loop_guard < 10: selected_line = bracket_pattern.sub(self._replace_brackets, selected_line) loop_guard += 1 if regex_pattern: try: compiled_regex = re.compile(regex_pattern) selected_line = compiled_regex.sub(regex_replacement, selected_line) except re.error: pass return (selected_line,)