File size: 3,478 Bytes
ce45d75
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
"""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())