Skip to main content

wandb-tui

A lightweight terminal dashboard for comparing Weights & Biases runs directly from W&B project or run URLs.

It was built for remote/cloud W&B runs when you want a LEET-like terminal view without needing the original local wandb/ run directories.

Full-screen train/loss for ten runs: a clean descent from 11 to 6, then divergent spikes

Ten runs from a 100-run project, filtered by config to one day's work: train/loss descending together from 11 to ~6, then fanning out into divergent spikes.

On a light terminal the TUI uses a matching white theme, so it sits on the page rather than painting a grey slab over it.

Screenshots

Enter opens the chart under the cursor full-screen, with zoom, pan, per-run focus, axis limits and a log/linear toggle — here grad/norm_preclip across six runs:

Full-screen zoom of grad/norm_preclip, six runs with distinct gradient spikes

Chart mode tiles every matching metric and scrolls, rather than capping how many you can see:

Chart grid with the default hd marker, showing train/loss and train/loss/max tiles

M cycles the marker. The default hd above packs 2×2 blocks per cell; braille below packs 2×4 dots, trading weight for vertical resolution:

The same chart grid rendered with braille markers

Group runs into a collapsible tree by any config keys, with per-run metrics alongside and a visibility gutter for the charts:

Runs grouped into a collapsible tree by world_size and model flavor

Compare every metric across runs in the table, narrowed by a config filter and a metric search:

Multi-run comparison table, filtered and searched

A single run gets min/mean/max and inline sparklines:

Single-run dashboard with sparklines

Launch with no arguments to pick an entity and project interactively:

Startup entity picker

The screenshots above alternate with your GitHub theme; the TUI itself follows your terminal's background the same way.

Features

  • Single-run metric dashboard
  • Multi-run project comparison from a W&B project URL
  • W&B-style per-run colored columns
  • plotext line charts for multi-run metric overlays
  • Metric search, group filtering, sorting, and refresh
  • JSON export for downstream analysis
  • Works against public runs without wandb installed; uses WANDB_API_KEY automatically for private runs

Install

No install needed — run it straight from PyPI:

uvx wandb-tui --help

To pin a version, or to run the development version:

uvx wandb-tui@latest --help
uvx --from git+https://github.com/saforem2/wandb-tui wandb-tui --help
Working on wandb-tui itself
uv sync
uv run wandb-tui --help
uv run python -m wandb_tui --help   # or as a module
uv run pytest                       # tests

Usage

Interactive startup picker

If you launch without a W&B URL or ENTITY/PROJECT, wandb-tui opens a startup picker:

uvx wandb-tui

The picker lets you choose a W&B owner/entity, then a project, then opens the multi-run project dashboard. For private entities, set WANDB_API_KEY first.

Compare recent runs in a project

uvx wandb-tui \
  'https://wandb.ai/aurora_gpt/ezpz.examples.fsdp_tp?nw=nwuserforemans' \
  --runs 8

Press m to toggle from table mode to plot mode.

View a single run

uvx wandb-tui \
  https://wandb.ai/aurora_gpt/ezpz.examples.fsdp_tp/runs/vrxuo55p

Non-interactive table snapshot

uvx wandb-tui \
  'https://wandb.ai/aurora_gpt/ezpz.examples.fsdp_tp?nw=nwuserforemans' \
  --runs 8 \
  --once \
  --search train/loss \
  --metric-group train

Export JSON

uvx wandb-tui \
  'https://wandb.ai/aurora_gpt/ezpz.examples.fsdp_tp?nw=nwuserforemans' \
  --runs 8 \
  --json /tmp/wandb_project_metrics.json

Controls

Key Action
q Quit
↑ / ↓ Move row cursor
PgUp / PgDn Page scroll
Home / End Jump to first/last row
Tab Move focus between search box and results
/ Open the search box (filters metric names)
f Open the run filter box (project view)
Esc Clear and close the focused box, or both when the results have focus
(tabs) Click a metric-group tab to filter by group
m Toggle table/chart mode in project view
enter Open the focused chart full-screen
s Cycle sort column
x Reverse sort direction (group order, when grouped)
h Hide/show the header (title, URL, filters, legend)
G Group runs into a tree by config keys (project view)
Enter Collapse/expand the selected group row
Space Show/hide the selected run (or group) in charts
X Cycle the chart x-axis
M Cycle the chart marker (hd / braille / fhd / dot / sd)
g Cycle chart scale: linear / y-log / x-log / log-log
o Hide outliers (keeps the 1st–99th percentile band)
L Set axis limits, e.g. x=0:5000 y=2.5:13
r Refresh from W&B

Single-letter keys act on the results pane. While a text box has focus they are typed as text instead — press Tab or Esc to return focus to the results.

The search and filter boxes stay hidden until you summon them, so the results get the full height. They close again when they lose focus — unless they still hold a query, since a filter you can't see is worse than a spent row. Run names in the legend are clickable links to the run on wandb.ai in terminals that support hyperlinks (kitty, iTerm2, WezTerm, modern VTE).

