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`).
130 lines
5.4 KiB
Python
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"
|