Skip to content

feat: add local run dashboard - #3351

Merged
mikasenghaas merged 82 commits into
mainfrom
feat/run-dashboard
Aug 24, 2026
Merged

feat: add local run dashboard#3351
mikasenghaas merged 82 commits into
mainfrom
feat/run-dashboard

Conversation

@mikasenghaas

@mikasenghaas mikasenghaas commented Aug 23, 2026

Copy link
Copy Markdown
Member

Summary

Adds a local run dashboard for SFT / RL / Eval runs. Start with

# default (all runs in outputs/, port 7788)
uv run dashboard 

For example, populate with

# rl run
uv run rl @ examples/basic/reverse-text/rl.toml --run.name reverse-text-rl

# sft run
uv run sft @ examples/basic/reverse-text/sft.toml --max-steps 10 --run.name reverse-text-sft

# eval run (multi-agent, multi-branch)
uv run eval \
  --env.id agentic-judge \
  --env.taskset.id terminal-bench-2 \
  --env.taskset.tasks '["fix-git"]' \
  --env.solver.harness.id rlm \
  --env.solver.harness.summarize-at-tokens 4000 \
  --env.judge.harness.id bash \
  --run.name tb2-agentic-judge

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

Screenshot 2026-08-23 at 6 01 35 PM

Metrics

Screenshot 2026-08-23 at 6 01 55 PM

Trace Overview

Screenshot 2026-08-23 at 6 02 08 PM

Trace Viewer

Screenshot 2026-08-23 at 6 02 47 PM

Logs

Screenshot 2026-08-23 at 6 03 08 PM

Breaking

  • scripts/tmux.sh is 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/*.json to {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/*.json directly.

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 from metrics.jsonl), configs (launch TOML vs merged resolved JSON), rollout traces with per-token advantage/logprob views, and merged component logs. It ships as a dashboard optional extra (FastAPI/uvicorn) or as uv run --script src/prime_rl/dashboard/server.py on machines without full GPU deps.

scripts/tmux.sh is 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 use uv run dashboard outputs to track multiple experiments.

Resolved configs from dry-run and launches are documented at {run_dir}/configs/resolved/*.json, with the launch TOML at configs/*.toml; the dashboard server still reads the old flat configs/*.json layout for older runs.

Reviewed by Cursor Bugbot for commit 99d9a8f. Bugbot is set up for automated code reviews on this repo. Configure here.

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>

@github-advanced-security github-advanced-security AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

mikasenghaas and others added 28 commits August 23, 2026 01:06
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>
mikasenghaas and others added 13 commits August 24, 2026 00:18
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>
@mikasenghaas
mikasenghaas marked this pull request as ready for review August 24, 2026 01:18
Comment thread src/prime_rl/dashboard/server.py Outdated
Comment thread src/prime_rl/dashboard/static/app.js
Comment thread src/prime_rl/dashboard/server.py Outdated
mikasenghaas and others added 3 commits August 24, 2026 01:30
- 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>
Comment thread src/prime_rl/orchestrator/inference_metrics.py
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>
samsja
samsja previously approved these changes Aug 24, 2026
mikasenghaas added a commit to PrimeIntellect-ai/verifiers that referenced this pull request Aug 24, 2026
## 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>
@mikasenghaas
mikasenghaas merged commit 75663f0 into main Aug 24, 2026
18 checks passed
@mikasenghaas
mikasenghaas deleted the feat/run-dashboard branch August 24, 2026 19:59

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ 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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 99d9a8f. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants