Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions hf-space/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# The Space pulls the package from PyPI with the leaderboard extra so the
# Gradio entrypoint and `commonlid leaderboard serve` CLI stay in sync. Pin a
# concrete release here once we tag one (e.g. `commonlid[leaderboard]==0.1.0`).
commonlid[leaderboard]
# Install commonlid directly from GitHub main so the Space picks up changes
# without a PyPI release cycle. Pin to a tag or commit SHA (e.g.
# `...@v0.2.3` or `...@<sha>`) if you need to lock the Space to a known build.
commonlid[leaderboard] @ git+https://github.com/commoncrawl/commonlid-eval.git@main
24 changes: 11 additions & 13 deletions src/commonlid/leaderboard/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -410,26 +410,25 @@ def _make_select_handler(
) -> Any:
"""Build the row-select callback as a closure over the captured state.

The callback looks up the clicked row in the *current* table value
(passed in via Gradio's event arg) so that switching the scope radio
and then clicking a row drills down the row at its post-toggle
position, not the row that would have been there before the swap.
Uses ``gr.SelectData.row_value`` (Gradio's per-click payload that
contains the clicked row as a 1-D list) so the drilldown picks up the
*current* table ordering — switching the scope radio and then clicking
a row resolves to the row at its post-toggle position. Passing the
Dataframe component as an event input would not work: Gradio 6
preprocesses Dataframe inputs into ``pandas.DataFrame`` objects, not
the ``{"data", "headers"}`` dict we feed in via ``_styled_value``.

Gradio inspects ``__defaults__`` when registering events, and comparing a
DataFrame default against a type annotation hits an unimplemented arrow
dtype path. A closure keeps the state out of the function signature.
"""

def _on_select(table_value: Any, evt: gr.SelectData) -> tuple[str, Any]:
if evt.index is None:
def _on_select(evt: gr.SelectData) -> tuple[str, Any]:
if evt.index is None or not evt.row_value:
return ("_Click a row to load per-language metrics._", None)
row_idx = evt.index[0] if isinstance(evt.index, list | tuple) else evt.index
try:
data = table_value.get("data") if isinstance(table_value, dict) else None
if data is None:
return ("_Click a row to load per-language metrics._", None)
model_id = data[row_idx][0]
except (IndexError, KeyError, TypeError):
model_id = evt.row_value[0]
except (IndexError, TypeError):
return ("_Could not resolve clicked row._", None)
per_lang = _per_language_drilldown(snapshot_root, dataset_id, model_id)
return (
Expand Down Expand Up @@ -540,7 +539,6 @@ def build_app(
)
leaderboard.select(
_make_select_handler(dataset_id, snapshot_root),
inputs=[leaderboard],
outputs=[drilldown_label, drilldown],
)
gr.Markdown(footer)
Expand Down
45 changes: 45 additions & 0 deletions tests/unit/test_leaderboard_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,51 @@ def test_format_table_cov_scope_renders_em_dashes(tmp_path: Path) -> None:
assert cov_table.iloc[1]["Samples/s"] == "1234.5"


def test_row_select_handler_loads_drilldown_from_row_value(tmp_path: Path) -> None:
"""Clicking a row resolves model_id via ``evt.row_value[0]`` and renders the drilldown.

Regression: an earlier version pulled the model_id from the Dataframe
input, but Gradio 6 preprocesses Dataframe inputs into ``pandas.DataFrame``
objects rather than the ``{"data", "headers"}`` dict the app feeds in,
so the handler silently returned the "click a row" placeholder.
"""
pytest.importorskip("gradio")
from types import SimpleNamespace

from commonlid.leaderboard.app import _make_select_handler

per_lang = _per_language_block({"eng": (10, 10, 8, 0.01), "fra": (10, 10, 4, 0.02)})
_write_summary(tmp_path, "commonlid", "GlotLID", per_language=per_lang)
handler = _make_select_handler("commonlid", tmp_path)

evt = SimpleNamespace(
index=(0, 0),
value="GlotLID",
row_value=["GlotLID", "80.0", "60.0", "0.10", "2", "1234.5"],
)
label, payload = handler(evt)
assert "GlotLID" in label
assert "commonlid" in label
assert payload is not None
headers = payload["headers"]
assert headers[:2] == ["Language", "F1"]
languages = [row[0] for row in payload["data"]]
assert set(languages) == {"eng", "fra"}


def test_row_select_handler_returns_placeholder_when_index_missing(tmp_path: Path) -> None:
pytest.importorskip("gradio")
from types import SimpleNamespace

from commonlid.leaderboard.app import _make_select_handler

handler = _make_select_handler("commonlid", tmp_path)
evt = SimpleNamespace(index=None, value=None, row_value=None)
label, payload = handler(evt)
assert "Click a row" in label
assert payload is None


def test_scope_radio_change_swaps_table_and_legend(tmp_path: Path) -> None:
"""The scope-change handler returns a fresh styled table + legend Markdown."""
pytest.importorskip("gradio")
Expand Down
Loading