feat: add local run dashboard - #3351
Conversation
Serve a web dashboard from a run's output directory: a W&B-overview replica read from metrics.jsonl, a merged ANSI-colored log stream across all components, and a per-step rollout trace viewer with a token-level advantage/logprob view. Works for RL and SFT runs, live via polling, and over a plain SSH tunnel. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
Restyle to the PI design language (prime-context): #111 canvas, lime accent used categorically, mono-dominant type, hairline dividers, zero radii, butterfly mark, segmented step progress bar, brand line-chart palette. Add a Config tab showing the run's resolved config JSONs; tab order is metrics, config, traces, logs. ABC Favorit is licensed and not vendored - the type stack falls back to system mono. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add a platform-style run overview card shown on every tab: completion percentage, step count, status/duration/objective/model/created fields, and a full-width finely-segmented progress bar. Violet is now the interactive accent and the default chart line color; lime remains the semantic positive color. Metrics gain a rolling-average smoothing slider and a global pane-size slider (wandb-style), both persisted in localStorage. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Return the interactive accent to lime; chart lines keep the brand purple. Replace the pane-size slider with a wandb-style corner drag that resizes every pane together. The all-metrics view drops the sidebar key list for sectioned charts of every key, grouped by namespace, behind a prominent regex filter; charts below the fold mount lazily. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Resize handles on all four card affordances wandb-style: bottom edge for height only, right edge for width only, corner for both - still applied to every pane together. The config tab gains a regex search that highlights all hits and shows a count. The overview card drops the objective field. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The inference router binds 8000 by default, so a dashboard left running silently swallows all rollout requests with 404s. Use 7788, the same default as the miles dashboard. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mirror inference metrics to the file monitor as time-keyed rows (step=None) whenever a FileMonitor is registered; the dashboard plots them in an overview inference section on relative wall time with s/m/h/d ticks. Polish across the UI: shared fixed-width empty-state boxes for every nothing-here case, a pulsing live indicator, tighter chart cards with integer step ticks and axes anchored at zero, green/red reward and advantage coding, scroll instead of pagination for episodes, and a config-tab regex search with highlighted hits. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Rebuild the episode view as a three-pane popup modeled on the platform's evaluation trace viewer: rollout selector with search and prev/next on the left, numbered collapsible message entries with per-message copy and collapse/expand-all in the center, and an overview pane (reward, identity with copyable ids, activity counts, tool definitions, state, rewards, sampling, timing) on the right. Metrics: the regex filter now also applies to the overview, lines use the accent color, axes auto-range to the data, and control heights align. New runs register without a page refresh, the brand is the logo alone, and the live dot is smaller. Tab order: config first, metrics still the default. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Charts gain a hover popover with the x and y values (replacing the uPlot legend) and smoothing now overlays the smoothed line on a faint raw ghost. Overview and all-mode sections are collapsible and the plain key count is gone. The traces step selector becomes a slider with prev/next buttons and a step label; the episode count stays in the status text. More bottom room for x-axis labels. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Metrics: multi-run comparison via a compare menu (per-run colors, metrics only), drag a pane header to reorder panes (persisted), a plain click resets drag-zoom, panel titles show matched keys instead of regexes, and the scrollbar gets breathing room. Logs: the sidebar is gone - trainer/orchestrator/inference/evals/envs render as a vertical stack of panes (masters by default, env logs merged), each maximizable, with a shared regex filter, always-on follow, abbreviated line counts, and per-pane file selects. Traces: env/sort/errors-only move into a filter popup on the right. Trace viewer: prev/next step navigation with a clear step header, and the overview pane is reordered - rewards and metrics under the big reward, runtime ID, activity trimmed to turns + tool calls, a usage section, timing as a tree, sampling dropped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Single-node inference.log interleaves Rust router lines with the vLLM engine output. The inference pane now hides router lines by default and offers a virtual "router" entry in its file select that shows only them; separate multi-node router.log files are untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The traces step selector becomes a full-width block bar in the progress-bar style: one block per rollout step, filled through the selection, eval steps marked, click or scrub to select. Runs without max_steps show the step count with an unfilled track that carries a lime sweep while running instead of a fake percentage. Log panes get a fixed 300px height with the stack scrolling - maximize still fills the tab. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Rewards and advantages render with exactly three decimals everywhere (table, rollout list, overview reward and rewards rows) so columns stay aligned - 1 shows as 1.000. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The config regex now filters the JSON to matching subtrees - a matching key keeps its whole dict, nested matches keep their ancestors - instead of only highlighting in the full file. The traces subset preference (all/effective) re-applies as soon as the preferred file exists at a step instead of sticking on a fallback, so advantages reappear when returning to shipped steps. Drop the redundant config path caption; revert the step-arrow sizing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The logs tab defaults to a single merged pane interleaving the selected components by timestamp; a components dropdown toggles trainer/orchestrator/inference/evals/envs and a merge/split seg switches to the stacked per-component panes. Drop the disp.step column from the trace table. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The run card drops the progress bar and percentage for a plain step headline that also fits unbounded runs, and gains resumes plus train/ eval env fields (long lists fold into +N with a tooltip). Section headers become full-width bars so the clickable region is obvious, and y-axis labels use K-notation above 1000 to stop gutter overflow. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Panes share the available height equally instead of a fixed 300px stack - toggle components off for more room per pane. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two significant digits below 0.1 (0.001234 was 8 chars) and a slightly wider gutter so labels like 0.0025 never clip. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Collapsed metric sections stack directly. One collapse/expand toggle drives all sections on the metrics tab and all message entries in the trace viewer, whose left pane now says episodes instead of rollouts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Run card: status leads, step is a regular field (79/200, unbounded shows inf), duration and created sit on the right, resumes dropped. Trace overview pane: reward and advantage headline together (n/a when missing), accent section titles, is_truncated in state, identity at the bottom, env/dispatch-step rows dropped, message roles all accent. The episode table gains a branches column, the preferred subset re-applies automatically when a live step ships its effective file, and a shared fmtCompact helper renders counts as 1.1K/2.2M/3.3B. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Selects drop the native chrome for a flat hairline-chevron style, dropdown buttons carry a caret and the floating menus share one panel style. The drag-zoom selection shows a lime highlight, and pane reordering moves the card the moment the cursor enters another pane. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
All view selections survive a refresh via localStorage: metrics mode, filters, collapsed sections, trace kind/subset preference, sort and errors-only, log view, components, level and search, config search, and the token signal. The traces tab now defaults to the newest step whose preferred subset has shipped, so advantages are visible on open instead of landing on the in-flight step. Merge-view log lines carry a minimal grey component prefix. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Drop the tunnel hint; print the serving path and the URL, underlined in the accent green when attached to a terminal. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Core installs no longer pull the web stack; `uv sync --extra dashboard` (CI already syncs --all-extras) enables `uv run dashboard`, and running without it exits with that hint. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Left-pane episodes render as '#n · env · reward' on one line (env clips with an ellipsis), and step labels read 'step 60/69' instead of 'step 60 (60/69)' in both the trace table and the viewer. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The viewer's left pane gets the same train/eval and all/effective toggles as the table (synced both ways, hidden per run type); a filter change reopens the first episode of the new subset. The episode search box is gone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The chunk fetch itself yields the main thread, so the extra setTimeout(0) macrotask only added latency. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Matching a [section] header in the config search keeps the whole section (same semantics as the JSON subtree pruning); a matching key still keeps just its line plus the header for context. The trace viewer's step label is plain white 'step 9/10' - the kind/subset readout moved into the new filter toggles. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The popover pins to the snapped point (nearest x) instead of following the mouse, sitting beside the series value at that step. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Every native select (run picker, env/sort filters, attempt, token signal, per-pane log files) now wears the compare-dropdown look: a flat button with the current option and a scrollable option panel; the hidden select stays the source of truth so existing listeners keep working, and a dropdown nested inside another menu keeps its ancestors open. Charts show a highlight dot on the snapped point under the anchored tooltip. Also fixed: the run 'started' time was cached forever per path, so relaunching into the same run dir showed absurd durations (5h duration on a 39-minute-old run) - the first metrics row is now read fresh. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The dress guard matched any ancestor .dd-wrap, so the env/sort selects inside the trace filter menu stayed native. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The rich Live display hid the terminal cursor and an interrupt never ran its exit path (it lived in a with-block on a daemon thread), so Ctrl-C left the shell cursorless; the Live now starts/stops around uvicorn.run with a finally. Graceful shutdown is capped at 2s so open browser keep-alives can't hold the port after an interrupt. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- rollout_steps caches known-present subsets (presence is monotonic), re-stats absent ones only for the newest 64 steps, and does a full rescan every 10th call for late-landing evals: a steady poll over 2000 steps costs ~130 stats instead of ~8000 - episode summaries persist to a sidecar under ~/.cache/prime-rl/dashboard (never inside the run dir), so revisits and dashboard restarts skip re-parsing gigabytes of traces; append picks up from the stored count, a shrunken file discards the sidecar - the offsets/summaries caches are LRU-bounded to 64 files, so paging through thousands of steps no longer grows the server without limit Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The dressed run-select's button label only synced on the poll tick, so the picker sat empty for up to 3s on load; labels now sync the moment options or the selection change. The run list also refreshes every tick instead of every third - /api/runs is ~1ms warm since the availability caching, so a freshly launched run shows up within ~3s instead of ~10s. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
# Conflicts: # deps/verifiers
- metrics rows carry a producer field stamped by FileMonitor (each process passes its name to monitors.setup); the dashboard prefers it and keeps the key-prefix regexes only for legacy files - the W&B time axis is defined lazily for whichever key prefixes arrive via step=None, and the monitor stamps its own _timestamp - the inference collector no longer smuggles a wandb-ism through the generic fan-out - launch-TOML multi-file headers pair with their filtered sources (a non-toml root reference shifted every header; fixed in verifiers alike, submodule bumped - also merges vf main into the companion) - the eval root's virtual (0, eval, all) address lives in one helper shared by the step listing and traces_path - the six pasted test_dashboard blocks become make_dashboard_test(<fixture>, RUN_NAME) - which also fixes a latent failure: they requested the test_no_error TEST as a fixture, so every test_dashboard would have errored once GPU CI ran them - docs: the standalone no-GPU-deps invocation (uv run --script src/prime_rl/dashboard/server.py) is documented in the dashboard README, docs/training.md, and the monitor-run skill Also merges origin/main into the branch (clean). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Colliding run names qualify with ':' instead of '/' (uvicorn decodes %2F before routing, so a slash can never survive a path segment and every detail endpoint 404'd). The eval overview cost sums the merged series, not just the latest increment. line_offsets scans into a local list and merges under the lock, so concurrent growers of the same file can't double-append a line start (verified with 16 threads). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
PrimeMonitor forwarded payloads unchanged, so after the collector stopped injecting _timestamp, platform-bound step=None rows had no time anchor. Prime and W&B now stamp _timestamp on every log (the file monitor already writes time per row). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
## Summary - Write each run's launch `@` TOML file verbatim to `configs/eval.toml`, next to the resolved dump, which moves to `configs/resolved/eval.json`. - Only root `@ file` references are copied (nested `--flag @ file` / `--flag @file` configs belong under their flag and are excluded), and only `.toml` sources — a resume from the resolved JSON writes no launch copy. - `saved_config_path` (resume/replay) checks `configs/resolved/` first and falls back to the legacy `configs/<cli>.json` location. - Drop the startup INFO log of the full eval config — it is persisted to disk, so the log line was redundant. Companion to the prime-rl run-dashboard PR (PrimeIntellect-ai/prime-rl#3351), which reads both layouts: the launch TOML as the default config view and the resolved JSONs concatenated into one searchable document. ## Breaking - The resolved config moves from `configs/eval.json` to `configs/resolved/eval.json`. Reading (resume, replay) handles both layouts; anything that writes or globs the old path directly must switch to `configs/resolved/`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Moves the on-disk resolved config path used for resume/replay. Reading falls back to the legacy location, but anything that writes or hardcodes configs/*.json will break. > > **Overview** > Runs now keep two config artifacts: a verbatim copy of root `@` launch TOML at `configs/<cli>.toml`, and the resolved dump at `configs/resolved/<cli>.json` instead of `configs/<cli>.json`. Nested `--flag @ file` refs and JSON resumes are not copied. > > `saved_config_path` looks in `configs/resolved/` first, then the old `configs/*.json` layout, so resume/replay still work on existing runs. Help text points at the new path. The full eval-config INFO dump at startup is removed because the file is already on disk. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit f7c83ad. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> <!-- Macroscope's pull request summary starts here --> <!-- Macroscope will only edit the content between these invisible markers, and the markers themselves will not be visible in the GitHub rendered markdown. --> <!-- If you delete either of the start / end markers from your PR's description, Macroscope will append its summary at the bottom of the description. --> > [!NOTE] > ### Split run configs into launch TOML at `configs/<cli>.toml` and resolved JSON at `configs/resolved/<cli>.json` > - `save_config` now writes the resolved config JSON to `configs/resolved/<cli>.json` and copies the launch TOML (from `@`-referenced files in `sys.argv`) to `configs/<cli>.toml`, creating nested directories as needed. > - `saved_config_path` looks under `configs/resolved/` first and falls back to the legacy `configs/` path so older runs still resume. > - `write_launch_toml` concatenates multiple TOML inputs with a header comment naming each source path. > - Minor cleanups: usage string now points to `configs/resolved/eval.json`, and the startup config JSON log line in `run_eval` is removed. > - Behavioral Change: `write_config` in [output.py](https://github.com/PrimeIntellect-ai/verifiers/pull/2429/files#diff-07fce45dd0fa633f34d25e19962ab801b705a509774833de0d81bfe872787e8b) writes to a new `configs/resolved/` subdirectory; any code or scripts expecting `<run-dir>/configs/<cli>.json` will need to look under `configs/resolved/` instead. > > <!-- Macroscope's review summary starts here --> > > <sup><a href="https://app.macroscope.com">Macroscope</a> summarized f7c83ad.</sup> > <!-- Macroscope's review summary ends here --> > <!-- Macroscope's pull request summary ends here --> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 99d9a8f. Configure here.
| with _lock: | ||
| _lru_put(_summaries_cache, path, (len(offsets), summaries)) | ||
| write_sidecar(path, offsets, summaries) | ||
| return summaries |
There was a problem hiding this comment.
Stale partial-line episode summaries
High Severity
episode_summaries only invalidates by line count, while line_offsets intentionally rescans a possibly partial last line when the file grows. A live read of a large in-progress traces.jsonl can summarize a torn write as unparseable; once that count is cached (and written to the sidecar), later completion of the same line never reparses it, so the episode stays broken in the table for the rest of the session and across restarts.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 99d9a8f. Configure here.