The startup entity/project picker supports the same / search and Esc clear, matching across every visible column.

Charts

m switches the project view to charts: one tile per metric, sized to the terminal and re-fit on resize. When metrics fall into more than one top-level group (train, eval, _system, …) each group gets its own tab.

Three controls apply to every chart, in the grid and full-screen alike:

Key Action
g Cycle linear → y-log → x-log → log-log
o Hide outliers — keeps the 1st–99th percentile of each series
L Axis limits: x=0:5000 y=2.5:13; either end may be blank for auto

o is for the case where one spike flattens everything else. On a real grad_norm it takes the y-axis from 77.2 down to 26.1 while dropping ~2% of points, so the 0.1–3.0 range you actually care about fills the plot.

Focus a tile (tab, or click) and press enter to open it full-screen:

Key Action
z Cycle focus through runs — fit the view to one run, dim the others
Z / 0 Reset the view
+ / - Zoom x in / out
h / l Pan left / right
j / k Pan up / down
esc Back to the chart grid

Pan and zoom are local to the full-screen view, so they never disturb the grid behind it; Z drops back to whatever window L set. z skips runs you have hidden with Space.

Run loading and caching

Project view loads metadata for the 100 most recent runs in a single query, then fetches full history only for the runs that survive your filter — in parallel, cached on disk under ~/.cache/wandb-tui and keyed on each run's W&B updatedAt, so finished runs are read from disk on every later launch. Use --runs to change how many runs' metadata is loaded.

Measured on a 100-run project: a cold history pull is ~1.1s for 24 runs, 3.6s for 50 and 10.1s for 100, but every warm load is 0.01–0.08s regardless of size.

Chart x-axis

Charts default to Step. Press X to cycle the x-axis:

Axis Source
Step _step
Relative Time (Process) _runtime
Relative Time (Wall) _timestamp, zeroed at the run's first point
Wall Time _timestamp
n_tokens_seen train/tokens_seen (and common aliases)

The screenshots above use n_tokens_seen.

This matters for runs that log at uneven intervals: on the sample-index axis a 47-second stall looks identical to a 1-second one. If a run does not log the selected axis, that run falls back to its sample index rather than dropping out of the comparison, and the status line says so.

Grouping runs

Press G and type one or more config keys, comma-separated, to nest runs into a collapsible tree — the same idea as the W&B workspace's "Group runs by..." panel. Order is nesting order, and Tab completes key names.

G> world_size,model_spec.flavor

   Group / Run                n  loss_metrics/global_avg_loss  mfu(%)  tflops
 ◉ ▼ world_size: 12           2
 ◉   ▼ model_spec.flavor: 2b  2
 ◉       amber-snowflake-3897      3.20337                     29.135  86.876
 ◉       usual-fire-3895           2.96266                     34.939  104.2
 ◉ ▼ world_size: 48           9
 ◉   ▼ model_spec.flavor: 2b  9
 ◉       zany-hill-3733            2.70796                     29.019  86.532
 ◉       sage-shadow-3666          2.83919                     28.620  85.341

Space toggles a run's visibility in the charts, via the marker in the left gutter (◉ shown, ○ hidden, ◐ partly hidden). On a group row it toggles everything beneath — collapse to a group and hide 95 runs with one keypress. Hidden runs leave the plots but stay in the table, so their numbers are still readable.

Each group row shows how many runs sit beneath it. Enter on a group row collapses or expands it; Esc puts the input away but keeps the grouping, so you can drive the tree straight after typing. Clearing the box returns to the flat metric table.

Grouping transposes the grid: rows become the run tree, so the columns become the first few metrics matching your current search. Narrow them with /. Runs missing a key group under (unset). Any config key is accepted — the completion hint just lists the ones that actually split your runs first.

Grouping is independent of g, which filters which metrics are shown, and composes with f and / — see Combining filters, groups, and search.

The same keys work from the command line, where --group-by presets the tree, prints it in --once, and nests a groups array in --json:

uvx wandb-tui ENTITY/PROJECT --group-by 'world_size,model_spec.flavor'
uvx wandb-tui ENTITY/PROJECT --once --group-by world_size
uvx wandb-tui ENTITY/PROJECT --json out.json --group-by world_size

Note --group filters metric groups, not runs, and is deprecated in favour of the clearer --metric-group. It still works and warns on stderr.

Filtering runs by config

In project view, press f and type an expression to keep only the runs whose config matches — the same idea as filtering a W&B workspace in the browser. Space-separated terms are AND-ed:

lr>=0.001 model~llama state=finished
Operator Meaning
= / != Equal / not equal (numeric when both sides are numbers, else case-insensitive string)
> < >= <= Numeric comparison
~ / !~ Contains / does not contain (case-insensitive substring)

A comma-separated value means "any of" — set membership rather than a range:

world_size=3072,6144        # exactly these two, not 4096 in between
model=llama-3,mistral       # either model
world_size!=3072,6144       # everything except those two

