modelmeta puts dataset, training, code, timing, and integrity metadata beside every model checkpoint. Inspect it anywhere. Verify the exact bytes offline. No W&B. No MLflow. No tracking server.
Watch the full 45-second demo · Explore the reproducible example
checkpoints/
├── step_042000.safetensors
└── step_042000.safetensors.modelmeta.yaml
The sidecar records the checkpoint digest plus the training, dataset, Git, compute, and elapsed-time context supplied by the training process. It is portable YAML, readable without a tracking backend, and linked to the exact checkpoint bytes with SHA-256.
pip install modelmetaRequires Python 3.11 or newer. The runtime package uses PyYAML and RFC 8785 canonical JSON; it does not require PyTorch or another ML framework.
modelmeta verify checkpoints/step_042000.safetensors
# checkpoint integrity verified; metadata remains self-assertedExit 0 means the supplied checkpoint bytes match the digest in the adjacent sidecar. A changed byte returns exit 12 (digest_mismatch). JSON output is stable for automation:
modelmeta verify --json checkpoints/step_042000.safetensorsverify hashes bytes; it does not deserialize or execute the checkpoint. A matching result does not authenticate the sidecar, prove who produced the model, or make an unknown pickle safe to load. Treat untrusted .pkl/torch.load inputs as executable content and use an independent trusted source or sandbox.
Create one MetaWriter when the run starts and reuse it after each successful checkpoint save:
from modelmeta import MetaWriter
writer = MetaWriter(
run_context={
"run_id": "run_20260720_001",
"git": {"repository": "https://github.com/me/project", "commit": "abc123", "dirty": False},
"dataset": {"name": "curated-corpus", "version": "2026-07-18", "digest": "sha256:..."},
}
)
# ... training loop ...
sidecar = writer.on_checkpoint_saved(
"checkpoints/step_042000.safetensors",
training_state={"global_step": 42000, "loss": 1.2384, "learning_rate": 2e-5},
compute_state={"framework": "torch", "precision": "bf16", "accelerator_count": 8},
)The writer atomically replaces the sidecar only after hashing and validation succeed. It automatically records wall_hours from the monotonic run timer and estimates gpu_hours as wall_hours × accelerator_count when no explicit value is supplied. Detection describes visible/available accelerators; it is not GPU-utilisation proof. Explicit caller values win. Call writer.reset_timer() when training actually begins.
For a one-off integration, use the framework-neutral adapter:
from modelmeta.adapters import stamp_checkpoint
stamp_checkpoint("checkpoint.pt", training_state={"global_step": 100}, repo_path=".")That adapter creates a fresh writer, so its elapsed time is approximately zero. Reuse MetaWriter when run duration matters.
modelmeta inspect checkpoints/step_042000.safetensors
modelmeta inspect --json checkpoints/step_042000.safetensors
modelmeta diff checkpoints/step_040000.safetensors checkpoints/step_042000.safetensorsinspect shows the checkpoint digest, training snapshot, dataset identity, Git state, compute information, signing state, and missing high-value fields. diff compares metadata claims grouped by training, provenance, compute, and artifact; it does not rank model quality.
For a single file, the sidecar sits beside it:
step_042000.safetensors
step_042000.safetensors.modelmeta.yaml
For a directory checkpoint, modelmeta hashes every regular file using a deterministic relative-path manifest and writes the reserved sidecar inside the directory:
step_042000/
├── model-00001-of-00004.safetensors
├── optimizer.pt
└── step_042000.modelmeta.yaml
Copy or upload the checkpoint and sidecar together. modelmeta never follows a checkpoint path read from sidecar contents and never recovers missing metadata from a tracking service.
If verification succeeds, the checkpoint bytes match the sidecar digest and the sidecar passes schema validation. The metadata remains self-asserted: modelmeta does not prove the dataset, code, loss, hardware, author, or model quality claims. Anyone who can replace both files can create a new matching pair. Signed attestations and durable run identity are outside v0.1.
| Code | Status | Meaning |
|---|---|---|
| 0 | match |
Checkpoint and sidecar agree |
| 2 | — | CLI usage error |
| 10 | missing_sidecar |
No metadata available |
| 11 | invalid_schema |
Sidecar is structurally invalid |
| 12 | digest_mismatch |
Checkpoint bytes differ from the sidecar |
| 13 | unsupported_target / unsupported_schema |
v0.1 cannot safely proceed |
| 14 | io_error / race_detected |
Verification could not complete |
uv sync --extra dev
git config core.hooksPath .githooks
uv run pytest -m "not slow" # fast suite
uv run pytest -m slow # larger-than-memory streaming acceptance testThe project is Python 3.11+ and uses main ← dev ← feat/*. See CONTRIBUTING.md for local gates.
MIT
