diff --git a/.gitignore b/.gitignore index 084c0f5..b118f17 100644 --- a/.gitignore +++ b/.gitignore @@ -21,5 +21,6 @@ bundle/* # Input/output files input/* +!input/.gitkeep output/* diff --git a/AGENTS.md b/AGENTS.md index 6929111..6cf29ee 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -26,7 +26,7 @@ Dockerfile bakes it and rebuilds are only needed when it changes. ```bash uv sync # set up env uv run dltb-oneshot --help # cheap local sanity check (no CUDA) -uv run dltb-iterate --model sd-turbo --input input/test_512.png --iterations 3 +uv run dltb-iterate --model sd-turbo --input input_example/test_512.png --iterations 3 just bundle # stage pod-ready tarball in bundle/ just image-build /: # build pod image (linux/amd64) just hf-status | hf-keep | hf-clean # HF cache management @@ -41,6 +41,10 @@ scripts/smoke.sh # tiny run of every tool; needs GPU python3 src/dltb/analyze_drift.py # CPU-only drift metrics ``` +The drivers read their input files (`IMG`/`CLIP`) from `input/inputs.env`, +falling back to the tracked `input_example/inputs.env`; environment variables +win (see `scripts/inputs.sh`). + There is **no test suite and no linter**. Verification ladder: 1. `uv run --help` locally (catches import/arg breakage without CUDA). 2. `scripts/smoke.sh` on the pod after deploying a bundle. @@ -104,8 +108,10 @@ Key invariants: - **Any tar creation needs `COPYFILE_DISABLE=1`** or macOS pollutes archives with `._*` AppleDouble members (`bundle.sh` does this + a python3 `tarfile` verification guard; never verify with `tar -t`, it hides them). -- Bundles and images ship **tracked files only** — `git add` new scripts - before `just bundle`, or the pod silently misses them (both scripts warn). +- Bundles ship **tracked files only**, with one exception: gitignored `input/` + (user inputs) is packed explicitly by `bundle.sh`. `git add` new scripts + before `just bundle`, or the pod silently misses them (both scripts warn); + image builds remain tracked-only. - `.gitignore` covers `output/`, `bundle/`, `._*`, `.DS_Store`, `.pi/` — keep generated artifacts out of git. diff --git a/README.md b/README.md index 2a32236..d3faca1 100644 --- a/README.md +++ b/README.md @@ -99,3 +99,13 @@ the saved frames, and the output video(s). Run `uv run --help` for all options (strength, steps, seed handling, resolution, prompt, save frequency, video FPS, ...). + +## Inputs + +Sample inputs (a test image and a short clip, provenance in `SOURCES.txt`) +live in `input_example/`. The tools take any `--input` path, but the sweep +and smoke scripts read theirs from a config: copy +`input_example/inputs.env` to `input/inputs.env` (gitignored), point `IMG=` +and `CLIP=` at your files under `input/`, and every script picks them up. +An environment variable (`CLIP=... scripts/sweep.sh`) still wins over the +file; see `scripts/inputs.sh`. diff --git a/README_RUNPOD.md b/README_RUNPOD.md index 9259d07..08d0d35 100644 --- a/README_RUNPOD.md +++ b/README_RUNPOD.md @@ -213,6 +213,14 @@ scripts/sweep-klein.sh # klein prompt ladder + guidance probes (single model) re-loaded from disk per run. For a 32 GB model that load is ~15–30 s per run, times ~6 runs per model. This is inherent to `sweep.sh`'s per-run invocation. +Inputs: the run scripts read their input files from `input/inputs.env` +(`IMG=`, `CLIP=`); without one they fall back to the tracked examples in +`input_example/` (provenance in `SOURCES.txt`). Your own files under `input/` +ride in the bundle, so the usual flow is: drop files into `input/`, copy +`input_example/inputs.env` to `input/inputs.env`, point it at them, `just +bundle`, send. A one-off run with a different clip does not need the file: +`CLIP=input/other.mp4 scripts/sweep.sh` (the environment beats the file). + Smoke test after deploying a new bundle — one tiny run of every tool (`dltb-oneshot`, `dltb-iterate`, `dltb-continuous` both modes + tails, and the hf-cache helper), with artifact checks: @@ -269,6 +277,10 @@ disk. ## 6. Getting data in and out +- Input files do not need a separate transfer: `just bundle` packs the + gitignored `input/` directory into every bundle (tracked sample inputs live + in `input_example/`). Alternatively `runpodctl send`/`scp` files into + `input/` on the pod after extracting. - `scp` works with the connection from `runpodctl pod get ` / `ssh info`. - Or `runpodctl send ` locally and `runpodctl receive ` on the pod (install runpodctl there first). @@ -306,7 +318,7 @@ disk. Runs one pass per model in a fresh process, reporting native fit, peak VRAM and whether `--offload` is needed. Run it after `uv sync`, from the extracted repo -root (`input/test_768.png` ships in the bundle): +root (`input_example/test_768.png` ships in every bundle): ```bash for m in sd-turbo sdxl-turbo flux2-klein-4b flux-schnell flux2-klein-9b; do @@ -322,7 +334,7 @@ key, offload = os.environ["MODEL"], os.environ["OFFLOAD"] == "1" spec = MODELS[key] settings = PassSettings(num_inference_steps=1, strength=1.0) w, h = spec.default_width, spec.default_height -src = prepare_frame(Image.open("input/test_768.png"), w, h) +src = prepare_frame(Image.open("input_example/test_768.png"), w, h) t0 = time.time() try: diff --git a/input/.gitkeep b/input/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/input_example/SOURCES.txt b/input_example/SOURCES.txt index 4fb22e1..3086acb 100644 --- a/input_example/SOURCES.txt +++ b/input_example/SOURCES.txt @@ -1,2 +1,2 @@ -input/test_512.png, input/test_768.png from https://picography.co/unleashed/ (CC0 license) -video_raw.mp4 , video_croped.mp4 from https://www.pexels.com/video/underwater-octopus-in-coral-habitat-34857508/ (https://www.pexels.com/license/) \ No newline at end of file +test_512.png, test_768.png from https://picography.co/unleashed/ (CC0 license) +video_raw.mp4, video_cropped.mp4 from https://www.pexels.com/video/underwater-octopus-in-coral-habitat-34857508/ (https://www.pexels.com/license/) diff --git a/input_example/inputs.env b/input_example/inputs.env new file mode 100644 index 0000000..7690f11 --- /dev/null +++ b/input_example/inputs.env @@ -0,0 +1,14 @@ +# inputs.env -- default input files for the run scripts +# (scripts/sweep.sh, scripts/sweep-klein.sh, scripts/smoke.sh). +# +# The scripts source input/inputs.env when it exists, otherwise this file. +# input/ is gitignored, but scripts/bundle.sh packs it into pod bundles, so a +# copy of this file there travels with your runs. +# +# The ${VAR:-...} guards keep one-off environment overrides working: +# CLIP=input/other.mp4 scripts/sweep.sh +# Plain assignments (CLIP=input/my.mp4) work too, but then the file wins over +# the environment for that variable. + +IMG="${IMG:-input_example/test_512.png}" +CLIP="${CLIP:-input_example/video_cropped.mp4}" diff --git a/scripts/bundle.sh b/scripts/bundle.sh index 063f587..0cfc763 100755 --- a/scripts/bundle.sh +++ b/scripts/bundle.sh @@ -35,6 +35,21 @@ bundle_file="${out_dir}/${top}.tar.gz" mkdir -p "${stage}" git ls-files -z | tar --no-xattrs --null -T - -cf - | tar -xf - -C "${stage}" +# User inputs ride along: input/ is gitignored (tracked sample files live in +# input_example/), but pod runs need it -- the one exception to tracked-files- +# only. The untracked-file warning below cannot cover this, because `git +# ls-files --others` never lists ignored paths, so the files are added +# explicitly here. AppleDouble/DS_Store litter is skipped by name; .gitkeep is +# already staged via git ls-files. +user_inputs=() +while IFS= read -r -d '' f; do + user_inputs+=("$f") +done < <(find input -type f ! -name '._*' ! -name '.DS_Store' ! -name '.gitkeep' -print0 2>/dev/null) +if [[ ${#user_inputs[@]} -gt 0 ]]; then + printf '%s\n' "${user_inputs[@]}" | tar --no-xattrs -T - -cf - | tar -xf - -C "${stage}" + printf 'bundle: added user input: %s\n' "${user_inputs[@]}" +fi + # Guard against staging something broken. for f in pyproject.toml uv.lock \ src/dltb/models.py src/dltb/imaging.py src/dltb/output.py src/dltb/args.py \ diff --git a/scripts/inputs.sh b/scripts/inputs.sh new file mode 100755 index 0000000..7e06bb5 --- /dev/null +++ b/scripts/inputs.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +# Permission to use, copy, modify, and/or distribute this software for +# any purpose with or without fee is hereby granted. +# +# THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL +# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE +# FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING +# OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +# +# inputs.sh -- shared IMG/CLIP selection for the run scripts. +# +# Source this from the repo root (after the cd in sweep.sh / sweep-klein.sh / +# smoke.sh): +# source scripts/inputs.sh +# +# Sources input/inputs.env (user config: KEY=VALUE lines, IMG= and CLIP=) when +# present, otherwise the tracked input_example/inputs.env. input/ is +# gitignored, but scripts/bundle.sh packs it into pod bundles, so user inputs +# travel with the code. +# +# Precedence for a variable: environment > input/inputs.env > example +# defaults -- provided the conf file assigns with ${VAR:-value} guards, as +# input_example/inputs.env does. Plain assignments in a user file work too; +# they just make the file win over the environment for that variable. +# +# Callers still apply their own ${VAR:-...} fallbacks after sourcing, so a +# missing conf file only costs the built-in defaults. + +if [[ -f input/inputs.env ]]; then + source input/inputs.env +elif [[ -f input_example/inputs.env ]]; then + source input_example/inputs.env +fi diff --git a/scripts/smoke.sh b/scripts/smoke.sh index fdcfd15..0036fdb 100755 --- a/scripts/smoke.sh +++ b/scripts/smoke.sh @@ -15,10 +15,11 @@ # smoke.sh -- post-deploy smoke test: one tiny run of every tool. # # Verifies the three console scripts and the cache helper against a real GPU -# using the cheapest model, bundle-shipped inputs, and minimal budgets: +# using the cheapest model, inputs from input/inputs.env (falling back to the +# tracked input_example/ files), and minimal budgets: # # 0. scripts/hf-cache.sh status (dltb.models import + cache probe) -# 1. dltb-oneshot 1 pass on input/test_512.png +# 1. dltb-oneshot 1 pass on the configured image # 2. dltb-iterate 3 free-running passes + timelapse.mp4 # 3. dltb-continuous stateful 3 reprojected source frames + 2 frames # per tail (freeze, free, black) @@ -39,8 +40,10 @@ # # Environment: # MODEL model key to smoke (default sd-turbo) -# IMG / CLIP input files (default input/test_512.png, -# input/video_cropped.mp4) +# IMG / CLIP input files (default: input/inputs.env if +# present, else the tracked +# input_example/ files; see +# scripts/inputs.sh) # SKIP_GPU_CHECK=1 bypass the CUDA preflight # # Log: output/smoke_.log @@ -49,9 +52,12 @@ set -euo pipefail cd "$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +# Input files: env > input/inputs.env (user) > input_example/inputs.env. +source scripts/inputs.sh + MODEL="${MODEL:-sd-turbo}" -IMG="${IMG:-input/test_512.png}" -CLIP="${CLIP:-input/video_cropped.mp4}" +IMG="${IMG:-input_example/test_512.png}" +CLIP="${CLIP:-input_example/video_cropped.mp4}" # Keep these in sync with the expected-dir strings below (they mirror the # tools' run-tag encoding: stateful-a_tails). diff --git a/scripts/sweep-klein.sh b/scripts/sweep-klein.sh index 5a2a49c..45b5652 100755 --- a/scripts/sweep-klein.sh +++ b/scripts/sweep-klein.sh @@ -52,7 +52,10 @@ # # Environment overrides: # MODEL klein model key (default flux2-klein-9b; 4b is ungated) -# CLIP source video (default input/video_cropped.mp4) +# CLIP source video (default: input/inputs.env if +# present, else the tracked +# input_example/video_cropped.mp4; +# see scripts/inputs.sh) # CONDITIONING blend | dual-ref (default blend) # REF_ORDER state-first | frame-first (default state-first; dual-ref only) # BLEND anchor-blend for all runs (default 0.1 - klein's active range @@ -81,8 +84,11 @@ set -euo pipefail cd "$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +# Input files: env > input/inputs.env (user) > input_example/inputs.env. +source scripts/inputs.sh + MODEL="${MODEL:-flux2-klein-9b}" -CLIP="${CLIP:-input/video_cropped.mp4}" +CLIP="${CLIP:-input_example/video_cropped.mp4}" CONDITIONING="${CONDITIONING:-blend}" REF_ORDER="${REF_ORDER:-state-first}" BLEND="${BLEND:-0.1}" diff --git a/scripts/sweep.sh b/scripts/sweep.sh index bea603b..7de9901 100755 --- a/scripts/sweep.sh +++ b/scripts/sweep.sh @@ -51,7 +51,10 @@ # MODELS="sd-turbo" MAX_FRAMES=20 TAIL_FRAMES=5 scripts/sweep.sh # smoke test # # Environment overrides: -# CLIP source video (default input/video_cropped.mp4) +# CLIP source video (default: input/inputs.env if +# present, else the tracked +# input_example/video_cropped.mp4; +# see scripts/inputs.sh) # MODELS models to sweep (default sd-turbo sdxl-turbo flux-schnell flux2-klein-9b) # BLENDS anchor-blend values (default 0.1 0.3 0.5) # BASELINE baseline blend (default 0.3) @@ -74,7 +77,10 @@ set -euo pipefail cd "$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -CLIP="${CLIP:-input/video_cropped.mp4}" +# Input files: env > input/inputs.env (user) > input_example/inputs.env. +source scripts/inputs.sh + +CLIP="${CLIP:-input_example/video_cropped.mp4}" MODELS="${MODELS:-sd-turbo sdxl-turbo flux-schnell flux2-klein-9b}" BLENDS="${BLENDS:-0.1 0.3 0.5}" BASELINE="${BASELINE:-0.3}"