Runner for generative models with local and W&B tracking.
Write a small Python script per model that declares params, outputs, and metrics.
lite-runner handles the rest: CLI parsing, interactive prompts for missing values,
subprocess execution, stdout/stderr capture, metric extraction, file uploads to W&B,
and code snapshots for reproducibility.
Create a run.py for your model:
#!/usr/bin/env -S uv run
# /// script
# dependencies = ["lite-runner"]
# ///
from lite_runner import Runner, Param, Metric
runner = Runner(
command="python generate.py",
params=[
Param("prompt", help="Text prompt"),
Param("seed", type="int", default=42),
Param("output-path", value="$output/video.mp4", type="path-video"),
],
metrics=[
Metric("loss", pattern=r"loss=([\d.]+)"),
],
)
if __name__ == "__main__":
runner.run()Then run it (requires uv):
chmod +x run.py
./run.py --prompt "a cat walking" # interactive TUI fills missing params
./run.py --prompt "a cat" --no-interactive # non-interactive, fail if missing
./run.py --prompt "a cat" --dry-run # print command, don't run
./run.py --seed=- # unset a param (omit from command)
./run.py --image - - - # unset a multi-value paramEach runner.run() call:
- Parses CLI args (all params are optional in argparse; missing ones trigger TUI prompts)
- Creates an output directory at
~/lite_runs/<project>/<timestamp>_<run_name>/ - Inits a W&B run and logs all params, git info, and host metadata
- Saves a code snapshot (git archive + dirty diff) as a W&B artifact
- Builds and runs the subprocess, streaming stdout/stderr to terminal and log files
- Extracts metrics from stdout via regex
- Uploads output files (videos, images, artifacts) to W&B
- Logs duration, exit code, and status to W&B summary
Param("name") # basic string param
Param("seed", type="int", default=42) # typed with default
Param("mode", choices=["fast", "quality"]) # select from choices
Param("verbose", type="bool") # --verbose flag (store_true, always defaults to False)
Param("image", type="path-image") # file input, uploaded to W&B before run
Param(
"output-path",
value="$output/video.mp4", # fixed value, $output interpolated
type="path-video",
) # after the run, uploaded to W&B as a video
Param(
"input-image",
type=["path-image", "float", "float"], # multi-value flag
labels=["img", "start", "strength"],
) # each part prompted separately in TUIType — controls parsing, casting, and file upload intent.
All param values are logged to run.config.
The path-* variants additionally upload the file at that path to W&B:
| Type | Parsed as | File upload |
|---|---|---|
"str" (default) |
str |
— |
"int" |
int |
— |
"float" |
float |
— |
"bool" |
flag | — |
"path" |
str |
— |
"path-image" |
str |
as image |
"path-video" |
str |
as video |
"path-artifact" |
str |
as artifact |
"path-text" |
str |
as text |
"bool" params are special: they generate a --flag (no value), always default
to False (any other default= is ignored), and cannot appear in multi-value type lists.
Other fields:
value=makes a param fixed (never prompted, not in CLI). Can be a callable (called at resolve time).default=can be a callable (called at resolve time to compute the default)flag=overrides the CLI flag name (default:--<name with hyphens>)prompt=Falseskips interactive prompting (falls through to default). Requires adefault=. The param still accepts CLI flags and is logged normally.$outputin value is replaced with the run's output directorylog_when=auto-inferred:"before"for inputs,"after"for$outputpathstype=[...]gives per-element types for multi-value flags (nargs inferred from length)- Pass
-on CLI to unset a param (omit it from the subprocess command). For single-value:--seed=-. For multi-value:--image - - -(one-per element). This mirrors typing-at the interactive TUI prompt.
For files the model writes to uncontrolled locations:
Output("model_metadata.json", log_as="artifact", copy_to="$output/model_metadata.json")Supports glob patterns and directory zipping:
Output("debug/**/*.png", log_as="image") # upload each matched png
Output("debug/", log_as="image") # upload each file in directory
Output("debug/", log_as="zip") # zip entire directory, upload as artifact
Output("$output/frames/*.jpg", log_as="zip") # zip glob matches into archive
Output("weights/", log_as="zip", name="model-weights") # name= sets the W&B key (disambiguates zips)Extract values from stdout:
Metric("loss", pattern=r"loss=([\d.]+)")
Metric("status", pattern=r"status: (\w+)", type="str")
Metric("steps_per_sec", pattern=r"steps/s=([\d.]+)", type="int")
Metric(
"elapsed", pattern=r"elapsed: ([\d:.]+)", type="timedelta"
) # [[HH:]MM:]SS[.ddd] → secondsLast match wins. Patterns are matched against both stdout and stderr. Stored in wandb.run.summary.
Supported types: "float" (default), "int", "str", "timedelta".
Loop with override(). Runs are grouped in W&B for easy comparison:
runner = Runner(
command="python gen.py",
params=[...],
run_group="lr-sweep", # groups all runs together in W&B UI
)
for lr in [1e-3, 1e-4, 1e-5]:
runner.override(learning_rate=lr).run(no_interactive=True)Each call creates a separate W&B run, all grouped under the same group.
You can also update metadata per-run:
runner.override(seed=42).with_metadata(tags=["baseline"]).run()Runner(
command="python gen.py", # str or list[str] (list avoids shell splitting)
params=[...],
outputs=[...],
metrics=[...],
tags=["experiment-1"], # W&B run tags
env={
"CUDA_VISIBLE_DEVICES": "0",
"NOISY_VAR": None,
}, # set or unset env vars
secret_env={
"HF_TOKEN": "hf_xxx",
}, # like env, but redacted in logs / recorded config
project="my-project", # default: git repo name
run_group="my-sweep", # W&B run group for sweeps (None = no grouping)
)Each method returns a new Runner (immutable copies), so you can branch:
base = runner.parse_cli() # parse sys.argv
r1 = base.override(seed=42) # override params by name
r2 = base.override(seed=99)
r1.run() # auto-resolves defaults & prompts
r2.run()Methods:
| Method | Description |
|---|---|
parse_cli(argv) |
Parse CLI args (default: sys.argv[1:]) |
override(**kwargs) |
Set param values by name |
with_metadata(project=, run_group=, tags=) |
Update W&B metadata |
resolve_defaults() |
Apply defaults and fixed values |
ask_user(no_interactive=) |
Prompt for missing values |
run(...) |
Auto-calls any unapplied steps, then executes |
run() accepts kwargs dry_run, min_free_space_gib, no_interactive, no_wandb, project, run_name as alternatives to CLI flags. It returns a RunResult with fields: output_dir, exit_code, duration, run_name, project, config, param_values, param_sources.
| Flag | Description |
|---|---|
--dry-run |
Print command and exit |
--min-free-space-gib N |
Minimum free disk space in GiB (default: 1.0) |
--no-interactive |
Fail if required params missing |
--no-wandb |
Skip W&B logging (still logs to JSON) |
--run-name NAME |
Override W&B run name |
--project NAME |
Override project name |
| Location | Content |
|---|---|
run.config["param/*"] |
All param values |
run.config["param_source/*"] |
Where each param value came from (cli, default, fixed, override, prompt) |
run.config["git/*"] |
commit, branch, repo, dirty |
run.config["meta/*"] |
hostname, user, cwd, datetime, command, full_command, output_dir, env (secrets as ***) |
run.summary |
exit_code, duration_seconds, status, metrics |
| Artifacts | Log files, code snapshot, artifact-type outputs |
| Media | Videos and images from path-* type params/outputs |
This repo is also a Claude Code plugin
marketplace. Install the lite-runner skill so Claude Code writes
idiomatic run.py scripts and drives sweeps correctly:
/plugin marketplace add moonmath-ai/LiteRunner
/plugin install lite-runner@lite-runner-marketplace
See plugins/lite-runner/ for the plugin source.
Interested in contributing? See CONTRIBUTING.md for development setup and guidelines.