Skip to main content

Emry

Gentle observability for long training runs.

CI PyPI Python License

Emry watches your training run the way you'd want a good colleague to: quietly, without ever getting in the way. A training loop calls run.emit(); metrics flow through a lock-free ring into an event-sourced engine that persists an append-only log and serves a live dashboard. No accounts, no phone-home — just your metrics, on your machine, in a file you can read.

Emry's terminal dashboard streaming a live training run

The terminal dashboard (emry watch) — live loss curve with a dashed amber baseline overlay for run comparison, phase bands, checkpoint markers, metric cards, and alerts. Full parity with the web dashboard.

Emry's web dashboard with a live loss curve, baseline overlay, phase bands and checkpoint markers

The self-hosted web dashboard (emry web) — live chart with a dashed baseline overlay for run comparison, phase bands, and checkpoint markers. No CDN; works air-gapped.

  • Stays out of the way. emit() targets well under 10 µs amortized (tens of nanoseconds in our benchmarks) and never blocks the training thread — every queue is bounded and drops-and-counts under load, so observability can never harm the run.
  • Event-sourced. An append-only events.jsonl is the audit trail; a wide metrics.jsonl is plain JSONL you can read with jq, pandas, or anything.
  • Observe live or after the fact. A terminal dashboard and a self-hosted web dashboard (no CDN — air-gap friendly) at full parity — live chart, phase bands, checkpoint markers, and a baseline overlay to compare against a prior run — or just tail the files.
  • Built for clusters. Embedded, sidecar, or file modes; auto-detects SSH/SLURM. The training process survives an engine crash.

Install

pip install emry

Quickstart

Your training loop calls emry.run(...) and run.emit(...). That's it:

import emry

with emry.run("llama-sft", config={"lr": 2e-5}, metrics=["loss", "lr"]) as run:
    for step in run.steps(10_000):
        loss = train_step()
        run.emit(loss=loss, lr=scheduler.get_last_lr()[0])

run.steps(n) yields steps and advances Emry's step counter for you; emit() takes any metrics as keyword arguments. Mark phases with run.phase = emry.Phase.EVAL, and iterate epochs with run.epochs(n) to track the epoch automatically. Values are duck-typed — tensors and numpy scalars are coerced, so you can pass loss directly without .item(). When an NVIDIA GPU is present, Emry samples nvidia-smi automatically and charts GPU utilization, memory, and temperature alongside your metrics (gpu=False to disable). Pass alert_webhook= (or set EMRY_ALERT_WEBHOOK) to get a Slack/Discord ping the moment a metric goes NaN/Inf.

By default Emry writes a run directory under ./logs/ and, when attached to a TTY, brings up the live terminal dashboard. Set EMRY_MODE (embedded | sidecar | file) to control how it runs, or observe any run after the fact with the commands below.

Observe a run

emry runs                                   # list runs under ./logs
emry watch ./logs/llama-sft_…               # live terminal dashboard
emry web   --run-dir ./logs/…               # live web dashboard at http://127.0.0.1:8787
emry watch ./logs/new --compare ./logs/old  # overlay a prior run as a baseline (TUI or web)
emry compare run_a/ run_b/                  # final metrics side by side
emry export csv --run-dir ./logs/… --output history.csv

The dashboard binds loopback and is unauthenticated by default. To expose it (e.g. from a container), bind a public interface and turn on auth + TLS:

export EMRY_AUTH_TOKEN=$(openssl rand -hex 32)   # require this token; /healthz stays open
emry web --project ./logs --host 0.0.0.0 \
  --tls-cert cert.pem --tls-key key.pem          # serve HTTPS from your own PEM files

Prefer the EMRY_AUTH_TOKEN env var over --auth-token so the token stays out of your shell history and process list. Binding a non-loopback --host without a token prints a warning — anyone who can reach the host can read the metrics.

EMRY_AUTH_TOKEN is the full-access (admin) token. For read-only access, hand out EMRY_VIEWER_TOKEN (--viewer-token): viewers can watch a single run's dashboard but not the multi-run --project overlay, which requires admin.

On a cluster, run the engine as a sidecar so observability outlives the training process — see the SLURM runbook.

Documentation

Development

Prerequisites

  • Rust 1.97+ (rust-toolchain.toml pins the toolchain)
  • llvm-tools-preview for coverage: rustup component add llvm-tools-preview
  • cargo-llvm-cov: cargo install cargo-llvm-cov
  • Python 3.10+

Commands

# Full local CI (fmt, clippy, test, ≥90% coverage)
./scripts/pre-commit-rust.sh

# Coverage only
./scripts/check-coverage.sh

# Python tests
pip install -e ".[dev]"
pytest

# Build the native extension locally (maturin)
pip install maturin && maturin develop

# Run the demos
cargo run -p emry-tui --example tui_demo
cargo run -p emry-web --example web_demo   # http://127.0.0.1:8788

Pre-commit

pip install pre-commit
pre-commit install

Hooks run: trailing whitespace, YAML/TOML checks, then ./scripts/pre-commit-rust.sh (fmt + clippy + test + 90% line coverage gate).

Quality bar

Check Threshold
cargo clippy -D warnings (pedantic)
Rust line coverage ≥ 90% (workspace)
Python line coverage ≥ 90% (pytest --cov-fail-under=90)

License

Apache License 2.0 — see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

emry-0.2.2.tar.gz (92.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

emry-0.2.2-cp310-abi3-manylinux_2_28_x86_64.whl (4.2 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.28+ x86-64

File details

Details for the file emry-0.2.2.tar.gz.

File metadata

  • Download URL: emry-0.2.2.tar.gz
  • Upload date:
  • Size: 92.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for emry-0.2.2.tar.gz
Algorithm Hash digest
SHA256 56019a2e6c9f8e7d9d2cc15cd20adcb1890a153ebe695c1bd200bbe30067473f
MD5 782818bf286973b2ce7cc07d32d2adbd
BLAKE2b-256 e56951287415daf7ec338c9fe8eb193d3f0304b6ef834c9fb14c941e0f3912e3

See more details on using hashes here.

Provenance

The following attestation bundles were made for emry-0.2.2.tar.gz:

Publisher: release.yml on femboyisp/emry

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file emry-0.2.2-cp310-abi3-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for emry-0.2.2-cp310-abi3-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 e9a733cd3ac1426fb7d024b44ed6e7e3bf39367947450821f275ccb1f70bae32
MD5 ab1cfa4f3cd6bf4fa5a1a2ae513bc3e0
BLAKE2b-256 228540bf2b6992a52d7a19ac285803bb1b646696a6339025c703012f290b3c9e

See more details on using hashes here.

Provenance

The following attestation bundles were made for emry-0.2.2-cp310-abi3-manylinux_2_28_x86_64.whl:

Publisher: release.yml on femboyisp/emry

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page