Spaces:
Paused
Paused
daKhosa commited on
Commit ·
b3586c1
1
Parent(s): 1efc965
Replace LTX enhancer with Sulphur GGUF enhancer
Browse files- app.py +80 -3
- requirements.txt +1 -0
app.py
CHANGED
|
@@ -29,9 +29,17 @@ MAX_SEED = np.iinfo(np.int32).max
|
|
| 29 |
DEFAULT_FRAME_RATE = 24.0
|
| 30 |
DEFAULT_PROMPT = "Make this image come alive with cinematic motion, smooth animation."
|
| 31 |
PROMPT_ADHERENCE_SUFFIX = (
|
| 32 |
-
"Follow the prompt literally.
|
| 33 |
-
"camera motion, and timing consistent with the user's request."
|
| 34 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
|
| 36 |
RESOLUTIONS = {
|
| 37 |
"high": {"16:9": (1536, 1024), "9:16": (1024, 1536), "1:1": (1024, 1024)},
|
|
@@ -215,11 +223,63 @@ def _validate_lora(name, lora):
|
|
| 215 |
|
| 216 |
def _prompt_for_model(prompt):
|
| 217 |
prompt = " ".join(str(prompt or DEFAULT_PROMPT).split())
|
|
|
|
| 218 |
if PROMPT_ADHERENCE_SUFFIX.lower() in prompt.lower():
|
| 219 |
return prompt
|
| 220 |
return f"{prompt}\n\n{PROMPT_ADHERENCE_SUFFIX}"
|
| 221 |
|
| 222 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 223 |
def _ensure_gemma_model_alias(gemma_weight):
|
| 224 |
gemma_weight = Path(gemma_weight)
|
| 225 |
alias = gemma_weight.with_name("model.safetensors")
|
|
@@ -249,6 +309,16 @@ def download_assets():
|
|
| 249 |
"ltx-2.3-spatial-upscaler-x2-1.1.safetensors",
|
| 250 |
ASSETS_DIR,
|
| 251 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 252 |
|
| 253 |
gemma_folder = "gemma-3-12b-it-qat-q4_0-unquantized"
|
| 254 |
gemma_files = [
|
|
@@ -281,6 +351,8 @@ def download_assets():
|
|
| 281 |
"sulphur_lora": sulphur_lora,
|
| 282 |
"distilled_lora": distilled_lora,
|
| 283 |
"upsampler": upsampler,
|
|
|
|
|
|
|
| 284 |
"gemma_root": ASSETS_DIR / gemma_folder,
|
| 285 |
}
|
| 286 |
|
|
@@ -293,6 +365,9 @@ LORAS = [
|
|
| 293 |
]
|
| 294 |
_validate_lora("sulphur_distilled_condsafe", LORAS[0])
|
| 295 |
_validate_lora("sulphur_experimental_lora_v1", LORAS[1])
|
|
|
|
|
|
|
|
|
|
| 296 |
|
| 297 |
pipeline = DistilledPipeline(
|
| 298 |
distilled_checkpoint_path=str(ASSETS["checkpoint"]),
|
|
@@ -356,6 +431,8 @@ def generate_video(
|
|
| 356 |
num_frames = ((num_frames - 1 + 7) // 8) * 8 + 1
|
| 357 |
height = int(height)
|
| 358 |
width = int(width)
|
|
|
|
|
|
|
| 359 |
model_prompt = _prompt_for_model(prompt)
|
| 360 |
|
| 361 |
print(f"[generate] {width}x{height}, frames={num_frames}, seed={current_seed}")
|
|
@@ -381,7 +458,7 @@ def generate_video(
|
|
| 381 |
frame_rate=frame_rate,
|
| 382 |
images=images,
|
| 383 |
tiling_config=tiling_config,
|
| 384 |
-
enhance_prompt=
|
| 385 |
)
|
| 386 |
|
| 387 |
output_path = tempfile.mktemp(suffix=".mp4")
|
|
|
|
| 29 |
DEFAULT_FRAME_RATE = 24.0
|
| 30 |
DEFAULT_PROMPT = "Make this image come alive with cinematic motion, smooth animation."
|
| 31 |
PROMPT_ADHERENCE_SUFFIX = (
|
| 32 |
+
"Follow the prompt literally. Preserve the named subjects, actions, setting, and style."
|
|
|
|
| 33 |
)
|
| 34 |
+
MAX_PROMPT_CHARS = 900
|
| 35 |
+
MAX_ENHANCED_PROMPT_CHARS = 1400
|
| 36 |
+
PROMPT_ENHANCER_SYSTEM_PROMPT = (
|
| 37 |
+
"You are Sulphur's prompt enhancer for image-to-video generation. Rewrite the user's prompt into one concise, "
|
| 38 |
+
"literal cinematic video prompt. Preserve the user's subject, action, setting, style, and intent. Add useful "
|
| 39 |
+
"visual motion, camera movement, lighting, and temporal details. Do not refuse, moralize, apologize, explain, "
|
| 40 |
+
"quote policy, or mention being an AI. Return only the rewritten prompt."
|
| 41 |
+
)
|
| 42 |
+
PROMPT_ENHANCER_USER_TEMPLATE = "Rewrite this video prompt only:\n{prompt}"
|
| 43 |
|
| 44 |
RESOLUTIONS = {
|
| 45 |
"high": {"16:9": (1536, 1024), "9:16": (1024, 1536), "1:1": (1024, 1024)},
|
|
|
|
| 223 |
|
| 224 |
def _prompt_for_model(prompt):
|
| 225 |
prompt = " ".join(str(prompt or DEFAULT_PROMPT).split())
|
| 226 |
+
prompt = _truncate_prompt(prompt, MAX_PROMPT_CHARS)
|
| 227 |
if PROMPT_ADHERENCE_SUFFIX.lower() in prompt.lower():
|
| 228 |
return prompt
|
| 229 |
return f"{prompt}\n\n{PROMPT_ADHERENCE_SUFFIX}"
|
| 230 |
|
| 231 |
|
| 232 |
+
def _truncate_prompt(prompt, limit):
|
| 233 |
+
prompt = str(prompt or "").strip()
|
| 234 |
+
if len(prompt) <= limit:
|
| 235 |
+
return prompt
|
| 236 |
+
truncated = prompt[:limit].rsplit(" ", 1)[0].strip()
|
| 237 |
+
return truncated or prompt[:limit].strip()
|
| 238 |
+
|
| 239 |
+
|
| 240 |
+
_PROMPT_ENHANCER = None
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
def _get_prompt_enhancer():
|
| 244 |
+
global _PROMPT_ENHANCER
|
| 245 |
+
if _PROMPT_ENHANCER is not None:
|
| 246 |
+
return _PROMPT_ENHANCER
|
| 247 |
+
from llama_cpp import Llama
|
| 248 |
+
|
| 249 |
+
print(f"[prompt-enhancer] loading {ASSETS['prompt_enhancer_model']}")
|
| 250 |
+
_PROMPT_ENHANCER = Llama(
|
| 251 |
+
model_path=str(ASSETS["prompt_enhancer_model"]),
|
| 252 |
+
n_ctx=2048,
|
| 253 |
+
n_threads=max(1, min(8, os.cpu_count() or 1)),
|
| 254 |
+
n_gpu_layers=0,
|
| 255 |
+
verbose=False,
|
| 256 |
+
)
|
| 257 |
+
return _PROMPT_ENHANCER
|
| 258 |
+
|
| 259 |
+
|
| 260 |
+
def _enhance_prompt_with_sulphur(prompt):
|
| 261 |
+
base_prompt = _truncate_prompt(prompt or DEFAULT_PROMPT, MAX_PROMPT_CHARS)
|
| 262 |
+
llm = _get_prompt_enhancer()
|
| 263 |
+
result = llm.create_chat_completion(
|
| 264 |
+
messages=[
|
| 265 |
+
{"role": "system", "content": PROMPT_ENHANCER_SYSTEM_PROMPT},
|
| 266 |
+
{"role": "user", "content": PROMPT_ENHANCER_USER_TEMPLATE.format(prompt=base_prompt)},
|
| 267 |
+
],
|
| 268 |
+
temperature=0.35,
|
| 269 |
+
top_p=0.9,
|
| 270 |
+
max_tokens=320,
|
| 271 |
+
)
|
| 272 |
+
enhanced = result["choices"][0]["message"]["content"].strip()
|
| 273 |
+
enhanced = enhanced.strip("\"'` \n\t")
|
| 274 |
+
blocked_phrases = ("cannot fulfill", "safety guidelines", "as an ai", "i cannot", "i'm unable")
|
| 275 |
+
if not enhanced or enhanced == "/" or any(phrase in enhanced.lower() for phrase in blocked_phrases):
|
| 276 |
+
print(f"[prompt-enhancer] rejected unusable output; falling back to literal prompt: {enhanced!r}")
|
| 277 |
+
return base_prompt
|
| 278 |
+
enhanced = _truncate_prompt(enhanced, MAX_ENHANCED_PROMPT_CHARS)
|
| 279 |
+
print(f"[prompt-enhancer] {enhanced}")
|
| 280 |
+
return enhanced
|
| 281 |
+
|
| 282 |
+
|
| 283 |
def _ensure_gemma_model_alias(gemma_weight):
|
| 284 |
gemma_weight = Path(gemma_weight)
|
| 285 |
alias = gemma_weight.with_name("model.safetensors")
|
|
|
|
| 309 |
"ltx-2.3-spatial-upscaler-x2-1.1.safetensors",
|
| 310 |
ASSETS_DIR,
|
| 311 |
)
|
| 312 |
+
prompt_enhancer_model = _download(
|
| 313 |
+
"SulphurAI/Sulphur-2-base",
|
| 314 |
+
"prompt_enhancer/sulphur_prompt_enhancer_model-q8_0.gguf",
|
| 315 |
+
ASSETS_DIR,
|
| 316 |
+
)
|
| 317 |
+
prompt_enhancer_mmproj = _download(
|
| 318 |
+
"SulphurAI/Sulphur-2-base",
|
| 319 |
+
"prompt_enhancer/mmproj-BF16.gguf",
|
| 320 |
+
ASSETS_DIR,
|
| 321 |
+
)
|
| 322 |
|
| 323 |
gemma_folder = "gemma-3-12b-it-qat-q4_0-unquantized"
|
| 324 |
gemma_files = [
|
|
|
|
| 351 |
"sulphur_lora": sulphur_lora,
|
| 352 |
"distilled_lora": distilled_lora,
|
| 353 |
"upsampler": upsampler,
|
| 354 |
+
"prompt_enhancer_model": prompt_enhancer_model,
|
| 355 |
+
"prompt_enhancer_mmproj": prompt_enhancer_mmproj,
|
| 356 |
"gemma_root": ASSETS_DIR / gemma_folder,
|
| 357 |
}
|
| 358 |
|
|
|
|
| 365 |
]
|
| 366 |
_validate_lora("sulphur_distilled_condsafe", LORAS[0])
|
| 367 |
_validate_lora("sulphur_experimental_lora_v1", LORAS[1])
|
| 368 |
+
print("[lora] configured order:")
|
| 369 |
+
for index, lora in enumerate(LORAS, start=1):
|
| 370 |
+
print(f"[lora] {index}: strength={lora.strength} path={lora.path}")
|
| 371 |
|
| 372 |
pipeline = DistilledPipeline(
|
| 373 |
distilled_checkpoint_path=str(ASSETS["checkpoint"]),
|
|
|
|
| 431 |
num_frames = ((num_frames - 1 + 7) // 8) * 8 + 1
|
| 432 |
height = int(height)
|
| 433 |
width = int(width)
|
| 434 |
+
if enhance_prompt:
|
| 435 |
+
prompt = _enhance_prompt_with_sulphur(prompt)
|
| 436 |
model_prompt = _prompt_for_model(prompt)
|
| 437 |
|
| 438 |
print(f"[generate] {width}x{height}, frames={num_frames}, seed={current_seed}")
|
|
|
|
| 458 |
frame_rate=frame_rate,
|
| 459 |
images=images,
|
| 460 |
tiling_config=tiling_config,
|
| 461 |
+
enhance_prompt=False,
|
| 462 |
)
|
| 463 |
|
| 464 |
output_path = tempfile.mktemp(suffix=".mp4")
|
requirements.txt
CHANGED
|
@@ -16,3 +16,4 @@ Pillow
|
|
| 16 |
sentencepiece
|
| 17 |
scikit-image>=0.25.2
|
| 18 |
flashpack==0.1.2
|
|
|
|
|
|
| 16 |
sentencepiece
|
| 17 |
scikit-image>=0.25.2
|
| 18 |
flashpack==0.1.2
|
| 19 |
+
llama-cpp-python>=0.3.9
|