burtenshaw's picture
burtenshaw HF Staff
Publish codex workspace
4f4b027 verified
Raw
History Blame Contribute Delete
12.5 kB
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "huggingface-hub>=1.1.4",
# "python-dotenv>=1.0.0",
# "pyyaml>=6.0.0",
# "requests>=2.31.0",
# ]
# ///
"""
Import evaluation results from Artificial Analysis API.
Usage:
# Look up a specific benchmark (dry run - prints YAML)
AA_API_KEY=... uv run scripts/import_aa.py --repo-id "org/model" --benchmark HLE
# Look up a benchmark and create PR
AA_API_KEY=... uv run scripts/import_aa.py --repo-id "org/model" --benchmark GPQA --create-pr
# Import all available benchmarks
AA_API_KEY=... uv run scripts/import_aa.py --repo-id "org/model" --all
# Provide value manually (skip lookup)
uv run scripts/import_aa.py --repo-id "org/model" --benchmark HLE --value 22.5 --create-pr
"""
from __future__ import annotations
import argparse
import json
import os
import re
import sys
from datetime import date
from pathlib import Path
from typing import Any
import requests
AA_INDEX_URL = "https://artificialanalysis.ai/api/v2/data/llms/models"
def load_env() -> None:
try:
import dotenv
dotenv.load_dotenv()
except ModuleNotFoundError:
pass
def load_benchmark_mapping() -> dict[str, Any]:
script_dir = Path(__file__).parent
mapping_file = script_dir.parent / "examples" / "metric_mapping.json"
if not mapping_file.exists():
return {
"GPQA": {"dataset_id": "Idavidrein/gpqa", "task_id": "gpqa_diamond", "aliases": ["gpqa"]},
"HLE": {"dataset_id": "cais/hle", "task_id": "default", "aliases": ["hle"]},
"SimpleQA": {"dataset_id": "OpenEvals/SimpleQA", "task_id": "default", "aliases": ["simpleqa"]},
"MMLU": {"dataset_id": "cais/mmlu", "task_id": "default", "aliases": ["mmlu"]},
"GSM8K": {"dataset_id": "openai/gsm8k", "task_id": "default", "aliases": ["gsm8k"]},
}
with open(mapping_file) as f:
mapping = json.load(f)
mapping.pop("_comment", None)
return mapping
def find_benchmark_dataset(benchmark_name: str, mapping: dict[str, Any]) -> dict[str, str] | None:
cleaned = re.sub(r'\[([^\]]+)\]\([^\)]+\)', r'\1', benchmark_name)
cleaned = re.sub(r'\*\*([^\*]+)\*\*', r'\1', cleaned)
cleaned = re.sub(r'\*([^\*]+)\*', r'\1', cleaned)
cleaned = cleaned.strip()
normalized = cleaned.lower().replace(" ", "_").replace("-", "_")
base_name = re.sub(r'\s*\([^)]*\)\s*$', '', cleaned).strip()
base_normalized = base_name.lower().replace(" ", "_").replace("-", "_")
if cleaned in mapping:
entry = mapping[cleaned]
return {"dataset_id": entry["dataset_id"], "task_id": entry.get("task_id", "default")}
for key, entry in mapping.items():
if key.lower() == cleaned.lower():
return {"dataset_id": entry["dataset_id"], "task_id": entry.get("task_id", "default")}
for key, entry in mapping.items():
aliases = entry.get("aliases", [])
normalized_aliases = [a.lower().replace(" ", "_").replace("-", "_") for a in aliases]
if normalized in normalized_aliases:
return {"dataset_id": entry["dataset_id"], "task_id": entry.get("task_id", "default")}
for key, entry in mapping.items():
key_normalized = key.lower().replace(" ", "_").replace("-", "_")
if normalized == key_normalized:
return {"dataset_id": entry["dataset_id"], "task_id": entry.get("task_id", "default")}
if base_normalized != normalized:
for key, entry in mapping.items():
if key.lower() == base_name.lower():
return {"dataset_id": entry["dataset_id"], "task_id": entry.get("task_id", "default")}
key_normalized = key.lower().replace(" ", "_").replace("-", "_")
if base_normalized == key_normalized:
return {"dataset_id": entry["dataset_id"], "task_id": entry.get("task_id", "default")}
return None
def fetch_aa_models(api_key: str) -> list[dict[str, Any]]:
response = requests.get(
AA_INDEX_URL,
headers={"x-api-key": api_key},
timeout=30,
)
response.raise_for_status()
data = response.json()
return list(data.get("data", []))
def find_model_in_aa(models: list[dict[str, Any]], repo_id: str) -> dict[str, Any] | None:
model_name = repo_id.split("/")[-1] if "/" in repo_id else repo_id
model_name_normalized = model_name.lower().replace("-", " ").replace("_", " ")
for model in models:
aa_name = model.get("name", "").lower().replace("-", " ").replace("_", " ")
aa_slug = model.get("slug", "").lower().replace("-", " ").replace("_", " ")
if model_name_normalized in aa_name or model_name_normalized in aa_slug:
return model
return None
def lookup_benchmark_from_aa(
models: list[dict[str, Any]],
repo_id: str,
benchmark_name: str,
) -> float | None:
model = find_model_in_aa(models, repo_id)
if not model:
return None
evaluations = model.get("evaluations", {})
benchmark_normalized = benchmark_name.lower().replace(" ", "_").replace("-", "_")
for key, value in evaluations.items():
key_normalized = key.lower().replace(" ", "_").replace("-", "_")
if benchmark_normalized == key_normalized or benchmark_normalized in key_normalized:
if value is not None:
return float(value)
return None
def get_all_benchmarks_from_aa(
models: list[dict[str, Any]],
repo_id: str,
) -> list[dict[str, Any]]:
model = find_model_in_aa(models, repo_id)
if not model:
return []
evaluations = model.get("evaluations", {})
metrics = []
for key, value in evaluations.items():
if value is not None:
metrics.append({
"name": key.replace("_", " ").title(),
"type": key,
"value": float(value),
})
return metrics
def convert_to_eval_results_format(
metrics: list[dict[str, Any]],
source_url: str | None = None,
source_name: str | None = None,
source_user: str | None = None,
) -> list[dict[str, Any]]:
mapping = load_benchmark_mapping()
results = []
today = date.today().isoformat()
for metric in metrics:
benchmark_name = metric.get("name", "")
value = metric.get("value")
if value is None:
continue
dataset_info = find_benchmark_dataset(benchmark_name, mapping)
if not dataset_info:
print(f"Warning: Could not find Hub dataset ID for benchmark '{benchmark_name}'. Skipping.", file=sys.stderr)
continue
entry: dict[str, Any] = {
"dataset": {"id": dataset_info["dataset_id"]},
"value": value,
"date": today,
}
if dataset_info.get("task_id") and dataset_info["task_id"] != "default":
entry["dataset"]["task_id"] = dataset_info["task_id"]
if source_url:
entry["source"] = {"url": source_url}
if source_name:
entry["source"]["name"] = source_name
if source_user:
entry["source"]["user"] = source_user
results.append(entry)
return results
def upload_eval_results(
repo_id: str,
results: list[dict[str, Any]],
filename: str = "evaluations.yaml",
create_pr: bool = False,
commit_message: str | None = None,
) -> bool:
import yaml
from huggingface_hub import HfApi
load_env()
hf_token = os.getenv("HF_TOKEN")
if not hf_token:
print("Error: HF_TOKEN environment variable is not set", file=sys.stderr)
return False
api = HfApi(token=hf_token)
yaml_content = yaml.dump(results, sort_keys=False, allow_unicode=True, default_flow_style=False)
file_path = f".eval_results/{filename}"
if not commit_message:
model_name = repo_id.split("/")[-1] if "/" in repo_id else repo_id
commit_message = f"Add Artificial Analysis evaluation results for {model_name}"
pr_description = """## Evaluation Results
This PR adds structured evaluation results using the new [`.eval_results/` format](https://huggingface.co/docs/hub/eval-results).
**Source:** [Artificial Analysis](https://artificialanalysis.ai)
### What This Enables
- **Model Page**: Results appear on the model page with benchmark links
- **Leaderboards**: Scores are aggregated into benchmark dataset leaderboards
- **Verification**: Support for cryptographic verification of evaluation runs
---
*Generated by [community-evals](https://github.com/huggingface/community-evals)*"""
try:
api.upload_file(
path_or_fileobj=yaml_content.encode("utf-8"),
path_in_repo=file_path,
repo_id=repo_id,
repo_type="model",
commit_message=commit_message,
commit_description=pr_description,
create_pr=create_pr,
)
action = "Pull request created" if create_pr else "Evaluation results uploaded"
print(f"✓ {action} successfully for {repo_id}")
print(f" File: {file_path}")
return True
except Exception as e:
print(f"Error uploading evaluation results: {e}", file=sys.stderr)
return False
def main() -> None:
parser = argparse.ArgumentParser(
description="Import evaluation results from Artificial Analysis API.",
)
parser.add_argument("--repo-id", required=True, help="HuggingFace repository ID")
parser.add_argument("--benchmark", help="Specific benchmark to look up (e.g., HLE, GPQA)")
parser.add_argument("--value", type=float, help="Manually provide the score (skips AA lookup)")
parser.add_argument("--all", action="store_true", help="Import all available benchmarks")
parser.add_argument("--source-user", help="HF username/org for attribution")
parser.add_argument("--filename", default="artificial_analysis.yaml", help="Output filename")
parser.add_argument("--create-pr", action="store_true", help="Create PR instead of direct push")
parser.add_argument("--apply", action="store_true", help="Apply changes (default is dry run)")
parser.add_argument("--pretty", action="store_true", help="Pretty-print YAML output")
parser.add_argument("--verbose", action="store_true", help="Print progress to stderr")
args = parser.parse_args()
load_env()
if args.value is not None and args.benchmark:
metrics = [{"name": args.benchmark, "type": args.benchmark.lower(), "value": args.value}]
else:
api_key = os.getenv("AA_API_KEY")
if not api_key:
print("Error: AA_API_KEY is required to query Artificial Analysis.", file=sys.stderr)
sys.exit(1)
if args.verbose:
print("Fetching models from Artificial Analysis...", file=sys.stderr)
models = fetch_aa_models(api_key)
if args.all:
metrics = get_all_benchmarks_from_aa(models, args.repo_id)
if not metrics:
print(f"No benchmarks found for {args.repo_id} in Artificial Analysis", file=sys.stderr)
sys.exit(1)
elif args.benchmark:
value = lookup_benchmark_from_aa(models, args.repo_id, args.benchmark)
if value is None:
print(f"Could not find {args.benchmark} score for {args.repo_id} in Artificial Analysis", file=sys.stderr)
sys.exit(1)
print(f"Found: {args.benchmark} = {value}")
metrics = [{"name": args.benchmark, "type": args.benchmark.lower(), "value": value}]
else:
print("Error: Either --benchmark or --all is required", file=sys.stderr)
sys.exit(1)
eval_results = convert_to_eval_results_format(
metrics=metrics,
source_url="https://artificialanalysis.ai",
source_name="Artificial Analysis",
source_user=args.source_user,
)
if not eval_results:
print("No benchmarks could be mapped to Hub dataset IDs", file=sys.stderr)
sys.exit(1)
import yaml
print("\nImported evaluations (.eval_results/ format):")
print(yaml.dump(eval_results, sort_keys=False, allow_unicode=True, default_flow_style=False))
if args.apply or args.create_pr:
upload_eval_results(
repo_id=args.repo_id,
results=eval_results,
filename=args.filename,
create_pr=args.create_pr,
)
if __name__ == "__main__":
main()