ICBCBench-Leaderboard / tabs /shared_data.py
Leonnel1220's picture
Upload folder using huggingface_hub
5148820 verified
Raw
History Blame Contribute Delete
8.06 kB
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Lazy-loaded shared data cache for data viewer tabs.
Loads data_viewer.jsonl once on first access, not at import time.
"""
from __future__ import annotations
import json
import pandas as pd
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
# Bucket mount point (HF Storage Bucket mounted at /data in Space runtime)
_BUCKET_DIR = Path("/data")
# Prefer bucket path if available, fallback to repo-local path
DATA_VIEWER_FILE = (
_BUCKET_DIR / "data_viewer.jsonl"
if (_BUCKET_DIR / "data_viewer.jsonl").exists()
else BASE_DIR / "data" / "data_viewer.jsonl"
)
DATA_VIEWER_INDEX_FILE = BASE_DIR / "data" / "data_viewer_index.json"
# ICBCBench data viewer schema: dual-track scores + optional legacy DeepResearch Bench fields
_REQUIRED_COLS = [
"model_name", "id", "prompt", "article",
"overall_score", "track", # track: "objective" or "subjective"
]
_OPTIONAL_SCORE_COLS = [
"objective_score", "subjective_score",
"expert_score", "citation_score", "source_quality_score",
"confidence", "correct",
# Legacy DeepResearch Bench fields (kept for backward compatibility)
"comprehensiveness_score", "insight_score",
"instruction_following_score", "readability_score",
]
_cache: pd.DataFrame | None = None
_index_cache: dict | None = None
def _normalize_record(item: dict) -> dict:
"""Ensure every record has the ICBCBench score fields, falling back to legacy fields."""
record = dict(item)
# Determine track
if "track" not in record or not record["track"]:
# Heuristic: if article is present and non-empty, treat as subjective
article = record.get("article") or ""
record["track"] = "subjective" if isinstance(article, str) and article.strip() else "objective"
# Normalize scores to 0-100 scale if they look like ratios
for key in ["overall_score", "objective_score", "subjective_score",
"expert_score", "citation_score", "source_quality_score",
"comprehensiveness_score", "insight_score",
"instruction_following_score", "readability_score"]:
val = record.get(key)
if isinstance(val, (int, float)) and val is not None and val <= 1.0:
record[key] = val * 100
# If the new ICBCBench fields are missing but legacy fields exist, synthesize them.
if record.get("subjective_score") is None:
legacy = []
for k in ["comprehensiveness_score", "insight_score",
"instruction_following_score", "readability_score"]:
val = record.get(k)
if val is None:
continue
try:
legacy.append(float(val))
except (TypeError, ValueError):
pass
if legacy:
record["subjective_score"] = sum(legacy) / len(legacy)
if record.get("overall_score") is None:
candidates = []
for k in ["objective_score", "subjective_score"]:
val = record.get(k)
if val is None:
continue
try:
candidates.append(float(val))
except (TypeError, ValueError):
pass
if candidates:
record["overall_score"] = sum(candidates) / len(candidates)
return record
def get_data() -> pd.DataFrame:
global _cache
if _cache is not None:
return _cache
records = []
if DATA_VIEWER_FILE.exists():
with DATA_VIEWER_FILE.open(encoding="utf-8") as fh:
for line in fh:
line = line.strip()
if not line:
continue
try:
records.append(_normalize_record(json.loads(line)))
except json.JSONDecodeError:
continue
df = pd.DataFrame(records)
if df.empty or not all(c in df.columns for c in _REQUIRED_COLS):
_cache = pd.DataFrame(columns=_REQUIRED_COLS + _OPTIONAL_SCORE_COLS)
else:
df["id"] = df["id"].astype(str)
_cache = df
return _cache
def get_index() -> dict:
global _index_cache
if _index_cache is not None:
return _index_cache
if DATA_VIEWER_INDEX_FILE.exists():
try:
_index_cache = json.loads(DATA_VIEWER_INDEX_FILE.read_text(encoding="utf-8"))
return _index_cache
except json.JSONDecodeError:
pass
models = set()
tasks = {}
if DATA_VIEWER_FILE.exists():
with DATA_VIEWER_FILE.open(encoding="utf-8") as fh:
for line in fh:
if not line.strip():
continue
try:
item = json.loads(line)
except json.JSONDecodeError:
continue
item = _normalize_record(item)
model = item.get("model_name")
item_id = str(item.get("id"))
prompt = item.get("prompt") or ""
if model:
models.add(model)
if item_id and item_id not in tasks:
tasks[item_id] = prompt
_index_cache = {
"models": sorted(models),
"tasks": [
{"id": item_id, "prompt": tasks[item_id]}
for item_id in sorted(tasks, key=lambda value: int(value) if value.isdigit() else value)
],
}
return _index_cache
def get_entry(model_name: str, item_id: str) -> dict | None:
if not model_name or not item_id or not DATA_VIEWER_FILE.exists():
return None
item_id = str(item_id)
index = get_index()
location = index.get("lookup", {}).get(f"{model_name}\t{item_id}")
if location:
offset, length = location
with DATA_VIEWER_FILE.open("rb") as fh:
fh.seek(offset)
line = fh.read(length).decode("utf-8")
try:
item = _normalize_record(json.loads(line))
if item.get("model_name") == model_name and str(item.get("id")) == item_id:
return item
except json.JSONDecodeError:
pass
with DATA_VIEWER_FILE.open(encoding="utf-8") as fh:
for line in fh:
if not line.strip():
continue
try:
item = _normalize_record(json.loads(line))
except json.JSONDecodeError:
continue
if item.get("model_name") == model_name and str(item.get("id")) == item_id:
return item
return None
def get_entries_for_task(item_id: str, model_names: set[str]) -> dict[str, dict]:
if not item_id or not model_names or not DATA_VIEWER_FILE.exists():
return {}
item_id = str(item_id)
index = get_index()
locations = {
model: index.get("lookup", {}).get(f"{model}\t{item_id}")
for model in model_names
}
locations = {model: loc for model, loc in locations.items() if loc}
if locations:
found = {}
with DATA_VIEWER_FILE.open("rb") as fh:
for model, (offset, length) in locations.items():
fh.seek(offset)
try:
item = _normalize_record(json.loads(fh.read(length).decode("utf-8")))
if item.get("model_name") == model and str(item.get("id")) == item_id:
found[model] = item
except json.JSONDecodeError:
pass
if len(found) == len(locations):
return found
found = {}
with DATA_VIEWER_FILE.open(encoding="utf-8") as fh:
for line in fh:
if not line.strip():
continue
try:
item = _normalize_record(json.loads(line))
except json.JSONDecodeError:
continue
model = item.get("model_name")
if str(item.get("id")) == item_id and model in model_names:
found[model] = item
if len(found) == len(model_names):
break
return found