bjooo commited on
Commit
0d7539b
·
verified ·
1 Parent(s): d140a6f

Delete promp_logic_v32_vision.py

Browse files
Files changed (1) hide show
  1. promp_logic_v32_vision.py +0 -247
promp_logic_v32_vision.py DELETED
@@ -1,247 +0,0 @@
1
- import os
2
- import random
3
- import re
4
- import json
5
- import urllib.request
6
- import urllib.error
7
- import base64
8
- import io
9
- import time
10
- import numpy as np
11
- from PIL import Image
12
- import torch
13
-
14
-
15
- class DolphinMultiActionPromptNode_V32:
16
- @classmethod
17
- def INPUT_TYPES(s):
18
- return {
19
- "required": {
20
- "image": ("IMAGE",),
21
- "mode": (["🤖 Auto Vision+LLM", "✍️ Manual Override"], {"default": "🤖 Auto Vision+LLM"}),
22
- "character_name": ("STRING", {"multiline": False, "default": "AUTO"}),
23
- "artistic_vibe": ("STRING", {"multiline": True, "default": "cinematic lighting, high-speed action, dark fantasy"}),
24
-
25
- "master_story": ("STRING", {
26
- "multiline": True,
27
- "default": "어두운 골목길. 갑자기 나타난 적들을 향해 돌진한다, 화려하게 검을 휘둘러 적을 쓰러뜨린다, 날아오는 총알을 튕겨낸다, 적에게 다가가 숨통을 끊는다."
28
- }),
29
-
30
- "openrouter_api_key": ("STRING", {"multiline": False, "default": ""}),
31
- "openrouter_model": ("STRING", {"multiline": False, "default": "qwen/qwen-2-vl-72b-instruct"}),
32
- "creativity": ("FLOAT", {"default": 0.85, "min": 0.1, "max": 1.5, "step": 0.05}),
33
- "max_tokens": ("INT", {"default": 1500, "min": 256, "max": 8192, "step": 64}),
34
- "retries": ("INT", {"default": 2, "min": 0, "max": 5}),
35
- "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
36
- },
37
- }
38
-
39
- RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING", "STRING")
40
- RETURN_NAMES = (
41
- "prompt_1 (Clip 1: 0-5s)",
42
- "prompt_2 (Clip 2: 0-5s)",
43
- "prompt_3 (Clip 3: 0-5s)",
44
- "prompt_4 (Clip 4: 0-5s)",
45
- "raw_llm_output",
46
- )
47
- FUNCTION = "generate_sequence"
48
- CATEGORY = "Dolphin"
49
-
50
- # -----------------------------------------------------------------
51
- def _encode_image(self, image):
52
- img_tensor = image[0]
53
- i = 255. * img_tensor.cpu().numpy()
54
- img_pil = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
55
- if img_pil.mode != "RGB":
56
- img_pil = img_pil.convert("RGB")
57
- buffered = io.BytesIO()
58
- img_pil.save(buffered, format="JPEG", quality=90)
59
- b64 = base64.b64encode(buffered.getvalue()).decode('utf-8')
60
- return f"data:image/jpeg;base64,{b64}"
61
-
62
- def _split_sentences(self, master_story):
63
- clean_story = re.sub(r'([.!?,\n])', r'\1|', master_story)
64
- raw_sentences = clean_story.split('|')
65
- return [s.strip() for s in raw_sentences if len(s.strip()) > 1]
66
-
67
- def _build_chunks(self, sentences):
68
- n = len(sentences)
69
- if n == 0:
70
- base = "dynamic high-speed action"
71
- return (base, base, base, base)
72
- if n == 1:
73
- s = sentences[0]
74
- return (
75
- f"Phase 1: Rapid approach and high-speed dynamic movement. DO NOT stand still. (Target: {s})",
76
- f"Phase 2: Swift, explosive execution of the action. (Target: {s})",
77
- f"Phase 3: The climax at full 1x real-time speed. Lightning fast! (Target: {s})",
78
- f"Phase 4: Fast-paced completion and quick recovery. (Target: {s})",
79
- )
80
- if n == 2:
81
- return (
82
- f"Phase 1: High-speed buildup and rapid preparation. (Target: {sentences[0]})",
83
- f"Phase 2: Explosively execute -> {sentences[0]}",
84
- f"Phase 3: Rapid transition, sprinting or moving quickly. (Target: {sentences[1]})",
85
- f"Phase 4: Lightning-fast execution -> {sentences[1]}",
86
- )
87
- if n == 3:
88
- return (
89
- f"Phase 1: Start this action rapidly -> {sentences[0]}",
90
- f"Phase 2: Explosively complete -> {sentences[0]}",
91
- sentences[1],
92
- sentences[2],
93
- )
94
- # n >= 4: 균등 분배
95
- k, m = divmod(n, 4)
96
- chunks = []
97
- start = 0
98
- for idx in range(4):
99
- end = start + k + (1 if idx < m else 0)
100
- chunks.append(" ".join(sentences[start:end]))
101
- start = end
102
- return tuple(chunks)
103
-
104
- def _call_llm(self, url, payload, api_key, retries, timeout=120):
105
- last_err = None
106
- for attempt in range(retries + 1):
107
- try:
108
- req = urllib.request.Request(
109
- url,
110
- data=json.dumps(payload).encode('utf-8'),
111
- headers={'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json'}
112
- )
113
- response = urllib.request.urlopen(req, timeout=timeout)
114
- body = json.loads(response.read().decode('utf-8'))
115
- return body['choices'][0]['message']['content'].strip(), None
116
- except Exception as e:
117
- last_err = e
118
- if attempt < retries:
119
- time.sleep(1.5 * (attempt + 1))
120
- return None, last_err
121
-
122
- # -----------------------------------------------------------------
123
- def generate_sequence(self, image, mode, character_name, artistic_vibe, master_story,
124
- openrouter_api_key, openrouter_model, creativity, max_tokens, retries, seed):
125
-
126
- user_defined_name = "" if character_name.upper() in ["AUTO", ""] else character_name.strip()
127
-
128
- def build_final(action, master_scene, name):
129
- tags_list = []
130
- if name:
131
- tags_list.append(name)
132
- tag_block = ", ".join(tags_list)
133
-
134
- sentence_list = []
135
- if master_scene.strip():
136
- sentence_list.append(master_scene.strip().strip(",. "))
137
- if action.strip():
138
- sentence_list.append(action.strip())
139
- sentence_block = " ".join(sentence_list)
140
-
141
- if tag_block and sentence_block:
142
- return f"{tag_block}\n{sentence_block}"
143
- elif tag_block:
144
- return tag_block
145
- return sentence_block
146
-
147
- # ---- Manual Override ----
148
- if mode == "✍️ Manual Override":
149
- fp = build_final(master_story, "", user_defined_name)
150
- return (fp, fp, fp, fp, "[Manual Override]")
151
-
152
- random.seed(seed)
153
-
154
- base64_image = self._encode_image(image)
155
- sentences = self._split_sentences(master_story)
156
- chunk_1, chunk_2, chunk_3, chunk_4 = self._build_chunks(sentences)
157
-
158
- sys_prompt = (
159
- "You are an Elite Action Director prioritizing RAW SPEED and KINETIC ENERGY.\n"
160
- f"1. CHARACTER: If NAME is 'AUTO', assign a name. If '{user_defined_name}', use it.\n"
161
- "2. VISUAL ANALYSIS: You MUST base your descriptions EXACTLY on the character's clothing and weapons in the attached IMAGE.\n"
162
- "3. MASTER SCENE: Write a 1-sentence environment description (lighting, weather).\n"
163
- "4. SPEED-FOCUSED CHOREOGRAPHY (CRITICAL):\n"
164
- " - 🚫 BAN SLOW-MOTION TRIGGERS: NEVER use words like 'micro-expressions', 'muscle tension', 'slowly turning', 'floating', or 'gradually'. These cause AI video models to render in slow-motion.\n"
165
- " - ✅ FORCE 1x REAL-TIME SPEED: Describe large, sweeping, high-velocity movements. Use aggressive verbs (dashing, sprinting, whipping, snapping).\n"
166
- " - ✅ KINETIC ADVERBS: Inject phrases like 'in a flash', 'at lightning speed', 'with explosive real-time velocity' into EVERY part.\n"
167
- " - Example: 'suddenly dashes forward at full speed and delivers a lightning-fast horizontal strike, moving so quickly the rain splatters'.\n"
168
- " - Strictly confine the actions. DO NOT animate future events early.\n"
169
- " - 🔥 OUTPUT RULE: DO NOT quote the Korean text. Only output English.\n"
170
- "Format EXACTLY:\nCHARACTER: [Name]\nMASTER SCENE: [Description]\n"
171
- "PART 1: [0-1s] [Action A] [2-3s] [Action B] [4-5s] [Action C]\n"
172
- "PART 2: [0-1s] [Action D] [2-3s] [Action E] [4-5s] [Action F]\n"
173
- "PART 3: [0-1s] [Action G] [2-3s] [Action H] [4-5s] [Action I]\n"
174
- "PART 4: [0-1s] [Action J] [2-3s] [Action K] [4-5s] [Action L]"
175
- )
176
-
177
- usr_text = (
178
- f"NAME: {character_name}\n"
179
- f"VIBE: {artistic_vibe}\n\n"
180
- "=== HIGH-SPEED ACTION SCRIPT ===\n"
181
- f"▶ For PART 1 (0-5s), ONLY animate this: \"{chunk_1}\"\n"
182
- f"▶ For PART 2 (5-10s), ONLY animate this: \"{chunk_2}\"\n"
183
- f"▶ For PART 3 (10-15s), ONLY animate this: \"{chunk_3}\"\n"
184
- f"▶ For PART 4 (15-20s), ONLY animate this: \"{chunk_4}\"\n"
185
- "CRITICAL: Keep the action moving FAST. Avoid still poses or micro-details that look like slow-mo!"
186
- )
187
-
188
- url = "https://openrouter.ai/api/v1/chat/completions"
189
- payload = {
190
- "model": openrouter_model.strip(),
191
- "messages": [
192
- {"role": "system", "content": sys_prompt},
193
- {"role": "user", "content": [
194
- {"type": "text", "text": usr_text},
195
- {"type": "image_url", "image_url": {"url": base64_image}}
196
- ]}
197
- ],
198
- "temperature": creativity,
199
- "max_tokens": max_tokens,
200
- }
201
-
202
- if not openrouter_api_key.strip():
203
- err_msg = "⚠️ API Error: OpenRouter API key is empty."
204
- return (err_msg, err_msg, err_msg, err_msg, err_msg)
205
-
206
- llm_prompt, err = self._call_llm(url, payload, openrouter_api_key, retries)
207
- if llm_prompt:
208
- print(f"\n✅ [Dolphin V32 - Action Speed Optimized]\n{llm_prompt}\n")
209
- else:
210
- print(f"❌ [에러] API 호출 실패: {err}")
211
- llm_prompt = ""
212
-
213
- final_char_name = user_defined_name
214
- r_master = ""
215
- p1 = p2 = p3 = p4 = ""
216
-
217
- if llm_prompt and "[removed]" not in llm_prompt:
218
- cl = re.sub(r'[*#]', '', llm_prompt)
219
- m_char = re.search(r'CHARACTER:\s*(.*?)(?=MASTER SCENE|$)', cl, re.I | re.S)
220
- m_master = re.search(r'MASTER SCENE:\s*(.*?)(?=PART 1|$)', cl, re.I | re.S)
221
-
222
- m1 = re.search(r'PART 1:\s*(.*?)(?=PART 2|$)', cl, re.I | re.S)
223
- m2 = re.search(r'PART 2:\s*(.*?)(?=PART 3|$)', cl, re.I | re.S)
224
- m3 = re.search(r'PART 3:\s*(.*?)(?=PART 4|$)', cl, re.I | re.S)
225
- m4 = re.search(r'PART 4:\s*(.*?)(?=\n\n|===|Note:|$)', cl, re.I | re.S)
226
-
227
- if not user_defined_name and m_char:
228
- final_char_name = m_char.group(1).strip()
229
-
230
- r_master = m_master.group(1).strip() if m_master else ""
231
-
232
- # 파싱 실패 시 해당 청크(영문 지시문)를 폴백으로 사용해 비디오 프롬프트가 비지 않도록 함
233
- p1 = m1.group(1).strip() if m1 else chunk_1
234
- p2 = m2.group(1).strip() if m2 else chunk_2
235
- p3 = m3.group(1).strip() if m3 else chunk_3
236
- p4 = m4.group(1).strip() if m4 else chunk_4
237
- else:
238
- # API 실패 시에도 스크립트 청크를 폴백으로 반환 (완전 실패보다 유용)
239
- p1, p2, p3, p4 = chunk_1, chunk_2, chunk_3, chunk_4
240
-
241
- return (
242
- build_final(p1, r_master, final_char_name),
243
- build_final(p2, r_master, final_char_name),
244
- build_final(p3, r_master, final_char_name),
245
- build_final(p4, r_master, final_char_name),
246
- llm_prompt if llm_prompt else "⚠️ API Error / empty response",
247
- )