# Phase 04 — LLM page is readable again **Source:** `TODO.md` L6 — "LLM usage drifed from initial request to copy Experimental/llm_monitor. The charts got less useful. Need simplified and more readable." **Story:** n/a **Context:** `.agents/PLAN.md` → *Anchors* (hand-rolled SVG only), `~/Projects/Experimental/llm_monitor/static/index.html` (the reference implementation), `src/dashboards/web/static/page-llm.js` (`CELLS`, `makeCharts`), `src/dashboards/web/static/charts.js` (`sparkline`, `LineChart`), `src/dashboards/sources/llm.py` ## Objective The LLM page drifted into a 14-cell grid where everything is the same size and nothing shows recent shape. Bring back what made `llm_monitor` legible — big throughput numbers, a sparkline in each metric cell, percentiles as foot lines — without losing what this project added since: per-engine provenance tags, history windows, and severity colour. `llm_monitor` is the design reference, not a dependency. It is a separate app on `:8095` and stays that way; nothing here imports from it. ## Dependencies - `01_severity_language` (todo) — KV-cache pressure and queue depth get coloured from the API verdict, not a per-page threshold. - `03_system_per_host_panels` (todo) — touches `charts.js` first; this phase builds on the same shared helpers and must not fight it for them. ## Tasks 1. `01_chart_set_decision.md` — read both implementations side by side and write the decision note (which cells live, which get sparklines, what is demoted). 2. `02_sparkline_upgrade.md` — bring `DashCharts.sparkline()` up to `llm_monitor`'s `drawSpark` behaviour: right-aligned growth, fixed max, current/max labels, severity colour. 3. `03_llm_layout_simplified.md` — restructure `llm.html` + `page-llm.js`: hero throughput, compact sparkline cells, percentile foot lines, conditional alarm cells. 4. `04_side_by_side_verification.md` — verify against `llm_monitor` on `:8095` with the same live engines. ## Testing & Quality - Python: none expected beyond the phase-01 severity fields; `/api/llm/*` stays as-is (additive-only anchor). - `uv run pytest -m live --live` must stay green — `test_engine_still_publishes_the_series_we_parse` is the assertion that the numbers on screen are backed by resolved upstream series. - Browser: both engines live (gufo on `deskwork:8731`, vLLM on `driveripper:8000`), plus the single-engine case: with one `*_BASE_URL` unset the page must show one clean half rather than a red panel. - Coverage: ≥94% overall maintained. ## Completion Criteria - [ ] decode and prompt throughput are the two largest numbers per engine, colour-coded - [ ] every remaining metric cell carries a sparkline showing the selected window - [ ] latency cells show mean **and** p95 without needing a hover - [ ] a source-provenance tag is still visible on every throughput number (`AGENTS.md`: never invent a number) - [ ] `/llm` with only one engine configured still renders cleanly - [ ] screenshots of this page and `llm_monitor` side by side in `data/logs/`, plus `NOTES-chart-set.md` - [ ] `uv run pytest` green, coverage ≥94%, `ruff` + `pyright` clean - [ ] no behavior change in completed phases