#!/usr/bin/env python3 """Build the dashboard's public JSON payload from a W&B project.""" from __future__ import annotations import argparse import json import math import os import sys from collections import defaultdict from datetime import datetime, timezone _SCORE_PREFIX = "miner/" _SCORE_SUFFIX = "/score" _LEN_SUFFIX = "/completion_len" _ORIG_SCORE = "original/score" _ORIG_LEN = "original/completion_len" def _miner_hotkey_from_score_key(key: str) -> str | None: if key.startswith(_SCORE_PREFIX) and key.endswith(_SCORE_SUFFIX): hk = key[len(_SCORE_PREFIX) : -len(_SCORE_SUFFIX)] return hk or None return None def _as_float(value) -> float | None: try: f = float(value) except (TypeError, ValueError): return None return f if math.isfinite(f) else None def extract_rankings(summary: dict) -> list[dict]: """Return the current miner ranking from a run summary.""" out = [] for key, value in summary.items(): hk = _miner_hotkey_from_score_key(key) if hk is None: continue score = _as_float(value) if score is None: continue out.append({"hotkey": hk, "score": score}) out.sort(key=lambda r: r["score"], reverse=True) return out def extract_history(run) -> dict: """Return aligned per-epoch miner and base-model series for one run.""" rows_by_epoch: dict[float, dict] = {} miner_keys: set[str] = set() saw_orig_score = saw_orig_len = False for row in run.scan_history(): epoch = _as_float(row.get("epoch")) if epoch is None: continue bucket = rows_by_epoch.setdefault(epoch, {}) for key, value in row.items(): hk = _miner_hotkey_from_score_key(key) if hk is not None: bucket[("miner", hk, "score")] = _as_float(value) miner_keys.add(hk) elif key.startswith(_SCORE_PREFIX) and key.endswith(_LEN_SUFFIX): hk2 = key[len(_SCORE_PREFIX) : -len(_LEN_SUFFIX)] if hk2: bucket[("miner", hk2, "completion_len")] = _as_float(value) miner_keys.add(hk2) elif key == _ORIG_SCORE: bucket[("orig", "score")] = _as_float(value) saw_orig_score = True elif key == _ORIG_LEN: bucket[("orig", "completion_len")] = _as_float(value) saw_orig_len = True epochs = sorted(rows_by_epoch) miners: dict[str, dict] = {} for hk in miner_keys: miners[hk] = { "score": [rows_by_epoch[e].get(("miner", hk, "score")) for e in epochs], "completion_len": [rows_by_epoch[e].get(("miner", hk, "completion_len")) for e in epochs], } original = {} if saw_orig_score: original["score"] = [rows_by_epoch[e].get(("orig", "score")) for e in epochs] if saw_orig_len: original["completion_len"] = [rows_by_epoch[e].get(("orig", "completion_len")) for e in epochs] return {"epochs": [int(e) if e == int(e) else e for e in epochs], "miners": miners, "original": original} def build_aggregate(validators: list[dict]) -> dict: """Average miner scores across validators that ranked each miner.""" score_sums: dict[str, float] = defaultdict(float) score_counts: dict[str, int] = defaultdict(int) for v in validators: for r in v["rankings"]: score_sums[r["hotkey"]] += r["score"] score_counts[r["hotkey"]] += 1 rankings = [ {"hotkey": hk, "score": score_sums[hk] / score_counts[hk], "validators": score_counts[hk]} for hk in score_sums ] rankings.sort(key=lambda r: r["score"], reverse=True) history = {"epochs": [], "miners": {}, "original": {}} if rankings: top = rankings[0]["hotkey"] history = _mean_history(validators, top) return {"rankings": rankings, "history": history} def _mean_history(validators: list[dict], top_hotkey: str) -> dict: """Average per-epoch history for the leading miner and base model.""" epochs = sorted({e for v in validators for e in v["history"].get("epochs", [])}) def mean_series(getter): out = [] for e in epochs: vals = [] for v in validators: hist = v["history"] if e not in hist.get("epochs", []): continue pos = hist["epochs"].index(e) val = getter(hist, pos) if val is not None and val == val: vals.append(val) out.append(sum(vals) / len(vals) if vals else None) return out miner_score = mean_series(lambda h, p: (h["miners"].get(top_hotkey, {}).get("score") or [None] * (p + 1))[p] if h["miners"].get(top_hotkey) else None) miner_len = mean_series(lambda h, p: (h["miners"].get(top_hotkey, {}).get("completion_len") or [None] * (p + 1))[p] if h["miners"].get(top_hotkey) else None) orig_score = mean_series(lambda h, p: (h["original"].get("score") or [None] * (p + 1))[p] if h["original"].get("score") else None) orig_len = mean_series(lambda h, p: (h["original"].get("completion_len") or [None] * (p + 1))[p] if h["original"].get("completion_len") else None) original = {} if any(x is not None for x in orig_score): original["score"] = orig_score if any(x is not None for x in orig_len): original["completion_len"] = orig_len return { "epochs": epochs, "miners": {top_hotkey: {"score": miner_score, "completion_len": miner_len}}, "original": original, } def build(project: str, entity: str | None, api=None) -> dict: if api is None: import wandb api = wandb.Api() path = f"{entity}/{project}" if entity else project validators: list[dict] = [] seen: set[str] = set() for run in api.runs(path, order="-created_at"): hotkey = dict(run.config).get("validator_hotkey") if not isinstance(hotkey, str) or not hotkey or hotkey in seen: continue seen.add(hotkey) validators.append( { "hotkey": hotkey, "rankings": extract_rankings(dict(run.summary)), "history": extract_history(run), } ) return { "generated_at": datetime.now(timezone.utc).isoformat(), "validators": validators, "aggregate": build_aggregate(validators), } def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser(description="Generate the static dashboard's data.json from wandb.") parser.add_argument("--wandb-project", required=True, help="Shared wandb project every validator logs into") parser.add_argument("--wandb-entity", default=None, help="Wandb entity/team (default: the API key's default entity)") parser.add_argument("--out", default="data.json", help="Output path for the generated JSON") args = parser.parse_args(argv) key = os.environ.get("WANDB_KEY") or os.environ.get("WANDB_API_KEY") if not key: print("error: set WANDB_KEY (or WANDB_API_KEY) in the environment", file=sys.stderr) return 2 os.environ["WANDB_API_KEY"] = key data = build(args.wandb_project, args.wandb_entity) with open(args.out, "w", encoding="utf-8") as fh: json.dump(data, fh, indent=2) fh.write("\n") print(f"wrote {args.out}: {len(data['validators'])} validators, " f"{len(data['aggregate']['rankings'])} ranked miners") return 0 if __name__ == "__main__": raise SystemExit(main())