customNode / dolphin_director.py
bjooo's picture
Upload 6 files
7aaa385 verified
Raw
History Blame Contribute Delete
3.83 kB
import os
import re
import json
import hashlib
import urllib.request
import urllib.error
import folder_paths
import comfy.sd
import comfy.lora
import comfy.utils
from safetensors import safe_open
try:
from .promp_logic_v32_story import MODEL_PRESETS, MODEL_PRESET_LABELS, resolve_model
except Exception:
MODEL_PRESETS = {"๐Ÿ”ž DeepSeek V3.2": "deepseek/deepseek-v3.2"}
MODEL_PRESET_LABELS = list(MODEL_PRESETS.keys())
def resolve_model(p, c): return c if p == "custom" else "deepseek/deepseek-v3.2"
class DolphinCinematicDirector:
@classmethod
def INPUT_TYPES(s):
loras = ["None"] + folder_paths.get_filename_list("loras")
return {
"required": {
"model_base": ("MODEL",), "model_high": ("MODEL",), "model_low": ("MODEL",),
"mode": (["๐Ÿค– Auto LLM (Story Splitter)", "โœ๏ธ Manual"], {"default": "๐Ÿค– Auto LLM (Story Splitter)"}),
"master_story": ("STRING", {"multiline": True}),
"tagger_context": ("STRING", {"multiline": True}),
"openrouter_api_key": ("STRING", {"default": ""}),
},
"optional": {
"external_triggers": ("STRING", {"forceInput": True}),
}
}
RETURN_TYPES = ("MODEL", "MODEL", "MODEL", "STRING") * 4 + ("STRING",)
RETURN_NAMES = tuple([f"c{i}_{n}" for i in range(1, 5) for n in ["base", "high", "low", "prompt"]] + ["debug_info"])
FUNCTION = "direct"
CATEGORY = "Dolphin"
def _build_final(self, action, tags, master_scene, name, trigger_str):
# 1. ํŠธ๋ฆฌ๊ฑฐ ์›Œ๋“œ(trigger_str)์™€ ์บ๋ฆญํ„ฐ ์ด๋ฆ„(name)๋งŒ ์ƒ๋‹จ์— ๋ฐฐ์น˜
header = []
if name: header.append(name)
if trigger_str: header.append(trigger_str)
# 2. ๋™์ž‘(action)๊ณผ ๋ฐฐ๊ฒฝ(master_scene)๋งŒ ํ•˜๋‹จ์— ๋ฐฐ์น˜
# ๐Ÿš€ ํ•ต์‹ฌ: ์—ฌ๊ธฐ์„œ 'tags' ๋ณ€์ˆ˜๋ฅผ ์•„์˜ˆ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ์œผ๋กœ์จ ๋ฌ˜์‚ฌ ํฌํ•จ์„ ์›์ฒœ ์ฐจ๋‹จ
body = []
if master_scene and master_scene.strip():
body.append(master_scene.strip())
if action and action.strip():
body.append(action.strip())
# ๊ฒฐ๊ณผ๊ฐ’ ๊ฒฐํ•ฉ
header_str = ", ".join(header)
body_str = " ".join(body)
if header_str and body_str:
return f"{header_str}\n{body_str}"
return header_str or body_str
def _llm_split(self, story, name, tags, key, model):
# ๐Ÿš€ ์ˆ˜์ •: LLM์—๊ฒŒ๋Š” ๋ฌ˜์‚ฌ๋ฅผ ๋ณด๋‚ด๋˜, ์ถœ๋ ฅ JSON์—์„œ๋Š” ๋™์ž‘๋งŒ ๋ฝ‘๋„๋ก ๊ฐ•์ œ
sys_p = (
"You are an action-only generator. "
"DO NOT output any visual descriptions, clothing, lighting, or setting details. "
"Your output must ONLY be the physical movement of the character. "
"If you include any adjective-heavy descriptions, you will be penalized. "
"Strictly output only the kinetic action in the JSON fields."
)
def direct(self, model_base, model_high, model_low, mode, master_story, tagger_context, openrouter_api_key, external_triggers="", **kw):
# ๐Ÿš€ ๊ฐ•๋ ฅํ•œ ๋ฐฉ์–ด: ์–ด๋–ค ๋ฌ˜์‚ฌ๊ฐ€ ๋“ค์–ด์™€๋„ ํƒœ๊ทธ๋Š” ๊ฐ•์ œ๋กœ ๋นˆ ๊ฐ’์œผ๋กœ ๋Œ€์ฒด
tagger_context = ""
# ์ดํ›„ ๋กœ์ง...
# ๋ชจ๋“œ๋ณ„ ์ฒ˜๋ฆฌ
clips = [master_story] * 4 # ๋‹จ์ˆœํ™” ๋กœ์ง
master_scene = "action"
final_name = "character"
outs = []
for c in range(1, 5):
# ๐Ÿš€ ๊ฐ•์ œ๋กœ tags(tagger_context)๋ฅผ ๋นˆ ๋ฌธ์ž์—ด("")๋กœ ์ „๋‹ฌํ•˜์—ฌ ๋ฌด๋ ฅํ™”
prompt = self._build_final(clips[c-1], "", master_scene, final_name, external_triggers)
outs.extend([model_base, model_high, model_low, prompt])
return tuple(outs) + ("Success",)
NODE_CLASS_MAPPINGS = {"DolphinCinematicDirector": DolphinCinematicDirector}