| from __future__ import annotations |
|
|
| import base64 |
| import json |
| import os |
| import re |
| from fnmatch import fnmatch |
| from io import BytesIO |
| from pathlib import Path |
| from urllib.parse import quote |
|
|
| import gradio as gr |
| import pandas as pd |
| from huggingface_hub import HfApi, hf_hub_download |
|
|
| from theme import POVISLE_THEME |
| from views import ( |
| render_examples_tab, |
| render_leaderboard_category_tab, |
| render_leaderboard_filters, |
| render_leaderboard_task_tab, |
| ) |
|
|
| EXCLUDED_MODELS = {"LLaVA-v1.6-Vicuna-13B-HF"} |
| MODEL_TYPE_LABELS = { |
| "open": "Open-weight", |
| "proprietary": "Proprietary", |
| "random": "Random", |
| } |
| APP_CSS = """ |
| :root { |
| --povisle-bg: #f8f7f6; |
| --povisle-surface: #ffffff; |
| --povisle-soft: #efedeb; |
| --povisle-soft-red: #fff1f2; |
| --povisle-soft-blue: #edf3f9; |
| --povisle-ink: #181416; |
| --povisle-muted: #5f585b; |
| --povisle-faint: #8b8588; |
| --povisle-line: #e3dfdc; |
| --povisle-accent: #d7263d; |
| --povisle-blue: #2c5f8a; |
| --povisle-green: #2a7a4e; |
| --povisle-shadow: 0 2px 16px rgba(39, 33, 31, 0.07); |
| --povisle-shadow-lg: 0 8px 40px rgba(39, 33, 31, 0.11); |
| --povisle-serif: "Source Serif 4", Georgia, serif; |
| --povisle-sans: "DM Sans", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; |
| --povisle-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace; |
| } |
| body { |
| color-scheme: light; |
| background: var(--povisle-bg) !important; |
| color: var(--povisle-ink) !important; |
| font-family: var(--povisle-sans) !important; |
| line-height: 1.7; |
| } |
| .gradio-container { |
| max-width: 1480px !important; |
| margin: 0 auto !important; |
| padding: 0 14px 42px !important; |
| background: transparent !important; |
| color: var(--povisle-ink) !important; |
| font-family: var(--povisle-sans) !important; |
| } |
| .contain { |
| gap: 0 !important; |
| } |
| footer { |
| display: none !important; |
| } |
| .povisle-hero { |
| background: transparent; |
| border: 0; |
| border-radius: 0; |
| box-shadow: none; |
| margin: 0 auto 34px; |
| max-width: 1120px; |
| padding: 84px 28px 48px; |
| text-align: center; |
| } |
| .povisle-kicker { |
| align-items: center; |
| background: var(--povisle-soft-red); |
| border: 1px solid rgba(215, 38, 61, 0.22); |
| border-radius: 20px; |
| color: var(--povisle-accent); |
| display: inline-flex; |
| font-size: 0.78rem; |
| font-weight: 700; |
| gap: 6px; |
| letter-spacing: 0.04em; |
| line-height: 1; |
| margin: 0 0 26px; |
| padding: 7px 14px; |
| text-transform: uppercase; |
| } |
| .povisle-hero h1 { |
| color: var(--povisle-ink) !important; |
| display: block !important; |
| font-family: var(--povisle-serif) !important; |
| font-size: clamp(2.65rem, 4.5vw, 4.35rem) !important; |
| font-weight: 600 !important; |
| letter-spacing: 0 !important; |
| line-height: 1.02 !important; |
| margin: 0 auto 12px !important; |
| max-width: 1200px !important; |
| } |
| .povisle-title-accent { |
| color: var(--povisle-accent) !important; |
| } |
| .povisle-hero em { |
| color: var(--povisle-ink); |
| font-style: italic; |
| } |
| .povisle-hero p { |
| color: var(--povisle-muted); |
| font-size: 0.98rem; |
| line-height: 1.8; |
| margin: 26px auto 0; |
| max-width: 720px; |
| } |
| .povisle-authors { |
| color: var(--povisle-muted); |
| font-size: 0.9rem; |
| line-height: 1.65; |
| margin: 24px auto 0; |
| max-width: 760px; |
| } |
| .povisle-authors strong { |
| color: var(--povisle-ink); |
| font-weight: 500; |
| } |
| .povisle-links-row { |
| display: flex; |
| flex-wrap: wrap; |
| gap: 10px; |
| justify-content: center; |
| margin-top: 30px; |
| } |
| .povisle-hero a.povisle-btn, |
| .povisle-hero a.povisle-btn:hover, |
| .povisle-hero a.povisle-btn:focus, |
| .povisle-hero a.povisle-btn:visited { |
| text-decoration: none !important; |
| } |
| .povisle-btn { |
| align-items: center; |
| border: 1px solid transparent; |
| border-radius: 6px; |
| display: inline-flex; |
| font-size: 0.84rem; |
| font-weight: 500; |
| gap: 7px; |
| line-height: 1; |
| padding: 10px 18px; |
| text-decoration: none; |
| transition: all 0.18s ease; |
| } |
| .povisle-btn svg { |
| flex-shrink: 0; |
| height: 15px; |
| width: 15px; |
| } |
| .povisle-btn svg.arxiv-icon { |
| height: 22.5px; |
| width: 22.5px; |
| } |
| .povisle-btn-primary { |
| background: var(--povisle-accent); |
| color: #ffffff !important; |
| } |
| .povisle-btn-primary:hover { |
| background: #b91c33; |
| box-shadow: var(--povisle-shadow); |
| transform: translateY(-1px); |
| } |
| .povisle-btn-dataset { |
| background: var(--povisle-soft-blue); |
| border-color: rgba(44, 95, 138, 0.2); |
| color: var(--povisle-blue) !important; |
| } |
| .povisle-hero a.povisle-btn-dataset, |
| .povisle-hero a.povisle-btn-dataset:visited { |
| color: var(--povisle-blue) !important; |
| } |
| .povisle-btn-dataset:hover { |
| background: #dbe9f5; |
| transform: translateY(-1px); |
| } |
| .povisle-btn-outline { |
| background: var(--povisle-surface); |
| border-color: var(--povisle-line); |
| color: var(--povisle-ink) !important; |
| } |
| .povisle-hero a.povisle-btn-outline, |
| .povisle-hero a.povisle-btn-outline:visited { |
| color: var(--povisle-ink) !important; |
| } |
| .povisle-btn-outline:hover { |
| border-color: var(--povisle-ink); |
| box-shadow: var(--povisle-shadow); |
| transform: translateY(-1px); |
| } |
| .povisle-stat-row { |
| display: grid; |
| gap: 16px; |
| grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); |
| margin: 30px auto 0; |
| max-width: 760px; |
| } |
| .povisle-stat { |
| background: var(--povisle-bg); |
| border: 1px solid var(--povisle-line); |
| border-radius: 10px; |
| padding: 18px 14px; |
| text-align: center; |
| } |
| .povisle-stat strong { |
| color: var(--povisle-accent) !important; |
| display: block; |
| font-family: var(--povisle-serif); |
| font-size: 1.7rem; |
| font-weight: 600; |
| line-height: 1.1; |
| } |
| .povisle-stat span { |
| color: var(--povisle-faint); |
| display: block; |
| font-size: 0.78rem; |
| font-weight: 500; |
| margin-top: 5px; |
| } |
| .tab-nav { |
| justify-content: center !important; |
| margin: 0 auto !important; |
| max-width: 860px !important; |
| } |
| .tab-nav button { |
| color: var(--povisle-muted) !important; |
| font-family: var(--povisle-sans) !important; |
| font-size: 0.88rem !important; |
| font-weight: 600 !important; |
| } |
| .tabitem { |
| padding-top: 24px !important; |
| } |
| #leaderboard-section { |
| padding-top: 0 !important; |
| } |
| #leaderboard-search { |
| margin-bottom: 14px !important; |
| } |
| #leaderboard-filter-row { |
| margin-bottom: 14px !important; |
| } |
| #category-view-select { |
| margin-bottom: 14px !important; |
| } |
| .leaderboard-split-tabs { |
| margin-top: 0 !important; |
| } |
| #leaderboard-table, |
| .leaderboard-table { |
| margin-top: 0 !important; |
| } |
| .form, |
| .block { |
| border-color: var(--povisle-line) !important; |
| border-radius: 8px !important; |
| box-shadow: none !important; |
| } |
| .wrap.default { |
| background: transparent !important; |
| } |
| #leaderboard-filter-row { |
| background: var(--povisle-surface) !important; |
| border: 1px solid var(--povisle-line) !important; |
| border-radius: 8px !important; |
| gap: 0 !important; |
| overflow: hidden !important; |
| } |
| #leaderboard-filter-row > *, |
| #split-filter, |
| #type-filter, |
| #size-filter, |
| #family-filter { |
| background: var(--povisle-surface) !important; |
| border-color: transparent !important; |
| border-radius: 0 !important; |
| box-shadow: none !important; |
| } |
| #split-filter { |
| border-radius: 8px 0 0 8px !important; |
| } |
| #type-filter { |
| border-left: 0 !important; |
| border-radius: 8px 0 0 8px !important; |
| } |
| #family-filter { |
| border-radius: 0 8px 8px 0 !important; |
| } |
| #type-filter, |
| #size-filter, |
| #family-filter { |
| border-left: 1px dashed #ddd7d3 !important; |
| } |
| #type-filter { |
| border-left: 0 !important; |
| } |
| #leaderboard-filter-row .form, |
| #leaderboard-filter-row .block { |
| background: transparent !important; |
| border: 0 !important; |
| border-radius: 0 !important; |
| } |
| #leaderboard-filter-row [data-testid="token"], |
| #leaderboard-filter-row .token, |
| #leaderboard-filter-row .selected-token, |
| #leaderboard-filter-row .multiselect-token, |
| #leaderboard-filter-row div:has(> button[aria-label^="Remove"]), |
| #leaderboard-filter-row div:has(> button[aria-label^="remove"]), |
| #leaderboard-filter-row button[aria-label^="Remove"], |
| #leaderboard-filter-row button[aria-label^="remove"] { |
| align-items: center !important; |
| background: #efedeb !important; |
| border: 1px solid #ddd7d3 !important; |
| border-radius: 999px !important; |
| color: var(--povisle-ink) !important; |
| display: inline-flex !important; |
| font-size: 0.82rem !important; |
| font-weight: 500 !important; |
| gap: 6px !important; |
| line-height: 1 !important; |
| min-height: 28px !important; |
| padding: 5px 10px !important; |
| } |
| #leaderboard-filter-row [data-testid="token"] button, |
| #leaderboard-filter-row .token button, |
| #leaderboard-filter-row .selected-token button, |
| #leaderboard-filter-row .multiselect-token button { |
| background: transparent !important; |
| border: 0 !important; |
| color: var(--povisle-muted) !important; |
| min-height: auto !important; |
| padding: 0 0 0 3px !important; |
| } |
| #leaderboard-filter-row [data-testid="token"] svg, |
| #leaderboard-filter-row .token svg, |
| #leaderboard-filter-row .selected-token svg, |
| #leaderboard-filter-row .multiselect-token svg { |
| height: 12px !important; |
| width: 12px !important; |
| } |
| #leaderboard-search, |
| #category-view-select, |
| #leaderboard-table, |
| .leaderboard-table, |
| #examples-table { |
| border-radius: 10px !important; |
| } |
| #leaderboard-table, |
| .leaderboard-table, |
| #examples-table { |
| box-shadow: var(--povisle-shadow); |
| } |
| #leaderboard-table table th, |
| .leaderboard-table table th, |
| #examples-table table th { |
| background: var(--povisle-soft) !important; |
| color: var(--povisle-ink) !important; |
| font-size: 0.7rem !important; |
| font-weight: 700 !important; |
| line-height: 1.2 !important; |
| padding: 8px 10px !important; |
| white-space: normal !important; |
| } |
| #leaderboard-table table td, |
| .leaderboard-table table td, |
| #examples-table table td { |
| border-color: var(--povisle-line) !important; |
| color: var(--povisle-muted) !important; |
| } |
| #leaderboard-table table th, |
| .leaderboard-table table th, |
| #leaderboard-table table td, |
| .leaderboard-table table td { |
| min-width: 8.75rem; |
| } |
| #leaderboard-table table th:nth-child(1), |
| .leaderboard-table table th:nth-child(1), |
| #leaderboard-table table td:nth-child(1), |
| .leaderboard-table table td:nth-child(1) { |
| min-width: 7rem; |
| } |
| #leaderboard-table table th:nth-child(2), |
| .leaderboard-table table th:nth-child(2), |
| #leaderboard-table table td:nth-child(2), |
| .leaderboard-table table td:nth-child(2) { |
| min-width: 15rem; |
| } |
| #leaderboard-table table th:nth-child(3), |
| .leaderboard-table table th:nth-child(3), |
| #leaderboard-table table td:nth-child(3), |
| .leaderboard-table table td:nth-child(3) { |
| min-width: 7rem; |
| } |
| #leaderboard-table table th:nth-child(4), |
| .leaderboard-table table th:nth-child(4), |
| #leaderboard-table table td:nth-child(4), |
| .leaderboard-table table td:nth-child(4) { |
| min-width: 8rem; |
| } |
| #leaderboard-table table tbody tr:nth-child(even) td, |
| .leaderboard-table table tbody tr:nth-child(even) td, |
| #examples-table table tbody tr:nth-child(even) td { |
| background: #fbfaf9; |
| } |
| #leaderboard-table table tbody tr:hover td, |
| .leaderboard-table table tbody tr:hover td, |
| #examples-table table tbody tr:hover td { |
| background: #fff1f2 !important; |
| } |
| .povisle-section-label { |
| color: var(--povisle-accent) !important; |
| font-size: 0.72rem; |
| font-weight: 700; |
| letter-spacing: 0.1em; |
| margin-bottom: 12px; |
| text-transform: uppercase; |
| } |
| .povisle-dataset-card .povisle-section-label, |
| .povisle-leaderboard-heading .povisle-section-label, |
| .povisle-examples-heading .povisle-section-label { |
| color: var(--povisle-accent) !important; |
| } |
| .povisle-section-title { |
| color: var(--povisle-ink); |
| font-family: var(--povisle-serif); |
| font-size: clamp(1.3rem, 2.5vw, 1.7rem); |
| font-weight: 600; |
| margin: 0 0 18px; |
| } |
| .povisle-dataset-card { |
| background: transparent; |
| border-color: transparent; |
| border-radius: 0; |
| box-shadow: none; |
| margin: 30px auto 8px; |
| max-width: 1440px; |
| padding: 12px 0 4px; |
| } |
| .povisle-dataset-card p { |
| color: var(--povisle-muted); |
| font-size: 0.97rem; |
| line-height: 1.8; |
| margin: 0 0 14px; |
| max-width: 100%; |
| } |
| .povisle-dataset-card p:last-child { |
| margin-bottom: 0; |
| } |
| .povisle-about-grid { |
| align-items: start; |
| display: grid; |
| gap: 28px; |
| grid-template-columns: 1fr; |
| margin-top: 26px; |
| } |
| .povisle-about-copy { |
| margin-top: 22px; |
| } |
| .povisle-about-grid .povisle-about-copy { |
| margin-top: 0; |
| } |
| .povisle-category-layout { |
| align-items: start; |
| display: grid; |
| gap: 24px; |
| grid-template-columns: repeat(2, minmax(0, 1fr)); |
| } |
| .povisle-task-layout { |
| align-items: start; |
| display: grid; |
| gap: 24px; |
| grid-template-columns: repeat(2, minmax(0, 1fr)); |
| } |
| .povisle-creation-layout { |
| align-items: start; |
| display: grid; |
| gap: 24px; |
| grid-template-columns: repeat(2, minmax(0, 1fr)); |
| } |
| .povisle-creation-figure { |
| background: var(--povisle-surface); |
| border: 1px solid var(--povisle-line); |
| border-radius: 8px; |
| margin: 0; |
| overflow: hidden; |
| padding: 12px; |
| } |
| .povisle-creation-figure img { |
| display: block; |
| height: auto; |
| width: 100%; |
| } |
| .povisle-category-intro { |
| margin-bottom: 8px !important; |
| } |
| .povisle-about-copy h3 { |
| color: var(--povisle-ink); |
| font-family: var(--povisle-serif); |
| font-size: 1.18rem; |
| font-weight: 600; |
| line-height: 1.25; |
| margin: 0 0 10px; |
| } |
| .povisle-angled-sunburst { |
| background: var(--povisle-surface) !important; |
| border: 1px solid var(--povisle-line) !important; |
| border-radius: 8px !important; |
| box-shadow: none !important; |
| margin: 0 !important; |
| overflow: hidden !important; |
| padding: 14px !important; |
| } |
| .povisle-angled-sunburst img { |
| display: block; |
| height: clamp(260px, 36vw, 520px); |
| margin: 0 auto; |
| max-width: 720px; |
| object-fit: contain; |
| width: 100%; |
| } |
| .povisle-examples-heading { |
| margin: 38px auto 18px; |
| max-width: 1440px; |
| } |
| .povisle-leaderboard-heading { |
| margin: 26px auto 18px; |
| max-width: 1440px; |
| } |
| .povisle-leaderboard-heading .povisle-section-title { |
| margin-bottom: 10px; |
| } |
| .povisle-leaderboard-heading p { |
| color: var(--povisle-muted); |
| font-size: 0.97rem; |
| line-height: 1.7; |
| margin: 0; |
| max-width: 100%; |
| } |
| .povisle-examples-heading p { |
| color: var(--povisle-muted); |
| font-size: 0.97rem; |
| line-height: 1.8; |
| margin: 0; |
| } |
| .povisle-acknowledgement { |
| border-top: 1px solid var(--povisle-line); |
| margin: 46px auto 0; |
| max-width: 1440px; |
| padding: 24px 0 0; |
| } |
| .povisle-acknowledgement .povisle-section-title { |
| font-size: 1.15rem; |
| margin-bottom: 8px; |
| } |
| .povisle-acknowledgement p { |
| color: var(--povisle-muted); |
| font-size: 0.94rem; |
| line-height: 1.7; |
| margin: 0; |
| max-width: 100%; |
| } |
| .povisle-citation { |
| border-top: 1px solid var(--povisle-line); |
| margin: 30px auto 0; |
| max-width: 1440px; |
| padding: 24px 0 0; |
| } |
| .povisle-citation .povisle-section-title { |
| font-size: 1.15rem; |
| margin-bottom: 8px; |
| } |
| .povisle-citation pre { |
| background: var(--povisle-surface); |
| border: 1px solid var(--povisle-line); |
| border-radius: 8px; |
| color: var(--povisle-ink); |
| font-family: var(--povisle-mono); |
| font-size: 0.82rem; |
| line-height: 1.55; |
| margin: 0; |
| overflow-x: auto; |
| padding: 14px; |
| white-space: pre; |
| } |
| #examples-filter-row { |
| background: var(--povisle-surface) !important; |
| border: 1px solid var(--povisle-line) !important; |
| border-radius: 10px !important; |
| gap: 0 !important; |
| margin: 0 0 16px !important; |
| overflow: hidden !important; |
| padding: 0 !important; |
| width: 100% !important; |
| } |
| #examples-filter-row > * { |
| background: transparent !important; |
| position: relative !important; |
| } |
| #examples-filter-row > * + *::before { |
| background: #ddd7d3; |
| bottom: 14px; |
| content: ""; |
| left: 0; |
| position: absolute; |
| top: 14px; |
| width: 1px; |
| z-index: 1; |
| } |
| #examples-filter-row .block, |
| #examples-filter-row .form, |
| #examples-filter-row .wrap, |
| #examples-filter-row .wrap-inner, |
| #examples-filter-row .input-container { |
| background: transparent !important; |
| border: 0 !important; |
| border-radius: 0 !important; |
| box-shadow: none !important; |
| } |
| #examples-filter-row input, |
| #examples-filter-row textarea { |
| background: transparent !important; |
| } |
| #examples-table { |
| overflow-x: auto !important; |
| } |
| #examples-table table { |
| table-layout: fixed !important; |
| min-width: 1530px !important; |
| } |
| #examples-table table th, |
| #examples-table table td, |
| #examples-table [role="columnheader"], |
| #examples-table [role="gridcell"] { |
| line-height: 1.35 !important; |
| padding: 6px 8px !important; |
| } |
| #examples-table table td, |
| #examples-table [role="gridcell"] { |
| font-size: 0.64rem !important; |
| line-height: 1.3 !important; |
| } |
| #examples-table table th, |
| #examples-table [role="columnheader"] { |
| font-size: 0.66rem !important; |
| line-height: 1.15 !important; |
| padding: 7px 8px !important; |
| } |
| #examples-table table td *, |
| #examples-table table th *, |
| #examples-table [role="gridcell"] *, |
| #examples-table [role="columnheader"] * { |
| font-size: inherit !important; |
| line-height: inherit !important; |
| } |
| #examples-table table td, |
| #examples-table table th, |
| #examples-table [role="gridcell"], |
| #examples-table [role="columnheader"] { |
| min-width: 0 !important; |
| overflow-wrap: normal !important; |
| white-space: normal !important; |
| word-break: normal !important; |
| } |
| #examples-table table td *, |
| #examples-table table th *, |
| #examples-table [role="gridcell"] *, |
| #examples-table [role="columnheader"] * { |
| overflow-wrap: break-word !important; |
| white-space: normal !important; |
| word-break: normal !important; |
| } |
| #examples-table table td:nth-child(1), |
| #examples-table table th:nth-child(1) { |
| width: 90px !important; |
| } |
| #examples-table table td:nth-child(2), |
| #examples-table table th:nth-child(2) { |
| width: 128px !important; |
| } |
| #examples-table table td:nth-child(3), |
| #examples-table table th:nth-child(3) { |
| width: 130px !important; |
| } |
| #examples-table table td:nth-child(4), |
| #examples-table table th:nth-child(4) { |
| width: 110px !important; |
| text-align: center; |
| vertical-align: middle; |
| } |
| #examples-table table td:nth-child(5), |
| #examples-table table th:nth-child(5) { |
| width: 300px !important; |
| } |
| #examples-table table td:nth-child(6), |
| #examples-table table th:nth-child(6) { |
| width: 230px !important; |
| } |
| #examples-table table td:nth-child(6), |
| #examples-table [role="gridcell"]:nth-child(6) { |
| white-space: pre-line !important; |
| } |
| #examples-table table td:nth-child(7), |
| #examples-table table th:nth-child(7) { |
| width: 170px !important; |
| } |
| #examples-table table td:nth-child(8), |
| #examples-table table th:nth-child(8) { |
| width: 190px !important; |
| } |
| #examples-table table td:nth-child(9), |
| #examples-table table th:nth-child(9) { |
| width: 90px !important; |
| } |
| #examples-table table td:nth-child(10), |
| #examples-table table th:nth-child(10) { |
| width: 100px !important; |
| } |
| #examples-table table td:nth-child(4) img.example-image { |
| display: block; |
| margin: 0 auto; |
| max-height: 6rem; |
| max-width: 6rem; |
| object-fit: contain; |
| } |
| #leaderboard-section, |
| #examples-section { |
| background: transparent !important; |
| border: 0 !important; |
| box-shadow: none !important; |
| } |
| .povisle-category-list { |
| color: var(--povisle-muted); |
| font-size: 0.88rem; |
| line-height: 1.45; |
| margin: 4px 0 0; |
| padding-left: 1.1rem; |
| } |
| .povisle-category-list li + li { |
| margin-top: 8px; |
| } |
| .povisle-category-list strong { |
| color: var(--povisle-ink); |
| font-weight: 720; |
| } |
| .povisle-category-note { |
| margin-top: 18px !important; |
| } |
| .povisle-task-list { |
| color: var(--povisle-muted); |
| font-size: 0.9rem; |
| line-height: 1.55; |
| margin: 0; |
| padding-left: 1.1rem; |
| } |
| .povisle-task-list li + li { |
| margin-top: 8px; |
| } |
| .povisle-task-list strong { |
| color: var(--povisle-ink); |
| font-weight: 720; |
| } |
| .povisle-task-chart { |
| background: var(--povisle-surface); |
| border: 1px solid var(--povisle-line); |
| border-radius: 8px; |
| padding: 16px; |
| } |
| .povisle-task-chart-legend { |
| display: flex; |
| flex-wrap: wrap; |
| gap: 10px 14px; |
| margin-bottom: 16px; |
| } |
| .povisle-task-chart-legend span { |
| align-items: center; |
| color: var(--povisle-muted); |
| display: inline-flex; |
| font-size: 0.78rem; |
| gap: 6px; |
| line-height: 1.2; |
| } |
| .povisle-task-chart-legend span::before { |
| background: var(--segment-color); |
| border-radius: 999px; |
| content: ""; |
| display: inline-block; |
| height: 8px; |
| width: 8px; |
| } |
| .povisle-task-split + .povisle-task-split { |
| margin-top: 18px; |
| } |
| .povisle-task-split-header { |
| align-items: baseline; |
| display: flex; |
| justify-content: space-between; |
| gap: 12px; |
| margin-bottom: 8px; |
| } |
| .povisle-task-split-header span { |
| color: var(--povisle-muted); |
| font-size: 0.82rem; |
| line-height: 1.25; |
| } |
| .povisle-task-split-header strong { |
| color: var(--povisle-ink); |
| font-family: var(--povisle-mono); |
| font-size: 0.82rem; |
| font-weight: 650; |
| text-align: right; |
| } |
| .povisle-task-chart-track { |
| background: var(--povisle-soft); |
| border-radius: 999px; |
| display: flex; |
| height: 24px; |
| overflow: hidden; |
| } |
| .povisle-task-chart-segment { |
| align-items: center; |
| background: var(--segment-color); |
| color: #ffffff; |
| display: flex; |
| font-family: var(--povisle-mono); |
| font-size: 0.72rem; |
| font-weight: 650; |
| height: 100%; |
| justify-content: center; |
| line-height: 1; |
| min-width: 28px; |
| } |
| .povisle-task-chart-segment + .povisle-task-chart-segment { |
| border-left: 1px solid rgba(255, 255, 255, 0.75); |
| } |
| .block label span, |
| .block .label-wrap span { |
| color: #3c4149 !important; |
| font-weight: 680 !important; |
| } |
| @media (max-width: 760px) { |
| .gradio-container { |
| padding: 0 14px 32px !important; |
| } |
| .povisle-hero { |
| padding: 48px 18px 38px; |
| } |
| .povisle-hero h1 { |
| font-size: clamp(1.95rem, 9.5vw, 2.75rem) !important; |
| } |
| .povisle-links-row { |
| align-items: stretch; |
| flex-direction: column; |
| } |
| .povisle-btn { |
| justify-content: center; |
| } |
| .povisle-dataset-card { |
| padding: 8px 0 4px; |
| } |
| .povisle-about-grid { |
| grid-template-columns: 1fr; |
| } |
| .povisle-category-layout { |
| grid-template-columns: 1fr; |
| } |
| .povisle-task-layout { |
| grid-template-columns: 1fr; |
| } |
| .povisle-creation-layout { |
| grid-template-columns: 1fr; |
| } |
| } |
| |
| /* Leaderboard layout reset after adding nested split tabs. */ |
| #leaderboard-section, |
| #leaderboard-section > *, |
| #leaderboard-section .tabitem, |
| #leaderboard-view-tabs, |
| #leaderboard-view-tabs > *, |
| #leaderboard-view-tabs .tabitem, |
| .leaderboard-split-tabs, |
| .leaderboard-split-tabs > *, |
| .leaderboard-split-tabs .tabitem { |
| background: transparent !important; |
| } |
| #leaderboard-section { |
| display: block !important; |
| background: var(--povisle-bg) !important; |
| margin-top: 0 !important; |
| padding-top: 0 !important; |
| } |
| .tabitem:has(#leaderboard-section) { |
| padding-top: 10px !important; |
| } |
| #leaderboard-section .form, |
| #leaderboard-section .block { |
| box-shadow: none !important; |
| } |
| #leaderboard-search-row, |
| #leaderboard-filter-row, |
| #leaderboard-category-row { |
| background: var(--povisle-surface) !important; |
| border: 1px solid var(--povisle-line) !important; |
| border-radius: 8px !important; |
| box-shadow: none !important; |
| filter: none !important; |
| margin: 0 0 16px !important; |
| overflow: hidden !important; |
| } |
| #leaderboard-search, |
| #category-view-select { |
| background: transparent !important; |
| border: 0 !important; |
| border-radius: 0 !important; |
| box-shadow: none !important; |
| filter: none !important; |
| margin: 0 !important; |
| overflow: visible !important; |
| } |
| #leaderboard-search textarea, |
| #leaderboard-search input { |
| background: var(--povisle-surface) !important; |
| } |
| #leaderboard-search-row, |
| #leaderboard-search-row *, |
| #leaderboard-category-row, |
| #leaderboard-category-row * { |
| box-shadow: none !important; |
| filter: none !important; |
| } |
| #leaderboard-search, |
| #leaderboard-search *, |
| #category-view-select, |
| #category-view-select * { |
| box-shadow: none !important; |
| filter: none !important; |
| } |
| #leaderboard-search, |
| #leaderboard-search .block, |
| #leaderboard-search .form, |
| #leaderboard-search .wrap, |
| #leaderboard-search .wrap-inner, |
| #leaderboard-search .input-container, |
| #leaderboard-search textarea, |
| #leaderboard-search input, |
| #category-view-select, |
| #category-view-select .block, |
| #category-view-select .form, |
| #category-view-select .wrap, |
| #category-view-select .wrap-inner, |
| #category-view-select .input-container { |
| background: var(--povisle-surface) !important; |
| } |
| #leaderboard-search .block, |
| #leaderboard-search .form, |
| #leaderboard-search .wrap, |
| #leaderboard-search .wrap-inner, |
| #leaderboard-search .input-container, |
| #category-view-select .block, |
| #category-view-select .form, |
| #category-view-select .wrap, |
| #category-view-select .wrap-inner, |
| #category-view-select .input-container { |
| border: 0 !important; |
| border-radius: 0 !important; |
| } |
| #leaderboard-category-row { |
| background: transparent !important; |
| border: 0 !important; |
| border-radius: 0 !important; |
| padding: 12px 0 !important; |
| overflow: visible !important; |
| } |
| #leaderboard-category-row > *, |
| #leaderboard-category-row #category-view-select, |
| #leaderboard-category-row #category-view-select .block, |
| #leaderboard-category-row #category-view-select .form, |
| #leaderboard-category-row #category-view-select .wrap, |
| #leaderboard-category-row #category-view-select .wrap-inner, |
| #leaderboard-category-row #category-view-select .input-container, |
| #leaderboard-category-row #category-view-select [role="radiogroup"] { |
| background: transparent !important; |
| border: 0 !important; |
| box-shadow: none !important; |
| } |
| #category-view-select .wrap, |
| #category-view-select .wrap-inner, |
| #category-view-select .input-container, |
| #category-view-select [role="radiogroup"] { |
| align-items: center !important; |
| display: flex !important; |
| flex-wrap: wrap !important; |
| gap: 8px !important; |
| } |
| #category-view-select label { |
| align-items: center !important; |
| background: var(--povisle-soft) !important; |
| border: 1px solid var(--povisle-line) !important; |
| border-radius: 999px !important; |
| color: var(--povisle-muted) !important; |
| cursor: pointer !important; |
| display: inline-flex !important; |
| font-size: 0.86rem !important; |
| font-weight: 600 !important; |
| line-height: 1 !important; |
| margin: 0 !important; |
| max-width: 100% !important; |
| min-height: 34px !important; |
| padding: 9px 13px !important; |
| transition: background 0.16s ease, border-color 0.16s ease, color 0.16s ease; |
| } |
| #category-view-select label:hover { |
| background: #fff1f2 !important; |
| border-color: #f2b8c0 !important; |
| color: var(--povisle-ink) !important; |
| } |
| #category-view-select label:has(input:checked) { |
| background: var(--povisle-accent) !important; |
| border-color: var(--povisle-accent) !important; |
| color: #ffffff !important; |
| } |
| #category-view-select label:has(input:focus-visible) { |
| outline: 2px solid rgba(215, 38, 61, 0.26) !important; |
| outline-offset: 2px !important; |
| } |
| #category-view-select input[type="radio"] { |
| height: 1px !important; |
| margin: 0 !important; |
| opacity: 0 !important; |
| position: absolute !important; |
| width: 1px !important; |
| } |
| #category-view-select label span { |
| color: inherit !important; |
| white-space: nowrap !important; |
| } |
| #leaderboard-filter-row { |
| gap: 0 !important; |
| overflow: hidden !important; |
| } |
| #leaderboard-filter-row > * { |
| background: var(--povisle-surface) !important; |
| position: relative !important; |
| } |
| .povisle-filter-field, |
| .leaderboard-filter-field { |
| background: var(--povisle-surface) !important; |
| gap: 0 !important; |
| padding: 14px 14px 12px !important; |
| } |
| .povisle-filter-field .block, |
| .povisle-filter-field .form, |
| .povisle-filter-field .wrap, |
| .povisle-filter-field .wrap-inner, |
| .povisle-filter-field .input-container, |
| .leaderboard-filter-field .block, |
| .leaderboard-filter-field .form, |
| .leaderboard-filter-field .wrap, |
| .leaderboard-filter-field .wrap-inner, |
| .leaderboard-filter-field .input-container { |
| background: transparent !important; |
| border: 0 !important; |
| box-shadow: none !important; |
| } |
| #leaderboard-filter-row > * + *::before { |
| background: #ddd7d3; |
| bottom: 18px; |
| content: ""; |
| left: 0; |
| position: absolute; |
| top: 18px; |
| width: 1px; |
| } |
| #type-filter, |
| #size-filter, |
| #family-filter { |
| background: var(--povisle-surface) !important; |
| border: 0 !important; |
| border-radius: 0 !important; |
| box-shadow: none !important; |
| } |
| #size-filter, |
| #family-filter { |
| border-left: 1px dashed #ddd7d3 !important; |
| } |
| #leaderboard-filter-row label, |
| #leaderboard-filter-row label span, |
| #leaderboard-filter-row .label-wrap, |
| #leaderboard-filter-row .label-wrap span, |
| #leaderboard-filter-row [data-testid="block-label"], |
| #leaderboard-filter-row [data-testid="block-label"] span { |
| color: var(--povisle-ink) !important; |
| opacity: 1 !important; |
| } |
| #leaderboard-filter-row .label-wrap span, |
| #leaderboard-filter-row [data-testid="block-label"] span { |
| font-weight: 700 !important; |
| } |
| #leaderboard-filter-row input, |
| #leaderboard-filter-row textarea, |
| #leaderboard-filter-row [role="combobox"], |
| #leaderboard-filter-row [data-testid="token"], |
| #leaderboard-filter-row .token, |
| #leaderboard-filter-row .selected-token, |
| #leaderboard-filter-row .multiselect-token { |
| color: var(--povisle-ink) !important; |
| } |
| #leaderboard-view-tabs { |
| border: 0 !important; |
| box-shadow: none !important; |
| margin: 8px 0 0 !important; |
| padding: 0 !important; |
| } |
| #leaderboard-view-tabs > .tab-nav { |
| background: transparent !important; |
| border-bottom: 1px solid var(--povisle-line) !important; |
| justify-content: flex-start !important; |
| margin: 0 0 16px !important; |
| max-width: none !important; |
| padding: 0 !important; |
| } |
| #leaderboard-view-tabs > .tab-nav button { |
| font-size: 0.94rem !important; |
| font-weight: 700 !important; |
| padding: 10px 14px !important; |
| } |
| #leaderboard-view-tabs > .tabitem { |
| border: 0 !important; |
| padding: 0 !important; |
| } |
| .leaderboard-split-tabs { |
| border: 0 !important; |
| box-shadow: none !important; |
| margin: 4px 0 0 !important; |
| padding: 0 !important; |
| } |
| .leaderboard-split-tabs .tab-nav { |
| background: transparent !important; |
| border-bottom: 1px solid var(--povisle-line) !important; |
| justify-content: flex-start !important; |
| margin: 0 0 14px !important; |
| max-width: none !important; |
| padding: 0 !important; |
| } |
| .leaderboard-split-tabs .tab-nav button { |
| font-size: 0.84rem !important; |
| padding: 8px 12px !important; |
| } |
| .leaderboard-split-tabs .tabitem { |
| border: 0 !important; |
| padding: 0 !important; |
| } |
| .leaderboard-table { |
| margin-top: 0 !important; |
| } |
| """ |
| APP_HEAD = """ |
| <link rel="preconnect" href="https://fonts.googleapis.com"> |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> |
| <link href="https://fonts.googleapis.com/css2?family=Source+Serif+4:ital,opsz,wght@0,8..60,300;0,8..60,400;0,8..60,600;0,8..60,700;1,8..60,300;1,8..60,400&family=DM+Sans:ital,wght@0,300;0,400;0,500;0,600;1,300&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet"> |
| """ |
| HERO_HTML = """ |
| <section class="povisle-hero"> |
| <h1> |
| <em>Jako Tako</em> or Fluent? Presenting <span class="povisle-title-accent">PoVisLE</span> |
| </h1> |
| <p> |
| We introduce PoVisLE, a monocultural vision-language benchmark for Polish designed to evaluate |
| culturally grounded multimodal understanding under a grounded evaluation paradigm, where language |
| is interpreted in interaction with visual context. The dataset contains 1,117 images and 2,366 |
| manually annotated VQA pairs. |
| </p> |
| <div class="povisle-authors"> |
| <strong>Anna Kołos</strong> · <strong>Grzegorz Statkiewicz</strong> · <strong>Karolina Seweryn</strong> · |
| <strong>Katarzyna Kowol</strong> · <strong>Karolina Piosek</strong> · <strong>Wojciech Kusa</strong><br> |
| NASK National Research Institute, Warsaw, Poland |
| </div> |
| <div class="povisle-links-row"> |
| <a href="https://huggingface.co/collections/NASK-PIB/povisle" class="povisle-btn povisle-btn-dataset" target="_blank"> |
| <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/></svg> |
| Dataset (Validation) |
| </a> |
| <a href="https://github.com/NASK-NLP/PoVisLE" class="povisle-btn povisle-btn-outline" target="_blank"> |
| <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg> |
| Code |
| </a> |
| <a href="https://arxiv.org/abs/2608.07763" class="povisle-btn povisle-btn-outline" target="_blank"> |
| <svg class="arxiv-icon" width="23" height="23" viewBox="0 0 448 512" aria-hidden="true"><path fill="currentColor" d="m 119.65,351.996 c -5.84961,0.13477 -11.1943,-3.29883 -13.502,-8.67578 -2.19629,-5.27051 -0.61914,-8.9668 4.19727,-15.8652 7.05469,-10.3799 78.8242,-96.5625 78.8242,-96.5625 l -15.8828,-14.8633 c -13.3809,-13.3779 -13.9561,-31.333 -1.50977,-43.7754 l 18.4922,-17.6133 -51.5977,-63.377 c -4.00586,-4.26758 -6.48535,-11.7559 -4.24805,-17.1309 2.27832,-5.53613 7.69727,-9.12891 13.6836,-9.07031 3.83398,0.0957 7.43262,1.87402 9.83789,4.86133 l 61.3691,57.0566 94.5762,-90.0703 c 3.19434,-3.08398 7.44336,-4.83984 11.8828,-4.91016 1.60449,0.0039 3.19824,0.245117 4.73242,0.714844 5.7793,1.80566 10.249,6.41797 11.8711,12.252 1.2998,5.47363 -0.27637,11.2334 -4.18164,15.2832 l -83.0859,100.011996 14.8789,13.834 c 11.0957,10.0029 11.1543,27.3906 0.12695,37.4688 l -16.2949,15.6309 56.2559,66.4434 0.0742,0.0859 0.0664,0.0899 c 5.02734,6.53125 7.43164,11.5615 4.83984,17.9395 -3.13379,5.96875 -8.69727,10.29 -15.2559,11.8516 -0.67676,0.0908 -1.35938,0.13574 -2.04297,0.13671 l -0.004,-0.0117 c -4.42871,-0.27051 -8.61035,-2.13086 -11.7754,-5.24023 l -0.13086,-0.10743 -0.12304,-0.11132 -65.207,-59.127 -89.8965,86.2402 c 0,0 -5.33398,6.47754 -10.9707,6.61133 z m 178.104,-33.041 c 0.47852,-0.002 0.95703,-0.0332 1.43164,-0.0957 4.84277,-1.33301 8.95605,-4.54004 11.4316,-8.91016 1.44922,-3.5625 1.00293,-6.45703 -4.19727,-13.2148 l -56.0586,-66.2188 -26.375,25.3027 64.9551,58.9062 c 2.33887,2.41504 5.46484,3.91113 8.8125,4.2168 v 0.0137 z M 192.436,227.402 334.446,57.244 c 2.78711,-3.48926 4.5293,-6.97949 3.3418,-10.9121 -1.16113,-4.30273 -4.43457,-7.71777 -8.68555,-9.05664 -1.08105,-0.333984 -2.20508,-0.503906 -3.33594,-0.505859 -3.20801,0.07617 -6.26953,1.35645 -8.57617,3.58789 l -142.033,135.284999 c -11.1719,11.1719 -9.48242,26.0195 1.39258,36.8945 z"/></svg> |
| Paper |
| </a> |
| </div> |
| <div class="povisle-stat-row" aria-label="Dataset summary"> |
| <div class="povisle-stat"><strong>1,117</strong><span>images</span></div> |
| <div class="povisle-stat"><strong>2,366</strong><span>questions</span></div> |
| <div class="povisle-stat"><strong>7</strong><span>main categories</span></div> |
| <div class="povisle-stat"><strong>3</strong><span>task formats</span></div> |
| </div> |
| </section> |
| """ |
| DATASET_CREATION_IMAGE_URL = f"/gradio_api/file={quote(str((Path('public') / 'dataset.png').resolve()))}" |
| CATEGORY_SUNBURST_URL = f"/gradio_api/file={quote(str((Path('public') / 'categories_sunburst.png').resolve()))}" |
| DATASET_DESCRIPTION_HTML = f""" |
| <section class="povisle-dataset-card"> |
| <div class="povisle-section-label">Dataset</div> |
| <h2 class="povisle-section-title">About the Benchmark</h2> |
| <p> |
| PoVisLE is a monocultural vision-language evaluation benchmark centered on Polish cultural |
| and linguistic competence. It is designed for grounded evaluation: answers should depend on |
| the interaction between the image and the question, rather than on text-only associations or |
| surface-level entity recognition. |
| |
| </p> |
| <div class="povisle-about-grid"> |
| <div class="povisle-about-copy"> |
| <h3>Tasks</h3> |
| <div class="povisle-task-layout"> |
| <div> |
| <p> |
| During annotation, each VQA pair is assigned one of three task types. These |
| are multiple-choice, binary yes/no, and open-ended questions. All questions |
| are designed to require image understanding, and the answer should not be |
| obtainable from textual knowledge alone without reference to the image. |
| </p> |
| <ul class="povisle-task-list"> |
| <li><strong>Multiple-choice questions</strong> include answer options appended below the question with letter labels.</li> |
| <li><strong>Yes/no questions</strong> use the raw question as the prompt and are evaluated as binary answers.</li> |
| <li><strong>Open-ended questions</strong> also use the raw question as the prompt, without appended answer options.</li> |
| </ul> |
| </div> |
| <div class="povisle-task-chart" aria-label="Question distribution by task format and split"> |
| <div class="povisle-task-chart-legend" aria-hidden="true"> |
| <span style="--segment-color: #d7263d;">Multiple choice</span> |
| <span style="--segment-color: #2c5f8a;">Yes/no</span> |
| <span style="--segment-color: #2a7a4e;">Open-ended</span> |
| </div> |
| <div class="povisle-task-split"> |
| <div class="povisle-task-split-header"> |
| <span>Test split</span> |
| <strong>1,960</strong> |
| </div> |
| <div class="povisle-task-chart-track" title="Test: 643 multiple-choice, 714 yes/no, 603 open-ended"> |
| <div class="povisle-task-chart-segment" style="--segment-color: #d7263d; width: 32.8%;" aria-label="Multiple choice: 643, 32.8%">643</div> |
| <div class="povisle-task-chart-segment" style="--segment-color: #2c5f8a; width: 36.4%;" aria-label="Yes/no: 714, 36.4%">714</div> |
| <div class="povisle-task-chart-segment" style="--segment-color: #2a7a4e; width: 30.8%;" aria-label="Open-ended: 603, 30.8%">603</div> |
| </div> |
| </div> |
| <div class="povisle-task-split"> |
| <div class="povisle-task-split-header"> |
| <span>Validation split</span> |
| <strong>406</strong> |
| </div> |
| <div class="povisle-task-chart-track" title="Validation: 212 multiple-choice, 154 yes/no, 40 open-ended"> |
| <div class="povisle-task-chart-segment" style="--segment-color: #d7263d; width: 52.2%;" aria-label="Multiple choice: 212, 52.2%">212</div> |
| <div class="povisle-task-chart-segment" style="--segment-color: #2c5f8a; width: 37.9%;" aria-label="Yes/no: 154, 37.9%">154</div> |
| <div class="povisle-task-chart-segment" style="--segment-color: #2a7a4e; width: 9.9%;" aria-label="Open-ended: 40, 9.9%">40</div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| <div class="povisle-about-copy"> |
| <h3>Categories</h3> |
| <div class="povisle-category-layout"> |
| <div> |
| <p class="povisle-category-intro"> |
| The taxonomy is adapted from PLCC and refined for the VQA setting. Grammar |
| and vocabulary are merged into a unified Language category, while the hierarchy |
| supports fine-grained diagnostics across cultural, linguistic, and visual |
| reasoning domains. The dataset is organized into the following main categories. |
| </p> |
| <ul class="povisle-category-list"> |
| <li><strong>Art and Entertainment</strong> covers Polish and Poland-related artistic, media, and cultural references, including architecture, film, literature, music, paintings, sculpture, sport, and media.</li> |
| <li><strong>Culture and Tradition</strong> covers shared customs, practices, symbols, cuisine, religion, traditions, pop culture, and regional or ethnic cultural variation.</li> |
| <li><strong>Geography and Nature</strong> covers Polish physical, natural, urban, infrastructural, and socio-political spaces, including landscapes, landmarks, regions, and administrative entities.</li> |
| <li><strong>History and Society</strong> covers historical and contemporary Polish social context, including the Middle Ages, World War II, post-war history, and current affairs.</li> |
| <li><strong>Language</strong> covers visually grounded Polish linguistic phenomena, such as colloquial speech, slang, dialects, regionalisms, grammar, orthography, phraseology, rhetorical figures, and semantics.</li> |
| <li><strong>Image Understanding</strong> focuses on direct recognition and interpretation of visual content in Polish or Poland-related contexts.</li> |
| <li><strong>Visual Reasoning</strong> focuses on relationships, context, and inferred information within an image.</li> |
| </ul> |
| <p class="povisle-category-note"> |
| Image Understanding and Visual Reasoning form a smaller complementary subset |
| focused on general multimodal skills while remaining embedded in Polish visual |
| and linguistic contexts. |
| </p> |
| </div> |
| <div class="povisle-angled-sunburst" aria-label="Static sunburst of categories and subcategories"> |
| <img src="{CATEGORY_SUNBURST_URL}" alt="PoVisLE category and subcategory distribution"> |
| </div> |
| </div> |
| </div> |
| </div> |
| <div class="povisle-about-copy"> |
| <h3>Dataset Creation</h3> |
| <div class="povisle-creation-layout"> |
| <div> |
| <p> |
| PoVisLE was created through manual, template-free annotation. Annotators |
| selected or reviewed images from Wikimedia Commons, other permissively |
| available public sources, and personal collections contributed for research |
| use. Each image was paired with one or more Polish VQA prompts and labeled |
| with a task type, category, and subcategory. |
| </p> |
| <p> |
| The dataset construction also included a Wikimedia-based augmentation stage |
| to increase visual diversity and reduce selection bias. Candidate images |
| were reviewed by annotators, and visually similar replacements were used |
| only when the original question remained answerable from the new image. |
| </p> |
| <p> |
| Quality assurance included cross-validation by a second annotator, metadata |
| and license checks, regular team discussion, and supervision by an expert |
| annotator. The annotation guidelines required questions to be visually |
| grounded, unambiguous, linguistically natural, and suitable for deterministic |
| evaluation. |
| </p> |
| <p> |
| The benchmark is divided into a held-out test split for final evaluation and |
| a public validation split. The validation split is intended mainly to |
| illustrate the range of question types, so it is not sampled from the same |
| distribution as the test set. In particular, it contains fewer open-ended |
| questions, which are largely retained in the held-out test split. |
| </p> |
| </div> |
| <div class="povisle-creation-figure"> |
| <img src="{DATASET_CREATION_IMAGE_URL}" alt="Overview of the PoVisLE dataset construction process"> |
| </div> |
| </div> |
| </div> |
| <div class="povisle-about-copy"> |
| <h3>Evaluation Protocol</h3> |
| <p> |
| Models receive the image and a Polish prompt specifying the expected answer format, length, |
| word order, and, where relevant, grammatical form. Macro accuracy is the main metric. |
| Multiple-choice questions use circular evaluation: answer options are cyclically rotated, and |
| a prediction is counted as correct only if the model selects the gold answer under every |
| rotation. This reduces option-position bias while keeping evaluation efficient. |
| </p> |
| <p> |
| Yes/no and open-ended questions are evaluated in a single pass. Yes/no questions require a |
| binary answer in Polish. Open-ended predictions are compared against the gold answers, with |
| correct diacritics required in all cases and correct capitalization required where relevant. |
| For selected questions, multiple answer variants are accepted through predefined inclusion |
| patterns developed through iterative human validation. |
| </p> |
| </div> |
| </section> |
| """ |
| LEADERBOARD_SPLITS = ("test", "validation") |
| RESULTS_REPO = os.getenv("RESULTS_REPO", "NASK-PIB/PoVisLE-results") |
| SOURCE_DATASET_REPO = os.getenv("SOURCE_DATASET_REPO", "NASK-PIB/PoVisLE") |
| SOURCE_DATASET_CONFIGS = [ |
| config.strip() |
| for config in os.getenv("SOURCE_DATASET_CONFIGS", "mcq,open,yn").split(",") |
| if config.strip() |
| ] |
| EXAMPLES_DATASET_SPLITS = [ |
| split.strip() |
| for split in os.getenv("EXAMPLES_DATASET_SPLITS", os.getenv("EXAMPLES_DATASET_SPLIT", "validation")).split(",") |
| if split.strip() |
| ] |
| SOURCE_DATASET_REVISION = os.getenv("SOURCE_DATASET_REVISION", "v1.2.0") |
| HF_TOKEN = os.getenv("HF_TOKEN") |
| CACHE_PATH = Path(os.getenv("HF_HOME", ".")).expanduser() |
| LOCAL_RESULTS_PATH = Path(os.getenv("LOCAL_RESULTS_PATH", CACHE_PATH / "PoVisLE-results")).expanduser() |
| PUBLIC_PATH = Path("public").expanduser() |
| PUBLIC_IMAGES_PATH = Path(os.getenv("PUBLIC_IMAGES_PATH", PUBLIC_PATH / "images")).expanduser() |
| LEADERBOARD_RELATIVE_PATH = Path("leaderboard") |
| BENCHMARK_VERSION = os.getenv("BENCHMARK_VERSION", "v1.2.0") |
| CATEGORY_DISPLAY_ORDER = [ |
| "Art & Entertainment", |
| "Culture & Tradition", |
| "Geography & Nature", |
| "History & Society", |
| "Language", |
| "Image Understanding", |
| "Visual Reasoning", |
| ] |
| CATEGORY_ORDER_ALIASES = { |
| "art entertainment": "Art & Entertainment", |
| "art and entertainment": "Art & Entertainment", |
| "culture tradition": "Culture & Tradition", |
| "culture and tradition": "Culture & Tradition", |
| "geography nature": "Geography & Nature", |
| "geography and nature": "Geography & Nature", |
| "history society": "History & Society", |
| "history and society": "History & Society", |
| "language": "Language", |
| "image understanding": "Image Understanding", |
| "visual reasoning": "Visual Reasoning", |
| } |
|
|
|
|
| def model_size_bucket(model_size: str | None) -> str: |
| if not model_size: |
| return "Unknown" |
|
|
| normalized = str(model_size).strip().upper() |
| if not normalized.endswith("B"): |
| return "Unknown" |
|
|
| try: |
| size_value = float(normalized[:-1]) |
| except ValueError: |
| return "Unknown" |
|
|
| if size_value < 10: |
| return "1-10B" |
| if size_value < 20: |
| return "10-20B" |
| if size_value < 40: |
| return "20-40B" |
| if size_value <= 100: |
| return "30-100B" |
| return ">100B" |
|
|
|
|
| def result_file_patterns() -> list[str]: |
| return [ |
| f"leaderboard/*/*/{BENCHMARK_VERSION}/test/circular_circular/results.json", |
| f"leaderboard/*/*/{BENCHMARK_VERSION}/validation/circular_circular/results.json", |
| ] |
|
|
|
|
| def matching_result_files(repo_files: list[str], patterns: list[str]) -> list[str]: |
| return sorted(path for path in repo_files if any(fnmatch(path, pattern) for pattern in patterns)) |
|
|
|
|
| def refresh_results_from_hub(local_results_path: Path) -> None: |
| patterns = result_file_patterns() |
| repo_files = HfApi(token=HF_TOKEN).list_repo_files(repo_id=RESULTS_REPO, repo_type="dataset") |
| matched_files = matching_result_files(repo_files, patterns) |
| if not matched_files: |
| raise FileNotFoundError( |
| f"No result files matching {patterns} were found in dataset {RESULTS_REPO}. " |
| f"Check RESULTS_REPO, BENCHMARK_VERSION={BENCHMARK_VERSION}, and HF_TOKEN access." |
| ) |
|
|
| local_results_path.mkdir(parents=True, exist_ok=True) |
| for filename in matched_files: |
| hf_hub_download( |
| repo_id=RESULTS_REPO, |
| filename=filename, |
| repo_type="dataset", |
| local_dir=str(local_results_path), |
| etag_timeout=30, |
| token=HF_TOKEN, |
| ) |
|
|
|
|
| def resolve_results_dir() -> Path: |
| env_path = os.environ.get("VPLCC_RESULTS_DIR") |
| if env_path: |
| candidate = Path(env_path).expanduser() |
| if candidate.exists(): |
| return candidate |
|
|
| try: |
| refresh_results_from_hub(LOCAL_RESULTS_PATH) |
| except Exception as error: |
| fallback = LOCAL_RESULTS_PATH / LEADERBOARD_RELATIVE_PATH |
| if fallback.exists(): |
| return fallback |
| raise FileNotFoundError( |
| f"Could not download results dataset from {RESULTS_REPO} and no local cache was found. ({error})" |
| ) from error |
|
|
| leaderboard_path = LOCAL_RESULTS_PATH / LEADERBOARD_RELATIVE_PATH |
| if leaderboard_path.exists(): |
| return leaderboard_path |
| raise FileNotFoundError(f"Could not find leaderboard directory inside downloaded dataset: {leaderboard_path}") |
|
|
|
|
| def format_category_label(category_name: str) -> str: |
| return str(category_name).replace("_", " ").title() |
|
|
|
|
| def make_subcategory_column_key(category_name: str, subcategory_name: str) -> str: |
| return f"__subcategory__::{category_name}::{subcategory_name}" |
|
|
|
|
| def percentage(value: float) -> float: |
| return round(float(value) * 100, 2) |
|
|
|
|
| def build_category_scores(metrics: dict) -> dict[str, float]: |
| return { |
| format_category_label(category_name): percentage(category_metrics["macro_accuracy"]) |
| for category_name, category_metrics in metrics.get("by_category", {}).items() |
| } |
|
|
|
|
| def build_subcategory_scores(metrics: dict) -> tuple[dict[str, float], dict[str, list[tuple[str, str]]]]: |
| subcategory_scores: dict[str, float] = {} |
| subcategory_groups: dict[str, list[tuple[str, str]]] = {} |
|
|
| for category_name, subcategory_metrics in metrics.get("by_category_and_subcategory", {}).items(): |
| category_label = format_category_label(category_name) |
| group_columns: list[tuple[str, str]] = [] |
|
|
| for subcategory_name, details in subcategory_metrics.items(): |
| column_key = make_subcategory_column_key(category_label, subcategory_name) |
| subcategory_label = format_category_label(subcategory_name) |
| group_columns.append((column_key, subcategory_label)) |
| subcategory_scores[column_key] = percentage(details["macro_accuracy"]) |
|
|
| if group_columns: |
| subcategory_groups[category_label] = group_columns |
|
|
| return subcategory_scores, subcategory_groups |
|
|
|
|
| def build_task_scores(metrics: dict) -> dict[str, float]: |
| task_scores = {} |
| task_column_labels = { |
| "mcq": "MCQ", |
| "yn": "Y/N", |
| "open": "Open", |
| } |
| for task_name, task_metrics in metrics.get("by_task", {}).items(): |
| normalized_task_name = normalize_result_task_name(task_name) |
| column_label = task_column_labels.get(normalized_task_name) |
| if column_label: |
| task_scores[column_label] = percentage(task_metrics["accuracy"]) |
| return task_scores |
|
|
|
|
| def iter_results_paths(results_dir: Path, split: str): |
| return sorted(results_dir.glob(f"*/*/{BENCHMARK_VERSION}/{split}/circular_circular/results.json")) |
|
|
|
|
| def normalize_result_task_name(task_name: object) -> str: |
| normalized = str(task_name).strip().lower().replace("_", "-") |
| if normalized in {"yes/no", "yes-no", "yesno"}: |
| return "yn" |
| return normalized |
|
|
|
|
| def results_path_priority(results_path: Path, payload: dict) -> tuple[int, int, int, int, str]: |
| metadata = payload.get("run_metadata", {}) |
| metrics = payload.get("metrics", {}) |
| mode = str(metadata.get("evaluation_mode") or results_path.parent.name).lower().replace("-", "_") |
| ablation_markers = ("no_image", "without_image", "no_question", "without_question") |
| is_ablation = any(metadata.get(marker) for marker in ablation_markers) or any( |
| marker in mode for marker in ablation_markers |
| ) |
| full_input_priority = 0 if is_ablation else 1 |
| expected_tasks = {normalize_result_task_name(task_name) for task_name in SOURCE_DATASET_CONFIGS} |
| result_tasks = { |
| normalize_result_task_name(task_name) |
| for task_name in (metadata.get("tasks") or metrics.get("by_task", {}).keys()) |
| } |
| all_tasks_priority = 1 if expected_tasks and expected_tasks.issubset(result_tasks) else 0 |
| total_priority = int(metrics.get("overall", {}).get("total") or 0) |
| default_mode_priority = 1 if mode == "circular_circular" else 0 |
| return full_input_priority, all_tasks_priority, total_priority, default_mode_priority, str(results_path) |
|
|
|
|
| def sort_category_columns(category_labels: set[str]) -> list[str]: |
| priority = {label: index for index, label in enumerate(CATEGORY_DISPLAY_ORDER)} |
|
|
| def category_sort_key(label: str) -> tuple[int, str]: |
| normalized = re.sub(r"[^a-z0-9]+", " ", str(label).lower()).strip() |
| canonical_label = CATEGORY_ORDER_ALIASES.get(normalized, label) |
| return priority.get(canonical_label, len(CATEGORY_DISPLAY_ORDER)), label |
|
|
| return sorted(category_labels, key=category_sort_key) |
|
|
|
|
| def safe_image_filename(task_id: object, index: object | None = None) -> str: |
| raw_name = str(task_id) |
| if index is not None: |
| raw_name = f"{raw_name}-{index}" |
| safe_name = re.sub(r"[^A-Za-z0-9_.-]+", "_", raw_name).strip("._") |
| return f"{safe_name or 'image'}.png" |
|
|
|
|
| def public_image_path(task_id: object, index: object | None = None) -> Path: |
| return PUBLIC_IMAGES_PATH.resolve() / safe_image_filename(task_id, index) |
|
|
|
|
| def public_file_url(path: Path) -> str: |
| return f"/gradio_api/file={quote(str(path.resolve()))}" |
|
|
|
|
| def pil_image_from_value(image: object): |
| if image is None: |
| return None |
|
|
| try: |
| from PIL import Image |
| except ImportError: |
| return None |
|
|
| image_bytes: bytes | None = None |
|
|
| if isinstance(image, dict): |
| raw_bytes = image.get("bytes") |
| image_path = image.get("path") |
| if raw_bytes: |
| image_bytes = raw_bytes |
| elif image_path: |
| candidate = Path(image_path) |
| if candidate.exists(): |
| image_bytes = candidate.read_bytes() |
| elif isinstance(image, bytes): |
| image_bytes = image |
| elif isinstance(image, str): |
| if image.startswith("data:image/") and "," in image: |
| image = image.split(",", 1)[1] |
| try: |
| candidate = Path(image) |
| if len(image) < 512 and candidate.exists(): |
| image_bytes = candidate.read_bytes() |
| except OSError: |
| image_bytes = None |
| if image_bytes is None: |
| try: |
| image_bytes = base64.b64decode(image, validate=True) |
| except Exception: |
| image_bytes = None |
| elif hasattr(image, "copy") and hasattr(image, "save"): |
| return image.copy() |
|
|
| if image_bytes is None: |
| return None |
|
|
| try: |
| return Image.open(BytesIO(image_bytes)).copy() |
| except Exception: |
| return None |
|
|
|
|
| def save_image_thumbnail(image: object, output_path: Path, max_size: tuple[int, int] = (128, 128)) -> bool: |
| if output_path.exists(): |
| return True |
|
|
| pil_image = pil_image_from_value(image) |
| if pil_image is None: |
| return False |
|
|
| thumbnail = pil_image.copy() |
| thumbnail.thumbnail(max_size) |
| if thumbnail.mode not in {"RGB", "RGBA"}: |
| thumbnail = thumbnail.convert("RGB") |
|
|
| output_path.parent.mkdir(parents=True, exist_ok=True) |
| thumbnail.save(output_path, format="PNG") |
| return True |
|
|
|
|
| def image_file_to_html(image_path: Path) -> str: |
| if not image_path.exists(): |
| return "" |
|
|
| return f'<img class="example-image" src="{public_file_url(image_path)}" alt="task image" loading="lazy">' |
|
|
|
|
| def format_choices(item: dict[str, object]) -> str: |
| choices = [] |
| for label in "ABCDEFGH": |
| value = item.get(label) |
| if value is None or pd.isna(value): |
| continue |
| text = str(value).strip() |
| if text: |
| choices.append(f"{label}. {text}") |
| return "\n".join(choices) |
|
|
|
|
| def format_example_value(value: object) -> object: |
| if isinstance(value, (list, dict)): |
| return json.dumps(value, ensure_ascii=False) |
| if value is None or pd.isna(value): |
| return "" |
| return value |
|
|
|
|
| def load_examples_rows() -> list[dict[str, object]]: |
| try: |
| from datasets import load_dataset |
| except ImportError as error: |
| print(f"Could not load examples because datasets is not installed: {error}") |
| return [] |
|
|
| rows: list[dict[str, object]] = [] |
| loaded_sources: set[tuple[str, str]] = set() |
| for config_name in SOURCE_DATASET_CONFIGS: |
| dataset = None |
| selected_split = None |
| errors: list[str] = [] |
| for split_name in EXAMPLES_DATASET_SPLITS: |
| load_kwargs = { |
| "path": SOURCE_DATASET_REPO, |
| "name": config_name, |
| "split": split_name, |
| "token": HF_TOKEN, |
| } |
| if SOURCE_DATASET_REVISION: |
| load_kwargs["revision"] = SOURCE_DATASET_REVISION |
|
|
| try: |
| dataset = load_dataset(**load_kwargs) |
| selected_split = split_name |
| break |
| except Exception as error: |
| errors.append(f"{split_name}: {error}") |
|
|
| if dataset is None or selected_split is None: |
| print(f"Could not load examples from {SOURCE_DATASET_REPO}/{config_name}. Tried {', '.join(errors)}.") |
| continue |
|
|
| loaded_sources.add((config_name, selected_split)) |
| for item in dataset: |
| image_path = public_image_path(item["id"]) |
| image_html = image_file_to_html(image_path) if save_image_thumbnail(item.get("image"), image_path) else "" |
| rows.append( |
| { |
| "id": item["id"], |
| "image": image_html, |
| "task": item.get("task") or config_name, |
| "category": item.get("category"), |
| "subcategory": item.get("subcategory"), |
| "question": item.get("question"), |
| "choices": format_choices(item), |
| "answer": item.get("answer"), |
| "include": format_example_value(item.get("include")), |
| "check_casing": format_example_value(item.get("check_casing")), |
| "check_diacritics": format_example_value(item.get("check_diacritics")), |
| } |
| ) |
|
|
| if rows: |
| sources = ", ".join(f"{config}/{split}" for config, split in sorted(loaded_sources)) |
| print(f"Loaded {len(rows)} examples from {SOURCE_DATASET_REPO}: {sources}.") |
| else: |
| print(f"No examples were loaded from {SOURCE_DATASET_REPO}.") |
| return rows |
|
|
|
|
| def load_dashboard_data() -> tuple[pd.DataFrame, list[str], dict[str, list[tuple[str, str]]], pd.DataFrame, str]: |
| results_dir = resolve_results_dir() |
| leaderboard_rows = [] |
| latest_results_by_model: dict[tuple[str, str], tuple[Path, tuple[int, int, int, int, str]]] = {} |
| category_labels: set[str] = set() |
| all_subcategory_groups: dict[str, list[tuple[str, str]]] = {} |
|
|
| for split in LEADERBOARD_SPLITS: |
| for results_path in iter_results_paths(results_dir, split): |
| payload = json.loads(results_path.read_text(encoding="utf-8")) |
| metadata = payload["run_metadata"] |
| metadata_split = metadata.get("split") |
| if metadata_split and str(metadata_split).lower() != split: |
| continue |
| model_name = metadata["model_name"] |
| if model_name in EXCLUDED_MODELS: |
| continue |
| priority = results_path_priority(results_path, payload) |
| key = (split, model_name) |
| if key not in latest_results_by_model or priority > latest_results_by_model[key][1]: |
| latest_results_by_model[key] = (results_path, priority) |
|
|
| for (split, model_name), (results_path, _) in sorted(latest_results_by_model.items()): |
| payload = json.loads(results_path.read_text(encoding="utf-8")) |
| metadata = payload["run_metadata"] |
| metrics = payload["metrics"] |
| category_scores = build_category_scores(metrics) |
| subcategory_scores, subcategory_groups = build_subcategory_scores(metrics) |
| task_scores = build_task_scores(metrics) |
| overall_score = percentage(metrics["overall"]["macro_accuracy"]) |
| category_labels.update(category_scores) |
| for category_label, columns in subcategory_groups.items(): |
| known_column_keys = {column_key for column_key, _ in all_subcategory_groups.get(category_label, [])} |
| all_subcategory_groups.setdefault(category_label, []) |
| for column_key, column_label in columns: |
| if column_key not in known_column_keys: |
| all_subcategory_groups[category_label].append((column_key, column_label)) |
| known_column_keys.add(column_key) |
| leaderboard_rows.append( |
| { |
| "Split": split.title(), |
| "Org": metadata.get("org", "unknown"), |
| "Model": model_name, |
| "Type": MODEL_TYPE_LABELS.get(metadata["model_type"], metadata["model_type"].title()), |
| "Family": metadata.get("model_family", "unknown"), |
| "Size": metadata.get("model_size", "unknown"), |
| "Size range": model_size_bucket(metadata.get("model_size")), |
| "Overall": overall_score, |
| **task_scores, |
| **category_scores, |
| **subcategory_scores, |
| } |
| ) |
|
|
| category_columns = sort_category_columns(category_labels) |
| leaderboard = pd.DataFrame(leaderboard_rows) |
| for task_column in ["MCQ", "Y/N", "Open"]: |
| if task_column not in leaderboard: |
| leaderboard[task_column] = pd.NA |
| for category_column in category_columns: |
| if category_column not in leaderboard: |
| leaderboard[category_column] = pd.NA |
| for subcategory_columns in all_subcategory_groups.values(): |
| for column_key, _ in subcategory_columns: |
| if column_key not in leaderboard: |
| leaderboard[column_key] = pd.NA |
|
|
| leaderboard = leaderboard.sort_values(by=["Split", "Overall", *category_columns], ascending=[True, False, *([False] * len(category_columns))]) |
| leaderboard.insert(0, "Rank", range(1, len(leaderboard) + 1)) |
| examples_df = pd.DataFrame( |
| load_examples_rows(), |
| columns=[ |
| "id", |
| "image", |
| "task", |
| "category", |
| "subcategory", |
| "question", |
| "choices", |
| "answer", |
| "include", |
| "check_casing", |
| "check_diacritics", |
| ], |
| ) |
| return leaderboard, category_columns, all_subcategory_groups, examples_df, str(results_dir) |
|
|
|
|
| PUBLIC_IMAGES_PATH.mkdir(parents=True, exist_ok=True) |
| gr.set_static_paths(paths=[PUBLIC_PATH.resolve(), PUBLIC_IMAGES_PATH.resolve()]) |
|
|
| LEADERBOARD_DF, CATEGORY_COLUMNS, SUBCATEGORY_GROUPS, EXAMPLES_DF, RESULTS_DIR = load_dashboard_data() |
|
|
| with gr.Blocks( |
| title="PoVisLE", |
| ) as demo: |
| gr.HTML(HERO_HTML) |
| with gr.Group(elem_id="leaderboard-section"): |
| gr.HTML( |
| """ |
| <section class="povisle-leaderboard-heading"> |
| <div class="povisle-section-label">Results</div> |
| <h2 class="povisle-section-title">Leaderboard</h2> |
| <p>Compare model performance by category or task type across the test and validation splits. Overall and category scores report <strong>macro accuracy</strong>, while task columns report task-level accuracy.</p> |
| </section> |
| """ |
| ) |
| search_box, type_filter, size_filter, family_filter = render_leaderboard_filters(LEADERBOARD_DF) |
| with gr.Tabs(elem_id="leaderboard-view-tabs"): |
| with gr.Tab("By category"): |
| render_leaderboard_category_tab( |
| LEADERBOARD_DF, |
| CATEGORY_COLUMNS, |
| SUBCATEGORY_GROUPS, |
| search_box, |
| type_filter, |
| size_filter, |
| family_filter, |
| ) |
| with gr.Tab("By task"): |
| render_leaderboard_task_tab( |
| LEADERBOARD_DF, |
| search_box, |
| type_filter, |
| size_filter, |
| family_filter, |
| ) |
| gr.HTML(DATASET_DESCRIPTION_HTML) |
| render_examples_tab(EXAMPLES_DF) |
| gr.HTML( |
| """ |
| <section class="povisle-acknowledgement"> |
| <h2 class="povisle-section-title">Acknowledgement</h2> |
| <p>This work was supported by the Polish Ministry of Digital Affairs (subsidy no. 4/WII/DBI/2026). The computational resources were provided by the Polish high-performance computing infrastructure PLGrid (HPC Center: ACK Cyfronet AGH) under computational grant no. PLG/2026/019138.</p> |
| </section> |
| <section class="povisle-citation"> |
| <h2 class="povisle-section-title">Citation</h2> |
| <pre>@article{kolos2026povisle, |
| title = {Jako Tako or Fluent? Presenting PoVisLE: A Polish Vision-Language Evaluation}, |
| author = {Ko{\\l}os, Anna and Statkiewicz, Grzegorz and Seweryn, Karolina and Kowol, Katarzyna and Piosek, Karolina and Kusa, Wojciech}, |
| journal = {arXiv preprint}, |
| year = {2026} |
| }</pre> |
| </section> |
| """ |
| ) |
|
|
|
|
| if __name__ == "__main__": |
| demo.launch( |
| theme=POVISLE_THEME, |
| css=APP_CSS, |
| head=APP_HEAD, |
| ) |
|
|