| |
| |
| |
| |
| |
| |
| |
| |
| |
| """ |
| 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() |
|
|