RunRaccoon 🦝
Local-only, drop-in replacement for the parts of Weights & Biases you actually look at.
RunRaccoon keeps wandb's API and file layout but has no cloud, no login, and no sync. Runs
are written to a runraccoon/ folder inside the output directory your training script
already uses. Each run gets the files wandb would write, plus publication-ready figures:
- a progress plot refreshed every epoch,
- QC images saved exactly where wandb saves them,
- summary figures when the run ends,
- an optional localhost dashboard that shows every run on the machine, live and past.
- import wandb
+ import runraccoon as wandb
That one-line change is all most scripts need, including scripts that rely on Ultralytics' built-in wandb integration.
Contents
- Install
- Quick start
- What you get on disk
- The figures
- The dashboard
- Ultralytics / YOLO
- Swapping an existing wandb script
- API compatibility
- Settings and environment variables
- Command line
- How it works
- Limitations
- Development
- Releasing to PyPI
Install
Option 1: from PyPI
Install
pip install runraccoon
Upgrade
pip install --upgrade runraccoon
Option 2: from source (editable)
git clone https://github.com/Gene-Weaver/RunRaccoon.git
cd RunRaccoon
pip install -e . # into the environment you train in
Requirements: Python ≥ 3.9, numpy, matplotlib, pillow, pyyaml. The dashboard uses
only the standard library and works fully offline.
Quick start
import runraccoon as wandb
wandb.init(project="leaf-segmentation", name="unet-baseline", config={"lr": 3e-4, "epochs": 50},
dir="outputs/unet-baseline") # your output dir; RunRaccoon adds runraccoon/ inside it
for epoch in range(50):
...
wandb.log({"train/loss": train_loss, "val/loss": val_loss, "val/iou": val_iou, "epoch": epoch})
if epoch % 5 == 0:
wandb.log({"qc/overlay": wandb.Image(overlay, caption=f"epoch {epoch}")}, commit=False)
wandb.summary["test/iou"] = test_iou
wandb.finish()
When the run starts, RunRaccoon prints where everything goes:
runraccoon: started run unet-baseline (k3x9q2ab) -> outputs/unet-baseline/runraccoon/run-20261002_105640-k3x9q2ab
runraccoon: live dashboard -> http://127.0.0.1:8473/#run=k3x9q2ab
finish() prints a short wandb-style summary with sparklines and the paths to the plots.
Runnable examples are in examples/:
| Example | What it shows |
|---|---|
minimal.py |
No framework needed |
pytorch_loop.py |
A plain PyTorch loop |
ultralytics_yolo.py |
YOLO training through Ultralytics' wandb integration |
What you get on disk
Runs go in a runraccoon/ folder inside the directory passed as init(dir=...). Without
dir, they go in the current working directory. Ultralytics passes its own output folder
(project/name), so YOLO runs land next to weights/ and results.csv.
Inside the run folder, the layout and file names are the same as wandb's:
<dir>/runraccoon/
├── debug.log -> latest run's logs/debug.log
├── latest-run -> run-20260924_083909-t0rb91yy
└── run-20260924_083909-t0rb91yy/
├── files/
│ ├── config.yaml wandb format: {key: {value: ...}}
│ ├── wandb-summary.json final / best value of every key
│ ├── wandb-metadata.json host, GPUs, git commit, argv, python, ...
│ ├── wandb-history.jsonl every logged step, one JSON object per line
│ ├── output.log captured stdout/stderr (progress bars collapsed)
│ ├── requirements.txt pip freeze of the environment
│ ├── media/
│ │ ├── images/<key>_<step>_<sha20>.<ext> e.g. qc/contact_sheet_73_fff6edfaacd84552d328.jpg
│ │ └── table/<key>_<step>_<sha20>.table.json e.g. curves/F1-Confidence(B)_table_302_....table.json
│ └── plots/ <- RunRaccoon's figures (next section)
├── logs/debug.log, logs/plots.log
└── artifacts/<name>/manifest.json
wandb stores history in a binary .wandb file. RunRaccoon writes wandb-history.jsonl
instead, the plain-text name older wandb versions used. You can open it with any tool, or
use plots/history.csv.
The figures
All figures go in files/plots/:
| File | When | What |
|---|---|---|
progress.png |
about once per epoch while training | The important metrics only: paired train/val/test losses, validation scores, your QC numbers. Learning rates, counters and model info are left out. |
summary/run_summary.png |
at finish() |
A report card: headline numbers on top (test results, best validation scores and the epoch they occurred), key curves below. |
summary/<section>.png |
at finish() |
Every chart, one figure per wandb panel section (loss, performance, metrics, lr, val_px, ...). |
charts/<key>.png |
when logged | Custom charts: wandb.plot.* and plot_table, e.g. Ultralytics' PR/F1 curves and confusion matrices. |
history.csv |
at finish() |
Every scalar, one row per step, for Excel / pandas / R. |
How the plots are put together:
- Pairing.
train/loss,val/lossandtest/lossshare one panel, and so do Keras-styleloss/val_lossandtrain_acc/val_acc. - Colors and line styles. Train is blue solid, val is orange dashed, test is green dotted. The colors are checked to be colorblind-safe, and the dashes keep the figure readable in grayscale print. Curves with many points are drawn solid and thinner so dashes don't turn into noise.
- Best point. For each metric whose direction is known, the best value is circled and
labeled, e.g.
min 0.108 @ epoch 278. Losses and errors are minimized; accuracy, mAP, IoU, F1 and similar are maximized. You can override this withdefine_metric. - Scales. Losses that span more than ~30× switch to a log axis. Series longer than 400 points get light smoothing drawn over the faint raw line.
- Typography. Text stays in vector form in PDF/SVG output (
pdf.fonttype 42), so journal checks and Illustrator edits work. Addplot_formats=("png", "pdf")to get vector copies.
To re-render the figures at any time, for example after a crash or after changing settings:
runraccoon replot path/to/outputs/unet-baseline --formats png,pdf
The dashboard
The first run on the machine starts a small server on http://127.0.0.1:8473 (localhost only).
Every later run, in any process or environment, registers with it.
Left panel
- Active lists every run currently training, so several GPUs or jobs each get their own entry.
- Past lists finished, failed, and crashed runs.
Tabs for the selected run
- Charts: the same panels as the figures, with hover tooltips, a smoothing slider, a metric filter, and a table view on every chart. Live runs update every few seconds.
- Media: every logged image with a step slider, like wandb's image panels.
- Plots: the rendered PNGs from
files/plots/. - Summary / Config: searchable key/value tables.
- Logs: the tail of
output.log.
The auto-started server shuts itself down after an hour with no active runs. To browse past runs at any time:
runraccoon dashboard # opens your browser
The dashboard follows your system's light/dark setting. Set RUNRACCOON_DASHBOARD=0 to turn
off the auto-start.
Ultralytics / YOLO
Ultralytics' wandb callback runs import wandb internally. Importing RunRaccoon registers it
as the wandb module, so the callback logs to RunRaccoon instead:
- per-epoch losses, metrics and learning rates,
train_batch*.jpg,val_batch*_pred.jpg,labels.jpgandresults.png,- confusion matrices and PR/F1/P/R curves (saved as tables and rendered to
plots/charts/), - the best weights, recorded as an artifact manifest.
import runraccoon as wandb # before model.train()
from ultralytics import YOLO, settings
settings.update({"wandb": True})
YOLO("yolo11n.pt").train(data="coco8.yaml", epochs=10, project="runs", name="exp")
Ultralytics' YOLO resume=True looks for a previous run in save_dir/wandb/latest-run. With
the runraccoon/ folder, a resumed YOLO training therefore starts a new RunRaccoon run instead
of appending to the old one. Calling wandb.init(id=..., resume="must") yourself is not
affected. Set RUNRACCOON_DIRNAME=wandb if you need Ultralytics' automatic resume to find the
earlier run.
Importing RunRaccoon also sets WANDB_MODE=disabled for child processes. If something
launches the real wandb in a subprocess, such as multi-GPU DDP workers, it stays offline.
Swapping an existing wandb script
Most scripts need only the import change. For example, here is
Honey/annotation_app_build/train_yolo26_pose.py:
- import wandb
+ import runraccoon as wandb
Everything else in that script works unchanged:
wandb.init(..., dir=run_dir),- the custom
EpochQCcallback loggingqc/contact_sheetimages andval_px/*atstep=epoch, - Ultralytics finishing the run, then
wandb.init(id=..., resume="must")reopening it to addtest/*to the summary.
Resuming creates a new run-<time>-<same id> directory, as wandb does. RunRaccoon carries the
earlier history and media into it, so the figures cover the whole run.
To switch every script in an environment without editing each import, add this near the start of your entry point:
import runraccoon # noqa: F401 (registers itself as `wandb`)
From then on, any import wandb in that process returns RunRaccoon.
API compatibility
| wandb | RunRaccoon |
|---|---|
init(project, name, config, dir, id, group, job_type, tags, notes, resume, reinit, mode, settings, ...) |
✓ (entity and cloud-only options are accepted and ignored) |
log(data, step=, commit=) |
✓ same step semantics; a late write to an already-committed step is kept, where wandb would drop it |
run.config, wandb.config.update(...), argparse Namespace |
✓ |
run.summary[...], summary.update(...) |
✓ |
define_metric(name, step_metric=, summary=, goal=, hidden=) |
✓ also controls the plots (x-axis, best marker, hidden) |
Image (path, PIL, numpy HW/HWC/CHW, torch tensor, matplotlib figure; caption) |
✓ (boxes=/masks= overlays are accepted but not drawn) |
lists of Image under one key |
✓ (images/separated, as wandb) |
Table, plot.line, plot.line_series, plot.scatter, plot.bar, plot.histogram, plot.pr_curve, plot.roc_curve, plot.confusion_matrix, plot_table |
✓ saved as tables and rendered to PNG |
Histogram, raw arrays |
✓ stored in history (not plotted) |
Artifact, log_artifact, log_model |
✓ local manifest (files referenced, optionally copied) |
save(glob), alert, finish(exit_code), run.dir, run.id, run.name, run.step, context manager |
✓ |
login, watch, unwatch, Settings(...) |
accepted, no-op |
Video, Audio, Html |
✓ from a file path |
Api(), use_artifact(...).download() from a server, sweeps, reports |
✗ local-only; these raise a clear error |
Settings and environment variables
Pass settings in code with wandb.init(settings=wandb.Settings(plot_every_s=30)) (or a plain
dict), or set them through the environment:
| Setting | Env var | Default | Meaning |
|---|---|---|---|
dashboard |
RUNRACCOON_DASHBOARD |
1 |
auto-start the localhost dashboard |
dashboard_port |
RUNRACCOON_PORT |
8473 |
dashboard port (also used by runraccoon dashboard) |
live_plots |
RUNRACCOON_LIVE_PLOTS |
1 |
refresh progress.png during training |
plot_every_s |
RUNRACCOON_PLOT_EVERY_S |
10 |
minimum seconds between refreshes (at most once per epoch either way) |
final_plots |
RUNRACCOON_FINAL_PLOTS |
1 |
render the summary figures in finish() |
plot_formats |
RUNRACCOON_PLOT_FORMATS |
png |
e.g. png,pdf,svg for the final figures |
live_metrics |
RUNRACCOON_LIVE_METRICS |
auto | glob patterns choosing the progress-plot metrics, e.g. train/*,val/* |
dirname |
RUNRACCOON_DIRNAME |
runraccoon |
name of the run folder created inside the output dir |
console |
RUNRACCOON_CONSOLE |
wrap |
off to skip output.log capture |
artifact_copy |
RUNRACCOON_ARTIFACT_COPY |
0 |
copy artifact files instead of referencing them |
quiet |
RUNRACCOON_QUIET |
0 |
silence RunRaccoon's console messages |
RUNRACCOON_HOME |
~/.runraccoon |
where the run index and dashboard log live | |
RUNRACCOON_SHIM |
1 |
0 to stop import runraccoon from registering as wandb |
The usual wandb variables are honored as defaults: WANDB_PROJECT, WANDB_NAME,
WANDB_RUN_ID, WANDB_RUN_GROUP, WANDB_JOB_TYPE, WANDB_TAGS, WANDB_NOTES, WANDB_DIR,
WANDB_RESUME, and WANDB_MODE=disabled, which turns logging off entirely.
Command line
runraccoon dashboard [--port 8473] open the dashboard (all runs, live + past)
runraccoon ls [--all] list runs and their status
runraccoon replot <run dir | id> re-render every figure for a run [--formats png,pdf]
runraccoon register <folder>... add existing/moved run folders to the dashboard
runraccoon forget <id>... remove runs from the dashboard index (files are kept)
runraccoon gc forget runs whose folders were deleted
python -m runraccoon ... works the same way.
How it works
training script runraccoon/run-<time>-<id>/files/
runraccoon.init / log ── appends ─▶ history · summary · config · media
│ ▲ ▲
│ spawns ≤ 1× per epoch │ reads │ reads
▼ │ │
renderer subprocess (matplotlib) ────────────┘ │
│ writes files/plots/*.png │
│ │
└ heartbeat ─▶ ~/.runraccoon/runs/<id>.json ◀─ reads ─ dashboard server
127.0.0.1:8473
- The training process only writes files. Media is hashed and written when logged, and history rows are appended as each step is committed. Plotting runs in a short-lived subprocess, so it never competes with training for the GIL and never touches your script's matplotlib state.
- One module decides what gets plotted.
runraccoon/panels.pyhandles key pairing, sections, the x-axis, and the better direction. The PNG renderer and the dashboard both use it, so they always agree. - The dashboard is read-only. It reads the same files and the per-run heartbeat records. It therefore works across processes, Python environments, and runs that crashed.
The code is organized by job:
| Module | Job |
|---|---|
sdk.py |
Module-level wandb.* functions |
run.py |
The Run class and step semantics |
media.py |
Image, Table, ... |
plot.py |
wandb.plot |
paths.py |
Directory layout |
reader.py |
Reading a run back from disk |
registry.py |
The machine-wide run index |
plotting/ |
Style, figures, renderer, scheduler |
dashboard/ |
Server and static app |
Limitations
- Nothing is uploaded, ever. No sweeps, reports, team sharing, or
wandb.Api(). - Image overlays (
boxes=,masks=) are not drawn. Render them into the image before logging. - System metrics (GPU utilization over time) are not tracked. The GPU model and count are
recorded in
wandb-metadata.json. output.logcaptures Python-levelprint/logging. Output written directly by C extensions to file descriptor 1/2 is not captured.- If the real
wandbwas imported before RunRaccoon, code already holding that module is not redirected (RunRaccoon prints a warning). Import RunRaccoon first.
Development
pip install -e ".[dev]"
pytest
Tests cover the on-disk layout, step semantics, media naming, resume, metric pairing, rendering, and the dashboard API.
Releasing to PyPI
The release flow matches VoucherVisionGO-client: setup.py holds the package metadata, and
builds go to dist/, which is gitignored.
- Bump
__version__inrunraccoon/_version.py. This is the only place the version lives;setup.pyandrunraccoon.__version__both read it. - Add an entry to
CHANGELOG.md. - Build and upload:
pip install build twine # once
python -m build
python -m twine upload dist/* --skip-existing --verbose
--skip-existing lets dist/ keep older builds without twine failing on versions already
on PyPI. Twine asks for credentials: use __token__ as the username and a PyPI API token as
the password, or put them in ~/.pypirc.
Before the first upload you can rehearse on TestPyPI with
python -m twine upload --repository testpypi dist/*.
License: MIT.
Metadata
Release files for runraccoon 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| runraccoon-0.1.1.tar.gz | 89.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| runraccoon-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 174.6 kB
Release files / runraccoon-0.1.1.tar.gz
| Download URL | runraccoon-0.1.1.tar.gz |
|---|---|
| Size | 89.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3ad7e2a3f4c1f397aa144d71be4b5dd5994bf56d12a88be5af99e6d5c02447ad
|
|
BLAKE2b-256 checksum How to use checksums |
3c7c0f64c83010d8bbafa94dd11084263661b2e8a5483cb25bda13be19bcd1da
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.5
|
Release files / runraccoon-0.1.1-py3-none-any.whl
| Download URL | runraccoon-0.1.1-py3-none-any.whl |
|---|---|
| Size | 85.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ded0db9129611448fb84d1b1f4ce78d0adacf7c53e3499a0215b798d54f54fbd
|
|
BLAKE2b-256 checksum How to use checksums |
2766b049caf570d4069945f9185a15c263cc255620e73461a8ea8d896828e6fb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.5
|