Spaces:
Running on Zero
Running on Zero
| """ | |
| OmniForcing text-encoder microservice (Gemma + LTX-2 AV connectors). | |
| Runs the AVGemmaTextEncoder in isolation and returns the conditioning tensors | |
| (video_context, audio_context, attention_mask) as a safetensors file, so the | |
| main generation Space can fetch them over the Gradio API and never has to load | |
| Gemma itself. Only needs ~30GB on disk (Gemma ~24GB + the 6GB LTX slim file), | |
| so it fits the free ephemeral disk with no persistent storage. | |
| """ | |
| from __future__ import annotations | |
| import os | |
| import sys | |
| import threading | |
| import traceback | |
| from pathlib import Path | |
| import gradio as gr | |
| import spaces | |
| import torch | |
| from huggingface_hub import hf_hub_download, snapshot_download | |
| from safetensors.torch import save_file | |
| SLIM_REPO = os.environ.get("SLIM_REPO", "linoyts/ltx2-vae-connectors-slim") | |
| SLIM_FILE = "ltx2_slim.safetensors" | |
| GEMMA_REPO = os.environ.get("GEMMA_REPO", "google/gemma-3-12b-it-qat-q4_0-unquantized") | |
| GEMMA_REPO_FALLBACK = "unsloth/gemma-3-12b-it" | |
| HF_TOKEN = os.environ.get("HF_TOKEN") | |
| DTYPE = torch.bfloat16 | |
| HERE = Path(__file__).resolve().parent | |
| PKG = HERE / "ltx2" / "packages" | |
| for _p in ("ltx-distillation", "ltx-causal", "ltx-core", "ltx-pipelines"): | |
| _s = PKG / _p / "src" | |
| if _s.exists(): | |
| sys.path.insert(0, str(_s)) | |
| from ltx_distillation.models.text_encoder_wrapper import create_text_encoder_wrapper # noqa: E402 | |
| STATE: dict = {} | |
| LOAD = {"stage": "starting", "error": None, "ready": False} | |
| def _set(s): | |
| LOAD["stage"] = s | |
| print(f"[load] {s}", flush=True) | |
| def _load(): | |
| try: | |
| _set("downloading LTX slim (connectors)") | |
| slim = hf_hub_download(SLIM_REPO, SLIM_FILE, token=HF_TOKEN) | |
| _set("downloading Gemma") | |
| repo = GEMMA_REPO | |
| try: | |
| gemma_dir = snapshot_download( | |
| repo, token=HF_TOKEN, | |
| allow_patterns=["*.safetensors", "*.json", "*.model", "tokenizer*"], | |
| ) | |
| except Exception as exc: | |
| print(f"[load] {repo} failed ({exc}); fallback {GEMMA_REPO_FALLBACK}", flush=True) | |
| repo = GEMMA_REPO_FALLBACK | |
| gemma_dir = snapshot_download( | |
| repo, token=HF_TOKEN, | |
| allow_patterns=["*.safetensors", "*.json", "*.model", "tokenizer*"], | |
| ) | |
| _set("building text encoder (CPU)") | |
| te = create_text_encoder_wrapper( | |
| checkpoint_path=slim, gemma_path=gemma_dir, device=torch.device("cpu"), dtype=DTYPE, | |
| ).eval() | |
| STATE["te"] = te | |
| STATE["gemma_repo"] = repo | |
| LOAD["ready"] = True | |
| _set("ready") | |
| except Exception as exc: | |
| LOAD["error"] = f"{type(exc).__name__}: {exc}" | |
| _set(f"ERROR: {LOAD['error']}") | |
| traceback.print_exc() | |
| threading.Thread(target=_load, daemon=True).start() | |
| _OUT = Path("/tmp/enc"); _OUT.mkdir(exist_ok=True) | |
| def encode(prompt: str): | |
| if not prompt or not prompt.strip(): | |
| raise gr.Error("Empty prompt.") | |
| if LOAD["error"]: | |
| raise gr.Error(f"Encoder failed to load: {LOAD['error']}") | |
| if not LOAD["ready"]: | |
| raise gr.Error(f"Still loading ({LOAD['stage']}).") | |
| te = STATE["te"].to("cuda") | |
| with torch.no_grad(): | |
| cond = te(text_prompts=[prompt.strip()]) | |
| STATE["te"] = te.to("cpu") | |
| torch.cuda.empty_cache() | |
| out = { | |
| "video_context": cond["video_context"].to("cpu").contiguous(), | |
| "audio_context": cond["audio_context"].to("cpu").contiguous(), | |
| "attention_mask": cond["attention_mask"].to("cpu").contiguous(), | |
| } | |
| path = _OUT / "cond.safetensors" | |
| save_file(out, str(path), metadata={"format": "pt"}) | |
| return str(path) | |
| with gr.Blocks(title="OmniForcing Gemma Encoder") as demo: | |
| gr.Markdown( | |
| "# OmniForcing — Text Encoder microservice\n" | |
| "Encodes a prompt into `{video_context, audio_context, attention_mask}` " | |
| "and returns them as a safetensors file. Consumed by the main generation Space." | |
| ) | |
| inp = gr.Textbox(label="Prompt", lines=2) | |
| btn = gr.Button("Encode", variant="primary") | |
| out = gr.File(label="conditioning (.safetensors)") | |
| status = gr.Textbox(label="Status", value="loading…", interactive=False) | |
| btn.click(encode, inputs=inp, outputs=out, api_name="encode") | |
| demo.load(lambda: LOAD["stage"], outputs=status) | |
| if __name__ == "__main__": | |
| demo.queue(max_size=16).launch(server_name="0.0.0.0", server_port=7860) | |