import json import os import shutil import tempfile import zipfile from dataclasses import dataclass from src.about import MMRED_GROUP_KEYS, MMRED_SUBTASK_KEYS, Tasks from src.leaderboard.metrics import extract_exact_match_max_flexible_strict, extract_metric_value LOGS_PUBLIC_ZIP = "logs_public.zip" LOGS_PUBLIC_DIR = "logs_public" RESULTS_PREFIX = "results_" @dataclass class ParsedResultFile: path: str benchmark: str data: dict TASK_KEY_ALIASES: dict[str, set[str]] = { task.value.benchmark: {task.value.benchmark, task.value.col_name.lower()} for task in Tasks } TASK_KEY_ALIASES["t_math"].update({"tmath", "t-math"}) for task in Tasks: benchmark = task.value.benchmark TASK_KEY_ALIASES[benchmark].add(benchmark.replace("_", "")) def _normalize_task_key(task_key: str) -> str: key = task_key.lower().removesuffix("_gen") return key.replace("-", "_") def _benchmark_for_task_key(task_key: str) -> str | None: normalized = _normalize_task_key(task_key) if normalized == "tmath": return "t_math" if normalized == "mmred" or normalized.startswith("mmred_"): return "mmred" for benchmark, aliases in TASK_KEY_ALIASES.items(): alias_norm = {_normalize_task_key(alias) for alias in aliases} if normalized in alias_norm or normalized.startswith(benchmark): return benchmark return None def _submission_name_candidates(task) -> set[str]: benchmark = task.value.benchmark col_name = task.value.col_name return { benchmark.lower(), col_name.lower(), benchmark.replace("_", "").lower(), col_name.replace("-", "").lower(), col_name.replace("-", "_").lower(), "tmath", } BENCHMARKS_MAX_EXACT_MATCH = {"ruaime", "luzitania"} def _extract_exact_match_score(benchmark: str, task_result: dict) -> float | None: if benchmark in BENCHMARKS_MAX_EXACT_MATCH: return extract_exact_match_max_flexible_strict(task_result) return extract_metric_value(task_result, "exact_match") def _extract_mmred_scores(task_key: str, task_result: dict, scores: dict[str, float]) -> None: normalized = _normalize_task_key(task_key) if normalized == "mmred": value = extract_metric_value(task_result, "em.dc_aggregate") if value is not None: scores["em.dc_aggregate"] = value return if normalized in MMRED_GROUP_KEYS: value = extract_metric_value(task_result, "em.dc_aggregate") if value is not None: scores[f"{normalized}::em.dc_aggregate"] = value return if normalized in MMRED_SUBTASK_KEYS: value = extract_metric_value(task_result, "exact_match") if value is not None: scores[f"{normalized}::exact_match"] = value def _extract_benchmark_scores(task_key: str, task_result: dict) -> dict[str, float]: benchmark = _benchmark_for_task_key(task_key) if benchmark is None: return {} if benchmark == "mmred": scores: dict[str, float] = {} _extract_mmred_scores(task_key, task_result, scores) return scores value = _extract_exact_match_score(benchmark, task_result) if value is None: return {} return {"exact_match": value} def parse_results_data(data: dict) -> dict[str, dict[str, float]]: merged: dict[str, dict[str, float]] = {} for task_key, task_result in data.get("results", {}).items(): if not isinstance(task_result, dict): continue benchmark = _benchmark_for_task_key(task_key) if benchmark is None: continue scores = _extract_benchmark_scores(task_key, task_result) if scores: merged.setdefault(benchmark, {}).update(scores) return merged def get_scores_from_result(parsed: ParsedResultFile) -> dict[str, float]: return parse_results_data(parsed.data).get(parsed.benchmark, {}) def extract_submission_archive(zip_path: str) -> str: if not zip_path or not os.path.isfile(zip_path): raise ValueError("Please upload a valid zip submission file.") if not zipfile.is_zipfile(zip_path): raise ValueError("Uploaded file is not a zip archive.") extract_dir = tempfile.mkdtemp(prefix="mera_reason_submission_") with zipfile.ZipFile(zip_path, "r") as archive: archive.extractall(extract_dir) return extract_dir def _find_submission_root(extract_dir: str) -> str: entries = [ name for name in os.listdir(extract_dir) if not name.startswith(".") and name not in ("__MACOSX",) ] if len(entries) == 1: only_entry = os.path.join(extract_dir, entries[0]) if os.path.isdir(only_entry): return only_entry return extract_dir def _resolve_logs_public_dir(submission_root: str) -> str: nested_zip = os.path.join(submission_root, LOGS_PUBLIC_ZIP) nested_dir = os.path.join(submission_root, LOGS_PUBLIC_DIR) if os.path.isfile(nested_zip): extract_dir = tempfile.mkdtemp(prefix="mera_reason_logs_public_") with zipfile.ZipFile(nested_zip, "r") as archive: archive.extractall(extract_dir) return extract_dir if os.path.isdir(nested_dir): return nested_dir raise ValueError( f"Submission must contain `{LOGS_PUBLIC_ZIP}` or `{LOGS_PUBLIC_DIR}/` with evaluation logs." ) def _collect_submission_json_files(submission_root: str) -> dict[str, str]: files = {} for name in os.listdir(submission_root): if not name.endswith(".json") or name == LOGS_PUBLIC_ZIP: continue path = os.path.join(submission_root, name) if os.path.isfile(path): files[os.path.splitext(name)[0].lower()] = path return files def validate_submission_files(submission_root: str) -> list[str]: submission_files = _collect_submission_json_files(submission_root) missing = [] for task in Tasks: candidates = _submission_name_candidates(task) if not any(candidate in submission_files for candidate in candidates): missing.append(task.value.col_name) return missing def _iter_results_files(*roots: str): seen = set() for root in roots: if not os.path.isdir(root): continue for name in sorted(os.listdir(root)): if not name.startswith(RESULTS_PREFIX) or not name.endswith(".json"): continue path = os.path.join(root, name) if path in seen: continue seen.add(path) yield path def _count_results_files(*roots: str) -> int: count = 0 for root in roots: if not os.path.isdir(root): continue for name in os.listdir(root): if name.startswith(RESULTS_PREFIX) and name.endswith(".json"): count += 1 return count def parse_logs_public_results(logs_public_dir: str, submission_root: str | None = None) -> dict[str, dict[str, float]]: merged: dict[str, dict[str, float]] = {} roots = [logs_public_dir] if submission_root: roots.append(submission_root) for path in _iter_results_files(*roots): with open(path, encoding="utf-8") as fp: data = json.load(fp) for benchmark, scores in parse_results_data(data).items(): merged.setdefault(benchmark, {}).update(scores) return merged def _fill_missing_benchmarks( parsed_results: dict[str, dict[str, float]], ) -> tuple[dict[str, dict[str, float]], list[str]]: """Ensure all benchmarks are present; missing tasks score 0.""" complete: dict[str, dict[str, float]] = {} missing_names: list[str] = [] for task in Tasks: benchmark = task.value.benchmark primary = task.value.primary_metric raw_scores = parsed_results.get(benchmark, {}) if raw_scores and primary in raw_scores: complete[benchmark] = raw_scores continue if benchmark not in parsed_results or not raw_scores: missing_names.append(task.value.col_name) if raw_scores: complete[benchmark] = {primary: 0.0, **raw_scores} else: complete[benchmark] = {primary: 0.0} return complete, missing_names def build_normalized_result_payload( results: dict[str, dict[str, float]], model_name: str, team: str, ) -> dict: return { "model": model_name, "team": team, "results": results, } def process_submission_zip( zip_path: str, model_name: str, team: str, ) -> tuple[dict, dict[str, dict[str, float]], list[str], list[str]]: extract_dir = extract_submission_archive(zip_path) logs_public_dir = None try: submission_root = _find_submission_root(extract_dir) missing_files = validate_submission_files(submission_root) logs_public_dir = _resolve_logs_public_dir(submission_root) parsed_results = parse_logs_public_results(logs_public_dir, submission_root) if not parsed_results: roots = [logs_public_dir] if submission_root: roots.append(submission_root) has_results = _count_results_files(*roots) > 0 if has_results: detail = "Found results_*.json files but could not extract leaderboard metrics from them." else: detail = ( "logs_public contains no results_*.json files (only samples are not enough). " "Re-pack the submission with log_to_reasoning_submission.py after evaluation." ) raise ValueError( f"No leaderboard task scores found in logs_public. {detail} " f"Expected results for: {', '.join(t.value.col_name for t in Tasks)}." ) parsed_results, missing_benchmarks = _fill_missing_benchmarks(parsed_results) resolved_model = model_name.strip() if not resolved_model: raise ValueError("Model name is required.") payload = build_normalized_result_payload(parsed_results, resolved_model, team) return payload, parsed_results, missing_files, missing_benchmarks finally: shutil.rmtree(extract_dir, ignore_errors=True) if logs_public_dir and logs_public_dir.startswith(tempfile.gettempdir()): shutil.rmtree(logs_public_dir, ignore_errors=True)