DOoM-lb / app.py
Anonumous's picture
Update code base and add ruff format
2d440ee
import logging
import os
os.makedirs("tmp", exist_ok=True)
os.environ["TMP_DIR"] = "tmp"
import subprocess
import shutil
import glob
import gradio as gr
import numpy as np
from src.radial.radial import create_plot
from apscheduler.schedulers.background import BackgroundScheduler
from gradio_leaderboard import Leaderboard, SelectColumns
from gradio_space_ci import enable_space_ci
import json
from io import BytesIO
def handle_file_upload(file):
file_path = file.name.split("/")[-1] if "/" in file.name else file.name
logging.info("File uploaded: %s", file_path)
with open(file.name, "r") as f:
v = json.load(f)
return v, file_path
def submit_file(v, file_path, mn, profile: gr.OAuthProfile | None):
"""
Обрабатывает загрузку файлов с результатами пользовательских моделей
Args:
v: Загруженные данные результатов из JSON
file_path: Путь к загруженному файлу
mn: Имя модели, указанное пользователем
profile: Профиль пользователя HuggingFace
Returns:
str: Сообщение об успехе или ошибке
"""
if profile is None:
return "Hub Login Required: Войдите в HuggingFace, чтобы загрузить результаты"
try:
# Проверяем наличие обязательных полей
required_fields = ["score", "math_score", "physics_score"]
for field in required_fields:
if field not in v:
return f"Error: Отсутствует обязательное поле '{field}' в JSON файле"
# Создаем новый объект для сохранения в базе данных
new_file = {
"model_name": profile.username + "/" + mn,
"score": float(v["score"]),
"math_score": float(v["math_score"]),
"physics_score": float(v["physics_score"]),
"total_tokens": int(v.get("total_tokens", 0)),
"evaluation_time": float(v.get("evaluation_time", 0.0)),
"system_prompt": v.get(
"system_prompt", "Вы - полезный помощник по математике и физике. Ответьте на русском языке."
),
}
# Проверка значений на корректность
if not (
0 <= new_file["score"] <= 1 and 0 <= new_file["math_score"] <= 1 and 0 <= new_file["physics_score"] <= 1
):
return "Error: Все значения оценок должны быть в диапазоне от 0 до 1"
# Создаем уникальное имя файла на основе username и имени модели
safe_filename = profile.username + "_" + mn.replace("/", "_").replace(" ", "_") + ".json"
# Создаем JSON в памяти и загружаем его в репозиторий
buf = BytesIO()
buf.write(json.dumps(new_file, ensure_ascii=False).encode("utf-8"))
# Загружаем файл в репозиторий
API.upload_file(
path_or_fileobj=buf,
path_in_repo="model_data/external/" + safe_filename,
repo_id="Vikhrmodels/DeathMath-leaderboard-data",
repo_type="dataset",
)
# Устанавливаем флаг для обновления лидерборда
os.environ[RESET_JUDGEMENT_ENV] = "1"
# Логируем успешную загрузку
logging.info(f"Successfully uploaded model results: {new_file['model_name']}")
return f"Success! Результаты модели '{mn}' успешно отправлены в лидерборд DeathMath."
except Exception as e:
logging.error(f"Error submitting file: {e}")
return f"Error: Произошла ошибка при отправке файла: {str(e)}"
from src.display.about import INTRODUCTION_TEXT, TITLE, LLM_BENCHMARKS_TEXT
from src.display.css_html_js import custom_css
from src.display.utils import (
AutoEvalColumn,
fields,
)
from src.envs import API, H4_TOKEN, HF_HOME, REPO_ID, RESET_JUDGEMENT_ENV
from src.leaderboard.build_leaderboard import build_leadearboard_df, download_openbench, download_dataset
import huggingface_hub
# huggingface_hub.login(token=H4_TOKEN)
os.environ["GRADIO_ANALYTICS_ENABLED"] = "false"
# Configure logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
# Start ephemeral Spaces on PRs (see config in README.md)
enable_space_ci()
# download_openbench()
def restart_space():
API.restart_space(repo_id=REPO_ID)
download_openbench()
def update_plot(selected_models):
return create_plot(selected_models)
def build_demo():
"""
Строит интерфейс лидерборда DeathMath
"""
# Загружаем данные для лидерборда
download_openbench()
# Создаем интерфейс с настройками темы
demo = gr.Blocks(
title="DeathMath Leaderboard",
css=custom_css,
theme=gr.themes.Default(
primary_hue="indigo",
secondary_hue="purple",
),
)
# Получаем данные для лидерборда
leaderboard_df = build_leadearboard_df()
# Строим интерфейс
with demo:
# Заголовок и введение
gr.HTML(TITLE)
gr.Markdown(INTRODUCTION_TEXT, elem_classes="markdown-text")
# Основные вкладки
with gr.Tabs(elem_classes="tab-buttons"):
# Вкладка лидерборда
with gr.TabItem("🏅 Лидерборд", elem_id="llm-benchmark-tab-table", id=0):
gr.Markdown("### Таблица результатов моделей DeathMath")
# Таблица с результатами
Leaderboard(
value=leaderboard_df,
datatype=[c.type for c in fields(AutoEvalColumn)],
select_columns=SelectColumns(
default_selection=[c.name for c in fields(AutoEvalColumn) if c.displayed_by_default],
cant_deselect=[c.name for c in fields(AutoEvalColumn) if c.never_hidden or c.dummy],
label="Выберите колонки для отображения:",
),
search_columns=[
AutoEvalColumn.model.name,
],
)
# Вкладка для отправки результатов
with gr.TabItem("🚀 Отправить результаты", elem_id="submit-tab", id=1):
with gr.Row():
gr.Markdown(LLM_BENCHMARKS_TEXT, elem_classes="markdown-text")
with gr.Column():
model_name_textbox = gr.Textbox(label="Название модели")
file_output = gr.File(label="Перетащите JSON файл с результатами сюда", type="filepath")
uploaded_file = gr.State()
file_path = gr.State()
with gr.Row():
with gr.Column():
out = gr.Textbox("Здесь будет показан статус отправки")
with gr.Column():
login_button = gr.LoginButton(elem_id="oauth-button")
submit_button = gr.Button("Отправить результаты", elem_id="submit_button", variant="primary")
# Обработчики событий
file_output.upload(handle_file_upload, file_output, [uploaded_file, file_path])
submit_button.click(submit_file, [uploaded_file, file_path, model_name_textbox], [out])
# Вкладка с аналитикой
with gr.TabItem("📊 Аналитика", elem_id="analytics-tab", id=2):
with gr.Column():
if len(leaderboard_df) > 0:
model_dropdown = gr.Dropdown(
choices=leaderboard_df["model"].tolist(),
label="Модели",
value=leaderboard_df["model"].tolist()[:5]
if len(leaderboard_df) >= 5
else leaderboard_df["model"].tolist(),
multiselect=True,
info="Выберите модели для сравнения",
)
else:
model_dropdown = gr.Dropdown(
choices=["example/model-1", "example/model-2"],
label="Модели",
value=["example/model-1", "example/model-2"],
multiselect=True,
info="Выберите модели для сравнения",
)
# Вкладки для разных типов визуализации
with gr.Tabs():
with gr.TabItem("Столбчатая диаграмма"):
bar_plot = gr.Plot(create_plot(model_dropdown.value))
with gr.TabItem("Радарная диаграмма"):
from src.radial.radial import create_radar_plot
radar_plot = gr.Plot(create_radar_plot(model_dropdown.value))
# Обновление графиков при изменении выбранных моделей
model_dropdown.change(fn=create_plot, inputs=[model_dropdown], outputs=[bar_plot])
model_dropdown.change(fn=create_radar_plot, inputs=[model_dropdown], outputs=[radar_plot])
return demo
# print(os.system('cd src/gen && ../../.venv/bin/python gen_judgment.py'))
# print(os.system('cd src/gen/ && python show_result.py --output'))
def update_board():
need_reset = os.environ.get(RESET_JUDGEMENT_ENV)
logging.info("Updating the leaderboard: %s", need_reset)
if need_reset != "1":
return
os.environ[RESET_JUDGEMENT_ENV] = "0"
try:
# Загружаем актуальные данные из репозитория
download_dataset("Vikhrmodels/DeathMath-leaderboard-data", "m_data")
logging.info("Successfully downloaded model evaluation data")
# Собираем данные из всех файлов моделей
data_list = []
seen_models = set() # Для отслеживания дубликатов
for file in glob.glob("./m_data/model_data/external/*.json"):
try:
with open(file) as f:
data = json.load(f)
# Проверяем наличие необходимых полей
model_name = data.get("model_name", "")
if not model_name:
logging.error(f"Failed to parse {file}: 'model_name' not found")
continue
# Предотвращаем дублирование моделей
model_base_name = model_name.split("/")[-1].split("_v")[0]
if model_base_name in seen_models:
logging.info(f"Skipping duplicate model: {model_name}")
continue
seen_models.add(model_base_name)
# Добавляем модель в список
data_list.append(
{
"model_name": model_name,
"score": float(data.get("score", 0.0)),
"math_score": float(data.get("math_score", 0.0)),
"physics_score": float(data.get("physics_score", 0.0)),
"total_tokens": int(data.get("total_tokens", 0)),
"evaluation_time": float(data.get("evaluation_time", 0.0)),
"system_prompt": data.get(
"system_prompt",
"Вы - полезный помощник по математике и физике. Ответьте на русском языке.",
),
}
)
except Exception as e:
logging.error(f"Failed to process file {file}: {e}")
continue
# Если есть данные, сортируем их по общему баллу и сохраняем
if data_list:
# Сортируем по общему баллу
data_list.sort(key=lambda x: x["score"], reverse=True)
# Сохраняем обновленный лидерборд
with open("genned.json", "w", encoding="utf-8") as f:
json.dump(data_list, f, ensure_ascii=False, indent=2)
# Загружаем обновленный лидерборд в репозиторий
API.upload_file(
path_or_fileobj="genned.json",
path_in_repo="leaderboard.json",
repo_id="Vikhrmodels/DeathMath-leaderboard-metainfo",
repo_type="dataset",
)
logging.info(f"Updated leaderboard with {len(data_list)} models")
# Генерируем README с таблицей лидерборда
update_readme(data_list)
except Exception as e:
logging.error(f"Error updating leaderboard: {e}")
def update_readme(data_list):
"""
Генерирует README.md с таблицей лидерборда
"""
try:
import pandas as pd
from datetime import datetime
# Создаем DataFrame для удобного форматирования таблицы
df = pd.DataFrame(data_list)
# Форматируем числовые колонки
for col in ["score", "math_score", "physics_score"]:
if col in df.columns:
df[col] = df[col].apply(lambda x: f"{x:.3f}")
if "total_tokens" in df.columns:
df["total_tokens"] = df["total_tokens"].apply(lambda x: f"{int(x):,}")
if "evaluation_time" in df.columns:
df["evaluation_time"] = df["evaluation_time"].apply(lambda x: f"{x:.1f}s")
# Создаем содержимое README
current_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
readme_content = f"""---
title: DeathMath Leaderboard
emoji: 🔢
colorFrom: indigo
colorTo: purple
sdk: gradio
sdk_version: "4.20.0"
app_file: app.py
pinned: false
hf_oauth: true
---
# DeathMath Leaderboard
DeathMath - это бенчмарк для оценки способности моделей решать сложные математические и физические задачи на русском языке.
## Текущий лидерборд
Последнее обновление: {current_date}
| Модель | Общий балл | Математика | Физика | Токены | Время оценки |
|--------|------------|------------|---------|---------|--------------|
"""
# Добавляем строки таблицы
for _, row in df.iterrows():
model_name = row.get("model_name", "")
readme_content += f"| {model_name} | {row.get('score', 'N/A')} | {row.get('math_score', 'N/A')} | {row.get('physics_score', 'N/A')} | {row.get('total_tokens', 'N/A')} | {row.get('evaluation_time', 'N/A')} |\n"
readme_content += """
## Как принять участие в бенчмарке
Для участия в бенчмарке DeathMath:
1. Клонируйте репозиторий и запустите тесты вашей модели
2. Загрузите результаты через [HuggingFace Space](https://huggingface.co/spaces/Vikhrmodels/DeathMath-leaderboard)
3. Дождитесь проверки и добавления результатов в лидерборд
## Формат результатов
Результаты должны быть в формате JSON со следующей структурой:
```json
{
"score": 0.586,
"math_score": 0.8,
"physics_score": 0.373,
"total_tokens": 1394299,
"evaluation_time": 4533.2,
"system_prompt": "Вы - полезный помощник по математике и физике. Ответьте на русском языке."
}
```
## Лицензия
Бенчмарк распространяется под лицензией Apache 2.0
"""
# Сохраняем README
with open("README.md", "w", encoding="utf-8") as f:
f.write(readme_content)
# Загружаем README в репозиторий
API.upload_file(
path_or_fileobj="README.md",
path_in_repo="README.md",
repo_id="Vikhrmodels/DeathMath-leaderboard-metainfo",
repo_type="dataset",
)
logging.info("Updated README with leaderboard table")
except Exception as e:
logging.error(f"Failed to update README: {e}")
def update_board_():
"""
Инициализирующая версия функции обновления лидерборда.
Удаляет все существующие данные перед первой загрузкой
"""
try:
# Очищаем каталоги перед загрузкой данных
if os.path.exists("m_data"):
# Удаляем все JSON-файлы моделей из старой версии Small Shlepa
for old_file in glob.glob("./m_data/model_data/external/*.json"):
try:
os.remove(old_file)
logging.info(f"Removed old file: {old_file}")
except Exception as e:
logging.error(f"Error removing old file {old_file}: {e}")
# Вызываем основную функцию обновления
update_board()
except Exception as e:
logging.error(f"Error in update_board_(): {e}")
if __name__ == "__main__":
os.environ[RESET_JUDGEMENT_ENV] = "1"
scheduler = BackgroundScheduler()
update_board_()
scheduler.add_job(update_board, "interval", minutes=10)
scheduler.start()
demo_app = build_demo()
demo_app.launch()