pinder99's picture
Build Sulphur 2 Base demo
69939f9 verified
Raw
History Blame
5.76 kB
import os
import re
import tempfile
import time
os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF", "expandable_segments:True")
import spaces
import torch
import gradio as gr
from diffusers import AutoModel, LTX2Pipeline
from diffusers.pipelines.ltx2.export_utils import encode_video
from diffusers.pipelines.ltx2.utils import DEFAULT_NEGATIVE_PROMPT
MODEL_ID = "SulphurAI/Sulphur-2-base"
BASE_MODEL_ID = "diffusers/LTX-2.3-Diffusers"
CHECKPOINT_URL = (
"https://huggingface.co/SulphurAI/Sulphur-2-base/"
"blob/main/sulphur_dev_fp8mixed.safetensors"
)
WIDTH = 512
HEIGHT = 320
NUM_FRAMES = 49
FPS = 24.0
# A deliberately conservative public-demo guard. The upstream checkpoint is
# described as uncensored, but a public demo should not generate abuse content.
BLOCKED_PATTERNS = (
r"\b(child|children|kid|minor|underage|teen(?:ager)?)\b.{0,50}"
r"\b(nude|naked|sex|sexual|explicit|porn)\b",
r"\b(nude|naked|sex|sexual|explicit|porn)\b.{0,50}"
r"\b(child|children|kid|minor|underage|teen(?:ager)?)\b",
r"\b(rape|sexual assault|non[- ]consensual|revenge porn|csam)\b",
r"\b(gore|dismemberment|beheading|graphic violence)\b",
)
def _allowed(prompt: str) -> bool:
text = prompt.casefold()
return not any(re.search(pattern, text) for pattern in BLOCKED_PATTERNS)
transformer = AutoModel.from_single_file(
CHECKPOINT_URL,
torch_dtype=torch.bfloat16,
)
pipe = LTX2Pipeline.from_pretrained(
BASE_MODEL_ID,
transformer=transformer,
torch_dtype=torch.bfloat16,
).to("cuda")
pipe.vae.enable_tiling()
def _duration(prompt: str, seed: int, steps: int, *args, **kwargs) -> int:
del prompt, seed, args, kwargs
return min(300, 90 + int(steps) * 9)
@spaces.GPU(duration=_duration, size="xlarge")
def generate(prompt: str, seed: int, steps: int) -> tuple[str, str]:
"""Generate a short 512×320 video with synchronized audio from a text prompt."""
prompt = (prompt or "").strip()
if len(prompt) < 8:
raise gr.Error("Please enter a more descriptive prompt.")
if len(prompt) > 1_500:
raise gr.Error("Please keep the prompt under 1,500 characters.")
if not _allowed(prompt):
raise gr.Error(
"This public demo cannot process sexual, exploitative, or graphic-violence prompts."
)
started = time.perf_counter()
generator = torch.Generator(device="cuda").manual_seed(int(seed))
video, audio = pipe(
prompt=prompt,
negative_prompt=DEFAULT_NEGATIVE_PROMPT,
width=WIDTH,
height=HEIGHT,
num_frames=NUM_FRAMES,
frame_rate=FPS,
num_inference_steps=int(steps),
guidance_scale=3.0,
generator=generator,
output_type="np",
return_dict=False,
)
output = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False)
output.close()
encode_video(
video[0],
fps=FPS,
audio=audio[0].float().cpu(),
audio_sample_rate=pipe.vocoder.config.output_sampling_rate,
output_path=output.name,
)
elapsed = time.perf_counter() - started
return output.name, f"Finished in {elapsed:.1f}s · seed {int(seed)}"
CSS = """
.gradio-container { max-width: 1120px !important; }
.hero { text-align: center; margin: 1.5rem auto 1rem; }
.hero h1 { font-size: clamp(2rem, 5vw, 4rem); margin-bottom: .2rem; }
.hero p { color: #a1a1aa; font-size: 1.05rem; }
"""
with gr.Blocks(css=CSS, theme=gr.themes.Soft(primary_hue="purple")) as demo:
gr.HTML(
"""
<div class="hero">
<h1>🎬 Sulphur 2 Base</h1>
<p>Text-to-video with synchronized audio, powered by LTX 2.3.</p>
</div>
"""
)
with gr.Row():
with gr.Column(scale=5):
prompt = gr.Textbox(
label="Describe your shot",
placeholder=(
"A cinematic tracking shot of a tiny moss-covered robot "
"walking through a rain-soaked neon market..."
),
lines=7,
max_lines=12,
)
with gr.Row():
seed = gr.Number(label="Seed", value=42, precision=0)
steps = gr.Slider(
label="Inference steps", minimum=12, maximum=30, value=20, step=1
)
run = gr.Button("Generate video", variant="primary", size="lg")
gr.Markdown(
"Public demo guardrails apply. Avoid sexual, exploitative, "
"graphic, or deceptive content."
)
with gr.Column(scale=7):
video = gr.Video(label="Generated clip", autoplay=True)
status = gr.Markdown()
gr.Examples(
examples=[
[
"A macro cinematic shot of a glass terrarium at dawn. A tiny "
"clockwork hummingbird unfolds its brass wings, dew glints on "
"fern leaves, soft mechanical clicks and distant birdsong."
],
[
"A wide aerial shot over black volcanic sand at blue hour. "
"Bioluminescent waves roll ashore under a star-filled sky, with "
"wind and gentle surf in the soundtrack."
],
[
"Stop-motion style: a paper astronaut plants a small sunflower "
"on a handcrafted moon, warm studio lighting, subtle paper "
"rustling and a whimsical music-box melody."
],
],
inputs=[prompt],
cache_examples=False,
)
run.click(
fn=generate,
inputs=[prompt, seed, steps],
outputs=[video, status],
api_name="generate",
)
demo.queue(default_concurrency_limit=1).launch(mcp_server=True)