LiteRunner
[![Tests][tests-badge]][tests-link] [![codecov][codecov-badge]][codecov-link] [![PyPI version][pypi-version-badge]][pypi-link] [![PyPI platforms][pypi-platforms-badge]][pypi-link] [![Total downloads][pepy-badge]][pepy-link] \ [![Made Using tsvikas/python-template][template-badge]][template-link] [![GitHub Discussion][github-discussions-badge]][github-discussions-link] [![PRs Welcome][prs-welcome-badge]][prs-welcome-link]
Overview
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.
Quick start
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 param
What it does
Each runner.run() call:
1. Parses CLI args (all params are optional in argparse; missing ones trigger TUI prompts) 1. Creates an output directory at ~/lite_runs/<project>/<timestamp>_<run_name>/ 1. Inits a W&B run and logs all params, git info, and host metadata 1. Saves a code snapshot (git archive + dirty diff) as a W&B artifact 1. Builds and runs the subprocess, streaming stdout/stderr to terminal and log files 1. Extracts metrics from stdout via regex 1. Uploads output files (videos, images, artifacts) to W&B 1. Logs duration, exit code, and status to W&B summary
Param
<!-- blacken-docs:off -->
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 TUI
<!-- blacken-docs:on -->
Type — 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.
Output
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:
<!-- blacken-docs:off -->
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)
<!-- blacken-docs:on -->
Metric
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] → seconds
Last match wins. Patterns are matched against both stdout and stderr. Stored in wandb.run.summary.
Supported types: "float" (default), "int", "str", "timedelta".
Sweeps
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 options
<!-- blacken-docs:off -->
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)
)
<!-- blacken-docs:on -->
Pipeline API
Each method returns a new Runner (immutable copies), so you can branch:
<!-- blacken-docs:off -->
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()
<!-- blacken-docs:on -->
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.
Built-in CLI flags
| 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 |
What gets logged to W&B
| 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 |
Using with Claude Code
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.
Contributing
Interested in contributing? See CONTRIBUTING.md for development setup and guideline.
[codecov-badge]: https://codecov.io/gh/moonmath-ai/LiteRunner/graph/badge.svg [codecov-link]: https://codecov.io/gh/moonmath-ai/LiteRunner [github-discussions-badge]: https://img.shields.io/static/v1?label=Discussions&message=Ask&color=blue&logo=github [github-discussions-link]: https://github.com/moonmath-ai/LiteRunner/discussions [pepy-badge]: https://img.shields.io/pepy/dt/lite-runner [pepy-link]: https://pepy.tech/project/lite-runner [prs-welcome-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg [prs-welcome-link]: https://opensource.guide/how-to-contribute/ [pypi-link]: https://pypi.org/project/lite-runner/ [pypi-platforms-badge]: https://img.shields.io/pypi/pyversions/lite-runner [pypi-version-badge]: https://img.shields.io/pypi/v/lite-runner [template-badge]: https://img.shields.io/badge/%F0%9F%9A%80_Made_Using-tsvikas%2Fpython--template-gold [template-link]: https://github.com/tsvikas/python-template [tests-badge]: https://github.com/moonmath-ai/LiteRunner/actions/workflows/ci.yml/badge.svg [tests-link]: https://github.com/moonmath-ai/LiteRunner/actions/workflows/ci.yml










