Files
dashboards/tests/test_prom.py
T
ducoterra 1bbd868401 phase: 13_llm_page_and_decode_rate
All green. Final verification summary:

**Verified / implemented (phase 13 final pass)**
- All 4 tasks already in `complete/`; this was the verification pass. Confirmed the CSS rhythm rules (`#strip`, `#banners:empty ~ #fleet`) are served and pinned, `data/logs/NOTES-decode-spike.md` names the mechanism, and the phase-07/04 surfaces still pass their pins.
- **Found and fixed a residual defect in this phase's own code**: a stalled fetch stamps its body with the poll's *finish* time, so that sample anchors every window for the rest of the span; the jump gate "confirmed" a jump measured from that same anchor → live gufo rows 232.28 tok/s (one request at ~50) + a decaying 146/114/95 tail, repeated at 200.6. `rates.py::_publish` now requires an **independent anchor** (`_pending` stores the base sample's ts; the hold continues until it ages out). Pinned by `test_a_held_jump_is_not_confirmed_by_a_window_on_the_same_anchor` + `test_gufo_a_stale_scrape_body_never_becomes_a_rate_above_the_engine` (both fail on task 03's gate).
- Re-verified live after a dev-server restart: 10 min / 540 signed-in `/api/llm/snapshot` polls + read-only SQLite pass; **0 values > 300, 0 without a `Δ<window>` label**; 0 post-fix stored rows over the ceiling except the two pre-fix-shape rows above.

**Tests / lint (exact, all 100 % green)**
- `bash .agents/validate.sh` → **EXIT=0**; `uv run pytest --cov --cov-fail-under=94` → **1195 passed, 11 skipped, coverage 98.70 %**; `ruff check src tests agent` → All checks passed; `pyright` → 0 errors; `node --check` clean; repo hygiene clean.
- `uv run pytest -m live --live` → **11 passed / 0 failed**. `/api/health` → **all nine sources ok**.

**Completion criteria**
- Fleet/strip + head/fleet gap with screenshots (`llm-fleet-spacing{,-top,-banner-case}.png`) — met (CSS unchanged this pass, pinned on served bytes).
- Task-02 findings file names the confirmed mechanism — met.
- `test_rates.py`/`test_llm_source.py` pin label-set, both window bounds and the always-honest `Δ` — met (+2 new pins).
- Sustained watch under the recorded ceiling (300 tok/s) — met (worst 203.05, all labelled).
- Full suite + validate green; no phase-07/04 behaviour change — met.

**Documentation**
- README: `RATE_WINDOW_SECONDS` row now states the hold/confirm rule (only change needed; rest verified consistent).
- AGENTS.md: `rates.py` bullet + gap 9 rewritten (residual closed *and* the remaining sub-8× skew measured, root-caused to gap 4, explicitly not fixed by inventing a new threshold).
- **Fleet facts changed under this pass and were corrected**: operator added driveripper's bearer token → `host:driveripper`/`disks:driveripper` `ok`, live suite 9/2→11/0; but driveripper reads **identity only, zero SMART numbers** (root `smartctl` gives 55 °C/14 %; `/dev/nvme0`, `/dev/ng0n1`, `/dev/bsg/*` are root-only; its unit is `DevicePolicy=auto`, not the installer's `strict`). Evidence appended to `data/logs/NOTES-agent-install.md` + `NOTES-decode-stable.md`.

**Notable decisions / deviations**
- Did **not** add a span-relative window floor: that would re-decide task 03's pinned `MIN_WINDOW_SECONDS` and its "in-bounds diluted row publishes" test. Recorded as the next phase's call, with the root cause left at gap 4 / `14_store_performance` (the ~10 s stall).
- No live fleet change made (read-only probes + one dev-server restart). Fixtures deliberately not re-captured (no parse change, task 02 proved no shape drift). Dependency-currency advisory left untouched (no dependency moved in this phase): certifi/fastapi/starlette/websockets have newer releases.

**Next pending phase:** `.agents/phases/todo/14_store_performance` (then `15_agent_mtls`).
2026-10-19 14:53:58 -04:00

130 lines
5.4 KiB
Python

