# log-ui: full reference > Generated by scripts/build_llms_full.py from README.md and module docstrings. Source: https://github.com/AadityaSalgarkar/log-ui # log-ui A self-hosted, wandb-style dashboard for experiments logged with [trackio](https://github.com/gradio-app/trackio). One Python process serves a read-only JSON API over trackio's sqlite store and a prebuilt React app. No Node at runtime, no accounts, no cloud. **Site:** · **For LLMs:** [`llms.txt`](https://aadityasalgarkar.github.io/log-ui/llms.txt) ## Install and run ``` uvx log-ui # run it without installing; serves ~/.cache/huggingface/trackio at http://127.0.0.1:8765 uvx log-ui --dir /path/to/store --project my-project --open uv tool install log-ui # or install the `log-ui` command (pip install log-ui works too) uv run --with log-ui log-ui # or run it from a throwaway environment next to your project ``` The latest code from `main`: `uvx --from git+https://github.com/AadityaSalgarkar/log-ui log-ui`. No runs of your own yet? Write a synthetic store with a small learning-rate sweep and open it: ``` git clone https://github.com/AadityaSalgarkar/log-ui && cd log-ui && uv sync uv run python scripts/demo_store.py /tmp/log-ui-demo uv run log-ui --dir /tmp/log-ui-demo --project lm-sweep ``` ## Docker A prebuilt image (linux/amd64 and linux/arm64) is published from this repository's CI: ``` docker run --rm -p 127.0.0.1:8765:8765 ghcr.io/aadityasalgarkar/log-ui # bundled demo store docker run --rm -p 127.0.0.1:8765:8765 \ -v ~/.cache/huggingface/trackio:/data:ro \ --read-only --tmpfs /tmp --cap-drop ALL --security-opt no-new-privileges \ ghcr.io/aadityasalgarkar/log-ui # your runs, read-only ``` Check it was built by this repository's workflow (signed SLSA provenance; an SBOM is attached too), or skip the registry and build it yourself from source: ``` gh attestation verify oci://ghcr.io/aadityasalgarkar/log-ui:latest --owner AadityaSalgarkar docker build -t log-ui https://github.com/AadityaSalgarkar/log-ui.git # then run `log-ui` instead ``` Or, from a clone, `docker compose up` (store path via `TRACKIO_STORE`, port via `LOG_UI_PORT`). What the container can do, by construction: - **Read your store, never write it.** It is mounted `:ro`. A store on a read-only mount is read through a private snapshot in the container's `/tmp`, so rows still in trackio's WAL file are included. - **Nothing else on disk.** With `--read-only` (the default in `compose.yaml`) the root filesystem is immutable; only an in-memory `/tmp` is writable. - **Minimal surface.** Alpine plus a Python virtualenv: no compilers, no Node, no shell tools beyond BusyBox. Always runs as an unprivileged user (uid 10001); `--cap-drop ALL` (default in `compose.yaml`) removes every Linux capability. - **Local only.** The examples publish the port on `127.0.0.1`; log-ui makes no outbound connections. ## What you get - **Projects**: every trackio project in the store with its run count and last write. - **Workspace**: pick runs in the sidebar; every logged key gets a chart, nested by its path in collapsible groups (`loss/train/xent` sits in a "train" box inside "loss"; open/closed state is remembered). Global smoothing (EMA), step / relative / wall-clock x-axis, log y, and a free-form point budget per series. - **Key plots**: charts at the top that overlay several metrics on one y axis, e.g. `train/loss` with `val/loss`. Add a metric from any chart's pin menu or a plot's **+** picker; a metric can be in any number of key plots (A+B, A+C and A+D at once). Colour is the run; each metric gets its own line style (dash, marker, thickness), assigned automatically and changeable from the plot's legend. Shown on the workspace and run pages. - **Per-chart settings**: a mean ± std or min–max band over a trailing window of N raw points (never looks ahead), and fixed x/y limits. Settings persist per project in the browser. The window counts logged points, so a metric logged every 100 steps gets a band 100× wider in steps than one logged every step. - **Charts**: a synced cursor across charts, a tooltip on the chart under the mouse (every line's nearest logged value, so metrics logged at different steps still show), and a full-screen view with a range brush. - **Runs table**: status, steps, duration, the config columns that differ between runs, sorting, filtering and multi-select. - **Run page**: charts, flattened config, last value of every key, and system metrics when logged. - **Views**: declarative panels (ladder, heatmap, table, line grid, stats) contributed by plugins, for project-specific comparisons. - Refreshes when a selected run logs new rows; dark and light themes; page state lives in the URL. ## The trackio contract log-ui is read-only and does not import trackio. Its only I/O with trackio is the on-disk sqlite store, and all of that access lives in one module, [`log_ui/contract.py`](https://github.com/AadityaSalgarkar/log-ui/blob/main/log_ui/contract.py), which declares: - the files it reads: `/.db` for canonical project names (`[A-Za-z0-9_-]+`), excluding trackio's `registry-*` databases; - the tables and columns it reads (`configs`, `metrics`, `system_metrics`) and how their values are encoded; - the trackio versions it is verified against (`TRACKIO_VERSIONS`, currently `>=0.38,<0.40`). Connections are opened with `mode=ro`, and each one's schema is checked when it opens. If a store lacks a declared column, log-ui returns an `unsupported trackio store` error instead of guessing. On a read-only filesystem, where SQLite cannot create the `-shm` file a WAL database needs, log-ui reads a snapshot of the database and its WAL from the temp directory instead. To delete, rename or move runs, use trackio (`trackio.Api().runs(project)`); log-ui picks up the change on its next read. ## Configuration | Flag | Environment | Default | |---|---|---| | `--dir` | `LOG_UI_DIR`, then `TRACKIO_DIR` | `~/.cache/huggingface/trackio` | | `--host` | `LOG_UI_HOST` | `127.0.0.1` | | `--port` | `LOG_UI_PORT` | `8765` | | `--project` | `LOG_UI_PROJECT` | none (opens the project list) | | `--views module:function` (repeatable) | `LOG_UI_VIEWS` (comma-separated) | entry points in `log_ui.views` | | `--stale-seconds` | | `120` (a run that logged within this window is "running") | | `--open` | | open a browser | Flags override environment variables. The server binds to localhost by default; to reach it on a remote machine, forward the port (`ssh -L 8765:localhost:8765 box`) rather than binding to `0.0.0.0`. ## API All endpoints are read-only, return JSON under `/api`, and are documented interactively at `/docs`. | Method | Path | Purpose | |---|---|---| | GET | `/api/health` | version, store dir, supported trackio range | | GET | `/api/projects` | projects with run counts and last write | | GET | `/api/projects/{p}/runs` | runs with status, last step, flattened config, summary | | GET | `/api/projects/{p}/runs/{run}` | one run plus its keys and eval steps | | GET | `/api/projects/{p}/keys` | metric keys with prefix and run counts | | GET | `/api/projects/{p}/metrics?runs=a,b&keys=k1,k2&x=step&smoothing=0.5&max_points=1000&bands=k1:10` | series per run and key; `bands` adds a trailing-window mean/std/min/max | | GET | `/api/projects/{p}/system?runs=a,b&bands=gpu:10` | system metrics (GPU, CPU) when logged | | GET | `/api/projects/{p}/views` | view specs from plugins | | GET | `/api/projects/{p}/views/{id}?runs=a,b&metric=m&point=last` | resolved panels | ## Views (plugins) A view provider is a function `provide(project, runs) -> list[ViewSpec]`. Register it under the entry-point group `log_ui.views` in your package, or pass `--views my_module:provide`. `runs` carries each run's flattened config and summary, so a provider can derive categories from the config (datasets, species, seeds) and map them to metric keys with templates like `val/{category}/{metric}`. Panel types: `ladder` (one line per run across ordered categories), `heatmap` (runs × categories), `table`, `lines` (small multiples over steps) and `stats`. See [`log_ui/views.py`](https://github.com/AadityaSalgarkar/log-ui/blob/main/log_ui/views.py) for the dataclasses. ## Development ``` uv sync # backend + test deps (tests write real stores with trackio) uv run pytest uv run --with ruff ruff check log_ui tests && uv run --with ruff ruff format --check log_ui tests cd web npm ci npm run dev # Vite on :5173, proxies /api to http://127.0.0.1:8765 npm run lint && npm run typecheck && npm test npm run build # writes ../log_ui/static (committed, shipped in the wheel) ``` The built app in `log_ui/static` is committed so that installing needs no Node; CI checks it matches the sources. The project site lives in [`docs/`](https://github.com/AadityaSalgarkar/log-ui/tree/main/docs) and is published with GitHub Pages. ## Versions Releases follow [semantic versioning](https://semver.org/); see [CHANGELOG.md](https://github.com/AadityaSalgarkar/log-ui/blob/main/CHANGELOG.md) and [GitHub Releases](https://github.com/AadityaSalgarkar/log-ui/releases). Pin a version with `uvx log-ui@0.3.1`, `uv tool install log-ui==0.3.1` or `ghcr.io/aadityasalgarkar/log-ui:0.3.1` (`:0.3` follows the latest patch, `:latest` follows `main`). To release: set `__version__` in `log_ui/__init__.py`, move the `Unreleased` notes in `CHANGELOG.md` under the new version, commit, then `git tag vX.Y.Z && git push origin vX.Y.Z`. CI checks the tag matches `__version__`, creates the GitHub Release with the wheel and sdist, publishes to PyPI (trusted publishing, no stored token) and publishes the image. ## Roadmap Media and table panels, artifacts, traces, alerts, sweep pages, run tags and notes, parameter importance, report authoring. ## License [MIT](https://github.com/AadityaSalgarkar/log-ui/blob/main/LICENSE). # Module reference ## log_ui/contract.py The trackio store contract: the only module in log-ui that touches trackio's files. log-ui never imports trackio and never writes to its store. It reads the sqlite database trackio keeps per project, `/.db`, over a read-only connection, and uses exactly the tables and columns declared in `SCHEMA`. Every connection is checked against `SCHEMA` when it opens; a present table that lacks a declared column raises `ContractError`, so a trackio schema change fails loudly instead of rendering wrong data. An absent table reads as empty (older stores predate `system_metrics`). Value encoding, as written by trackio: - timestamps are ISO-8601 text; naive values are UTC. - `metrics.metrics` / `system_metrics.metrics` are JSON objects. Values are numbers, the string "NaN", or non-scalars (media, tables, histograms). Only finite numbers are kept; "NaN" decodes to None; booleans and non-scalars are dropped. Keys starting with "__" are trackio-internal and hidden. - `configs.config` is a JSON object. Keys starting with "_" are trackio-internal (`_Created`, ...). - project files are named by trackio's canonical project name (`[A-Za-z0-9_-]+`); `registry-*` files are trackio artifact registries, not projects. Verified against trackio `TRACKIO_VERSIONS`. Anything not declared here is outside the contract. ## log_ui/api.py JSON API under /api. Read-only: log-ui never modifies the trackio store. ## log_ui/series.py Turn metric rows into per-run, per-key series; smoothing and downsampling for the charts. ### smooth TensorBoard/wandb exponential moving average with debiasing. s_t = w * s_{t-1} + (1 - w) * v_t, reported as s_t / (1 - w^t). weight=0 returns the input. ### downsample_indices Indices keeping endpoints plus the min and max of each bucket; result has at most max_points entries. ### rolling_bands Mean, population std, min and max of the `window` raw values ending at each index in `at` (trailing window). The window for index i spans [i - window + 1, i]: only points logged up to i, never later ones. At the start of a run it holds the points that exist so far instead of padding. Independent of how many points are plotted. ### to_payload Plotted {x, y}; with `band_window`, also the raw-value spread in that many points around each plotted point. ### parse_bands `key:window` entries -> {key: window}. The last ':' splits, so keys may contain colons. ## log_ui/views.py Views: declarative panels resolved against run metrics (the generic replacement for wandb custom panels). A view provider is a callable `provide(project: str, runs: list[RunInfo]) -> list[ViewSpec]` registered under the entry-point group `log_ui.views` or given on the command line as `module:function`. Key templates: a panel's `key_template` is either a string with `{category}` and `{metric}` placeholders, or a dict `{metric_id: template}`; a template value may itself be `{"sub": [tmpl_a, tmpl_b]}` meaning the value is `a - b`. Missing keys yield null. ### Category (fields: id, label, group, order, meta) ### MetricOption (fields: id, label, help) ### PanelSpec (fields: type, title, categories, key_template, keys, description) ### ViewSpec (fields: id, title, description, panels, metrics, default_metric, best_key, points) ### pick_step 'last': the last step at which any considered key was logged; 'best': argmin of best_key. ### resolve_view ## log_ui/settings.py Server settings: dataclass defaults, overridden by LOG_UI_* environment variables, then CLI flags.