"""LTX 2.3 CPU Space — feasibility reference + ZeroGPU upgrade recipe. This Space documents why LTX 2.3 (22B) on free HF CPU is impractical and shows the upgrade path. Generation is disabled on CPU; the UI mirrors what a ZeroGPU fork would look like so users can clone and switch hardware in one click. """ from pathlib import Path import gradio as gr FEASIBILITY_TABLE = """\ | Hardware | Per 2-sec clip | Notes | |-----------------------|----------------|-----------------------------------| | Free CPU (this Space) | not feasible | 22B at Q3_K_M does not fit in 16 GB | | CPU Upgrade 32 GB | 30-60 min | marginal, $0.30/clip | | ZeroGPU (Pro) | 25-40 sec | recommended path | | GPU L40S 48 GB | ~8 sec | dedicated | """ PIPELINE_NOTE = """\ **Path A** (used here): Unsloth `distilled-1.1` GGUF Q3_K_M, 10.6 GB DiT + Gemma-3-12B-it Q3_K_M 6 GB encoder. ComfyUI-GGUF loader. **Path C** (research): 10Eros fine-tune + cond_safe distill LoRA — fine-tune, not distilled. Larger LoRAs harm 10Eros fine-tune; needs tuning. **Text encoder cannot be swapped** — diffusion U-Net is bound to `google/gemma-3-12b-it`. Only quantisation, not replacement, is valid. """ def cpu_generate_stub(prompt: str, duration_sec: float, steps: int) -> str: return ( "CPU inference is disabled on this free Space — 22B + 16 GB RAM is\n" "infeasible. Fork to ZeroGPU (see README) to enable generation.\n\n" f"Prompt received: {prompt[:100]}\n" f"Duration: {duration_sec:.1f} s\n" f"Steps: {steps}" ) def health() -> str: return "ok — LTX 2.3 CPU Space (documentation mode)" DEMO_VIDEOS = sorted(str(p) for p in Path("/app/assets/videos").glob("*.mp4")) with gr.Blocks(title="LTX 2.3 CPU") as demo: gr.Markdown("**LTX 2.3 CPU** — feasibility reference + ZeroGPU recipe. 22B video diffusion does not run on free CPU; this is a fork-and-upgrade template.") with gr.Row(equal_height=True): with gr.Column(scale=1): prompt_in = gr.Textbox(label="Prompt", placeholder="A woman walking through a neon-lit Tokyo alley at night, cinematic", lines=3) with gr.Row(): duration_in = gr.Slider(1.0, 4.0, value=2.0, step=0.5, label="Duration (s)") steps_in = gr.Slider(4, 16, value=8, step=1, label="Steps (distilled)") run_btn = gr.Button("Generate (disabled on CPU — fork to ZeroGPU)", variant="primary") status = gr.Textbox(label="Status", lines=5, interactive=False, show_copy_button=True) with gr.Column(scale=1): gr.Markdown("### Feasibility") gr.Markdown(FEASIBILITY_TABLE) gr.Markdown("### Pipeline") gr.Markdown(PIPELINE_NOTE) if DEMO_VIDEOS: gr.Examples( examples=[[v] for v in DEMO_VIDEOS], inputs=[gr.Video(visible=False)], examples_per_page=6, cache_examples=False, label="Reference outputs (pre-generated on GPU)", ) run_btn.click(fn=cpu_generate_stub, inputs=[prompt_in, duration_in, steps_in], outputs=[status], api_name="generate") gr.Button(visible=False).click(fn=health, outputs=[gr.Textbox(visible=False)], api_name="health") demo.queue(default_concurrency_limit=1) demo.launch(server_name="0.0.0.0", server_port=7860, theme=gr.themes.Soft())