Summary
Adds a local run dashboard for SFT / RL / Eval runs. Start with
# default (all runs in outputs/, port 7788) uv run dashboardFor example, populate with
Combines the best things from W&B + PI platform. Fully isolated agent-maintained logic in
prime_rl.dashboard. Fully supersedes the previously advertised tmux viewer. Should basically be always on when working with prl locally.Gallery
Configs
Metrics
Trace Overview
Trace Viewer
Logs
Breaking
scripts/tmux.shis removed — the dashboard replaces the tmux log-pane workflow (docs, example walkthroughs, and the monitor-run skill updated).Resolved per-component configs move from
{run_dir}/configs/*.jsonto{run_dir}/configs/resolved/*.json;configs/now holds the launch TOML (rl.toml/sft.toml). Anything reading the old paths must switch (the dashboard reads both layouts).🤖 Generated with Claude Code
Note
Low Risk
Changes are additive for training (new CLI and docs) plus removal of the tmux helper; the config path split may break external tooling that still reads
{run_dir}/configs/*.jsondirectly.Overview
This PR adds
uv run dashboard— a local web UI (default http://localhost:7788) for watching SFT/RL/eval runs from output directories only: metrics (W&B-style overview frommetrics.jsonl), configs (launch TOML vs merged resolved JSON), rollout traces with per-token advantage/logprob views, and merged component logs. It ships as adashboardoptional extra (FastAPI/uvicorn) or asuv run --script src/prime_rl/dashboard/server.pyon machines without full GPU deps.scripts/tmux.shis removed; docs, basic examples, and the monitor-run skill now recommend two terminals plus the dashboard instead of tmux panes. Side-by-side ablations on one node can useuv run dashboard outputsto track multiple experiments.Resolved configs from dry-run and launches are documented at
{run_dir}/configs/resolved/*.json, with the launch TOML atconfigs/*.toml; the dashboard server still reads the old flatconfigs/*.jsonlayout for older runs.Reviewed by Cursor Bugbot for commit 99d9a8f. Bugbot is set up for automated code reviews on this repo. Configure here.