Bare keys read the run's config. Use config.<key> to be explicit, and run.<attr> for run attributes (run.state, run.name) when a config key would otherwise shadow them. Quote values containing spaces: name~"my run".

Completion. The filter box suggests as you type and tab accepts. Typing a prefix lists the config keys under it (precision/ → every precision/* key); once you type an operator it lists the values that key actually takes (dim= → 128 256 2048). Earlier terms of a compound filter are preserved, so tp=4 dim= completes only the last term. With several matches tab extends to the longest common prefix, like shell completion.

The same expression works non-interactively:

uvx wandb-tui ENTITY/PROJECT --runs 20 --filter 'lr>=0.001 model~llama' --once

Filtering stacks with grouping and metric search — see Combining filters, groups, and search.

Searching metric names

Press / and type. Plain text is a case-insensitive substring match:

loss          matches train/loss, eval/loss, loss_metrics/global_avg_loss
mfu           matches mfu(%)

Wrap the query in slashes for a regex:

/^train/           anchored: train/loss, but not pretrain/loss
/loss|acc/         alternation
/^(train|eval)\//  either namespace
/(?i)LOSS/         inline flags work (search is already case-insensitive)

Regex is opt-in for a reason: metric names are full of regex metacharacters. Searching mfu(%) as a pattern would match nothing, because the parentheses become a capture group.

Note A malformed pattern shows bad regex: ... in the meta panel and keeps the previous results, so a half-typed /[a-/ never blanks the table. Note the asymmetry: that only applies inside slashes. Plain text is never a pattern, so loss( is a literal substring that simply matches nothing — no error, just an empty table. If a search unexpectedly comes up empty, check whether you meant to wrap it in slashes.

Because a metric name's separator is /, the closing slash is the last one: /^train/ searches for ^train. To match a literal slash inside the pattern, escape it — /^train\/loss$/.

The three act on different axes and compose freely — none of them is a mode you have to leave to use another:

Key Acts on Answers
f which runs (by config) which experiments do I care about?
G how runs are nested (by config) how should they be organised?
/ which metrics are columns what do I want to see about them?

A worked example — the 8-GPU runs, nested by model flavour, showing only training metrics:

f> world_size=8
G> model.flavor
/> /^train/
runs=3/6  metrics_shown=2  search='/^train/'  filter='world_size=8'

Group / Run              n    train/loss    train/mfu
------------------------------------------------------
model.flavor: 2b         3
      run-0                      19.000       30.000
      run-2                      19.000       30.000
      run-4                      19.000       30.000

Order does not matter, and clearing one leaves the others alone — widening the filter does not disturb your grouping.

Esc is not uniform across the three, though, and the difference is deliberate. In the search and filter boxes it clears the term. In the group box it only puts the box away and keeps the grouping, so you can start driving the tree with Enter and Space the moment you finish typing. To actually ungroup, empty the box instead.

Two things worth knowing:

  • Filtering happens before grouping. Runs excluded by f never reach the tree, so group counts reflect what survived the filter (n above is 3, not 6). This is usually what you want: group counts describe what you are actually looking at.
  • Grouping transposes the grid. Rows become the run tree, so columns become the first few metrics matching your search — which makes / the tool for keeping a grouped view readable, not just a convenience.

All three work together non-interactively, including with --json:

uvx wandb-tui ENTITY/PROJECT --once \
  --filter 'world_size=8' \
  --group-by model.flavor \
  --search '/^train/'

Quote the regex in your shell — /^train/ is fine unquoted in bash, but patterns containing |, (, or * are not.

W&B LEET comparison

W&B's official LEET TUI is excellent for local wandb/ directories and .wandb files, and newer versions support remote single-run URLs. This tool focuses on remote multi-run project comparisons over W&B's GraphQL API.

Notes

  • Public W&B projects/runs can be queried without authentication.
  • For private projects, set WANDB_API_KEY in your environment.
  • plotext is included as a dependency for chart mode.

Metadata

Release files for wandb-tui 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for wandb-tui 0.3.0
File Size Uploaded
wandb_tui-0.3.0.tar.gz 63.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for wandb-tui 0.3.0
File Interpreter ABI Platform
wandb_tui-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 128.1 kB

Release files / wandb_tui-0.3.0.tar.gz

Download URL wandb_tui-0.3.0.tar.gz
Size 63.6 kB
Tags Source
SHA-256 checksum
How to use checksums
1fad31092f4d1a7325172ef850581c6916664fd3f3d69b18c5dc48fbf8d746b3
BLAKE2b-256 checksum
How to use checksums
6bbae406ddbea773875fb06ab507c7a8bf6f2c1ad3e2384f5d10342566a89cf1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / wandb_tui-0.3.0-py3-none-any.whl

Download URL wandb_tui-0.3.0-py3-none-any.whl
Size 64.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d504c9613d366f4a4613e49f0216872ae66440345c5f1e3bd6cf727d3bc1192c
BLAKE2b-256 checksum
How to use checksums
236d60fa3a778eb06cbc309f1eb24e47110ff155d95a2b4e4c14e24117e582f9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page