Spaces:
Running
Running
Commit ·
6f5cc9b
1
Parent(s): 8d9d190
Add HF eval-results benchmark registry and API endpoint
Browse files- Add canonical EEG_BENCHMARKS registry (8 datasets) in config/benchmarks.py
- Add GET /api/benchmarks endpoint for benchmark metadata
- Refactor leaderboard transform_data to loop over registry instead of hardcoding
- Extract _build_entry_id helper for consistent unique ID generation
- Add push_eval_yaml.py script to register benchmarks on HF Hub
- Add pyyaml dependency
- backend/app/api/endpoints/benchmarks.py +38 -0
- backend/app/api/router.py +3 -2
- backend/app/config/benchmarks.py +102 -0
- backend/app/services/leaderboard.py +22 -53
- backend/pyproject.toml +1 -0
- backend/scripts/__init__.py +0 -0
- backend/scripts/push_eval_yaml.py +131 -0
backend/app/api/endpoints/benchmarks.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from dataclasses import asdict
|
| 2 |
+
|
| 3 |
+
from fastapi import APIRouter, HTTPException
|
| 4 |
+
from typing import Any, Dict, List
|
| 5 |
+
|
| 6 |
+
from app.config.benchmarks import EEG_BENCHMARKS
|
| 7 |
+
|
| 8 |
+
router = APIRouter()
|
| 9 |
+
|
| 10 |
+
# Fields that are internal implementation details, not exposed via API
|
| 11 |
+
_INTERNAL_FIELDS = {"accuracy_field"}
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def _benchmark_to_dict(key: str, benchmark) -> Dict[str, Any]:
|
| 15 |
+
"""Convert a BenchmarkInfo dataclass to a JSON-serializable dict."""
|
| 16 |
+
d = asdict(benchmark)
|
| 17 |
+
for field in _INTERNAL_FIELDS:
|
| 18 |
+
d.pop(field, None)
|
| 19 |
+
d["key"] = key
|
| 20 |
+
return d
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
@router.get("")
|
| 24 |
+
async def list_benchmarks() -> List[Dict[str, Any]]:
|
| 25 |
+
"""Return all registered EEG benchmarks with metadata."""
|
| 26 |
+
return [_benchmark_to_dict(k, b) for k, b in EEG_BENCHMARKS.items()]
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
@router.get("/{key}")
|
| 30 |
+
async def get_benchmark(key: str) -> Dict[str, Any]:
|
| 31 |
+
"""Return a single benchmark's details by key."""
|
| 32 |
+
benchmark = EEG_BENCHMARKS.get(key)
|
| 33 |
+
if not benchmark:
|
| 34 |
+
raise HTTPException(
|
| 35 |
+
status_code=404,
|
| 36 |
+
detail=f"Benchmark '{key}' not found. Available: {list(EEG_BENCHMARKS.keys())}",
|
| 37 |
+
)
|
| 38 |
+
return _benchmark_to_dict(key, benchmark)
|
backend/app/api/router.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
| 1 |
from fastapi import APIRouter
|
| 2 |
|
| 3 |
-
from app.api.endpoints import leaderboard, votes, models
|
| 4 |
|
| 5 |
router = APIRouter()
|
| 6 |
|
| 7 |
router.include_router(leaderboard.router, prefix="/leaderboard", tags=["leaderboard"])
|
| 8 |
router.include_router(votes.router, prefix="/votes", tags=["votes"])
|
| 9 |
-
router.include_router(models.router, prefix="/models", tags=["models"])
|
|
|
|
|
|
| 1 |
from fastapi import APIRouter
|
| 2 |
|
| 3 |
+
from app.api.endpoints import leaderboard, votes, models, benchmarks
|
| 4 |
|
| 5 |
router = APIRouter()
|
| 6 |
|
| 7 |
router.include_router(leaderboard.router, prefix="/leaderboard", tags=["leaderboard"])
|
| 8 |
router.include_router(votes.router, prefix="/votes", tags=["votes"])
|
| 9 |
+
router.include_router(models.router, prefix="/models", tags=["models"])
|
| 10 |
+
router.include_router(benchmarks.router, prefix="/benchmarks", tags=["benchmarks"])
|
backend/app/config/benchmarks.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Canonical benchmark registry for the EEG Finetune Arena.
|
| 3 |
+
|
| 4 |
+
Maps internal benchmark keys to HuggingFace dataset IDs, task IDs,
|
| 5 |
+
and metadata used by both eval.yaml generation and eval results pushing.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
from dataclasses import dataclass, field
|
| 9 |
+
from typing import Dict
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
@dataclass(frozen=True)
|
| 13 |
+
class BenchmarkInfo:
|
| 14 |
+
"""Metadata for a single EEG benchmark dataset."""
|
| 15 |
+
|
| 16 |
+
dataset_id: str
|
| 17 |
+
task_id: str
|
| 18 |
+
display_name: str
|
| 19 |
+
description: str
|
| 20 |
+
category: str
|
| 21 |
+
num_classes: int
|
| 22 |
+
accuracy_field: str
|
| 23 |
+
split: str = "test"
|
| 24 |
+
config: str = "default"
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
EEG_BENCHMARKS: Dict[str, BenchmarkInfo] = {
|
| 28 |
+
"bcic2a": BenchmarkInfo(
|
| 29 |
+
dataset_id="braindecode/bcic-2a",
|
| 30 |
+
task_id="motor_imagery_4class",
|
| 31 |
+
display_name="BCIC-2a",
|
| 32 |
+
description="BCI Competition IV Dataset 2a - 4-class motor imagery classification.",
|
| 33 |
+
category="Motor Imagery",
|
| 34 |
+
num_classes=4,
|
| 35 |
+
accuracy_field="bcic2a_accuracy",
|
| 36 |
+
),
|
| 37 |
+
"physionet": BenchmarkInfo(
|
| 38 |
+
dataset_id="braindecode/physionet-mi",
|
| 39 |
+
task_id="motor_imagery_4class",
|
| 40 |
+
display_name="PhysioNet MI",
|
| 41 |
+
description="PhysioNet Motor Imagery dataset - 4-class motor imagery classification.",
|
| 42 |
+
category="Motor Imagery",
|
| 43 |
+
num_classes=4,
|
| 44 |
+
accuracy_field="physionet_accuracy",
|
| 45 |
+
),
|
| 46 |
+
"isruc_sleep": BenchmarkInfo(
|
| 47 |
+
dataset_id="braindecode/isruc-sleep",
|
| 48 |
+
task_id="sleep_staging_5class",
|
| 49 |
+
display_name="ISRUC-SLEEP",
|
| 50 |
+
description="ISRUC-SLEEP dataset - 5-class sleep staging classification.",
|
| 51 |
+
category="Sleep Staging",
|
| 52 |
+
num_classes=5,
|
| 53 |
+
accuracy_field="isruc_sleep_accuracy",
|
| 54 |
+
),
|
| 55 |
+
"tuab": BenchmarkInfo(
|
| 56 |
+
dataset_id="braindecode/tuab",
|
| 57 |
+
task_id="pathology_binary",
|
| 58 |
+
display_name="TUAB",
|
| 59 |
+
description="Temple University Abnormal EEG corpus - binary pathology detection.",
|
| 60 |
+
category="Pathology Detection",
|
| 61 |
+
num_classes=2,
|
| 62 |
+
accuracy_field="tuab_accuracy",
|
| 63 |
+
),
|
| 64 |
+
"tuev": BenchmarkInfo(
|
| 65 |
+
dataset_id="braindecode/tuev",
|
| 66 |
+
task_id="event_classification_6class",
|
| 67 |
+
display_name="TUEV",
|
| 68 |
+
description="Temple University EEG Events corpus - 6-class event classification.",
|
| 69 |
+
category="Pathology Detection",
|
| 70 |
+
num_classes=6,
|
| 71 |
+
accuracy_field="tuev_accuracy",
|
| 72 |
+
),
|
| 73 |
+
"chbmit": BenchmarkInfo(
|
| 74 |
+
dataset_id="braindecode/chb-mit",
|
| 75 |
+
task_id="seizure_detection_binary",
|
| 76 |
+
display_name="CHB-MIT",
|
| 77 |
+
description="CHB-MIT Scalp EEG dataset - binary seizure detection.",
|
| 78 |
+
category="Seizure Detection",
|
| 79 |
+
num_classes=2,
|
| 80 |
+
accuracy_field="chbmit_accuracy",
|
| 81 |
+
),
|
| 82 |
+
"faced": BenchmarkInfo(
|
| 83 |
+
dataset_id="braindecode/faced",
|
| 84 |
+
task_id="emotion_recognition_9class",
|
| 85 |
+
display_name="FACED",
|
| 86 |
+
description="FACED dataset - 9-class emotion recognition from EEG.",
|
| 87 |
+
category="Emotion Recognition",
|
| 88 |
+
num_classes=9,
|
| 89 |
+
accuracy_field="faced_accuracy",
|
| 90 |
+
),
|
| 91 |
+
"seedv": BenchmarkInfo(
|
| 92 |
+
dataset_id="braindecode/seed-v",
|
| 93 |
+
task_id="emotion_recognition_5class",
|
| 94 |
+
display_name="SEED-V",
|
| 95 |
+
description="SEED-V dataset - 5-class emotion recognition from EEG.",
|
| 96 |
+
category="Emotion Recognition",
|
| 97 |
+
num_classes=5,
|
| 98 |
+
accuracy_field="seedv_accuracy",
|
| 99 |
+
),
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
EVALUATION_FRAMEWORK = "eeg-finetune-arena"
|
backend/app/services/leaderboard.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
| 1 |
from app.core.cache import cache_config
|
| 2 |
-
from datetime import datetime
|
| 3 |
from typing import List, Dict, Any
|
| 4 |
from pathlib import Path
|
| 5 |
import json
|
|
@@ -7,6 +6,7 @@ import datasets
|
|
| 7 |
from fastapi import HTTPException
|
| 8 |
import logging
|
| 9 |
from app.config.base import HF_ORGANIZATION
|
|
|
|
| 10 |
from app.core.formatting import LogFormatter
|
| 11 |
|
| 12 |
logger = logging.getLogger(__name__)
|
|
@@ -15,6 +15,16 @@ logger = logging.getLogger(__name__)
|
|
| 15 |
SAMPLE_DATA_PATH = Path(__file__).parent.parent / "data" / "sample_results.json"
|
| 16 |
|
| 17 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
class LeaderboardService:
|
| 19 |
def __init__(self):
|
| 20 |
pass
|
|
@@ -145,58 +155,17 @@ class LeaderboardService:
|
|
| 145 |
model_name = data.get("fullname", "Unknown")
|
| 146 |
logger.debug(LogFormatter.info(f"Transforming data for model: {model_name}"))
|
| 147 |
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
"bcic2a": {
|
| 160 |
-
"name": "BCIC-2a",
|
| 161 |
-
"value": data.get("bcic2a_accuracy", 0),
|
| 162 |
-
"normalized_score": data.get("bcic2a_accuracy", 0) * 100 if data.get("bcic2a_accuracy") else 0,
|
| 163 |
-
},
|
| 164 |
-
"physionet": {
|
| 165 |
-
"name": "PhysioNet MI",
|
| 166 |
-
"value": data.get("physionet_accuracy", 0),
|
| 167 |
-
"normalized_score": data.get("physionet_accuracy", 0) * 100 if data.get("physionet_accuracy") else 0,
|
| 168 |
-
},
|
| 169 |
-
"isruc_sleep": {
|
| 170 |
-
"name": "ISRUC-SLEEP",
|
| 171 |
-
"value": data.get("isruc_sleep_accuracy", 0),
|
| 172 |
-
"normalized_score": data.get("isruc_sleep_accuracy", 0) * 100 if data.get("isruc_sleep_accuracy") else 0,
|
| 173 |
-
},
|
| 174 |
-
"tuab": {
|
| 175 |
-
"name": "TUAB",
|
| 176 |
-
"value": data.get("tuab_accuracy", 0),
|
| 177 |
-
"normalized_score": data.get("tuab_accuracy", 0) * 100 if data.get("tuab_accuracy") else 0,
|
| 178 |
-
},
|
| 179 |
-
"tuev": {
|
| 180 |
-
"name": "TUEV",
|
| 181 |
-
"value": data.get("tuev_accuracy", 0),
|
| 182 |
-
"normalized_score": data.get("tuev_accuracy", 0) * 100 if data.get("tuev_accuracy") else 0,
|
| 183 |
-
},
|
| 184 |
-
"chbmit": {
|
| 185 |
-
"name": "CHB-MIT",
|
| 186 |
-
"value": data.get("chbmit_accuracy", 0),
|
| 187 |
-
"normalized_score": data.get("chbmit_accuracy", 0) * 100 if data.get("chbmit_accuracy") else 0,
|
| 188 |
-
},
|
| 189 |
-
"faced": {
|
| 190 |
-
"name": "FACED",
|
| 191 |
-
"value": data.get("faced_accuracy", 0),
|
| 192 |
-
"normalized_score": data.get("faced_accuracy", 0) * 100 if data.get("faced_accuracy") else 0,
|
| 193 |
-
},
|
| 194 |
-
"seedv": {
|
| 195 |
-
"name": "SEED-V",
|
| 196 |
-
"value": data.get("seedv_accuracy", 0),
|
| 197 |
-
"normalized_score": data.get("seedv_accuracy", 0) * 100 if data.get("seedv_accuracy") else 0,
|
| 198 |
-
},
|
| 199 |
-
}
|
| 200 |
|
| 201 |
features = {
|
| 202 |
"is_not_available_on_hub": data.get("Available on the hub", False),
|
|
|
|
| 1 |
from app.core.cache import cache_config
|
|
|
|
| 2 |
from typing import List, Dict, Any
|
| 3 |
from pathlib import Path
|
| 4 |
import json
|
|
|
|
| 6 |
from fastapi import HTTPException
|
| 7 |
import logging
|
| 8 |
from app.config.base import HF_ORGANIZATION
|
| 9 |
+
from app.config.benchmarks import EEG_BENCHMARKS
|
| 10 |
from app.core.formatting import LogFormatter
|
| 11 |
|
| 12 |
logger = logging.getLogger(__name__)
|
|
|
|
| 15 |
SAMPLE_DATA_PATH = Path(__file__).parent.parent / "data" / "sample_results.json"
|
| 16 |
|
| 17 |
|
| 18 |
+
def _build_entry_id(data: Dict[str, Any]) -> str:
|
| 19 |
+
"""Build a unique ID for a leaderboard entry."""
|
| 20 |
+
return (
|
| 21 |
+
f"{data.get('fullname', 'Unknown')}"
|
| 22 |
+
f"_{data.get('adapter', 'Unknown')}"
|
| 23 |
+
f"_{data.get('Precision', 'Unknown')}"
|
| 24 |
+
f"_{data.get('Model sha', 'Unknown')}"
|
| 25 |
+
)
|
| 26 |
+
|
| 27 |
+
|
| 28 |
class LeaderboardService:
|
| 29 |
def __init__(self):
|
| 30 |
pass
|
|
|
|
| 155 |
model_name = data.get("fullname", "Unknown")
|
| 156 |
logger.debug(LogFormatter.info(f"Transforming data for model: {model_name}"))
|
| 157 |
|
| 158 |
+
unique_id = _build_entry_id(data)
|
| 159 |
+
|
| 160 |
+
# EEG benchmark evaluations from the canonical registry
|
| 161 |
+
evaluations = {}
|
| 162 |
+
for key, benchmark in EEG_BENCHMARKS.items():
|
| 163 |
+
score = data.get(benchmark.accuracy_field, 0)
|
| 164 |
+
evaluations[key] = {
|
| 165 |
+
"name": benchmark.display_name,
|
| 166 |
+
"value": score,
|
| 167 |
+
"normalized_score": score * 100 if score else 0,
|
| 168 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 169 |
|
| 170 |
features = {
|
| 171 |
"is_not_available_on_hub": data.get("Available on the hub", False),
|
backend/pyproject.toml
CHANGED
|
@@ -19,6 +19,7 @@ safetensors = "^0.5.3"
|
|
| 19 |
aiofiles = "^24.1.0"
|
| 20 |
fastapi-cache2 = "^0.2.1"
|
| 21 |
python-dotenv = "^1.0.1"
|
|
|
|
| 22 |
|
| 23 |
[tool.poetry.group.dev.dependencies]
|
| 24 |
pytest = "^8.3.4"
|
|
|
|
| 19 |
aiofiles = "^24.1.0"
|
| 20 |
fastapi-cache2 = "^0.2.1"
|
| 21 |
python-dotenv = "^1.0.1"
|
| 22 |
+
pyyaml = "^6.0"
|
| 23 |
|
| 24 |
[tool.poetry.group.dev.dependencies]
|
| 25 |
pytest = "^8.3.4"
|
backend/scripts/__init__.py
ADDED
|
File without changes
|
backend/scripts/push_eval_yaml.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
One-time script to push eval.yaml files to benchmark dataset repos on HuggingFace.
|
| 3 |
+
|
| 4 |
+
This registers each EEG benchmark dataset with HF's decentralized eval system
|
| 5 |
+
by creating the dataset repo (if needed) and uploading an eval.yaml file.
|
| 6 |
+
|
| 7 |
+
Usage:
|
| 8 |
+
python -m scripts.push_eval_yaml
|
| 9 |
+
python -m scripts.push_eval_yaml --dry-run
|
| 10 |
+
python -m scripts.push_eval_yaml --benchmark bcic2a
|
| 11 |
+
"""
|
| 12 |
+
|
| 13 |
+
import argparse
|
| 14 |
+
import sys
|
| 15 |
+
import logging
|
| 16 |
+
|
| 17 |
+
import yaml
|
| 18 |
+
|
| 19 |
+
from app.config.base import HF_TOKEN
|
| 20 |
+
from app.config.benchmarks import EEG_BENCHMARKS, EVALUATION_FRAMEWORK
|
| 21 |
+
from app.config.hf_config import API as hf_api
|
| 22 |
+
from app.core.formatting import LogFormatter
|
| 23 |
+
|
| 24 |
+
logging.basicConfig(level=logging.INFO)
|
| 25 |
+
logger = logging.getLogger(__name__)
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def generate_eval_yaml(benchmark) -> str:
|
| 29 |
+
"""Generate eval.yaml content for a benchmark dataset."""
|
| 30 |
+
eval_config = {
|
| 31 |
+
"name": f"{benchmark.display_name} {benchmark.category}",
|
| 32 |
+
"description": benchmark.description,
|
| 33 |
+
"evaluation_framework": EVALUATION_FRAMEWORK,
|
| 34 |
+
"tasks": [
|
| 35 |
+
{
|
| 36 |
+
"id": benchmark.task_id,
|
| 37 |
+
"config": benchmark.config,
|
| 38 |
+
"split": benchmark.split,
|
| 39 |
+
}
|
| 40 |
+
],
|
| 41 |
+
}
|
| 42 |
+
return yaml.dump(eval_config, default_flow_style=False, sort_keys=False)
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def push_eval_yaml(
|
| 46 |
+
benchmark_key: str = None,
|
| 47 |
+
dry_run: bool = False,
|
| 48 |
+
):
|
| 49 |
+
"""Push eval.yaml to benchmark dataset repos.
|
| 50 |
+
|
| 51 |
+
Args:
|
| 52 |
+
benchmark_key: If specified, only push for this benchmark.
|
| 53 |
+
dry_run: If True, print what would happen without pushing.
|
| 54 |
+
"""
|
| 55 |
+
benchmarks = EEG_BENCHMARKS
|
| 56 |
+
if benchmark_key:
|
| 57 |
+
if benchmark_key not in benchmarks:
|
| 58 |
+
logger.error(f"Unknown benchmark key: {benchmark_key}")
|
| 59 |
+
logger.info(f"Available keys: {list(benchmarks.keys())}")
|
| 60 |
+
sys.exit(1)
|
| 61 |
+
benchmarks = {benchmark_key: benchmarks[benchmark_key]}
|
| 62 |
+
|
| 63 |
+
logger.info(LogFormatter.section("PUSHING EVAL.YAML TO BENCHMARK DATASETS"))
|
| 64 |
+
|
| 65 |
+
for key, benchmark in benchmarks.items():
|
| 66 |
+
dataset_id = benchmark.dataset_id
|
| 67 |
+
logger.info(LogFormatter.subsection(f"Processing: {dataset_id}"))
|
| 68 |
+
|
| 69 |
+
eval_yaml_content = generate_eval_yaml(benchmark)
|
| 70 |
+
|
| 71 |
+
if dry_run:
|
| 72 |
+
logger.info(f"[DRY RUN] Would create repo: {dataset_id} (type=dataset)")
|
| 73 |
+
logger.info(f"[DRY RUN] Would upload eval.yaml:\n{eval_yaml_content}")
|
| 74 |
+
continue
|
| 75 |
+
|
| 76 |
+
# Create the dataset repo if it doesn't exist
|
| 77 |
+
try:
|
| 78 |
+
hf_api.create_repo(
|
| 79 |
+
repo_id=dataset_id,
|
| 80 |
+
repo_type="dataset",
|
| 81 |
+
exist_ok=True,
|
| 82 |
+
)
|
| 83 |
+
logger.info(LogFormatter.success(f"Repo ready: {dataset_id}"))
|
| 84 |
+
except Exception as e:
|
| 85 |
+
logger.error(LogFormatter.error(f"Failed to create repo {dataset_id}", e))
|
| 86 |
+
continue
|
| 87 |
+
|
| 88 |
+
# Upload eval.yaml
|
| 89 |
+
try:
|
| 90 |
+
hf_api.upload_file(
|
| 91 |
+
path_or_fileobj=eval_yaml_content.encode("utf-8"),
|
| 92 |
+
path_in_repo="eval.yaml",
|
| 93 |
+
repo_id=dataset_id,
|
| 94 |
+
repo_type="dataset",
|
| 95 |
+
commit_message="Add eval.yaml for EEG Finetune Arena benchmark registration",
|
| 96 |
+
)
|
| 97 |
+
logger.info(LogFormatter.success(f"Uploaded eval.yaml to {dataset_id}"))
|
| 98 |
+
except Exception as e:
|
| 99 |
+
logger.error(
|
| 100 |
+
LogFormatter.error(f"Failed to upload eval.yaml to {dataset_id}", e)
|
| 101 |
+
)
|
| 102 |
+
|
| 103 |
+
logger.info(LogFormatter.section("DONE"))
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def main():
|
| 107 |
+
parser = argparse.ArgumentParser(
|
| 108 |
+
description="Push eval.yaml files to HF benchmark dataset repos."
|
| 109 |
+
)
|
| 110 |
+
parser.add_argument(
|
| 111 |
+
"--dry-run",
|
| 112 |
+
action="store_true",
|
| 113 |
+
help="Preview without pushing to HF.",
|
| 114 |
+
)
|
| 115 |
+
parser.add_argument(
|
| 116 |
+
"--benchmark",
|
| 117 |
+
type=str,
|
| 118 |
+
default=None,
|
| 119 |
+
help="Only push for a specific benchmark key (e.g. bcic2a).",
|
| 120 |
+
)
|
| 121 |
+
args = parser.parse_args()
|
| 122 |
+
|
| 123 |
+
if not HF_TOKEN and not args.dry_run:
|
| 124 |
+
logger.error("HF_TOKEN environment variable is required (set it or use --dry-run)")
|
| 125 |
+
sys.exit(1)
|
| 126 |
+
|
| 127 |
+
push_eval_yaml(benchmark_key=args.benchmark, dry_run=args.dry_run)
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
if __name__ == "__main__":
|
| 131 |
+
main()
|