import base64 import inspect import json import os import threading from html import escape from datetime import date from pathlib import Path from typing import Any os.environ.setdefault("GRADIO_SSR_MODE", "false") os.environ.setdefault("GRADIO_ANALYTICS_ENABLED", "False") os.environ.setdefault("HF_HUB_DISABLE_TELEMETRY", "1") import gradio as gr import pandas as pd from huggingface_hub import CommitOperationAdd, HfApi, hf_hub_download from huggingface_hub.errors import HfHubHTTPError ROOT = Path(__file__).resolve().parent DATASETS_PATH = ROOT/"data"/"datasets.json" LOGO_PATH = ROOT/"assets"/"rusBEIR_logo.png" RESULTS_PATH = Path(os.getenv("RUSBEIR_RESULTS_PATH", ROOT/"data"/"results.jsonl")) RESULTS_REPO_ID = os.getenv("RUSBEIR_RESULTS_REPO_ID") or os.getenv("SPACE_ID") RESULTS_REPO_TYPE = os.getenv("RUSBEIR_RESULTS_REPO_TYPE", "space") RESULTS_REPO_PATH = os.getenv("RUSBEIR_RESULTS_REPO_PATH", "data/results.jsonl") RESULTS_REVISION = os.getenv("RUSBEIR_RESULTS_REVISION", "main") HF_TOKEN = os.getenv("HF_TOKEN") RESULTS_LOCK = threading.Lock() DEFAULT_METRIC = "NDCG@10" METRICS = ["NDCG@10", "MAP@10", "Recall@10", "P@10", "MRR@10"] STATIC_COLUMNS = ["Rank", "Model"] META_COLUMNS = ["Model ID", "Organization", "Type", "Verified", "Date", "Source URL", ] TRAILING_COLUMNS = META_COLUMNS DISPLAY_COLUMN_NAMES = { "Model ID": "Model\nID", "Organization": "Org.", "Source URL": "Source\nURL", "sberquad-retrieval": "sberquad\nretrieval", "ruscibench-retrieval": "ruscibench\nretrieval", "wikifacts-articles": "wikifacts\narticles", "wikifacts-para": "wikifacts\npara", "wikifacts-sents": "wikifacts\nsents", "wikifacts-window_2": "wikifacts\nwindow 2", "wikifacts-window_3": "wikifacts\nwindow 3", "wikifacts-window_4": "wikifacts\nwindow 4", "wikifacts-window_5": "wikifacts\nwindow 5", "wikifacts-window_6": "wikifacts\nwindow 6", "legal_search_2004": "legal_search\n2004", "legal_search_2007": "legal_search\n2007" } CUSTOM_CSS = """ :root { --rusbeir-bg: #f7f8fb; --rusbeir-card: #ffffff; --rusbeir-text: #111827; --rusbeir-muted: #64748b; --rusbeir-line: #e2e8f0; --rusbeir-accent: #b45309; --rusbeir-accent-soft: #fff7ed; --rusbeir-green: #047857; --rusbeir-soft: #f8fafc; --rusbeir-table-head: #f8fafc; --rusbeir-table-alt: #fcfcfd; --rusbeir-table-border: #edf2f7; --rusbeir-shadow: rgba(15, 23, 42, 0.06); --rusbeir-panel-shadow: rgba(15, 23, 42, 0.04); } @media (prefers-color-scheme: dark) { :root { --rusbeir-bg: #0f1117; --rusbeir-card: #1f2028; --rusbeir-text: #f3f4f6; --rusbeir-muted: #c1c7d0; --rusbeir-line: #3f424c; --rusbeir-accent: #f59e0b; --rusbeir-accent-soft: #322719; --rusbeir-green: #34d399; --rusbeir-soft: #272933; --rusbeir-table-head: #272933; --rusbeir-table-alt: #23252e; --rusbeir-table-border: #383b46; --rusbeir-shadow: rgba(0, 0, 0, 0.25); --rusbeir-panel-shadow: rgba(0, 0, 0, 0.18); } } .dark, body.dark, [data-theme="dark"] { --rusbeir-bg: #0f1117; --rusbeir-card: #1f2028; --rusbeir-text: #f3f4f6; --rusbeir-muted: #c1c7d0; --rusbeir-line: #3f424c; --rusbeir-accent: #f59e0b; --rusbeir-accent-soft: #322719; --rusbeir-green: #34d399; --rusbeir-soft: #272933; --rusbeir-table-head: #272933; --rusbeir-table-alt: #23252e; --rusbeir-table-border: #383b46; --rusbeir-shadow: rgba(0, 0, 0, 0.25); --rusbeir-panel-shadow: rgba(0, 0, 0, 0.18); } body, .gradio-container { background: var(--rusbeir-bg) !important; color: var(--rusbeir-text) !important; font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important; } html, body { overflow-x: hidden !important; } .gradio-container { width: 100% !important; max-width: 1440px !important; min-width: 0 !important; margin: 0 auto !important; padding: 22px !important; box-sizing: border-box !important; } .rusbeir-shell { display: flex; flex-direction: column; gap: 18px; min-width: 0; width: 100%; } .rusbeir-hero { background: var(--rusbeir-card); border: 1px solid var(--rusbeir-line); border-radius: 18px; padding: 24px; box-shadow: 0 12px 32px var(--rusbeir-shadow); min-width: 0; display: grid; grid-template-columns: 1fr auto; gap: 18px; align-items: start; } .rusbeir-hero-copy { min-width: 0; } .rusbeir-logo { width: 148px; max-width: 24vw; height: auto; object-fit: contain; } .rusbeir-kicker { color: var(--rusbeir-accent); font-size: 12px; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase; margin-bottom: 8px; } .rusbeir-title { font-size: 42px; line-height: 1.05; font-weight: 850; letter-spacing: 0; margin: 0 0 10px; overflow-wrap: anywhere; } .rusbeir-subtitle { color: var(--rusbeir-muted); font-size: 16px; line-height: 1.55; max-width: 860px; margin: 0; overflow-wrap: anywhere; } .rusbeir-badges { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 18px; } .rusbeir-badge { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--rusbeir-line); border-radius: 999px; background: var(--rusbeir-soft); color: var(--rusbeir-text); padding: 6px 10px; font-size: 13px; font-weight: 650; } .rusbeir-cards { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; } .rusbeir-card { background: var(--rusbeir-card); border: 1px solid var(--rusbeir-line); border-radius: 16px; padding: 16px; box-shadow: 0 8px 24px var(--rusbeir-panel-shadow); min-width: 0; } .rusbeir-card-label { color: var(--rusbeir-muted); font-size: 12px; font-weight: 750; text-transform: uppercase; letter-spacing: 0.06em; } .rusbeir-card-value { color: var(--rusbeir-text); font-size: 28px; line-height: 1.15; font-weight: 820; margin-top: 8px; overflow-wrap: anywhere; } .rusbeir-card-note { color: var(--rusbeir-muted); font-size: 13px; margin-top: 6px; } .rusbeir-panel { background: var(--rusbeir-card); border: 1px solid var(--rusbeir-line); border-radius: 16px; padding: 16px; box-shadow: 0 8px 24px var(--rusbeir-panel-shadow); min-width: 0; overflow-x: visible; } .rusbeir-panel > *, .rusbeir-panel .block, .rusbeir-panel .wrap, .rusbeir-panel .form, .rusbeir-panel .container { min-width: 0 !important; } .rusbeir-filters, .rusbeir-filters .block, .rusbeir-filters .form, .rusbeir-filters .wrap, .rusbeir-filters .container, .rusbeir-filters .input-container, .rusbeir-filters .input-wrapper, .rusbeir-filters .secondary-wrap, .rusbeir-filters fieldset, .rusbeir-filters label, .rusbeir-filters input, .rusbeir-filters textarea, .rusbeir-filters select, .rusbeir-filters button { border-radius: 0 !important; } .rusbeir-verified-filter { align-self: stretch !important; } .rusbeir-verified-filter .wrap, .rusbeir-verified-filter .block, .rusbeir-verified-filter label { height: 100% !important; } .rusbeir-verified-filter label { display: flex !important; align-items: center !important; padding-top: 30px !important; box-sizing: border-box !important; } .rusbeir-section-title { color: var(--rusbeir-text); font-size: 18px; font-weight: 800; margin: 0 0 4px; } .rusbeir-section-note { color: var(--rusbeir-muted); font-size: 13px; margin: 0 0 14px; } .rusbeir-citation { margin: 10px 0 0; padding: 14px; border: 1px solid var(--rusbeir-line); background: var(--rusbeir-soft); color: var(--rusbeir-text); overflow-x: auto; white-space: pre-wrap; font-size: 12px; line-height: 1.45; } .rusbeir-table-scroll { width: 100%; max-height: 720px; overflow: auto; border: 1px solid var(--rusbeir-line); border-radius: 14px; background: var(--rusbeir-card); } .rusbeir-table { border-collapse: separate; border-spacing: 0; border: 0 !important; min-width: 100%; width: max-content; table-layout: fixed; font-size: 13px; } .rusbeir-table th, .rusbeir-table td { border: 0 !important; border-right: 1px solid var(--rusbeir-line) !important; border-bottom: 1px solid var(--rusbeir-table-border) !important; padding: 10px 10px; color: var(--rusbeir-text); background: var(--rusbeir-card); vertical-align: middle; overflow-wrap: anywhere; } .rusbeir-table th { position: sticky; top: 0; z-index: 4; background: var(--rusbeir-table-head); color: var(--rusbeir-text); font-weight: 800; white-space: normal; line-height: 1.15; vertical-align: bottom; border-bottom: 1px solid var(--rusbeir-line) !important; } .rusbeir-sort-button { width: 100%; min-height: auto !important; border: 0 !important; border-radius: 0 !important; background: transparent !important; color: inherit !important; box-shadow: none !important; padding: 0 !important; font: inherit !important; font-weight: inherit !important; line-height: inherit !important; text-align: inherit !important; cursor: pointer; } .rusbeir-sort-button:hover { color: #b45309 !important; } .rusbeir-sort-indicator { color: #b45309; font-size: 11px; font-weight: 800; } .rusbeir-table td { height: 48px; font-weight: 650; } .rusbeir-table tr:nth-child(even) td { background: var(--rusbeir-table-alt); } .rusbeir-table .col-rank { width: 58px; min-width: 58px; max-width: 58px; text-align: center; } .rusbeir-table .col-model { width: 260px; min-width: 260px; max-width: 260px; } .rusbeir-table .col-average { width: 104px; min-width: 104px; max-width: 104px; text-align: right; } .rusbeir-table .col-meta { width: 110px; min-width: 110px; max-width: 110px; } .rusbeir-table .col-model-id { width: 260px; min-width: 260px; max-width: 260px; } .rusbeir-table .col-dataset { width: 96px; min-width: 96px; max-width: 96px; text-align: right; } .rusbeir-table .col-date { width: 112px; min-width: 112px; max-width: 112px; } .rusbeir-table .col-source { width: 220px; min-width: 220px; max-width: 220px; } .rusbeir-table .sticky-rank, .rusbeir-table .sticky-model, .rusbeir-table .sticky-average { position: sticky; z-index: 3; } .rusbeir-table th.sticky-rank, .rusbeir-table th.sticky-model, .rusbeir-table th.sticky-average { z-index: 6; } .rusbeir-table .sticky-rank { left: 0; } .rusbeir-table .sticky-model { left: 58px; } .rusbeir-table .sticky-average { left: 318px; box-shadow: 8px 0 12px var(--rusbeir-shadow); } .rusbeir-source-link { color: #b45309; text-decoration: none; font-weight: 700; } @media (prefers-color-scheme: dark) { .rusbeir-source-link { color: #f59e0b; } } .dark .rusbeir-source-link, body.dark .rusbeir-source-link, [data-theme="dark"] .rusbeir-source-link { color: #f59e0b; } .rusbeir-empty { color: var(--rusbeir-muted); padding: 18px; } button { font-weight: 700 !important; } .tabs { border: 0 !important; } @media (max-width: 900px) { .gradio-container { padding: 12px !important; } .rusbeir-title { font-size: 30px; } .rusbeir-hero { grid-template-columns: 1fr; } .rusbeir-logo { width: 120px; max-width: 100%; } .rusbeir-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); } } @media (max-width: 560px) { .rusbeir-cards { grid-template-columns: 1fr; } } """ CUSTOM_JS = """ window.rusbeirSortTable = function(button) { const header = button.closest("th"); const table = button.closest("table"); const body = table?.querySelector("tbody"); if (!header || !table || !body) return; const headers = Array.from(header.parentElement.children); const index = headers.indexOf(header); const sortType = header.dataset.sortType || "text"; const previousIndex = table.dataset.sortIndex; const previousDirection = table.dataset.sortDirection; const nextDirection = previousIndex === String(index) && previousDirection === "desc" ? "asc" : "desc"; table.dataset.sortIndex = String(index); table.dataset.sortDirection = nextDirection; headers.forEach((item) => { const indicator = item.querySelector(".rusbeir-sort-indicator"); if (indicator) indicator.textContent = ""; }); const activeIndicator = header.querySelector(".rusbeir-sort-indicator"); if (activeIndicator) activeIndicator.textContent = nextDirection === "desc" ? " ▼" : " ▲"; const rows = Array.from(body.querySelectorAll("tr")); rows.sort((left, right) => { const leftText = (left.children[index]?.innerText || "").trim(); const rightText = (right.children[index]?.innerText || "").trim(); let result; if (sortType === "number") { const leftNumber = Number.parseFloat(leftText.replace(",", ".")); const rightNumber = Number.parseFloat(rightText.replace(",", ".")); const leftValue = Number.isFinite(leftNumber) ? leftNumber : Number.NEGATIVE_INFINITY; const rightValue = Number.isFinite(rightNumber) ? rightNumber : Number.NEGATIVE_INFINITY; result = leftValue - rightValue; } else { result = leftText.localeCompare(rightText, undefined, { numeric: true, sensitivity: "base" }); } return nextDirection === "desc" ? -result : result; }); rows.forEach((row, position) => { body.appendChild(row); const rankCell = row.querySelector(".col-rank"); if (rankCell) rankCell.textContent = String(position + 1); }); }; document.addEventListener("click", (event) => { const button = event.target.closest(".rusbeir-sort-button"); if (!button) return; event.preventDefault(); window.rusbeirSortTable(button); }); """ def read_json(path: Path, default: Any) -> Any: if not path.exists(): return default with path.open("r", encoding="utf-8") as file: return json.load(file) def read_jsonl(path: Path) -> list[dict[str, Any]]: if not path.exists(): return [] records: list[dict[str, Any]] = [] with path.open("r", encoding="utf-8") as file: for line_no, line in enumerate(file, start=1): line = line.strip() if not line or line.startswith("#"): continue try: records.append(json.loads(line)) except json.JSONDecodeError as exc: raise ValueError(f"Invalid JSONL at {path}:{line_no}: {exc}") from exc return records def metric_value(metrics: dict[str, Any], metric: str) -> float | None: value = metrics.get(metric) if value is None: return None try: return float(value) except (TypeError, ValueError): return None def compute_average(record: dict[str, Any], metric: str, dataset_names: set[str]) -> float | None: scores = record.get("scores", {}) explicit = metric_value(scores.get("average", {}), metric) dataset_scores = scores.get("datasets", {}) covered_dataset_names = { dataset_name for dataset_name, dataset_metrics in dataset_scores.items() if dataset_name in dataset_names and metric_value(dataset_metrics, metric) is not None } has_full_coverage = bool(dataset_names) and covered_dataset_names == dataset_names if explicit is not None and has_full_coverage: return explicit values = [] for dataset_name, dataset_metrics in dataset_scores.items(): if dataset_name not in dataset_names: continue value = metric_value(dataset_metrics, metric) if value is not None: values.append(value) if not values or len(values) != len(dataset_names): return None return sum(values) / len(values) def display_column_name(column: str) -> str: return DISPLAY_COLUMN_NAMES.get(column, column) def format_metric_columns(frame: pd.DataFrame) -> pd.DataFrame: metric_columns = [ column for column in frame.columns if column not in {*STATIC_COLUMNS, *TRAILING_COLUMNS} ] for column in metric_columns: values = pd.to_numeric(frame[column], errors="coerce") frame[column] = values.map(lambda value: "" if pd.isna(value) else f"{value:.4f}") return frame def finalize_leaderboard_frame(frame: pd.DataFrame, metric: str, dataset_names: set[str]) -> pd.DataFrame: ordered_columns = [ "Rank", "Model", metric, *sorted(dataset_names), *META_COLUMNS, ] ordered_columns = [column for column in ordered_columns if column in frame.columns] frame = frame.loc[:, ordered_columns].copy() frame = format_metric_columns(frame) return frame.rename(columns={column: display_column_name(column) for column in frame.columns}) def column_class(index: int, column: str, metric: str) -> str: if index == 0: return "col-rank sticky-rank" if index == 1: return "col-model sticky-model" if column == metric: return "col-average sticky-average" if column == "Model\nID": return "col-model-id" if column in {"Org.", "Type", "Verified"}: return "col-meta" if column == "Date": return "col-date" if column == "Source\nURL": return "col-source" return "col-dataset" def cell_html(value: Any, column: str) -> str: if value is None or pd.isna(value): return "" text = str(value) if column == "Source\nURL" and text: return f'source' return escape(text).replace("\n", "
") def sort_type_for_column(index: int, column: str, metric: str) -> str: text_columns = {"Model", "Model\nID", "Org.", "Type", "Verified", "Date", "Source\nURL"} if index == 0 or column == metric or column not in text_columns: return "number" return "text" def leaderboard_table_html(metric: str, task_filter: str, verified_only: bool, model_filter: str) -> str: frame = leaderboard_frame(metric, task_filter, verified_only, model_filter) if frame.empty: return '
No results match the selected filters.
' columns = list(frame.columns) header = "".join( f""" """ for index, column in enumerate(columns) ) body_rows = [] for _, row in frame.iterrows(): cells = "".join( f'{cell_html(row[column], column)}' for index, column in enumerate(columns) ) body_rows.append(f"{cells}") return f"""
{header}{''.join(body_rows)}
""" def load_datasets() -> list[dict[str, Any]]: return read_json(DATASETS_PATH, []) def load_results() -> list[dict[str, Any]]: return read_jsonl(RESULTS_PATH) def normalize_submission_record(record: dict[str, Any]) -> dict[str, Any]: model_id = str(record.get("model_id", "")).strip() if not model_id: raise ValueError("`model_id` is required.") scores = record.get("scores") if not isinstance(scores, dict): raise ValueError("`scores` must be an object.") average_scores = scores.get("average", {}) dataset_scores = scores.get("datasets", {}) if average_scores is None: average_scores = {} if dataset_scores is None: dataset_scores = {} if not isinstance(average_scores, dict): raise ValueError("`scores.average` must be an object.") if not isinstance(dataset_scores, dict): raise ValueError("`scores.datasets` must be an object.") has_metric = any(metric_value(average_scores, metric) is not None for metric in METRICS) if not has_metric: for metrics in dataset_scores.values(): if isinstance(metrics, dict) and any(metric_value(metrics, metric) is not None for metric in METRICS): has_metric = True break if not has_metric: raise ValueError(f"At least one numeric metric is required: {', '.join(METRICS)}.") normalized = dict(record) normalized["model_id"] = model_id normalized["model_name"] = str(record.get("model_name") or model_id.split("/")[-1]).strip() normalized["organization"] = str(record.get("organization") or (model_id.split("/", 1)[0] if "/" in model_id else "")).strip() normalized["type"] = str(record.get("type") or "dense").strip() normalized["date"] = str(record.get("date") or date.today().isoformat()).strip() normalized["verified"] = bool(record.get("verified", False)) normalized["source_url"] = str(record.get("source_url", "")).strip() normalized["scores"] = {"average": average_scores, "datasets": dataset_scores} return normalized def parse_submission_records(record_text: str) -> list[dict[str, Any]]: record_text = record_text.strip() if not record_text: return [] if record_text.startswith("["): parsed = json.loads(record_text) if not isinstance(parsed, list): raise ValueError("JSON array submission must contain result objects.") records = parsed else: records = [] for line_no, line in enumerate(record_text.splitlines(), start=1): line = line.strip() if not line: continue try: records.append(json.loads(line)) except json.JSONDecodeError as exc: raise ValueError(f"Invalid JSONL at line {line_no}: {exc}") from exc normalized_records = [] for index, record in enumerate(records, start=1): if not isinstance(record, dict): raise ValueError(f"Submission item {index} must be a JSON object.") normalized_records.append(normalize_submission_record(record)) return normalized_records def uploaded_file_text(uploaded_file: Any) -> str: if uploaded_file is None: return "" path = uploaded_file if isinstance(uploaded_file, dict): path = uploaded_file.get("path") or uploaded_file.get("name") else: path = getattr(uploaded_file, "path", None) or getattr(uploaded_file, "name", None) or uploaded_file if not path: return "" return Path(path).read_text(encoding="utf-8-sig") def merge_submission_records(current_records: list[dict[str, Any]], records: list[dict[str, Any]]) -> tuple[list[dict[str, Any]], int, int]: existing = set() for item in current_records: try: existing.add(json.dumps(normalize_submission_record(item), ensure_ascii=False, sort_keys=True, separators=(",", ":"))) except ValueError: continue merged_records = list(current_records) skipped = 0 for record in records: serialized = json.dumps(record, ensure_ascii=False, sort_keys=True, separators=(",", ":")) if serialized in existing: skipped += 1 continue existing.add(serialized) merged_records.append(record) return merged_records, len(merged_records) - len(current_records), skipped def serialize_jsonl(records: list[dict[str, Any]]) -> bytes: text = "".join( f"{json.dumps(record, ensure_ascii=False, sort_keys=True, separators=(',', ':'))}\n" for record in records ) return text.encode("utf-8") def write_local_results(content: bytes) -> None: RESULTS_PATH.parent.mkdir(parents=True, exist_ok=True) temporary_path = RESULTS_PATH.with_suffix(f"{RESULTS_PATH.suffix}.tmp") temporary_path.write_bytes(content) temporary_path.replace(RESULTS_PATH) def append_submission_records(records: list[dict[str, Any]]) -> tuple[int, int, str | None]: if not RESULTS_REPO_ID: merged, added, skipped = merge_submission_records(load_results(), records) if added: write_local_results(serialize_jsonl(merged)) return added, skipped, None if not HF_TOKEN: raise ValueError("HF_TOKEN secret is missing. Add a fine-grained write token in the Space settings.") api = HfApi(token=HF_TOKEN) with RESULTS_LOCK: for attempt in range(3): repo_info = api.repo_info(repo_id=RESULTS_REPO_ID, repo_type=RESULTS_REPO_TYPE, revision=RESULTS_REVISION) remote_path = hf_hub_download(repo_id=RESULTS_REPO_ID, filename=RESULTS_REPO_PATH, repo_type=RESULTS_REPO_TYPE, revision=repo_info.sha, token=HF_TOKEN) current_records = read_jsonl(Path(remote_path)) merged, added, skipped = merge_submission_records(current_records, records) if not added: write_local_results(serialize_jsonl(merged)) return 0, skipped, None content = serialize_jsonl(merged) try: commit = api.create_commit( repo_id=RESULTS_REPO_ID, repo_type=RESULTS_REPO_TYPE, revision=RESULTS_REVISION, parent_commit=repo_info.sha, operations=[CommitOperationAdd(path_in_repo=RESULTS_REPO_PATH, path_or_fileobj=content)], commit_message=f"Add {added} leaderboard result(s)") except HfHubHTTPError: if attempt < 2: continue raise write_local_results(content) return added, skipped, commit.commit_url raise RuntimeError("Could not update leaderboard results after multiple attempts.") def add_submission_record(uploaded_file: Any, metric: str, task_filter: str, verified_only: bool, model_filter: str) -> tuple[str, str, str]: try: file_text = uploaded_file_text(uploaded_file) except OSError as exc: return ( f"Submission was not added: could not read uploaded file: {exc}", summary_html(), leaderboard_table_html(metric, task_filter, verified_only, model_filter), ) if not file_text.strip(): return ( "Upload a non-empty results.jsonl file first.", summary_html(), leaderboard_table_html(metric, task_filter, verified_only, model_filter), ) try: records = parse_submission_records(file_text) added, skipped, commit_url = append_submission_records(records) except (json.JSONDecodeError, ValueError, OSError, HfHubHTTPError) as exc: return ( f"Submission was not added: {exc}", summary_html(), leaderboard_table_html(metric, task_filter, verified_only, model_filter), ) if added == 0 and skipped > 0: status = f"No new records were added; skipped {skipped} duplicate record(s)." else: status = f"Added {added} record(s) to `{RESULTS_PATH.name}`." if skipped: status += f" Skipped {skipped} duplicate record(s)." if commit_url: status += f" [Hub commit]({commit_url})" return ( status, summary_html(), leaderboard_table_html(metric, task_filter, verified_only, model_filter), ) def logo_data_uri() -> str: if not LOGO_PATH.exists(): return "" data = base64.b64encode(LOGO_PATH.read_bytes()).decode("ascii") return f"data:image/png;base64,{data}" def format_score(value: float | None) -> str: if value is None: return "n/a" try: numeric_value = float(value) except (TypeError, ValueError): return "n/a" return f"{numeric_value * 100:.2f}" def summary_html() -> str: datasets = [dataset for dataset in load_datasets() if dataset.get("official", True)] records = load_results() frame = leaderboard_frame(DEFAULT_METRIC, "All", False, "") best_model = "No results yet" best_score = "n/a" if not frame.empty and DEFAULT_METRIC in frame: best_row = frame.iloc[0] best_model = str(best_row.get("Model", "No results yet")) best_score = format_score(best_row.get(DEFAULT_METRIC)) types = sorted({str(record.get("type", "")).strip() for record in records if record.get("type")}) type_text = ", ".join(types) if types else "n/a" logo_uri = logo_data_uri() logo_html = f'' if logo_uri else "" return f"""
Russian Information Retrieval Benchmark

rusBEIR Leaderboard

Compare dense retrievers, sparse baselines, and reranker pipelines on official rusBEIR datasets. The default ranking is the macro-average of {DEFAULT_METRIC}.

Metric: {DEFAULT_METRIC} Official datasets: {len(datasets)} Rows: {len(records)} Types: {type_text}
{logo_html}
Best Model
{escape(best_model)}
Highest average {DEFAULT_METRIC}
Best Score
{best_score}
Shown as percentage points
Models
{len(records)}
Imported and reviewable JSONL rows
Datasets
{len(datasets)}
Official benchmark tasks
""" def leaderboard_frame(metric: str, task_filter: str, verified_only: bool, model_filter: str) -> pd.DataFrame: datasets = load_datasets() if task_filter != "All": datasets = [dataset for dataset in datasets if dataset["task"] == task_filter] dataset_names = {dataset["name"] for dataset in datasets if dataset.get("official", True)} rows = [] for record in load_results(): if verified_only and not record.get("verified", False): continue model_text = f"{record.get('model_id', '')} {record.get('model_name', '')}".lower() if model_filter and model_filter.lower() not in model_text: continue average = compute_average(record, metric, dataset_names) row = { "Rank": None, "Model": record.get("model_name") or record.get("model_id"), "Model ID": record.get("model_id", ""), "Organization": record.get("organization", ""), "Type": record.get("type", ""), metric: average, "Verified": "yes" if record.get("verified", False) else "no", "Date": record.get("date", ""), "Source URL": record.get("source_url", ""), } dataset_scores = record.get("scores", {}).get("datasets", {}) for dataset_name in sorted(dataset_names): row[dataset_name] = metric_value(dataset_scores.get(dataset_name, {}), metric) rows.append(row) if not rows: frame = pd.DataFrame(columns=[*STATIC_COLUMNS, metric, *TRAILING_COLUMNS]) return finalize_leaderboard_frame(frame, metric, dataset_names) frame = pd.DataFrame(rows) frame = frame.sort_values(metric, ascending=False, na_position="last").reset_index(drop=True) frame["Rank"] = frame.index + 1 return finalize_leaderboard_frame(frame, metric, dataset_names) def datasets_frame() -> pd.DataFrame: datasets = load_datasets() if not datasets: return pd.DataFrame(columns=["Dataset", "Task", "Split", "Corpus repo", "Qrels repo", "Origin"]) return pd.DataFrame( { "Dataset": item["name"], "Task": item["task"], "Split": item["split"], "Corpus repo": item["hf_repo"], "Qrels repo": item["qrels_repo"], "Origin": item["origin"], } for item in datasets if item.get("official", True) ) def task_choices() -> list[str]: tasks = sorted({dataset["task"] for dataset in load_datasets() if dataset.get("official", True)}) return ["All", *tasks] with gr.Blocks(title="rusBEIR Leaderboard") as demo: summary = gr.HTML(summary_html()) with gr.Tabs(): with gr.Tab("Leaderboard"): with gr.Column(elem_classes=["rusbeir-panel"]): gr.HTML( """

Model Rankings

Filter by task family, model name, or verification status. Scores are stored as fractions; leaderboard rankings use the selected average metric.

""" ) with gr.Row(elem_classes=["rusbeir-filters"]): metric = gr.Dropdown(METRICS, value=DEFAULT_METRIC, label="Metric", min_width=150) task_filter = gr.Dropdown(task_choices(), value="All", label="Task", min_width=180) model_filter = gr.Textbox(label="Model", placeholder="intfloat, BGE, FRIDA...", min_width=220) verified_only = gr.Checkbox(value=False, label="Verified", min_width=120, elem_classes=["rusbeir-verified-filter"]) gr.HTML('
Results
') table = gr.HTML(value=leaderboard_table_html(DEFAULT_METRIC, "All", False, "")) for control in [metric, task_filter, verified_only, model_filter]: control.change(leaderboard_table_html, inputs=[metric, task_filter, verified_only, model_filter], outputs=table) reload_button = gr.Button("Reload results", variant="secondary") reload_button.click(leaderboard_table_html, inputs=[metric, task_filter, verified_only, model_filter], outputs=table) with gr.Tab("Datasets"): with gr.Column(elem_classes=["rusbeir-panel"]): gr.HTML( """

Official Datasets

rusBEIR tasks used for the default macro-average ranking.

""" ) gr.Dataframe(value=datasets_frame(), label="Datasets", interactive=False, wrap=True, max_height=720) with gr.Tab("Submit"): with gr.Column(elem_classes=["rusbeir-panel"]): gr.HTML( """

Submit Results

Run the evaluator outside the Space and upload the generated results.jsonl. Accepted files contain one JSON result object per line.

We strongly recommend checking your results before uploading them to the Leaderboard. Retracting results is a manual process and can be handled only by @kaengreg.

""" ) gr.Markdown( """ Example: ```bash python leaderboard/scripts/evaluate_model.py --model-id intfloat/multilingual-e5-large --device cuda ``` """ ) results_file = gr.File(label="Upload results.jsonl", file_types=[".jsonl", ".json"], type="filepath") submit_status = gr.Markdown() add_button = gr.Button("Add to leaderboard", variant="primary") add_button.click(add_submission_record, inputs=[results_file, metric, task_filter, verified_only, model_filter], outputs=[submit_status, summary, table]) with gr.Tab("About"): with gr.Column(elem_classes=["rusbeir-panel"]): gr.HTML( """

About rusBEIR

rusBEIR is a Russian BEIR-style benchmark for zero-shot information retrieval. The leaderboard is backed by a plain JSONL file, so every row can be reviewed or mirrored to a Hugging Face Dataset.

Verified rows should point to reproducible logs or a commit with generated retrieval results.

Project repository: kaengreg/rusBEIR

Citation

@inproceedings{kovalev2025building,
  title={Building Russian Benchmark for Evaluation of Information Retrieval Models},
  author={Kovalev, Grigory and Tikhomirov, Mikhail and Kozhevnikov, Evgeny and Kornilov, Max and Loukachevitch, Natalia},
  booktitle={Proceedings of the International Conference “Dialogue},
  volume={2025},
  year={2025}
}
""" ) if __name__ == "__main__": launch_kwargs = { "server_name": "0.0.0.0", "server_port": int(os.getenv("PORT", "7860")), "show_error": True, "css": CUSTOM_CSS, "js": CUSTOM_JS } if "ssr_mode" in inspect.signature(demo.launch).parameters: launch_kwargs["ssr_mode"] = False demo.launch(**launch_kwargs)