File size: 6,791 Bytes
df3b181 eab219c df3b181 a3335a4 df3b181 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | from __future__ import annotations
import argparse
import json
import os
import re
import subprocess
import sys
from pathlib import Path
from typing import Any
from dotenv import load_dotenv
from leaderboard import LeaderboardClient, build_record, latest_log_file
load_dotenv()
def run_command(cmd: list[str]) -> subprocess.CompletedProcess[str]:
print(f"[leaderboard] running: {' '.join(cmd)}")
return subprocess.run(cmd, capture_output=True, text=True)
def build_inspect_command(args: argparse.Namespace) -> list[str]:
cmd = []
cmd.extend(args.inspect_launch)
cmd.append(args.inspect_task)
def add_task_arg(key: str, value: Any) -> None:
if value is None:
return
cmd.extend(["-T", f"{key}={value}"])
add_task_arg("solver_name", args.solver_name)
add_task_arg("solver_kwargs", json.dumps(args.solver_kwargs))
add_task_arg("dataset_name", args.dataset)
if args.limit is not None:
add_task_arg("limit", args.limit)
cmd.extend(["--log-dir", args.log_dir])
if args.log_format:
cmd.extend(["--log-format", args.log_format])
if args.extra_inspect_args:
cmd.extend(args.extra_inspect_args)
return cmd
def parse_score_from_outputs(log_dir: Path) -> tuple[float, Path, list[dict[str, Any]]]:
log_path = latest_log_file(log_dir)
if not log_path:
raise RuntimeError("Inspect log file not found.")
# Sanitization
content = log_path.read_text(encoding="utf-8")
# Regex to match hf_ followed by 34 alphanumeric chars
sanitized_content = re.sub(r"hf_[a-zA-Z0-9]{34}", "<REDACTED_TOKEN>", content)
if content != sanitized_content:
log_path.write_text(sanitized_content, encoding="utf-8")
print(f"[leaderboard] Redacted HF tokens in {log_path}")
content = sanitized_content
data = json.loads(content)
results = data.get("results", {})
scores = results.get("scores", [])
score_value = None
criterion_checks: list[dict[str, Any]] = []
for score_entry in scores:
metrics = score_entry.get("metrics", {})
for metric in metrics.values():
value = metric.get("value")
if isinstance(value, (int, float)):
score_value = float(value)
break
if score_value is not None:
break
if score_value is None:
raise RuntimeError("Could not find a numeric metric value in the Inspect log.")
for sample in data.get("samples", []):
# Grab the question from metadata (fallback to input)
question = "Unknown Question"
if "metadata" in sample and "question" in sample["metadata"]:
question = sample["metadata"]["question"]
elif "input" in sample:
question = sample["input"]
# Check if any scorer produced criterion_checks
for scorer in sample.get("scores", {}).values():
metadata = scorer.get("metadata") or {}
checks = metadata.get("criterion_checks")
if isinstance(checks, list) and checks:
# Create a grouped entry for this question/sample
grouped_entry = {"question": question, "checks": []}
for check in checks:
if isinstance(check, dict):
grouped_entry["checks"].append(check)
if grouped_entry["checks"]:
criterion_checks.append(grouped_entry)
return score_value, log_path, criterion_checks
def main() -> None:
parser = argparse.ArgumentParser(
description="Run Inspect eval and append the resulting score to a HF dataset."
)
parser.add_argument(
"--hf-dataset",
default="akseljoonas/hf-agent-leaderboard",
help="HF dataset repo id for the leaderboard (e.g. user/leaderboard).",
)
parser.add_argument(
"--solver-name",
required=True,
help="Solver name used in the Inspect task (e.g. hf_agent).",
)
parser.add_argument(
"--solver-kwargs",
type=json.loads,
default="{}",
help="JSON string with solver kwargs passed to the Inspect task.",
)
parser.add_argument(
"--dataset",
default="akseljoonas/hf-agent-rubrics@train",
help="Dataset spec in the form author/dataset@split.",
)
parser.add_argument(
"--limit",
type=int,
default=None,
help="Optional sample limit passed to Inspect.",
)
parser.add_argument(
"--inspect-task",
default="eval/task.py@hf-benchmark-with-rubrics",
help="Inspect task reference.",
)
parser.add_argument(
"--inspect-launch",
nargs="+",
default=["uv", "run", "inspect", "eval"],
help="Command used to invoke Inspect (default: uv run inspect eval).",
)
parser.add_argument(
"--log-dir",
default="logs/leaderboard",
help="Directory where Inspect outputs .eval logs.",
)
parser.add_argument(
"--extra-inspect-args",
nargs="*",
help="Additional args forwarded to Inspect after the standard task arguments.",
)
parser.add_argument(
"--log-format",
default="json",
help="Log format passed to Inspect (default: json).",
)
args = parser.parse_args()
if isinstance(args.solver_kwargs, str):
args.solver_kwargs = json.loads(args.solver_kwargs or "{}")
hf_token = os.getenv("HF_TOKEN")
if not hf_token:
print("ERROR: set HF_TOKEN in your environment.", file=sys.stderr)
sys.exit(1)
if "@" not in args.dataset:
raise ValueError("Dataset must be in the format 'author/dataset@split'.")
dataset_name, dataset_split = args.dataset.split("@", 1)
log_dir = Path(args.log_dir)
log_dir.mkdir(parents=True, exist_ok=True)
inspect_cmd = build_inspect_command(args)
result = run_command(inspect_cmd)
if result.returncode != 0:
print(result.stdout)
print(result.stderr, file=sys.stderr)
raise SystemExit(result.returncode)
score, log_path, criterion_checks = parse_score_from_outputs(log_dir)
client = LeaderboardClient(repo_id=args.hf_dataset, token=hf_token)
record = build_record(
solver_name=args.solver_name,
solver_kwargs=args.solver_kwargs,
dataset_name=dataset_name,
dataset_split=dataset_split,
limit=args.limit,
score=score,
command=inspect_cmd,
log_path=log_path,
criterion_checks=criterion_checks,
)
client.append_record(record)
print(
f"[leaderboard] recorded score {score:.3f} for solver '{args.solver_name}' to {args.hf_dataset}"
)
if __name__ == "__main__":
main()
|