Tiny ML experiment tracking on Cloudflare R2 + D1
Project description
r2d1
Tiny ML experiment tracking on Cloudflare R2 + D1.
- R2 stores checkpoint/artifact files.
- D1 stores job metadata, epoch metrics, and checkpoint pointers.
- Works from local scripts, notebooks, Colab, Kaggle, Vast.ai, Modal, RunPod, Docker, CI, and ordinary GPU servers.
- Keeps training code framework-agnostic: PyTorch, JAX, or anything that can write files.
pip install r2d1
0.1.5 credential discovery
r2d1 now has a universal secret resolver:
from r2d1 import secret
hf_token = secret("HF_TOKEN", aliases=["HF_HUB_TOKEN"], required=False)
github_token = secret("GITHUB_TOKEN", aliases=["GH_TOKEN"], required=False)
The resolver searches:
.envin the current directory or parents, withoverride=Falseos.environ- Google Colab secrets via
google.colab.userdata, when available - Kaggle secrets via
kaggle_secrets.UserSecretsClient, when available
Modal, Vast.ai, RunPod, Docker, GitHub Actions, SageMaker, Vertex AI Workbench, Lightning AI, Paperspace, JupyterHub, Hugging Face Spaces, and similar platforms are covered when they inject secrets as environment variables.
By default, found secrets are copied into os.environ[NAME], so downstream libraries can see them too.
R2D1 credentials
Put these in a private .env, notebook secret store, Modal secret, Vast.ai env vars, or your shell environment:
export R2D1_ACCOUNT_ID="..."
export R2D1_API_TOKEN="..."
export R2D1_D1_DATABASE_ID="..."
export R2D1_R2_BUCKET="..."
export R2D1_R2_ACCESS_KEY="..."
export R2D1_R2_SECRET_KEY="..."
# optional:
export R2D1_R2_ENDPOINT_URL="https://<account_id>.r2.cloudflarestorage.com"
Then use:
from r2d1 import Tracker
tracker = Tracker.from_env()
Tracker.from_env() is strict by default. If any required R2D1 key is missing, it raises MissingSecretError listing all names and environments it tried.
Basic usage
from pathlib import Path
from r2d1 import Tracker, r2d1
tracker = Tracker.from_env()
job = tracker.start_job(
"mnist_dit",
dataset_key="hf://datasets/ylecun/mnist",
config={"model": "tiny-pixel-dit"},
tags=["mnist", "flow-matching"],
)
for epoch in r2d1(range(10), job=job, checkpoint_every=1, keep_last=2):
loss = train_one_epoch(...)
# small JSON metrics/metadata -> D1
epoch.d1(loss=float(loss), lr=float(lr))
# files/artifacts/checkpoints -> R2
if epoch.should_checkpoint:
epoch.r2({
"checkpoint.pt": Path("ckpt/checkpoint.pt"),
"config.json": {"epoch": epoch.i},
})
job.complete()
Aliases are provided:
epoch.log(...) # same as epoch.d1(...)
epoch.checkpoint(...) # same as epoch.r2(...)
Decorator style
from r2d1 import Tracker, r2d1
tracker = Tracker.from_env()
@tracker.job(name="dit_run", dataset_key="hf://datasets/ylecun/mnist")
def train(job):
for epoch in r2d1(range(10), job=job, checkpoint_every=1, keep_last=2):
loss = train_one_epoch(...)
epoch.d1(loss=float(loss))
if epoch.should_checkpoint:
epoch.r2({"checkpoint.pt": "ckpt/checkpoint.pt"})
train()
Clean exit marks the job completed. Exceptions/interrupts mark it interrupted.
Rotating checkpoints
keep_last=2 uses rotating R2 slots:
jobs/job_3/checkpoints/slot_0/
jobs/job_3/checkpoints/slot_1/
Each upload writes files first, then manifest.json, then updates D1. D1 only points at complete checkpoints.
Resume
tracker = Tracker.from_env()
job = tracker.resume_job(3)
files, manifest = job.load_latest(include_manifest=True)
# files["checkpoint.pt"] contains checkpoint bytes.
# manifest["epoch"] tells you where to resume.
Universal secrets for notebooks/cloud jobs
from r2d1 import secret, export_secrets
# Optional secrets, no error if missing.
hf_token = secret("HF_TOKEN", aliases=["HF_HUB_TOKEN"], required=False)
github_token = secret("GITHUB_TOKEN", aliases=["GH_TOKEN"], required=False)
# Load several and populate os.environ.
export_secrets(["HF_TOKEN", "GITHUB_TOKEN", "WANDB_API_KEY"], required=False)
Strict mode:
github_token = secret("GITHUB_TOKEN") # raises MissingSecretError if absent
No secret values are logged by r2d1.
Install extras
pip install r2d1[torch]
pip install r2d1[jax]
pip install r2d1[dev]
r2d1 does not own your model serialization. Prefer framework-native or safe formats such as .safetensors, PyTorch .pt, Orbax/Flax outputs, JSON configs, logs, images, etc. epoch.r2(...) ships the files you provide.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file r2d1-0.1.5.tar.gz.
File metadata
- Download URL: r2d1-0.1.5.tar.gz
- Upload date:
- Size: 19.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0d060c38bd8dacbefa12b51f71bb0980dd25b4310d693b8524f026d19be9b407
|
|
| MD5 |
160a4f316af8dcef62b302a3c0dfe59f
|
|
| BLAKE2b-256 |
9268e53a9a4c06a15953fcc5e1435fe490a43b9601f8693bda437e59710399df
|
Provenance
The following attestation bundles were made for r2d1-0.1.5.tar.gz:
Publisher:
publish.yml on sparsetrace/r2d1
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
r2d1-0.1.5.tar.gz -
Subject digest:
0d060c38bd8dacbefa12b51f71bb0980dd25b4310d693b8524f026d19be9b407 - Sigstore transparency entry: 1682999433
- Sigstore integration time:
-
Permalink:
sparsetrace/r2d1@773f87a3e204df9fb62b0bcb66e72067cfb14234 -
Branch / Tag:
refs/tags/v0.1.5 - Owner: https://github.com/sparsetrace
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@773f87a3e204df9fb62b0bcb66e72067cfb14234 -
Trigger Event:
push
-
Statement type:
File details
Details for the file r2d1-0.1.5-py3-none-any.whl.
File metadata
- Download URL: r2d1-0.1.5-py3-none-any.whl
- Upload date:
- Size: 19.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2cc3a0f2756293c5e3bdeff598807d7b6194059d5065b5b021e9ccc81e79c95d
|
|
| MD5 |
1bfcb8b3302f0a52d0ff0ebb4131b9a6
|
|
| BLAKE2b-256 |
b6bf6fd6e19833eaab81bf52c7ecfe6cf37a273d6dbf65ece73a1ada8bdc4bde
|
Provenance
The following attestation bundles were made for r2d1-0.1.5-py3-none-any.whl:
Publisher:
publish.yml on sparsetrace/r2d1
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
r2d1-0.1.5-py3-none-any.whl -
Subject digest:
2cc3a0f2756293c5e3bdeff598807d7b6194059d5065b5b021e9ccc81e79c95d - Sigstore transparency entry: 1682999531
- Sigstore integration time:
-
Permalink:
sparsetrace/r2d1@773f87a3e204df9fb62b0bcb66e72067cfb14234 -
Branch / Tag:
refs/tags/v0.1.5 - Owner: https://github.com/sparsetrace
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@773f87a3e204df9fb62b0bcb66e72067cfb14234 -
Trigger Event:
push
-
Statement type: