Add Modal test environment for SCAIL-2 training/inference
A free-tier-friendly Modal app to validate the SCAIL-2 code path on real Linux/GPU without a local GPU or the 19B checkpoint. - modal/checks.py: device-aware (CPU/GPU auto) consolidation of the Phase 1-4 plumbing checks using tiny random-init models + synthetic data (no checkpoint, no dataset): driving concat, zero-init patchify_proj widening (output-preserving), a FlexibleStrategy driving+mask training step + loss, and inference conditioning assembly. Passes on CPU locally. - modal/app.py: builds the workspace via `uv sync` (skips CUDA-only ltx-kernels; attention falls back to SDPA). Functions: verify (CPU, ~free), smoke (T4, cents), train (A10G/A100, paid — runs the real trainer against a checkpoint + data on the scail-data Volume). - modal/README.md: free-tier setup (modal setup), run commands, cost table, the scale-up path, and the expected preprocessed dataset layout. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
"""Modal app for testing the SCAIL-2 LTX-2 training/inference path.
|
||||
|
||||
Free-tier friendly: `verify` runs on CPU and `smoke` on a cheap T4, both using
|
||||
tiny random-init models + synthetic data (no checkpoint, no dataset) so a full run
|
||||
costs pennies. `train` is the scale-up entrypoint that runs the real trainer once
|
||||
you upload a checkpoint + preprocessed data to the `scail-data` Volume -- that one
|
||||
uses a real GPU and is NOT free.
|
||||
|
||||
Setup (once):
|
||||
pip install modal && modal setup
|
||||
|
||||
Run:
|
||||
modal run modal/app.py::verify # CPU plumbing check (~free)
|
||||
modal run modal/app.py::smoke # same checks on a T4 GPU (cents)
|
||||
modal run modal/app.py::train --config-rel configs/scail_animation_lora.yaml
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import modal
|
||||
|
||||
REPO = Path(__file__).parent.parent
|
||||
|
||||
# Build the workspace once into the image. ltx-kernels (CUDA-compiled) is excluded
|
||||
# from the workspace and not needed -- attention falls back to SDPA.
|
||||
image = (
|
||||
modal.Image.debian_slim(python_version="3.11")
|
||||
.apt_install("git")
|
||||
.pip_install("uv")
|
||||
.env({"UV_LINK_MODE": "copy", "UV_PROJECT_ENVIRONMENT": "/root/LTX-2/.venv"})
|
||||
.add_local_dir(
|
||||
str(REPO),
|
||||
"/root/LTX-2",
|
||||
copy=True,
|
||||
ignore=[".git", ".venv", "**/__pycache__", "**/*.pyc", "outputs", "wandb", "**/.pytest_cache"],
|
||||
)
|
||||
.run_commands("cd /root/LTX-2 && uv sync --package ltx-trainer")
|
||||
)
|
||||
|
||||
app = modal.App("scail-ltx2", image=image)
|
||||
|
||||
# Persistent volume for the (large) checkpoint + preprocessed dataset used by `train`.
|
||||
data_volume = modal.Volume.from_name("scail-data", create_if_missing=True)
|
||||
|
||||
_UV_PY = ["uv", "run", "--package", "ltx-trainer", "python"]
|
||||
|
||||
|
||||
def _run(args: list[str]) -> None:
|
||||
subprocess.run([*_UV_PY, *args], cwd="/root/LTX-2", check=True)
|
||||
|
||||
|
||||
@app.function(timeout=1800)
|
||||
def verify() -> None:
|
||||
"""Run the SCAIL-2 plumbing checks on CPU (near-free)."""
|
||||
_run(["modal/checks.py"])
|
||||
|
||||
|
||||
@app.function(gpu="T4", timeout=1800)
|
||||
def smoke() -> None:
|
||||
"""Run the same checks on a T4 GPU to validate the CUDA path (cents)."""
|
||||
_run(["modal/checks.py"])
|
||||
|
||||
|
||||
@app.function(gpu="A10G", timeout=60 * 60 * 6, volumes={"/data": data_volume})
|
||||
def train(config_rel: str = "configs/scail_animation_lora.yaml") -> None:
|
||||
"""Run the real SCAIL trainer. NOT free -- needs a real checkpoint + data.
|
||||
|
||||
Upload your base checkpoint, Gemma text encoder, and preprocessed data to the
|
||||
``scail-data`` Volume (mounted at /data), and point the config's paths at /data.
|
||||
For the full 19B model use a bigger GPU (e.g. gpu="A100") and expect real cost.
|
||||
"""
|
||||
_run(["packages/ltx-trainer/scripts/train.py", config_rel])
|
||||
data_volume.commit()
|
||||
|
||||
|
||||
@app.local_entrypoint()
|
||||
def main(target: str = "verify", config_rel: str = "configs/scail_animation_lora.yaml") -> None:
|
||||
if target == "smoke":
|
||||
smoke.remote()
|
||||
elif target == "train":
|
||||
train.remote(config_rel)
|
||||
else:
|
||||
verify.remote()
|
||||
Reference in New Issue
Block a user