import gradio as gr import json from constants import INTRODUCTION_TEXT, DATASETS from utils import ( init_repo, load_data, process_submit, get_datasets_description, get_metrics_html, compute_wer_cer, get_submit_html, ) from styles import LEADERBOARD_CSS ASSETS_HTML = """ """ init_repo() gr.set_static_paths(paths=["."]) with gr.Blocks(css=LEADERBOARD_CSS, theme=gr.themes.Soft()) as demo: gr.HTML( '' ) gr.HTML(ASSETS_HTML) gr.Markdown(INTRODUCTION_TEXT, elem_classes="markdown-text") with gr.Tabs(): with gr.Tab("🏅 Лидерборд"): leaderboard_html = gr.HTML(value=load_data(), every=60) with gr.Tab("📈 Метрики"): gr.HTML(get_metrics_html()) with gr.Group(): gr.Markdown("### Песочница: посчитайте WER/CER на своих строках") with gr.Row(): ref = gr.Textbox(label="Референсный текст", lines=2) hyp = gr.Textbox(label="Гипотеза (распознанный текст)", lines=2) with gr.Row(): normalize = gr.Checkbox( value=True, label="Нормализовать (нижний регистр, без пунктуации)", ) btn_calc = gr.Button("Посчитать") with gr.Row(): out_wer = gr.Number(label="WER, %", precision=2) out_cer = gr.Number(label="CER, %", precision=2) def _ui_compute(ref_text, hyp_text, norm): wer, cer = compute_wer_cer(ref_text or "", hyp_text or "", norm) return wer, cer btn_calc.click( _ui_compute, inputs=[ref, hyp, normalize], outputs=[out_wer, out_cer], ) with gr.Tab("📊 Датасеты"): gr.HTML(get_datasets_description()) with gr.Tab("✉️ Отправить результат"): gr.HTML(get_submit_html()) with gr.Row(): with gr.Column(): model_name = gr.Textbox( label="Название модели *", placeholder="MyAwesomeASRModel" ) link = gr.Textbox( label="Ссылка на модель *", placeholder="https://huggingface.co/username/model", ) license_field = gr.Textbox( label="Лицензия *", placeholder="MIT / Apache-2.0 / Closed" ) with gr.Column(): metrics_json = gr.TextArea( label="Метрики JSON *", placeholder='{"Russian_LibriSpeech": {"wer": 0.1234, "cer": 0.0567}, ...}', lines=16, ) submit_btn = gr.Button("🚀 Отправить") output_msg = gr.HTML() def _alert(kind, text): return f'
{text}
' def build_json_and_submit(name, link_, lic, metrics_str): name = (name or "").strip() link_ = (link_ or "").strip() lic = (lic or "").strip() if not name: return ( gr.update(), _alert("error", "Укажите название модели."), name, link_, lic, metrics_str, ) if not link_ or not ( link_.startswith("http://") or link_.startswith("https://") ): return ( gr.update(), _alert( "error", "Ссылка должна начинаться с http:// или https://" ), name, link_, lic, metrics_str, ) if not lic: return ( gr.update(), _alert("error", "Укажите лицензию модели."), name, link_, lic, metrics_str, ) try: metrics = json.loads(metrics_str) except Exception as e: return ( gr.update(), _alert("error", f"Невалидный JSON метрик: {e}"), name, link_, lic, metrics_str, ) if not isinstance(metrics, dict): return ( gr.update(), _alert( "error", "Метрики должны быть объектом JSON с датасетами верхнего уровня.", ), name, link_, lic, metrics_str, ) missing = [ds for ds in DATASETS if ds not in metrics] extra = [k for k in metrics.keys() if k not in DATASETS] if missing: return ( gr.update(), _alert("error", f"Отсутствуют датасеты: {', '.join(missing)}"), name, link_, lic, metrics_str, ) if extra: return ( gr.update(), _alert("error", f"Лишние ключи в метриках: {', '.join(extra)}"), name, link_, lic, metrics_str, ) for ds in DATASETS: entry = metrics.get(ds) if not isinstance(entry, dict): return ( gr.update(), _alert( "error", f"{ds}: значение должно быть объектом с полями wer и cer", ), name, link_, lic, metrics_str, ) for k in ("wer", "cer"): v = entry.get(k) if not isinstance(v, (int, float)): return ( gr.update(), _alert("error", f"{ds}: поле {k} должно быть числом"), name, link_, lic, metrics_str, ) if not (0 <= float(v) <= 1): return ( gr.update(), _alert( "error", f"{ds}: поле {k} должно быть в диапазоне [0, 1]", ), name, link_, lic, metrics_str, ) payload = json.dumps( { "model_name": name, "link": link_, "license": lic, "metrics": metrics, }, ensure_ascii=False, ) updated_html, status_msg, _ = process_submit(payload) if updated_html is None: msg = status_msg.replace("Ошибка:", "").strip() return ( gr.update(), _alert("error", f"Не удалось добавить: {msg}"), name, link_, lic, metrics_str, ) return ( updated_html, _alert("success", "✅ Результат добавлен в лидерборд."), "", "", "", "", ) submit_btn.click( build_json_and_submit, inputs=[model_name, link, license_field, metrics_json], outputs=[ leaderboard_html, output_msg, model_name, link, license_field, metrics_json, ], ) demo.launch()