"""The hand-rolled Prometheus parser, tested against real engine expositions.
This parser exists so the app has no Prometheus dependency for the two upstreams that
already speak the format. That makes it load-bearing on text we do not control: llama.cpp
emits names with colons, vLLM emits label sets and histograms, and both are free to change
them. Fixtures are captured verbatim (scripts/capture-fixtures.sh --with-llm).
"""
from __future__ import annotations
import math
from dashboards.prom import Prom
def test_parses_counters_and_gauges_from_a_real_capture(metrics_text) -> None:
prom = Prom(metrics_text("gufo"))
assert prom.scalar(["llamacpp:tokens_predicted_total"]) == 877_336.0
assert prom.scalar(["llamacpp:kv_cache_usage_ratio"]) == 0.0
assert prom.parse_errors == 0
def test_help_and_type_comments_are_not_samples(metrics_text) -> None:
prom = Prom(metrics_text("gufo"))
assert not any(name.startswith("#") for name in prom.series)
assert "# HELP llamacpp:prompt_tokens_total" not in prom.series
def test_labels_are_parsed_and_filterable(metrics_text) -> None:
prom = Prom(metrics_text("vllm"))
running = prom.values(["vllm:num_requests_running"])
assert running, "vLLM exposes num_requests_running"
by_model = prom.by_label(["vllm:generation_tokens_total"], "model_name")
assert by_model and all(model for model in by_model)
def test_values_by_series_keeps_the_label_sets_apart() -> None:
"""Counter deltas diff per label set; the summed view would let a joining series
lend its carried value to one interval (phase 13). This is the accessor that view."""
prom = Prom('x{a="1"} 5.0\nx{a="2"} 7.0\ny 1.0\n')
assert prom.values_by_series(["x"]) == [({"a": "1"}, 5.0), ({"a": "2"}, 7.0)]
assert prom.values_by_series(["y"]) == [({}, 1.0)]
assert prom.values_by_series(["x"], {"a": "2"}) == [({"a": "2"}, 7.0)]
assert prom.values_by_series(["nope"]) == []
def test_values_by_series_on_a_real_labelled_capture(metrics_text) -> None:
prom = Prom(metrics_text("vllm"))
series = prom.values_by_series(["vllm:generation_tokens_total"])
assert series, "vLLM exposes generation_tokens_total"
assert all(labels.get("model_name") for labels, _v in series), "the capture lost its labels"
def test_aggregations(metrics_text) -> None:
prom = Prom(metrics_text("vllm"))
total = prom.scalar(["vllm:prompt_tokens_total"], agg="sum")
assert total is not None and total > 1e6
# ratios must never be summed across label sets — a 3-way sum would read as 300% used
usage = prom.scalar(["vllm:kv_cache_usage_perc"], agg="max")
assert usage is not None and 0.0 <= usage <= 1.0
def test_missing_series_is_none_not_zero(metrics_text) -> None:
prom = Prom(metrics_text("gufo"))
assert prom.scalar(["vllm:does_not_exist"]) is None
assert prom.values(["vllm:does_not_exist"]) == []
assert prom.by_label(["nope"], "model_name") == {}
def test_histogram_mean_and_p95(metrics_text) -> None:
hist = Prom(metrics_text("vllm")).histogram(["vllm:time_to_first_token_seconds"])
assert hist is not None
assert hist["count"] and hist["count"] > 0
assert hist["mean"] and hist["mean"] > 0
assert hist["p95"] is not None
assert hist["p95"] >= hist["mean"], "p95 below the mean means the interpolation is wrong"
assert math.isfinite(hist["p95"])
def test_histogram_resolves_through_suffixes(metrics_text) -> None:
prom = Prom(metrics_text("vllm"))
# the bare name never appears in the exposition; only _bucket/_sum/_count do
assert "vllm:time_to_first_token_seconds" not in prom.series
assert prom.resolve_hist(["vllm:time_to_first_token_seconds"]) == "vllm:time_to_first_token_seconds"
assert prom.resolve(["vllm:time_to_first_token_seconds"]) is None
def test_resolve_returns_the_first_present_name() -> None:
prom = Prom('a_total 5\nb_total 7\n')
assert prom.resolve(["missing_total", "b_total", "a_total"]) == "b_total"
assert prom.resolve(["nope"]) is None
def test_label_match_narrows_the_series() -> None:
prom = Prom('x{engine="0"} 1\nx{engine="1"} 2\n')
assert prom.scalar(["x"], agg="sum") == 3
assert prom.scalar(["x"], label_match={"engine": "1"}) == 2
assert prom.scalar(["x"], label_match={"engine": "9"}) is None
def test_malformed_lines_are_counted_not_fatal() -> None:
prom = Prom("good_metric 1\nthis is not prometheus\nalso_bad{unclosed 2\n")
assert prom.scalar(["good_metric"]) == 1.0
assert prom.parse_errors >= 1
def test_empty_exposition_is_harmless() -> None:
prom = Prom("")
assert prom.series == {}
assert prom.scalar(["anything"]) is None
assert prom.histogram(["anything"]) is None
def test_quantile_ignores_non_finite_values() -> None:
prom = Prom('m_sum 10\ncount 2\nm{le="1"} NaN\n')
assert prom.scalar(["m"], label_match={"le": "1"}) is None
def test_multiple_histogram_label_sets_do_not_invent_a_p95(metrics_text) -> None:
"""Mixing per-DP-rank buckets into one cumulative curve would fabricate a percentile."""
two = (
'h_sum{dp="0"} 10\nh_sum{dp="1"} 10\n'
'h_count{dp="0"} 2\nh_count{dp="1"} 2\n'
'h_bucket{dp="0",le="1"} 1\nh_bucket{dp="0",le="+Inf"} 2\n'
'h_bucket{dp="1",le="1"} 1\nh_bucket{dp="1",le="+Inf"} 2\n'
)
hist = Prom(two).histogram(["h"])
assert hist is not None
assert hist["mean"] == 5.0
assert hist["p95"] is None, "p95 across mixed label sets is a made-up number"