From 701f4d8ae5ab3847e0ddf1adbb643f0742ca5ada Mon Sep 17 00:00:00 2001 From: aimfordeb <133010426+aimfordeb@users.noreply.github.com> Date: Fri, 2 Jan 2026 06:15:22 +0200 Subject: [PATCH] Update funpack.py --- funpack.py | 104 ++++++++++++++++++++++++++++------------------------- 1 file changed, 55 insertions(+), 49 deletions(-) diff --git a/funpack.py b/funpack.py index 4ea07314..3e680584 100644 --- a/funpack.py +++ b/funpack.py @@ -31,17 +31,21 @@ MAX_KEYFRAME_NUM = 3 ADAPTIVE_ALPHA = 0.01 HPSV3_QUALITY_THRESHOLD = 3.0 -class FunPackLorebookEnhancer: +lass FunPackLorebookEnhancer: @classmethod def INPUT_TYPES(cls): return { "required": { "prompt": ("STRING", {"multiline": True, "default": "A passionate NSFW scene in the bedroom"}), - "lorebook_path": ("STRING", {"default": "nsfw_lorebook.json", "multiline": False}), "scan_depth": ("INT", {"default": 1, "min": 1, "max": 10}), }, "optional": { "context_history": ("STRING", {"multiline": True, "default": ""}), + # ── Multiple lorebook support ───────────────────────────────────── + "lorebook_1": ("STRING", {"default": "", "multiline": False}), + "lorebook_2": ("STRING", {"default": "", "multiline": False}), + "lorebook_3": ("STRING", {"default": "", "multiline": False}), + "lorebook_4": ("STRING", {"default": "", "multiline": False}), } } @@ -74,76 +78,77 @@ class FunPackLorebookEnhancer: if isinstance(secs, str): secs = [s.strip() for s in secs.split(",") if s.strip()] matches = [self._match_keys([s], text) for s in secs] - if logic == 0: # ANY - return any(matches) - if logic == 1: # ALL - return all(matches) - if logic == 2: # NOT ANY - return not any(matches) - if logic == 3: # NOT ALL - return not all(matches) + if logic == 0: return any(matches) + if logic == 1: return all(matches) + if logic == 2: return not any(matches) + if logic == 3: return not all(matches) return True - def enhance(self, prompt, lorebook_path, scan_depth, context_history=""): - if not os.path.exists(lorebook_path): - return (prompt, "ERROR: Lorebook file not found") - - with open(lorebook_path, 'r', encoding="utf-8") as f: - try: + def _process_lorebook(self, path, scan_text, activated): + if not path or not os.path.exists(path): + return activated + try: + with open(path, 'r', encoding="utf-8") as f: lorebook = json.load(f) - except: - return (prompt, "ERROR: Invalid JSON in lorebook") + entries = lorebook.get("entries", []) + for entry in entries: + if not entry.get("enabled", True): + continue + if entry.get("constant", False): + activated.append(entry) + continue + if not self._match_keys(entry.get("key"), scan_text): + continue + if entry.get("selective", False): + if not self._match_secondary(entry.get("keysecondary"), scan_text, entry.get("selectiveLogic", 0)): + continue + prob = entry.get("extensions", {}).get("probability", 100) + if random.randint(1, 100) > prob: + continue + activated.append(entry) + except Exception as e: + print(f"Warning: Failed to load lorebook {path}: {str(e)}") + return activated - entries = lorebook.get("entries", []) - if not entries: - return (prompt, "No entries in lorebook") - - # Prepare text to scan + def enhance(self, prompt, scan_depth, context_history="", + lorebook_1="", lorebook_2="", lorebook_3="", lorebook_4=""): + + # Prepare scan text scan_text = (context_history + "\n" + prompt).lower() lines = scan_text.split("\n") recent_lines = "\n".join(lines[-scan_depth:]) scan_text = recent_lines + # Collect all activated entries from all loaded lorebooks activated = [] - for entry in entries: - if not entry.get("enabled", True): - continue + + for path in [lorebook_1, lorebook_2, lorebook_3, lorebook_4]: + activated = self._process_lorebook(path, scan_text, activated) - if entry.get("constant", False): - activated.append(entry) - continue + if not activated: + return (prompt, "No lorebook entries triggered") - if not self._match_keys(entry.get("key"), scan_text): - continue - - if entry.get("selective", False): - if not self._match_secondary(entry.get("keysecondary"), scan_text, entry.get("selectiveLogic", 0)): - continue - - prob = entry.get("extensions", {}).get("probability", 100) - if random.randint(1, 100) > prob: - continue - - activated.append(entry) - - # Sort by insertion_order (higher = later = stronger recency) + # Sort by insertion_order (higher = inserted later = stronger influence) activated.sort(key=lambda e: e.get("insertion_order", 0)) - # Inject content + # Build enhanced prompt + collect injected text for debug injected = [] enhanced_prompt = prompt + for entry in activated: content = entry.get("content", "").strip() if not content: continue - position = entry.get("position", "after_char") - if position in ["after_char", "append"]: + + position = entry.get("position", "after_char").lower() + if position in ["after_char", "append", "bottom"]: enhanced_prompt += "\n" + content - else: # before_char or prepend + else: # before_char / prepend / top enhanced_prompt = content + "\n" + enhanced_prompt - injected.append(content) + + injected.append(f"[{entry.get('comment', 'no comment')}] {content}") - injected_text = "\n\n".join(injected) if injected else "No lorebook entries triggered" + injected_text = "\n\n".join(injected) if injected else "No matches" return (enhanced_prompt, injected_text) class FunPackStoryMemJSONConverter: @@ -1136,3 +1141,4 @@ NODE_DISPLAY_NAME_MAPPINGS = { +