Scroll the runs table, pick a range, and choose what a comparison plots against
Docs / docs (push) Successful in 35s
Playwright Tests / test-playwright (1, 2) (push) Failing after 3m14s
Playwright Tests / test-playwright (2, 2) (push) Failing after 1m44s
pre-commit / pre-commit (push) Failing after 3m54s
Test Backend / test-backend (push) Successful in 3m12s
Compose Smoke Test / test-compose (push) Successful in 32s
Playwright Tests / merge-reports (push) Failing after 1m28s
Docs / docs (push) Successful in 35s
Playwright Tests / test-playwright (1, 2) (push) Failing after 3m14s
Playwright Tests / test-playwright (2, 2) (push) Failing after 1m44s
pre-commit / pre-commit (push) Failing after 3m54s
Test Backend / test-backend (push) Successful in 3m12s
Compose Smoke Test / test-compose (push) Successful in 32s
Playwright Tests / merge-reports (push) Failing after 1m28s
This commit is contained in:
@@ -132,6 +132,9 @@ class MetricSeries(BaseModel):
|
||||
|
||||
class SeriesAnswer(BaseModel):
|
||||
metric: str
|
||||
#: What the x values are: "step", "time" (seconds since this run's first
|
||||
#: reading), or the name of another metric this one was plotted against.
|
||||
x: str = "step"
|
||||
lines: list[MetricSeries] = Field(default_factory=list)
|
||||
|
||||
|
||||
@@ -356,13 +359,38 @@ def read_metrics(
|
||||
return rows
|
||||
|
||||
|
||||
def _points(session: Session, run_id: str, metric: str, x: str) -> list[list[float]]:
|
||||
"""One run's readings of ``metric``, against whichever x was asked for.
|
||||
|
||||
The step is the default because it is what every run has. Time answers
|
||||
"which one got there sooner", and is measured from this run's own first
|
||||
reading so that runs started hours apart still lie on top of each other.
|
||||
Another metric answers "against what the loop was actually counting" — an
|
||||
epoch, or samples seen — and is joined on the step the two share, which is
|
||||
the only thing they have in common.
|
||||
"""
|
||||
rows = _series(session, run_id, metric)
|
||||
if x == "time":
|
||||
if not rows:
|
||||
return []
|
||||
start = min(row.ts for row in rows)
|
||||
return [[row.ts - start, row.value] for row in rows]
|
||||
if x and x != "step":
|
||||
against = {row.step: row.value for row in _series(session, run_id, x)}
|
||||
return [[against[row.step], row.value] for row in rows if row.step in against]
|
||||
return [[float(row.step), row.value] for row in rows]
|
||||
|
||||
|
||||
@router.get("/series/compare", response_model=SeriesAnswer)
|
||||
def compare_metric(session: SessionDep, ids: str, metric: str) -> Any:
|
||||
def compare_metric(session: SessionDep, ids: str, metric: str, x: str = "") -> Any:
|
||||
"""One metric across several runs, as the chart widget's series shape.
|
||||
|
||||
This is the comparison view: it answers in the same shape a flow answers a
|
||||
chart's query with, so putting three training curves beside each other is
|
||||
a widget binding rather than a screen of its own.
|
||||
|
||||
``x`` names what to plot against — nothing or "step", "time", or another
|
||||
metric of the same runs.
|
||||
"""
|
||||
run_ids = [part for part in ids.split(",") if part]
|
||||
if not run_ids:
|
||||
@@ -376,13 +404,10 @@ def compare_metric(session: SessionDep, ids: str, metric: str) -> Any:
|
||||
run = runs.get(run_id)
|
||||
if run is None:
|
||||
continue
|
||||
rows = _series(session, run_id, metric)
|
||||
label = run_id
|
||||
if run.seed is not None:
|
||||
label = f"{run_id} (seed {run.seed})"
|
||||
lines.append(
|
||||
MetricSeries(
|
||||
label=label, points=[[float(row.step), row.value] for row in rows]
|
||||
)
|
||||
MetricSeries(label=label, points=_points(session, run_id, metric, x))
|
||||
)
|
||||
return SeriesAnswer(metric=metric, lines=lines)
|
||||
return SeriesAnswer(metric=metric, x=x or "step", lines=lines)
|
||||
|
||||
Reference in New Issue
Block a user