Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
Updated board
#1
by mbagdasarova-nvidia - opened
- .gitattributes +0 -35
- .gitignore +0 -13
- .pre-commit-config.yaml +0 -53
- Makefile +0 -13
- README.md +102 -43
- app.py +919 -200
- benchmarks.py +385 -0
- pyproject.toml +0 -13
- ref/Coherence_Summary +84 -0
- ref/Overview +9 -0
- requirements.txt +1 -16
- scores.py +135 -0
- scoring.py +66 -0
- src/about.py +0 -89
- src/display/css_html_js.py +0 -105
- src/display/formatting.py +0 -27
- src/display/utils.py +0 -110
- src/envs.py +0 -25
- src/leaderboard/read_evals.py +0 -196
- src/populate.py +0 -58
- src/submission/check_validity.py +0 -99
- src/submission/submit.py +0 -119
.gitattributes
DELETED
|
@@ -1,35 +0,0 @@
|
|
| 1 |
-
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
-
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
-
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
-
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
-
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
-
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
-
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
-
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
-
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
-
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
-
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
-
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
-
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
-
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
-
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
-
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
-
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
-
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
-
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
-
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
-
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
-
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
-
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
-
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
-
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
-
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
-
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 29 |
-
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 30 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 31 |
-
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 32 |
-
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 33 |
-
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 34 |
-
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 35 |
-
scale-hf-logo.png filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.gitignore
DELETED
|
@@ -1,13 +0,0 @@
|
|
| 1 |
-
auto_evals/
|
| 2 |
-
venv/
|
| 3 |
-
__pycache__/
|
| 4 |
-
.env
|
| 5 |
-
.ipynb_checkpoints
|
| 6 |
-
*ipynb
|
| 7 |
-
.vscode/
|
| 8 |
-
|
| 9 |
-
eval-queue/
|
| 10 |
-
eval-results/
|
| 11 |
-
eval-queue-bk/
|
| 12 |
-
eval-results-bk/
|
| 13 |
-
logs/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.pre-commit-config.yaml
DELETED
|
@@ -1,53 +0,0 @@
|
|
| 1 |
-
# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
| 2 |
-
#
|
| 3 |
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
-
# you may not use this file except in compliance with the License.
|
| 5 |
-
# You may obtain a copy of the License at
|
| 6 |
-
#
|
| 7 |
-
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
-
#
|
| 9 |
-
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
-
# See the License for the specific language governing permissions and
|
| 13 |
-
# limitations under the License.
|
| 14 |
-
|
| 15 |
-
default_language_version:
|
| 16 |
-
python: python3
|
| 17 |
-
|
| 18 |
-
ci:
|
| 19 |
-
autofix_prs: true
|
| 20 |
-
autoupdate_commit_msg: '[pre-commit.ci] pre-commit suggestions'
|
| 21 |
-
autoupdate_schedule: quarterly
|
| 22 |
-
|
| 23 |
-
repos:
|
| 24 |
-
- repo: https://github.com/pre-commit/pre-commit-hooks
|
| 25 |
-
rev: v4.3.0
|
| 26 |
-
hooks:
|
| 27 |
-
- id: check-yaml
|
| 28 |
-
- id: check-case-conflict
|
| 29 |
-
- id: detect-private-key
|
| 30 |
-
- id: check-added-large-files
|
| 31 |
-
args: ['--maxkb=1000']
|
| 32 |
-
- id: requirements-txt-fixer
|
| 33 |
-
- id: end-of-file-fixer
|
| 34 |
-
- id: trailing-whitespace
|
| 35 |
-
|
| 36 |
-
- repo: https://github.com/PyCQA/isort
|
| 37 |
-
rev: 5.12.0
|
| 38 |
-
hooks:
|
| 39 |
-
- id: isort
|
| 40 |
-
name: Format imports
|
| 41 |
-
|
| 42 |
-
- repo: https://github.com/psf/black
|
| 43 |
-
rev: 22.12.0
|
| 44 |
-
hooks:
|
| 45 |
-
- id: black
|
| 46 |
-
name: Format code
|
| 47 |
-
additional_dependencies: ['click==8.0.2']
|
| 48 |
-
|
| 49 |
-
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
| 50 |
-
# Ruff version.
|
| 51 |
-
rev: 'v0.0.267'
|
| 52 |
-
hooks:
|
| 53 |
-
- id: ruff
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Makefile
DELETED
|
@@ -1,13 +0,0 @@
|
|
| 1 |
-
.PHONY: style format
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
style:
|
| 5 |
-
python -m black --line-length 119 .
|
| 6 |
-
python -m isort .
|
| 7 |
-
ruff check --fix .
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
quality:
|
| 11 |
-
python -m black --check --line-length 119 .
|
| 12 |
-
python -m isort --check-only .
|
| 13 |
-
ruff check .
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
README.md
CHANGED
|
@@ -1,48 +1,107 @@
|
|
| 1 |
---
|
| 2 |
-
title: GRM
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
colorTo: indigo
|
| 6 |
sdk: gradio
|
|
|
|
| 7 |
app_file: app.py
|
| 8 |
-
pinned:
|
| 9 |
-
license: unknown
|
| 10 |
-
short_description: GRM
|
| 11 |
-
sdk_version: 5.43.1
|
| 12 |
-
tags:
|
| 13 |
-
- leaderboard
|
| 14 |
---
|
| 15 |
|
| 16 |
-
#
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
-
|
| 47 |
-
|
| 48 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: GRM Leaderboard
|
| 3 |
+
colorFrom: gray
|
| 4 |
+
colorTo: blue
|
|
|
|
| 5 |
sdk: gradio
|
| 6 |
+
sdk_version: "5.23.0"
|
| 7 |
app_file: app.py
|
| 8 |
+
pinned: false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
---
|
| 10 |
|
| 11 |
+
# GRM Leaderboard
|
| 12 |
+
|
| 13 |
+
Static Gradio Space for comparing language models on a game-focused evaluation suite.
|
| 14 |
+
|
| 15 |
+
## What This Project Is
|
| 16 |
+
|
| 17 |
+
- Single-repo Hugging Face Space
|
| 18 |
+
- Frontend-only app with no database or external backend
|
| 19 |
+
- Static benchmark registry and static model score data stored in Python files
|
| 20 |
+
- Runtime leaderboard calculation from those local data files
|
| 21 |
+
|
| 22 |
+
## Runtime
|
| 23 |
+
|
| 24 |
+
- Platform: Hugging Face Spaces
|
| 25 |
+
- UI framework: Gradio
|
| 26 |
+
- Entry point: app.py
|
| 27 |
+
- Dependencies: requirements.txt
|
| 28 |
+
- Space metadata: this README frontmatter
|
| 29 |
+
- app.py includes compatibility handling for both Gradio 5 and Gradio 6
|
| 30 |
+
|
| 31 |
+
## Main Page Flow
|
| 32 |
+
|
| 33 |
+
Tab 1:
|
| 34 |
+
- Overview
|
| 35 |
+
- Leaderboard
|
| 36 |
+
- Per-Benchmark Score Breakdown
|
| 37 |
+
- Evaluation Suite
|
| 38 |
+
- Benchmark Details
|
| 39 |
+
|
| 40 |
+
Tab 2:
|
| 41 |
+
- GRM-Bench authored benchmark families
|
| 42 |
+
|
| 43 |
+
## File Ownership
|
| 44 |
+
|
| 45 |
+
- app.py: page layout, tabs, CSS, overview copy, table rendering, and GRM-Bench authored sections
|
| 46 |
+
- benchmarks.py: benchmark registry, category assignments, descriptions, summaries, and weights
|
| 47 |
+
- scores.py: per-model benchmark scores on a 0.0 to 1.0 scale
|
| 48 |
+
- scoring.py: category scoring, GRM score calculation, and ranking logic
|
| 49 |
+
- requirements.txt: runtime dependencies
|
| 50 |
+
- README.md: Space metadata and maintainer handoff notes
|
| 51 |
+
|
| 52 |
+
## Data Model
|
| 53 |
+
|
| 54 |
+
- benchmarks.py stores BENCHMARKS as a list of dicts
|
| 55 |
+
- Each benchmark entry includes: name, category, calc_weight, description, summary, paper
|
| 56 |
+
- Valid categories are ROLEPLAY, ACTIONS, and GENERAL
|
| 57 |
+
- scores.py stores MODEL_SCORES keyed by model display name
|
| 58 |
+
- Each model score dict is keyed by benchmark name
|
| 59 |
+
- Missing scores are skipped during weighted averaging
|
| 60 |
+
|
| 61 |
+
## Scoring
|
| 62 |
+
|
| 63 |
+
- Category score = sum(score x weight) / sum(weight)
|
| 64 |
+
- GRM score = average of Roleplay, Actions, and General category scores
|
| 65 |
+
- scores.py values stay on a 0.0 to 1.0 scale
|
| 66 |
+
- Displayed leaderboard values are converted to 0 to 100
|
| 67 |
+
|
| 68 |
+
## How To Update The Site
|
| 69 |
+
|
| 70 |
+
Update model scores:
|
| 71 |
+
- Edit scores.py
|
| 72 |
+
- Change benchmark values for an existing model or add a new model block
|
| 73 |
+
|
| 74 |
+
Update evaluation suite rows or benchmark descriptions:
|
| 75 |
+
- Edit benchmarks.py
|
| 76 |
+
- The evaluation table and benchmark detail sections are generated from this registry
|
| 77 |
+
|
| 78 |
+
Add a new benchmark:
|
| 79 |
+
- Add the benchmark entry to benchmarks.py
|
| 80 |
+
- Set its category and calc_weight
|
| 81 |
+
- Add corresponding values in scores.py for each model you want included
|
| 82 |
+
|
| 83 |
+
Update the authored GRM-Bench tab:
|
| 84 |
+
- Edit GRM_BENCH_SECTIONS in app.py
|
| 85 |
+
|
| 86 |
+
Update page structure, copy, or styling:
|
| 87 |
+
- Edit app.py
|
| 88 |
+
|
| 89 |
+
## Local Development
|
| 90 |
+
|
| 91 |
+
- Install dependencies: pip install -r requirements.txt
|
| 92 |
+
- Run locally: python app.py
|
| 93 |
+
- The app launches a local Gradio server using the same static content as the Space
|
| 94 |
+
|
| 95 |
+
## Deployment Notes
|
| 96 |
+
|
| 97 |
+
- The live Space deploys from the remote main branch
|
| 98 |
+
- README frontmatter controls the Space runtime metadata
|
| 99 |
+
- requirements.txt must match imports used by app.py
|
| 100 |
+
- Current scores in scores.py are placeholder/static values unless replaced with real outputs
|
| 101 |
+
|
| 102 |
+
## Maintenance Notes
|
| 103 |
+
|
| 104 |
+
- The UI uses Python-generated HTML tables, not Gradio Dataframes
|
| 105 |
+
- Leaderboard order is recalculated on each launch from scores.py
|
| 106 |
+
- Gradio theme and CSS are injected conditionally based on the installed Gradio major version
|
| 107 |
+
- If page scrolling behaves oddly, inspect the root overflow and flex overrides in app.py
|
app.py
CHANGED
|
@@ -1,213 +1,932 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
-
from gradio_leaderboard import Leaderboard, ColumnFilter, SelectColumns
|
| 3 |
-
import pandas as pd
|
| 4 |
-
from apscheduler.schedulers.background import BackgroundScheduler
|
| 5 |
-
from huggingface_hub import snapshot_download
|
| 6 |
-
|
| 7 |
-
from src.about import (
|
| 8 |
-
CITATION_BUTTON_LABEL,
|
| 9 |
-
CITATION_BUTTON_TEXT,
|
| 10 |
-
EVALUATION_QUEUE_TEXT,
|
| 11 |
-
INTRODUCTION_TEXT,
|
| 12 |
-
LLM_BENCHMARKS_TEXT,
|
| 13 |
-
ABOUT_TEXT,
|
| 14 |
-
PERFORMANCE_TEXT,
|
| 15 |
-
METRICS_TEXT,
|
| 16 |
-
TITLE,
|
| 17 |
-
)
|
| 18 |
-
from src.display.css_html_js import custom_css
|
| 19 |
-
from src.display.utils import (
|
| 20 |
-
BENCHMARK_COLS,
|
| 21 |
-
COLS,
|
| 22 |
-
EVAL_COLS,
|
| 23 |
-
EVAL_TYPES,
|
| 24 |
-
AutoEvalColumn,
|
| 25 |
-
ModelType,
|
| 26 |
-
fields,
|
| 27 |
-
WeightType,
|
| 28 |
-
Precision
|
| 29 |
-
)
|
| 30 |
-
from src.envs import API, EVAL_REQUESTS_PATH, EVAL_RESULTS_PATH, QUEUE_REPO, REPO_ID, RESULTS_REPO, TOKEN
|
| 31 |
-
from src.populate import get_evaluation_queue_df, get_leaderboard_df
|
| 32 |
-
from src.submission.submit import add_new_eval
|
| 33 |
|
|
|
|
|
|
|
|
|
|
| 34 |
|
| 35 |
-
def restart_space():
|
| 36 |
-
API.restart_space(repo_id=REPO_ID)
|
| 37 |
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
)
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
(
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
),
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
AutoEvalColumn.params.name,
|
| 81 |
-
type="slider",
|
| 82 |
-
min=0.01,
|
| 83 |
-
max=150,
|
| 84 |
-
label="Select the number of parameters (B)",
|
| 85 |
-
),
|
| 86 |
-
ColumnFilter(
|
| 87 |
-
AutoEvalColumn.still_on_hub.name, type="boolean", label="Deleted/incomplete", default=True
|
| 88 |
-
),
|
| 89 |
],
|
| 90 |
-
|
| 91 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
)
|
| 93 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 195 |
)
|
| 196 |
|
| 197 |
-
|
| 198 |
-
gr.Markdown(
|
| 199 |
-
|
| 200 |
-
with gr.Row():
|
| 201 |
-
with gr.Accordion("📙 Citation", open=False):
|
| 202 |
-
citation_button = gr.Textbox(
|
| 203 |
-
value=CITATION_BUTTON_TEXT,
|
| 204 |
-
label=CITATION_BUTTON_LABEL,
|
| 205 |
-
lines=20,
|
| 206 |
-
elem_id="citation-button",
|
| 207 |
-
show_copy_button=True,
|
| 208 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 209 |
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Nvidia Game Ready Model Score (GRM Score) Gradio app."""
|
| 2 |
+
|
| 3 |
+
from html import escape
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
|
| 6 |
import gradio as gr
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
+
from benchmarks import BENCHMARKS, CATEGORIES, CATEGORY_DISPLAY, get_benchmarks_by_category
|
| 9 |
+
from scores import MODEL_SCORES
|
| 10 |
+
from scoring import build_leaderboard
|
| 11 |
|
|
|
|
|
|
|
| 12 |
|
| 13 |
+
LEADERBOARD_COLUMNS = [
|
| 14 |
+
"Rank",
|
| 15 |
+
"Model",
|
| 16 |
+
"GRM Score",
|
| 17 |
+
"Roleplay (33%)",
|
| 18 |
+
"Actions (33%)",
|
| 19 |
+
"General (33%)",
|
| 20 |
+
]
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
GRADIO_MAJOR_VERSION = int(gr.__version__.split(".", 1)[0])
|
| 24 |
+
APP_ROOT = Path(__file__).resolve().parent
|
| 25 |
+
REF_ROOT = APP_ROOT / "ref"
|
| 26 |
+
|
| 27 |
+
DEFAULT_OVERVIEW_BLOCKS = [
|
| 28 |
+
"Nvidia Game Ready Model Score (GRM) is an aggregated quality metric designed to assess LLM capabilites in gaming use cases.",
|
| 29 |
+
"General state-of-the-art language models are optimized for broad benchmarks such as math, code, and general knowledge. That does not reliably translate to in-game performance, and it does not reliably predict NPC quality, gameplay actions, or immersion.",
|
| 30 |
+
"With game model evaluation, game developers can accelerate AI integration pipelines by reducing time spent on model evaluation and narrowing model choice earlier. The overall score is the average of Roleplay, Actions, and General, while benchmarks inside each category are combined with weighted averaging using core weights of 1.0 and supplementary weights of 0.5.",
|
| 31 |
+
"GRM Score = (Roleplay + Actions + General) / 3",
|
| 32 |
+
"Category Score = sum(score x weight) / sum(weight)",
|
| 33 |
+
]
|
| 34 |
+
|
| 35 |
+
PROPRIETARY_MODELS = frozenset({"GPT-5.4", "Gemini 2.5 Pro"})
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
BASE_GRM_BENCH_SECTIONS = [
|
| 39 |
+
{
|
| 40 |
+
"title": "Coherence",
|
| 41 |
+
"summary": [
|
| 42 |
+
"Above all other failure modes that break immersion in character and NPC interactions are responses that feel illogical, inconsistent, or irrelevant to the active game state.",
|
| 43 |
+
"Incoherence can surface as hallucinated details, role confusion, contradictions across turns, or answers that stop tracking the subject under discussion.",
|
| 44 |
+
],
|
| 45 |
+
"methodology": (
|
| 46 |
+
"Because coherence can fail in many different ways, the authored scenarios are designed to trigger a common failure mode and then measure whether the model stays grounded under pressure."
|
| 47 |
+
),
|
| 48 |
+
"scope": [
|
| 49 |
+
[
|
| 50 |
+
"Factual / Logical",
|
| 51 |
+
"Objectively false or contradicted by the system prompt or game state, including invented entities, rules, or details.",
|
| 52 |
+
],
|
| 53 |
+
["Cause / Effect", "Fails simple logical state transitions or obvious state changes."],
|
| 54 |
+
[
|
| 55 |
+
"Contradiction",
|
| 56 |
+
"Contradicts something previously said or done without an in-world justification.",
|
| 57 |
+
],
|
| 58 |
+
[
|
| 59 |
+
"Personality / Background Violation",
|
| 60 |
+
"Violates an established trait, limitation, or background fact.",
|
| 61 |
+
],
|
| 62 |
+
[
|
| 63 |
+
"Role Confusion",
|
| 64 |
+
"Confuses identities, facts, actions, or motivations across entities.",
|
| 65 |
+
],
|
| 66 |
+
[
|
| 67 |
+
"Irrelevance",
|
| 68 |
+
"Stops tracking the active subject or responds in a way that is not relevant to the discussion.",
|
| 69 |
+
],
|
| 70 |
+
[
|
| 71 |
+
"Knowledge Boundary",
|
| 72 |
+
"Invents knowledge the character cannot have instead of separating observation from speculation.",
|
| 73 |
+
],
|
| 74 |
+
[
|
| 75 |
+
"False Premise",
|
| 76 |
+
"Incorrectly accepts a smuggled-in user premise about something that never happened.",
|
| 77 |
+
],
|
| 78 |
+
],
|
| 79 |
+
"samples": (
|
| 80 |
+
"Representative cases include long multi-turn identity-confusion exchanges and hidden-information prompts where the character must avoid inventing unseen facts."
|
| 81 |
+
),
|
| 82 |
+
},
|
| 83 |
+
{
|
| 84 |
+
"title": "Response Diversity",
|
| 85 |
+
"summary": [
|
| 86 |
+
"Response Diversity measures whether a model stays engaging without collapsing into repetitive wording, sentence structure, or stock phrasing across similar prompts and multi-turn play.",
|
| 87 |
+
"The goal is not randomness. The goal is controlled variation that still preserves the correct task intent, tone, and world state.",
|
| 88 |
+
],
|
| 89 |
+
"methodology": (
|
| 90 |
+
"Equivalent requests are expressed across repeated turns and neighboring scenarios so the evaluation can separate healthy consistency from repetitive degeneration."
|
| 91 |
),
|
| 92 |
+
"scope": [
|
| 93 |
+
["Repetition Loop", "Repeats phrases, clauses, or sentence frames across adjacent responses."],
|
| 94 |
+
["Lexical Compression", "Collapses to a narrow vocabulary even when there is room for variation."],
|
| 95 |
+
["Originality Failure", "Paraphrases the prompt too literally instead of producing fresh in-world language."],
|
| 96 |
+
["Near-Duplicate Continuation", "Makes only superficial wording changes while repeating the same response content."],
|
| 97 |
+
["Style Stagnation", "Cannot vary tone or delivery while preserving the same underlying instruction."],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
],
|
| 99 |
+
},
|
| 100 |
+
{
|
| 101 |
+
"title": "Tool Recovery",
|
| 102 |
+
"summary": [
|
| 103 |
+
"Tool Recovery evaluates whether the model can recognize a failed tool step, repair the plan, and continue without fabricating results.",
|
| 104 |
+
"This matters for assistants that need to survive partial failures instead of derailing the whole interaction after one bad tool call.",
|
| 105 |
+
],
|
| 106 |
+
"methodology": (
|
| 107 |
+
"Benchmarks inject missing tool calls, malformed arguments, or explicit tool failures and then measure whether the model retries correctly, replans, or asks for the right follow-up."
|
| 108 |
+
),
|
| 109 |
+
"scope": [
|
| 110 |
+
["Missed Invocation", "Fails to issue a required tool call at all."],
|
| 111 |
+
["Malformed Retry", "Attempts recovery with incomplete or invalid tool arguments."],
|
| 112 |
+
["Fabricated Output", "Invents tool output after a failure instead of acknowledging the error."],
|
| 113 |
+
["Recovery Sequencing", "Does not replan correctly after a tool error or partial result."],
|
| 114 |
+
["Silent Drop", "Continues as if the failed tool step never mattered to the task."],
|
| 115 |
+
],
|
| 116 |
+
},
|
| 117 |
+
{
|
| 118 |
+
"title": "Context Adaptation",
|
| 119 |
+
"summary": [
|
| 120 |
+
"Context Adaptation measures whether a model tracks a changing world state without letting values, locations, inventories, or statuses drift across turns.",
|
| 121 |
+
"These tests target dynamic sessions where the model must stay synchronized with the newest state while preserving earlier facts that still remain true.",
|
| 122 |
+
],
|
| 123 |
+
"methodology": (
|
| 124 |
+
"Stateful scenarios update facts mid-conversation and require the model to carry forward the latest values while also keeping dependent details accurate."
|
| 125 |
+
),
|
| 126 |
+
"scope": [
|
| 127 |
+
["State Drift", "Values change without cause as the conversation continues."],
|
| 128 |
+
["Temporal Mismatch", "Old state is treated as current after a newer update is provided."],
|
| 129 |
+
["Entity Attribute Drift", "Names, inventory, location, or status details mutate incorrectly."],
|
| 130 |
+
["Partial Update Failure", "One field is updated but dependent fields are left stale."],
|
| 131 |
+
["Conflict Resolution", "Cannot reconcile new information with earlier context in a coherent way."],
|
| 132 |
+
],
|
| 133 |
+
},
|
| 134 |
+
{
|
| 135 |
+
"title": "Prompt Robustness",
|
| 136 |
+
"summary": [
|
| 137 |
+
"Prompt Robustness checks whether the same underlying intent is handled reliably across terse prompts, verbose instructions, structured payloads, and mixed formatting.",
|
| 138 |
+
"A model should not need one exact prompt style in order to understand the task, infer the right tool path, or preserve the requested output behavior.",
|
| 139 |
+
],
|
| 140 |
+
"methodology": (
|
| 141 |
+
"Equivalent requests are expressed in long-form prose, shorthand, JSON, XML, and other wrappers to measure sensitivity to presentation rather than intent."
|
| 142 |
+
),
|
| 143 |
+
"scope": [
|
| 144 |
+
["Format Sensitivity", "Succeeds in plain prose but fails when the request is wrapped in JSON, XML, or other structure."],
|
| 145 |
+
["Instruction Alias Failure", "Equivalent wording changes alter behavior more than they should."],
|
| 146 |
+
["Verbosity Dependency", "Requires unusually long prompting to perform a task it should infer directly."],
|
| 147 |
+
["Tool Intent Drift", "Misses the right tool plan when the same task is phrased in a different form."],
|
| 148 |
+
["Structure Overfitting", "Responds too literally to markup or formatting instead of following the underlying request."],
|
| 149 |
+
],
|
| 150 |
+
},
|
| 151 |
+
]
|
| 152 |
+
|
| 153 |
+
|
| 154 |
+
def _read_reference_file(name: str) -> str | None:
|
| 155 |
+
try:
|
| 156 |
+
return (REF_ROOT / name).read_text(encoding="utf-8").strip()
|
| 157 |
+
except OSError:
|
| 158 |
+
return None
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
def _split_reference_blocks(text: str) -> list[str]:
|
| 162 |
+
blocks = []
|
| 163 |
+
for chunk in text.split("\n\n"):
|
| 164 |
+
block = " ".join(line.strip() for line in chunk.splitlines() if line.strip())
|
| 165 |
+
if block:
|
| 166 |
+
blocks.append(block)
|
| 167 |
+
return blocks
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
def _build_overview_html(blocks: list[str]) -> str:
|
| 171 |
+
parts = ['<div class="longform-copy">']
|
| 172 |
+
title_prefix = "Nvidia Game Ready Model Score (GRM)"
|
| 173 |
+
|
| 174 |
+
for block in blocks:
|
| 175 |
+
if block.startswith("GRM Score ="):
|
| 176 |
+
parts.append(f'<p class="formula-line">{escape(block)}</p>')
|
| 177 |
+
continue
|
| 178 |
+
|
| 179 |
+
if block.startswith("Category Score ="):
|
| 180 |
+
parts.append(f'<p class="formula-line subdued">{escape(block)}</p>')
|
| 181 |
+
continue
|
| 182 |
+
|
| 183 |
+
if block.startswith(title_prefix):
|
| 184 |
+
suffix = block[len(title_prefix) :]
|
| 185 |
+
parts.append(f"<p><strong>{escape(title_prefix)}</strong>{escape(suffix)}</p>")
|
| 186 |
+
continue
|
| 187 |
+
|
| 188 |
+
parts.append(f"<p>{escape(block)}</p>")
|
| 189 |
+
|
| 190 |
+
parts.append("</div>")
|
| 191 |
+
return "".join(parts)
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
def _load_overview_html() -> str:
|
| 195 |
+
text = _read_reference_file("Overview")
|
| 196 |
+
blocks = _split_reference_blocks(text) if text else DEFAULT_OVERVIEW_BLOCKS
|
| 197 |
+
return _build_overview_html(blocks)
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
def _load_coherence_section() -> dict | None:
|
| 201 |
+
text = _read_reference_file("Coherence_Summary")
|
| 202 |
+
if not text:
|
| 203 |
+
return None
|
| 204 |
+
|
| 205 |
+
lines = text.splitlines()
|
| 206 |
+
index = 0
|
| 207 |
+
|
| 208 |
+
while index < len(lines) and not lines[index].strip():
|
| 209 |
+
index += 1
|
| 210 |
+
|
| 211 |
+
if index >= len(lines):
|
| 212 |
+
return None
|
| 213 |
+
|
| 214 |
+
title = lines[index].strip()
|
| 215 |
+
index += 1
|
| 216 |
+
|
| 217 |
+
summary_lines = []
|
| 218 |
+
while index < len(lines) and lines[index].strip() != "Test Methodology":
|
| 219 |
+
if lines[index].strip():
|
| 220 |
+
summary_lines.append(lines[index].strip())
|
| 221 |
+
index += 1
|
| 222 |
+
|
| 223 |
+
if index >= len(lines):
|
| 224 |
+
return None
|
| 225 |
+
|
| 226 |
+
index += 1
|
| 227 |
+
methodology_lines = []
|
| 228 |
+
while index < len(lines) and lines[index].strip() != "Detection Scope:":
|
| 229 |
+
if lines[index].strip():
|
| 230 |
+
methodology_lines.append(lines[index].strip())
|
| 231 |
+
index += 1
|
| 232 |
+
|
| 233 |
+
if index >= len(lines):
|
| 234 |
+
return None
|
| 235 |
+
|
| 236 |
+
index += 1
|
| 237 |
+
scope = []
|
| 238 |
+
while index < len(lines) and lines[index].strip() != "Test Samples":
|
| 239 |
+
line = lines[index].strip()
|
| 240 |
+
if line:
|
| 241 |
+
category, _, description = line.partition(" - ")
|
| 242 |
+
scope.append([category.strip(), description.strip()])
|
| 243 |
+
index += 1
|
| 244 |
+
|
| 245 |
+
samples = []
|
| 246 |
+
if index < len(lines) and lines[index].strip() == "Test Samples":
|
| 247 |
+
index += 1
|
| 248 |
+
|
| 249 |
+
while index < len(lines):
|
| 250 |
+
while index < len(lines) and not lines[index].strip():
|
| 251 |
+
index += 1
|
| 252 |
+
|
| 253 |
+
if index >= len(lines):
|
| 254 |
+
break
|
| 255 |
+
|
| 256 |
+
if not lines[index].strip().startswith("TEST_"):
|
| 257 |
+
index += 1
|
| 258 |
+
continue
|
| 259 |
+
|
| 260 |
+
sample_id = lines[index].strip()
|
| 261 |
+
index += 1
|
| 262 |
+
metadata = []
|
| 263 |
+
code_lines = []
|
| 264 |
+
|
| 265 |
+
while index < len(lines):
|
| 266 |
+
line = lines[index]
|
| 267 |
+
stripped = line.strip()
|
| 268 |
+
|
| 269 |
+
if stripped.startswith("TEST_"):
|
| 270 |
+
break
|
| 271 |
+
|
| 272 |
+
if stripped == "Messages:":
|
| 273 |
+
index += 1
|
| 274 |
+
while index < len(lines) and not lines[index].strip().startswith("TEST_"):
|
| 275 |
+
code_lines.append(lines[index].rstrip())
|
| 276 |
+
index += 1
|
| 277 |
+
break
|
| 278 |
+
|
| 279 |
+
if stripped and ":" in stripped:
|
| 280 |
+
label, value = stripped.split(":", 1)
|
| 281 |
+
metadata.append([label.strip(), value.strip()])
|
| 282 |
+
|
| 283 |
+
index += 1
|
| 284 |
+
|
| 285 |
+
samples.append(
|
| 286 |
+
{
|
| 287 |
+
"id": sample_id,
|
| 288 |
+
"metadata": metadata,
|
| 289 |
+
"code": "\n".join(code_lines).strip(),
|
| 290 |
+
}
|
| 291 |
+
)
|
| 292 |
+
|
| 293 |
+
if not summary_lines or not methodology_lines or not scope:
|
| 294 |
+
return None
|
| 295 |
+
|
| 296 |
+
return {
|
| 297 |
+
"title": title,
|
| 298 |
+
"summary": [" ".join(summary_lines)],
|
| 299 |
+
"methodology": " ".join(methodology_lines),
|
| 300 |
+
"scope": scope,
|
| 301 |
+
"samples": samples,
|
| 302 |
+
}
|
| 303 |
+
|
| 304 |
+
|
| 305 |
+
def _load_grm_bench_sections() -> list[dict]:
|
| 306 |
+
sections = list(BASE_GRM_BENCH_SECTIONS)
|
| 307 |
+
coherence_section = _load_coherence_section()
|
| 308 |
+
if coherence_section is not None:
|
| 309 |
+
sections[0] = coherence_section
|
| 310 |
+
return sections
|
| 311 |
+
|
| 312 |
+
|
| 313 |
+
GRM_BENCH_SECTIONS = _load_grm_bench_sections()
|
| 314 |
+
|
| 315 |
+
|
| 316 |
+
def _fmt(value: float | None) -> str:
|
| 317 |
+
return f"{value:.1f}" if value is not None else "-"
|
| 318 |
+
|
| 319 |
+
|
| 320 |
+
def _fmt_weight(value: float) -> str:
|
| 321 |
+
return f"{value:.2f}"
|
| 322 |
+
|
| 323 |
+
|
| 324 |
+
def _include_model(model_name: str, include_proprietary: bool) -> bool:
|
| 325 |
+
return include_proprietary or model_name not in PROPRIETARY_MODELS
|
| 326 |
+
|
| 327 |
+
|
| 328 |
+
def build_html_table(
|
| 329 |
+
headers: list[str],
|
| 330 |
+
rows: list[list[str]],
|
| 331 |
+
table_class: str = "",
|
| 332 |
+
shell_class: str = "table-scroll-shell",
|
| 333 |
+
) -> str:
|
| 334 |
+
class_attr = f' class="data-table {table_class}"' if table_class else ' class="data-table"'
|
| 335 |
+
shell_classes = " ".join(part for part in ["table-shell", shell_class] if part)
|
| 336 |
+
parts = [f'<div class="{shell_classes}">', f"<table{class_attr}>", "<thead><tr>"]
|
| 337 |
+
|
| 338 |
+
for header in headers:
|
| 339 |
+
parts.append(f"<th>{escape(header)}</th>")
|
| 340 |
+
|
| 341 |
+
parts.append("</tr></thead><tbody>")
|
| 342 |
+
|
| 343 |
+
for row in rows:
|
| 344 |
+
parts.append("<tr>")
|
| 345 |
+
for cell in row:
|
| 346 |
+
parts.append(f"<td>{escape(str(cell))}</td>")
|
| 347 |
+
parts.append("</tr>")
|
| 348 |
+
|
| 349 |
+
parts.append("</tbody></table></div>")
|
| 350 |
+
return "".join(parts)
|
| 351 |
+
|
| 352 |
+
|
| 353 |
+
def get_leaderboard_entries(include_proprietary: bool = True) -> list[dict]:
|
| 354 |
+
entries = []
|
| 355 |
+
|
| 356 |
+
for row in build_leaderboard():
|
| 357 |
+
if _include_model(row["Model"], include_proprietary):
|
| 358 |
+
row_entry = dict(row)
|
| 359 |
+
row_entry["Rank"] = len(entries) + 1
|
| 360 |
+
entries.append(row_entry)
|
| 361 |
+
|
| 362 |
+
return entries
|
| 363 |
+
|
| 364 |
+
|
| 365 |
+
def get_leaderboard_rows(include_proprietary: bool = True) -> list[list[str]]:
|
| 366 |
+
rows = []
|
| 367 |
+
for row in get_leaderboard_entries(include_proprietary):
|
| 368 |
+
rows.append(
|
| 369 |
+
[
|
| 370 |
+
str(row["Rank"]),
|
| 371 |
+
row["Model"],
|
| 372 |
+
_fmt(row["GRM Score"]),
|
| 373 |
+
_fmt(row["Roleplay (33%)"]),
|
| 374 |
+
_fmt(row["Actions (33%)"]),
|
| 375 |
+
_fmt(row["General (33%)"]),
|
| 376 |
+
]
|
| 377 |
+
)
|
| 378 |
+
return rows
|
| 379 |
+
|
| 380 |
+
|
| 381 |
+
def get_ranked_model_names(include_proprietary: bool = True) -> list[str]:
|
| 382 |
+
return [row["Model"] for row in get_leaderboard_entries(include_proprietary)]
|
| 383 |
+
|
| 384 |
+
|
| 385 |
+
def build_evaluation_suite_html() -> str:
|
| 386 |
+
parts = [
|
| 387 |
+
'<div class="table-shell evaluation-suite-shell">',
|
| 388 |
+
"<table class=\"data-table evaluation-suite-table\">",
|
| 389 |
+
"<colgroup>",
|
| 390 |
+
'<col class="evaluation-suite-category-col">',
|
| 391 |
+
'<col class="evaluation-suite-benchmark-col">',
|
| 392 |
+
'<col class="evaluation-suite-description-col">',
|
| 393 |
+
'<col class="evaluation-suite-weight-col">',
|
| 394 |
+
"</colgroup>",
|
| 395 |
+
"<thead><tr>",
|
| 396 |
+
"<th>Category</th>",
|
| 397 |
+
"<th>Benchmark</th>",
|
| 398 |
+
"<th>Description</th>",
|
| 399 |
+
'<th class="weight-column" title="Weight">Wt.</th>',
|
| 400 |
+
"</tr></thead><tbody>",
|
| 401 |
+
]
|
| 402 |
+
|
| 403 |
+
for category in CATEGORIES:
|
| 404 |
+
benchmarks = get_benchmarks_by_category(category)
|
| 405 |
+
rowspan = len(benchmarks)
|
| 406 |
+
for index, benchmark in enumerate(benchmarks):
|
| 407 |
+
parts.append("<tr>")
|
| 408 |
+
if index == 0:
|
| 409 |
+
parts.append(
|
| 410 |
+
f'<td class="category-cell" rowspan="{rowspan}">{escape(CATEGORY_DISPLAY[category])}</td>'
|
| 411 |
+
)
|
| 412 |
+
parts.append(f'<td class="benchmark-cell">{escape(benchmark["name"])}</td>')
|
| 413 |
+
parts.append(f'<td class="description-cell">{escape(benchmark["description"])}</td>')
|
| 414 |
+
parts.append(f'<td class="weight-cell">{_fmt_weight(benchmark["calc_weight"])}</td>')
|
| 415 |
+
parts.append("</tr>")
|
| 416 |
+
|
| 417 |
+
parts.append("</tbody></table></div>")
|
| 418 |
+
return "".join(parts)
|
| 419 |
+
|
| 420 |
+
|
| 421 |
+
def build_leaderboard_html(include_proprietary: bool = True) -> str:
|
| 422 |
+
return build_html_table(
|
| 423 |
+
LEADERBOARD_COLUMNS,
|
| 424 |
+
get_leaderboard_rows(include_proprietary),
|
| 425 |
+
table_class="leaderboard-table",
|
| 426 |
+
shell_class="leaderboard-shell",
|
| 427 |
+
)
|
| 428 |
+
|
| 429 |
+
|
| 430 |
+
def build_category_score_table_html(category: str, include_proprietary: bool = True) -> str:
|
| 431 |
+
benchmark_names = [benchmark["name"] for benchmark in get_benchmarks_by_category(category)]
|
| 432 |
+
rows = []
|
| 433 |
+
|
| 434 |
+
for model in get_ranked_model_names(include_proprietary):
|
| 435 |
+
row = [model]
|
| 436 |
+
for benchmark_name in benchmark_names:
|
| 437 |
+
score = MODEL_SCORES[model].get(benchmark_name)
|
| 438 |
+
row.append(f"{score * 100:.1f}" if score is not None else "-")
|
| 439 |
+
rows.append(row)
|
| 440 |
+
|
| 441 |
+
return build_html_table(["Model"] + benchmark_names, rows, table_class="category-score-table")
|
| 442 |
+
|
| 443 |
+
|
| 444 |
+
def update_leaderboard_tables(include_proprietary: bool) -> list[str]:
|
| 445 |
+
outputs = [build_leaderboard_html(include_proprietary)]
|
| 446 |
+
|
| 447 |
+
for category in CATEGORIES:
|
| 448 |
+
outputs.append(build_category_score_table_html(category, include_proprietary))
|
| 449 |
+
|
| 450 |
+
return outputs
|
| 451 |
+
|
| 452 |
+
|
| 453 |
+
def build_benchmark_details_html() -> str:
|
| 454 |
+
parts = []
|
| 455 |
+
|
| 456 |
+
for category in CATEGORIES:
|
| 457 |
+
parts.append(
|
| 458 |
+
"<section class=\"benchmark-section\">"
|
| 459 |
+
f"<h3>{escape(CATEGORY_DISPLAY[category])}</h3>"
|
| 460 |
+
)
|
| 461 |
+
|
| 462 |
+
for benchmark in get_benchmarks_by_category(category):
|
| 463 |
+
weight_label = "Core" if benchmark["calc_weight"] == 1.0 else "Supplementary"
|
| 464 |
+
paper_html = ""
|
| 465 |
+
if benchmark.get("paper"):
|
| 466 |
+
paper_html = (
|
| 467 |
+
"<div class=\"benchmark-link\">"
|
| 468 |
+
f"<a href=\"{escape(benchmark['paper'])}\" target=\"_blank\" rel=\"noreferrer\">"
|
| 469 |
+
"Paper / Source"
|
| 470 |
+
"</a>"
|
| 471 |
+
"</div>"
|
| 472 |
+
)
|
| 473 |
+
|
| 474 |
+
parts.append(
|
| 475 |
+
"<article class=\"benchmark-entry\">"
|
| 476 |
+
"<div class=\"benchmark-entry-top\">"
|
| 477 |
+
f"<h4>{escape(benchmark['name'])}</h4>"
|
| 478 |
+
f"<span class=\"benchmark-weight\">{weight_label} · {benchmark['calc_weight']}</span>"
|
| 479 |
+
"</div>"
|
| 480 |
+
f"<p class=\"benchmark-description\">{escape(benchmark['description'])}</p>"
|
| 481 |
+
f"<p>{escape(benchmark['summary'])}</p>"
|
| 482 |
+
f"{paper_html}"
|
| 483 |
+
"</article>"
|
| 484 |
+
)
|
| 485 |
+
|
| 486 |
+
parts.append("</section>")
|
| 487 |
+
|
| 488 |
+
return "".join(parts)
|
| 489 |
+
|
| 490 |
+
|
| 491 |
+
def _build_grm_bench_sample_html(sample: dict) -> str:
|
| 492 |
+
parts = [
|
| 493 |
+
'<article class="grm-bench-sample">',
|
| 494 |
+
f'<div class="grm-bench-sample-id">{escape(sample["id"])}</div>',
|
| 495 |
+
]
|
| 496 |
+
|
| 497 |
+
for label, value in sample.get("metadata", []):
|
| 498 |
+
parts.append(
|
| 499 |
+
'<p class="grm-bench-sample-meta">'
|
| 500 |
+
f'<span class="grm-bench-sample-label">{escape(label)}:</span> {escape(value)}'
|
| 501 |
+
"</p>"
|
| 502 |
+
)
|
| 503 |
+
|
| 504 |
+
if sample.get("code"):
|
| 505 |
+
parts.append('<pre class="grm-bench-sample-code"><code>')
|
| 506 |
+
parts.append(escape(sample["code"]))
|
| 507 |
+
parts.append("</code></pre>")
|
| 508 |
+
|
| 509 |
+
parts.append("</article>")
|
| 510 |
+
return "".join(parts)
|
| 511 |
+
|
| 512 |
+
|
| 513 |
+
def build_grm_bench_section_html(section: dict) -> str:
|
| 514 |
+
parts = [
|
| 515 |
+
"<section class=\"grm-bench-section\">",
|
| 516 |
+
"<div class=\"grm-bench-kicker\">Nvidia-Authored Benchmark</div>",
|
| 517 |
+
f"<h2>{escape(section['title'])}</h2>",
|
| 518 |
+
]
|
| 519 |
+
|
| 520 |
+
for paragraph in section["summary"]:
|
| 521 |
+
parts.append(f"<p>{escape(paragraph)}</p>")
|
| 522 |
+
|
| 523 |
+
parts.append("<div class=\"grm-bench-subtitle\">Test Methodology</div>")
|
| 524 |
+
parts.append(f"<p>{escape(section['methodology'])}</p>")
|
| 525 |
+
parts.append("<div class=\"grm-bench-subtitle\">Detection Scope</div>")
|
| 526 |
+
parts.append(
|
| 527 |
+
build_html_table(["Category", "Description"], section["scope"], table_class="grm-bench-scope-table")
|
| 528 |
)
|
| 529 |
|
| 530 |
+
samples = section.get("samples")
|
| 531 |
+
if samples:
|
| 532 |
+
parts.append("<div class=\"grm-bench-subtitle\">Representative Samples</div>")
|
| 533 |
+
if isinstance(samples, str):
|
| 534 |
+
parts.append(f"<p>{escape(samples)}</p>")
|
| 535 |
+
else:
|
| 536 |
+
for sample in samples:
|
| 537 |
+
parts.append(_build_grm_bench_sample_html(sample))
|
| 538 |
+
|
| 539 |
+
parts.append("</section>")
|
| 540 |
+
return "".join(parts)
|
| 541 |
+
|
| 542 |
+
|
| 543 |
+
def build_grm_bench_html() -> str:
|
| 544 |
+
parts = [
|
| 545 |
+
"<div class=\"longform-copy\">",
|
| 546 |
+
"<p><strong>GRM-Bench</strong> is the in-house authored benchmark suite for game-facing assistants, companions, and NPC behaviors that are not well-covered by broad academic leaderboards.</p>",
|
| 547 |
+
"<p>The sections below describe the initial authored benchmark families and the concrete failure modes each family is designed to surface.</p>",
|
| 548 |
+
"</div>",
|
| 549 |
+
]
|
| 550 |
+
|
| 551 |
+
for section in GRM_BENCH_SECTIONS:
|
| 552 |
+
parts.append(build_grm_bench_section_html(section))
|
| 553 |
+
|
| 554 |
+
return "".join(parts)
|
| 555 |
+
|
| 556 |
+
|
| 557 |
+
HEADER_HTML = """
|
| 558 |
+
<section class="page-header">
|
| 559 |
+
<div class="page-eyebrow">NVIDIA Game Ready Evaluation</div>
|
| 560 |
+
<h1>Game Ready Leaderboard</h1>
|
| 561 |
+
<p>
|
| 562 |
+
An open game model evaluation surface for comparing LLMs across roleplay, gameplay
|
| 563 |
+
actions, and practical in-game reasoning.
|
| 564 |
+
</p>
|
| 565 |
+
</section>
|
| 566 |
+
"""
|
| 567 |
|
| 568 |
+
|
| 569 |
+
OVERVIEW_HTML = _load_overview_html()
|
| 570 |
+
|
| 571 |
+
|
| 572 |
+
CUSTOM_CSS = """
|
| 573 |
+
/* ── Palette ──────────────────────────────────────────────────────────── */
|
| 574 |
+
:root {
|
| 575 |
+
--bg: #181b1e;
|
| 576 |
+
--surface: #1d2126;
|
| 577 |
+
--surface-raised: #24282d;
|
| 578 |
+
--surface-header: #2b2f34;
|
| 579 |
+
--border: #3c4048;
|
| 580 |
+
--border-subtle: #313539;
|
| 581 |
+
--text: #c8cdd2;
|
| 582 |
+
--text-bright: #f5f7f8;
|
| 583 |
+
--text-dim: #a2a8ae;
|
| 584 |
+
--accent: #76b900;
|
| 585 |
+
--accent-hover: #8fd400;
|
| 586 |
+
}
|
| 587 |
+
|
| 588 |
+
/* ── Page ─────────────────────────────────────────────────────────────── */
|
| 589 |
+
html, body {
|
| 590 |
+
display: block !important;
|
| 591 |
+
height: auto !important;
|
| 592 |
+
overflow-y: auto !important;
|
| 593 |
+
}
|
| 594 |
+
body { background: var(--bg) !important; }
|
| 595 |
+
|
| 596 |
+
/* ── Gradio container ─────────────────────────────────────────────────── */
|
| 597 |
+
.gradio-container {
|
| 598 |
+
max-width: 1260px !important;
|
| 599 |
+
margin: 0 auto !important;
|
| 600 |
+
padding: 24px 24px 48px !important;
|
| 601 |
+
background: transparent !important;
|
| 602 |
+
color: var(--text) !important;
|
| 603 |
+
font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif !important;
|
| 604 |
+
}
|
| 605 |
+
|
| 606 |
+
/* ── Tabs ─────────────────────────────────────────────────────────────── */
|
| 607 |
+
.gradio-container .tab-nav {
|
| 608 |
+
border-bottom: 1px solid var(--border) !important;
|
| 609 |
+
gap: 18px;
|
| 610 |
+
margin: 0 0 18px 0 !important;
|
| 611 |
+
}
|
| 612 |
+
.gradio-container .tab-nav button {
|
| 613 |
+
background: transparent !important;
|
| 614 |
+
border: none !important;
|
| 615 |
+
border-radius: 0 !important;
|
| 616 |
+
color: var(--text-dim) !important;
|
| 617 |
+
font-size: 0.8rem !important;
|
| 618 |
+
font-weight: 650 !important;
|
| 619 |
+
letter-spacing: 0.08em !important;
|
| 620 |
+
padding: 0 0 12px 0 !important;
|
| 621 |
+
text-transform: uppercase !important;
|
| 622 |
+
}
|
| 623 |
+
.gradio-container .tab-nav button.selected,
|
| 624 |
+
.gradio-container .tab-nav button[aria-selected="true"] {
|
| 625 |
+
box-shadow: inset 0 -2px 0 var(--accent) !important;
|
| 626 |
+
color: var(--text-bright) !important;
|
| 627 |
+
}
|
| 628 |
+
|
| 629 |
+
/* ── Gradio Markdown / Prose ──────────────────────────────────────────── */
|
| 630 |
+
.gradio-container .prose { color: var(--text) !important; background: transparent !important; }
|
| 631 |
+
.gradio-container .prose h2 { color: var(--text-bright) !important; font-size: 1.55rem !important; font-weight: 600 !important; }
|
| 632 |
+
.gradio-container .prose h3 { color: var(--text-bright) !important; font-size: 1.1rem !important; font-weight: 600 !important; }
|
| 633 |
+
.gradio-container .prose p,
|
| 634 |
+
.gradio-container .prose li { color: var(--text) !important; font-size: 1.05rem !important; line-height: 1.7 !important; }
|
| 635 |
+
.gradio-container .prose strong { color: var(--text-bright) !important; }
|
| 636 |
+
.gradio-container .prose a { color: var(--accent) !important; text-decoration: none !important; }
|
| 637 |
+
|
| 638 |
+
/* ── Page header ──────────────────────────────────────────────────────── */
|
| 639 |
+
.page-header { text-align: center; margin: 4px auto 26px; }
|
| 640 |
+
.page-eyebrow { color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.16em; font-size: 0.76rem; margin-bottom: 12px; }
|
| 641 |
+
.page-header h1 { color: var(--text-bright); font-size: 2.35rem; line-height: 1.1; letter-spacing: -0.02em; margin: 0; font-weight: 650; }
|
| 642 |
+
.page-header p { max-width: 860px; margin: 12px auto 0; color: var(--text); font-size: 1rem; line-height: 1.65; }
|
| 643 |
+
|
| 644 |
+
/* ── Overview copy ────────────────────────────────────────────────────── */
|
| 645 |
+
.section-note { color: var(--text-dim); font-size: 0.88rem; margin-top: 8px; }
|
| 646 |
+
.longform-copy p { color: var(--text); font-size: 1.05rem; line-height: 1.7; margin: 0 0 10px 0; }
|
| 647 |
+
.formula-line { color: var(--text-bright) !important; font-weight: 600; margin-top: 12px !important; }
|
| 648 |
+
.formula-line.subdued { color: var(--text-dim) !important; font-weight: 500; }
|
| 649 |
+
|
| 650 |
+
/* ── Custom HTML tables ───────────────────────────────────────────────── */
|
| 651 |
+
.table-shell { width: 100%; margin-top: 10px; }
|
| 652 |
+
.table-scroll-shell { overflow-x: auto; }
|
| 653 |
+
.leaderboard-shell,
|
| 654 |
+
.evaluation-suite-shell { overflow-x: auto; }
|
| 655 |
+
|
| 656 |
+
.data-table { width: 100%; border-collapse: collapse; }
|
| 657 |
+
|
| 658 |
+
.data-table thead th {
|
| 659 |
+
background: var(--surface-header);
|
| 660 |
+
color: var(--text-dim);
|
| 661 |
+
font-size: 0.8rem;
|
| 662 |
+
font-weight: 650;
|
| 663 |
+
text-transform: uppercase;
|
| 664 |
+
letter-spacing: 0.04em;
|
| 665 |
+
text-align: left;
|
| 666 |
+
padding: 11px 12px;
|
| 667 |
+
border: 1px solid var(--border);
|
| 668 |
+
}
|
| 669 |
+
.data-table thead th + th { border-left: 1px solid var(--border); }
|
| 670 |
+
|
| 671 |
+
.data-table tbody tr:nth-child(odd) td { background: var(--surface); }
|
| 672 |
+
.data-table tbody tr:nth-child(even) td { background: var(--surface-raised); }
|
| 673 |
+
.data-table tbody tr:hover td { background: #30353a; }
|
| 674 |
+
|
| 675 |
+
.data-table td {
|
| 676 |
+
color: var(--text);
|
| 677 |
+
font-size: 0.94rem;
|
| 678 |
+
line-height: 1.45;
|
| 679 |
+
padding: 10px 12px;
|
| 680 |
+
vertical-align: top;
|
| 681 |
+
border: 1px solid var(--border-subtle);
|
| 682 |
+
}
|
| 683 |
+
|
| 684 |
+
/* Rank-1 row highlight */
|
| 685 |
+
# .leaderboard-table tbody tr:first-child td { background: #252d1d; }
|
| 686 |
+
|
| 687 |
+
/* Evaluation suite overrides */
|
| 688 |
+
.evaluation-suite-table { table-layout: fixed; }
|
| 689 |
+
.evaluation-suite-table thead th { padding: 9px 11px; font-size: 0.78rem; }
|
| 690 |
+
.evaluation-suite-table td { padding: 7px 11px; font-size: 0.9rem; line-height: 1.3; }
|
| 691 |
+
|
| 692 |
+
.evaluation-suite-category-col { width: 8rem; }
|
| 693 |
+
.evaluation-suite-benchmark-col { width: 12.5rem; }
|
| 694 |
+
.evaluation-suite-weight-col { width: 5ch; }
|
| 695 |
+
|
| 696 |
+
.evaluation-suite-table .weight-column,
|
| 697 |
+
.evaluation-suite-table .weight-cell {
|
| 698 |
+
font-variant-numeric: tabular-nums;
|
| 699 |
+
text-align: center;
|
| 700 |
+
white-space: nowrap;
|
| 701 |
+
width: 5ch;
|
| 702 |
+
padding: 0 4px;
|
| 703 |
+
}
|
| 704 |
+
.evaluation-suite-table .category-cell {
|
| 705 |
+
color: var(--text-dim);
|
| 706 |
+
font-size: 0.79rem;
|
| 707 |
+
font-weight: 650;
|
| 708 |
+
text-transform: uppercase;
|
| 709 |
+
letter-spacing: 0.06em;
|
| 710 |
+
vertical-align: top;
|
| 711 |
+
min-width: 8rem;
|
| 712 |
+
}
|
| 713 |
+
.evaluation-suite-table .benchmark-cell { width: 12.5rem; overflow-wrap: anywhere; }
|
| 714 |
+
.evaluation-suite-table .description-cell { min-width: 0; overflow-wrap: anywhere; }
|
| 715 |
+
|
| 716 |
+
.grm-bench-scope-table th:first-child,
|
| 717 |
+
.grm-bench-scope-table td:first-child { min-width: 180px; width: 180px; }
|
| 718 |
+
|
| 719 |
+
/* ── Accordion ────────────────────────────────────────────────────────── */
|
| 720 |
+
/* Gradio 4: .gradio-accordion | Gradio 5: details */
|
| 721 |
+
.gradio-container .gradio-accordion,
|
| 722 |
+
.gradio-container details {
|
| 723 |
+
background: transparent !important;
|
| 724 |
+
border: 1px solid var(--border) !important;
|
| 725 |
+
border-radius: 8px !important;
|
| 726 |
+
margin-bottom: 6px !important;
|
| 727 |
+
overflow: hidden !important;
|
| 728 |
+
}
|
| 729 |
+
.gradio-container .gradio-accordion > .label-wrap,
|
| 730 |
+
.gradio-container details > summary {
|
| 731 |
+
background: var(--surface-raised) !important;
|
| 732 |
+
color: var(--text-bright) !important;
|
| 733 |
+
border: none !important;
|
| 734 |
+
padding: 0.75rem 0.9rem !important;
|
| 735 |
+
cursor: pointer;
|
| 736 |
+
font-weight: 600;
|
| 737 |
+
font-size: 0.92rem;
|
| 738 |
+
list-style: none;
|
| 739 |
+
}
|
| 740 |
+
.gradio-container .gradio-accordion > .label-wrap:hover,
|
| 741 |
+
.gradio-container details > summary:hover { background: #2a2f34 !important; }
|
| 742 |
+
.gradio-container details[open] > summary { border-bottom: 1px solid var(--border) !important; }
|
| 743 |
+
.gradio-container details > summary::-webkit-details-marker { display: none; }
|
| 744 |
+
|
| 745 |
+
/* ── Benchmark details ────────────────────────────────────────────────── */
|
| 746 |
+
.benchmark-section { margin-top: 18px; }
|
| 747 |
+
.benchmark-section h3 { color: var(--text-bright); font-size: 1.15rem; font-weight: 600; margin: 0 0 8px 0; }
|
| 748 |
+
.benchmark-entry { padding: 12px 0; border-bottom: 1px solid var(--border-subtle); }
|
| 749 |
+
.benchmark-entry-top { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
|
| 750 |
+
.benchmark-entry-top h4 { margin: 0; color: var(--text-bright); font-size: 1rem; font-weight: 600; }
|
| 751 |
+
.benchmark-weight { color: var(--accent); font-size: 0.84rem; white-space: nowrap; }
|
| 752 |
+
.benchmark-description { color: var(--text-dim); margin: 6px 0 !important; }
|
| 753 |
+
.benchmark-entry p { color: var(--text); line-height: 1.62; margin: 0; }
|
| 754 |
+
.benchmark-link { margin-top: 7px; font-size: 0.86rem; }
|
| 755 |
+
.benchmark-link a { color: var(--accent); text-decoration: none; }
|
| 756 |
+
|
| 757 |
+
/* ── GRM-Bench sections ───────────────────────────────────────────────── */
|
| 758 |
+
.grm-bench-section { border-top: 1px solid var(--border-subtle); margin-top: 24px; padding-top: 18px; }
|
| 759 |
+
.grm-bench-section:first-of-type { margin-top: 16px; }
|
| 760 |
+
.grm-bench-kicker,
|
| 761 |
+
.grm-bench-subtitle { color: var(--text-dim); font-size: 0.78rem; font-weight: 650; letter-spacing: 0.08em; text-transform: uppercase; }
|
| 762 |
+
.grm-bench-section h2 { color: var(--text-bright); font-size: 1.32rem; font-weight: 620; letter-spacing: -0.01em; margin: 4px 0 10px 0; }
|
| 763 |
+
.grm-bench-section p { color: var(--text); font-size: 1.05rem; line-height: 1.7; margin: 0 0 10px 0; }
|
| 764 |
+
.grm-bench-subtitle { margin: 14px 0 6px 0; }
|
| 765 |
+
.grm-bench-sample { background: #171b1f; border: 1px solid var(--border); border-radius: 10px; margin-top: 12px; padding: 14px 16px; }
|
| 766 |
+
.grm-bench-sample-id { color: var(--text-bright); font-size: 0.84rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; }
|
| 767 |
+
.grm-bench-sample-meta { margin: 6px 0 0 0 !important; }
|
| 768 |
+
.grm-bench-sample-label { color: var(--text-bright); font-weight: 600; }
|
| 769 |
+
.grm-bench-sample-code {
|
| 770 |
+
background: #0f1215;
|
| 771 |
+
border: 1px solid var(--border-subtle);
|
| 772 |
+
border-radius: 8px;
|
| 773 |
+
color: #d7dde3;
|
| 774 |
+
font-family: Consolas, "SFMono-Regular", monospace;
|
| 775 |
+
font-size: 0.84rem;
|
| 776 |
+
line-height: 1.55;
|
| 777 |
+
margin: 12px 0 0 0;
|
| 778 |
+
overflow-x: auto;
|
| 779 |
+
padding: 12px 14px;
|
| 780 |
+
white-space: pre-wrap;
|
| 781 |
+
}
|
| 782 |
+
.grm-bench-sample-code code { font-family: inherit; }
|
| 783 |
+
|
| 784 |
+
/* ── Scrollbars ───────────────────────────────────────────────────────── */
|
| 785 |
+
::-webkit-scrollbar { width: 6px; height: 6px; }
|
| 786 |
+
::-webkit-scrollbar-track { background: var(--bg); }
|
| 787 |
+
::-webkit-scrollbar-thumb { background: #3a3f45; border-radius: 3px; }
|
| 788 |
+
::-webkit-scrollbar-thumb:hover { background: var(--accent); }
|
| 789 |
+
|
| 790 |
+
/* ── Responsive ───────────────────────────────────────────────────────── */
|
| 791 |
+
@media (max-width: 720px) {
|
| 792 |
+
.gradio-container { padding: 20px 14px 40px !important; }
|
| 793 |
+
.page-header h1 { font-size: 2rem; }
|
| 794 |
+
.data-table thead th,
|
| 795 |
+
.data-table td { padding: 10px 9px; }
|
| 796 |
+
.leaderboard-shell,
|
| 797 |
+
.evaluation-suite-shell { overflow-x: auto; }
|
| 798 |
+
.evaluation-suite-table { min-width: 38rem; }
|
| 799 |
+
}
|
| 800 |
+
|
| 801 |
+
.red-text { color: red !important; }
|
| 802 |
+
"""
|
| 803 |
+
|
| 804 |
+
|
| 805 |
+
_NVIDIA_DARK_THEME = gr.themes.Base().set(
|
| 806 |
+
# Page
|
| 807 |
+
body_background_fill="#181b1e",
|
| 808 |
+
body_text_color="#f5f7f8",
|
| 809 |
+
body_text_color_subdued="#a2a8ae",
|
| 810 |
+
# Blocks / panels
|
| 811 |
+
background_fill_primary="#1d2126",
|
| 812 |
+
background_fill_secondary="#15181b",
|
| 813 |
+
block_background_fill="#1d2126",
|
| 814 |
+
block_border_color="#3c4048",
|
| 815 |
+
block_border_width="1px",
|
| 816 |
+
block_label_background_fill="#23272c",
|
| 817 |
+
block_label_border_color="#3c4048",
|
| 818 |
+
block_label_text_color="#f5f7f8",
|
| 819 |
+
block_title_background_fill="transparent",
|
| 820 |
+
block_title_text_color="#f5f7f8",
|
| 821 |
+
block_info_text_color="#a2a8ae",
|
| 822 |
+
panel_background_fill="#1d2126",
|
| 823 |
+
panel_border_color="#3c4048",
|
| 824 |
+
# Inputs
|
| 825 |
+
input_background_fill="#24282d",
|
| 826 |
+
input_background_fill_focus="#2b2f34",
|
| 827 |
+
input_border_color="#3c4048",
|
| 828 |
+
input_border_color_focus="#76b900",
|
| 829 |
+
input_placeholder_color="#6b7280",
|
| 830 |
+
input_shadow="none",
|
| 831 |
+
input_shadow_focus="0 0 0 2px rgba(118,185,0,0.25)",
|
| 832 |
+
# Buttons
|
| 833 |
+
button_primary_background_fill="#76b900",
|
| 834 |
+
button_primary_background_fill_hover="#8fd400",
|
| 835 |
+
button_primary_border_color="transparent",
|
| 836 |
+
button_primary_text_color="#0f1012",
|
| 837 |
+
button_secondary_background_fill="#24282d",
|
| 838 |
+
button_secondary_background_fill_hover="#2b2f34",
|
| 839 |
+
button_secondary_border_color="#3c4048",
|
| 840 |
+
button_secondary_text_color="#f5f7f8",
|
| 841 |
+
# Borders / accent
|
| 842 |
+
border_color_accent="#76b900",
|
| 843 |
+
border_color_primary="#3c4048",
|
| 844 |
+
color_accent="#76b900",
|
| 845 |
+
link_text_color="#76b900",
|
| 846 |
+
link_text_color_hover="#8fd400",
|
| 847 |
+
link_text_color_visited="#5a9000",
|
| 848 |
+
# Table
|
| 849 |
+
table_even_background_fill="#1c2024",
|
| 850 |
+
table_odd_background_fill="#24282d",
|
| 851 |
+
table_row_focus="#30353a",
|
| 852 |
+
# Checkbox / radio
|
| 853 |
+
checkbox_background_color="#24282d",
|
| 854 |
+
checkbox_background_color_selected="#76b900",
|
| 855 |
+
checkbox_background_color_hover="#2b2f34",
|
| 856 |
+
checkbox_border_color="#3c4048",
|
| 857 |
+
checkbox_border_color_focus="#76b900",
|
| 858 |
+
checkbox_border_color_selected="#76b900",
|
| 859 |
+
checkbox_label_background_fill="transparent",
|
| 860 |
+
checkbox_label_background_fill_selected="rgba(118,185,0,0.12)",
|
| 861 |
+
checkbox_label_text_color="#f5f7f8",
|
| 862 |
+
# Slider
|
| 863 |
+
slider_color="#76b900",
|
| 864 |
+
# Shadows
|
| 865 |
+
shadow_drop="0 2px 8px rgba(0,0,0,0.45)",
|
| 866 |
+
shadow_drop_lg="0 4px 20px rgba(0,0,0,0.6)",
|
| 867 |
+
)
|
| 868 |
+
|
| 869 |
+
blocks_kwargs = {"title": "GRM Score - Game Ready Leaderboard"}
|
| 870 |
+
if GRADIO_MAJOR_VERSION < 6:
|
| 871 |
+
blocks_kwargs["theme"] = _NVIDIA_DARK_THEME
|
| 872 |
+
blocks_kwargs["css"] = CUSTOM_CSS
|
| 873 |
+
|
| 874 |
+
|
| 875 |
+
with gr.Blocks(**blocks_kwargs) as demo:
|
| 876 |
+
gr.HTML(HEADER_HTML)
|
| 877 |
+
|
| 878 |
+
with gr.Tabs():
|
| 879 |
+
with gr.Tab("Game Ready Leaderboard"):
|
| 880 |
+
gr.Markdown("## Overview")
|
| 881 |
+
gr.HTML(OVERVIEW_HTML)
|
| 882 |
+
|
| 883 |
+
gr.Markdown("## Leaderboard")
|
| 884 |
+
gr.Markdown(
|
| 885 |
+
"The leaderboard now sits directly after the overview so rankings are visible before the deeper methodology sections.", elem_classes=["red-text"]
|
| 886 |
+
)
|
| 887 |
+
show_proprietary_models = gr.Checkbox(label="Show proprietary models", value=True)
|
| 888 |
+
gr.HTML(
|
| 889 |
+
"<div class=\"section-note\">Turn this off to switch the ranking and score breakdowns to an open-source-only view.</div>"
|
| 890 |
+
)
|
| 891 |
+
leaderboard_html = gr.HTML(build_leaderboard_html())
|
| 892 |
+
gr.HTML("<div class=\"section-note red-text\"> Placeholder data for layout validation. Replace with real benchmark outputs when ready.</div>")
|
| 893 |
+
|
| 894 |
+
gr.Markdown("## Per-Benchmark Score Breakdown")
|
| 895 |
+
gr.Markdown("Expand a category to inspect the individual benchmark scores backing the leaderboard.")
|
| 896 |
+
category_score_tables = []
|
| 897 |
+
for category in CATEGORIES:
|
| 898 |
+
with gr.Accordion(f"{CATEGORY_DISPLAY[category]} benchmark scores", open=False):
|
| 899 |
+
category_score_tables.append(gr.HTML(build_category_score_table_html(category)))
|
| 900 |
+
|
| 901 |
+
show_proprietary_models.change(
|
| 902 |
+
fn=update_leaderboard_tables,
|
| 903 |
+
inputs=show_proprietary_models,
|
| 904 |
+
outputs=[leaderboard_html, *category_score_tables],
|
| 905 |
)
|
| 906 |
|
| 907 |
+
gr.Markdown("## Evaluation Suite")
|
| 908 |
+
gr.Markdown(
|
| 909 |
+
"Benchmarks are grouped into fused category cells so the suite reads more like a methodology table than a generic spreadsheet."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 910 |
)
|
| 911 |
+
gr.HTML(build_evaluation_suite_html())
|
| 912 |
+
|
| 913 |
+
gr.Markdown("## Benchmark Details")
|
| 914 |
+
gr.Markdown(
|
| 915 |
+
"Detailed summaries of each benchmark in the evaluation suite, grouped by category."
|
| 916 |
+
)
|
| 917 |
+
gr.HTML(build_benchmark_details_html())
|
| 918 |
+
|
| 919 |
+
with gr.Tab("GRM-Bench"):
|
| 920 |
+
gr.Markdown("## GRM-Bench")
|
| 921 |
+
gr.Markdown(
|
| 922 |
+
"Nvidia-authored benchmark families targeting in-house game interaction failure modes and evaluation surfaces."
|
| 923 |
+
)
|
| 924 |
+
gr.HTML(build_grm_bench_html())
|
| 925 |
+
|
| 926 |
|
| 927 |
+
if __name__ == "__main__":
|
| 928 |
+
launch_kwargs = {}
|
| 929 |
+
if GRADIO_MAJOR_VERSION >= 6:
|
| 930 |
+
launch_kwargs["theme"] = _NVIDIA_DARK_THEME
|
| 931 |
+
launch_kwargs["css"] = CUSTOM_CSS
|
| 932 |
+
demo.launch(**launch_kwargs)
|
benchmarks.py
ADDED
|
@@ -0,0 +1,385 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
GRM Evaluation Suite — Benchmark Registry
|
| 3 |
+
|
| 4 |
+
Each benchmark is defined with:
|
| 5 |
+
- name: Display name
|
| 6 |
+
- category: ROLEPLAY | ACTIONS | GENERAL
|
| 7 |
+
- description: Short description of what the benchmark tests
|
| 8 |
+
- calc_weight: 1.0 (core) or 0.5 (supplementary)
|
| 9 |
+
- summary: Multi-sentence methodology summary for display
|
| 10 |
+
- paper: URL to the paper or resource (if applicable)
|
| 11 |
+
"""
|
| 12 |
+
|
| 13 |
+
BENCHMARKS = [
|
| 14 |
+
# ── ROLEPLAY (33%) ──────────────────────────────────────────────
|
| 15 |
+
{
|
| 16 |
+
"name": "MultiChallenge",
|
| 17 |
+
"category": "ROLEPLAY",
|
| 18 |
+
"calc_weight": 1.0,
|
| 19 |
+
"description": "Multi-turn instruction following & coherence across turns",
|
| 20 |
+
"summary": (
|
| 21 |
+
"MultiChallenge evaluates multi-turn instruction following where models must "
|
| 22 |
+
"maintain coherence across multiple challenging conversational turns. Each test "
|
| 23 |
+
"scenario involves complex, multi-constraint instructions that require the model "
|
| 24 |
+
"to track context, resolve references, and keep all prior commitments intact while "
|
| 25 |
+
"handling new user requests."
|
| 26 |
+
),
|
| 27 |
+
"paper": None,
|
| 28 |
+
},
|
| 29 |
+
{
|
| 30 |
+
"name": "RoleBench",
|
| 31 |
+
"category": "ROLEPLAY",
|
| 32 |
+
"calc_weight": 1.0,
|
| 33 |
+
"description": "Character-level roleplaying with explicit role framing",
|
| 34 |
+
"summary": (
|
| 35 |
+
"RoleBench is the first systematic and fine-grained character-level benchmark "
|
| 36 |
+
"for role-playing, comprising 168,093 samples covering 100 distinct roles. "
|
| 37 |
+
"Created via the RoleLLM framework using Context-Instruct for role-specific "
|
| 38 |
+
"knowledge extraction and RoleGPT for speaking style imitation. Evaluates a "
|
| 39 |
+
"model's ability to maintain character persona, domain knowledge, and consistent "
|
| 40 |
+
"speaking style throughout interactions."
|
| 41 |
+
),
|
| 42 |
+
"paper": "https://arxiv.org/abs/2310.00746",
|
| 43 |
+
},
|
| 44 |
+
{
|
| 45 |
+
"name": "GRM-Bench \u2014 Coherence",
|
| 46 |
+
"category": "ROLEPLAY",
|
| 47 |
+
"calc_weight": 1.0,
|
| 48 |
+
"description": "Logically sound and coherent across turns, without contradictions",
|
| 49 |
+
"summary": (
|
| 50 |
+
"Nvidia-authored benchmark testing resistance to incoherence in gaming dialogue. "
|
| 51 |
+
"Scenarios are crafted to invoke common coherence failures, then a model's "
|
| 52 |
+
"resilience is measured. Detection covers eight categories: factual/logical errors, "
|
| 53 |
+
"cause-effect failures, self-contradiction, personality/background violations, "
|
| 54 |
+
"role confusion, irrelevance, knowledge boundary violations, and false premise "
|
| 55 |
+
"acceptance."
|
| 56 |
+
),
|
| 57 |
+
"paper": None,
|
| 58 |
+
},
|
| 59 |
+
{
|
| 60 |
+
"name": "GRM-Bench \u2014 Response Diversity",
|
| 61 |
+
"category": "ROLEPLAY",
|
| 62 |
+
"calc_weight": 1.0,
|
| 63 |
+
"description": "Avoids using repetitive language and speech structure",
|
| 64 |
+
"summary": (
|
| 65 |
+
"Nvidia-authored benchmark measuring whether models avoid repetitive language "
|
| 66 |
+
"patterns, vocabulary, and sentence structures across varied dialogue exchanges. "
|
| 67 |
+
"Evaluates lexical diversity, syntactic variation, and stylistic range when a "
|
| 68 |
+
"character is placed in different conversation contexts."
|
| 69 |
+
),
|
| 70 |
+
"paper": None,
|
| 71 |
+
},
|
| 72 |
+
{
|
| 73 |
+
"name": "GRM-Bench \u2014 Context Adaption",
|
| 74 |
+
"category": "ROLEPLAY",
|
| 75 |
+
"calc_weight": 1.0,
|
| 76 |
+
"description": "Using latest knowledge/variable updates even if changed during convo",
|
| 77 |
+
"summary": (
|
| 78 |
+
"Nvidia-authored benchmark testing whether models correctly incorporate the latest "
|
| 79 |
+
"game-state and knowledge updates, even when facts change mid-conversation. "
|
| 80 |
+
"Scenarios involve dynamic variable mutations (e.g. inventory changes, NPC status "
|
| 81 |
+
"updates) and verify the model references the current state rather than stale data."
|
| 82 |
+
),
|
| 83 |
+
"paper": None,
|
| 84 |
+
},
|
| 85 |
+
{
|
| 86 |
+
"name": "DialogueNLI",
|
| 87 |
+
"category": "ROLEPLAY",
|
| 88 |
+
"calc_weight": 0.5,
|
| 89 |
+
"description": "Checks contradiction/consistency crumbling",
|
| 90 |
+
"summary": (
|
| 91 |
+
"Dialogue Natural Language Inference dataset for evaluating consistency in "
|
| 92 |
+
"dialogue agents. Uses NLI-style classification (entailment / contradiction / "
|
| 93 |
+
"neutral) to detect when a dialogue agent contradicts its established persona "
|
| 94 |
+
"or previous statements. Derived from the Persona-Chat dataset with human-"
|
| 95 |
+
"annotated sentence pairs."
|
| 96 |
+
),
|
| 97 |
+
"paper": "https://arxiv.org/abs/1811.00671",
|
| 98 |
+
},
|
| 99 |
+
{
|
| 100 |
+
"name": "RoleMRC",
|
| 101 |
+
"category": "ROLEPLAY",
|
| 102 |
+
"calc_weight": 0.5,
|
| 103 |
+
"description": "Follow complex nested instructions while remaining in character",
|
| 104 |
+
"summary": (
|
| 105 |
+
"Role-based Machine Reading Comprehension benchmark that tests the ability to "
|
| 106 |
+
"follow complex, nested instructions while remaining fully in character. Combines "
|
| 107 |
+
"reading comprehension challenges with role-playing constraints, requiring models "
|
| 108 |
+
"to extract and reason about information without breaking persona."
|
| 109 |
+
),
|
| 110 |
+
"paper": None,
|
| 111 |
+
},
|
| 112 |
+
{
|
| 113 |
+
"name": "EQBench v3",
|
| 114 |
+
"category": "ROLEPLAY",
|
| 115 |
+
"calc_weight": 0.5,
|
| 116 |
+
"description": "Detect nuances in tone/intent and modulate response accordingly",
|
| 117 |
+
"summary": (
|
| 118 |
+
"Emotional Quotient Benchmark v3 assesses a model's emotional intelligence — "
|
| 119 |
+
"specifically the ability to detect nuances in tone, intent, and emotional subtext, "
|
| 120 |
+
"and to modulate responses accordingly. Tests include recognizing sarcasm, empathy "
|
| 121 |
+
"calibration, emotional escalation/de-escalation, and context-appropriate tonal "
|
| 122 |
+
"shifts."
|
| 123 |
+
),
|
| 124 |
+
"paper": "https://eqbench.com/",
|
| 125 |
+
},
|
| 126 |
+
|
| 127 |
+
# ── ACTIONS (33%) ───────────────────────────────────────────────
|
| 128 |
+
{
|
| 129 |
+
"name": "BFCLv3",
|
| 130 |
+
"category": "ACTIONS",
|
| 131 |
+
"calc_weight": 1.0,
|
| 132 |
+
"description": "Serial/parallel tool calling, multi-step settings",
|
| 133 |
+
"summary": (
|
| 134 |
+
"Berkeley Function-Calling Leaderboard v3 evaluates serial and parallel tool "
|
| 135 |
+
"calling in multi-step settings across multiple programming languages and complex "
|
| 136 |
+
"function schemas. Tests include simple, multiple, parallel, and nested function "
|
| 137 |
+
"calls, as well as function relevance detection (knowing when no tool applies)."
|
| 138 |
+
),
|
| 139 |
+
"paper": "https://gorilla.cs.berkeley.edu/blogs/8_berkeley_function_calling_leaderboard.html",
|
| 140 |
+
},
|
| 141 |
+
{
|
| 142 |
+
"name": "Tau2-Bench",
|
| 143 |
+
"category": "ACTIONS",
|
| 144 |
+
"calc_weight": 1.0,
|
| 145 |
+
"description": "Multi-turn interactions w/ real-world commercial operations",
|
| 146 |
+
"summary": (
|
| 147 |
+
"\u03c4\u00b2-Bench from Sierra Research is a multi-turn agentic benchmark using "
|
| 148 |
+
"dual-control agent-user simulation for testing tool use in real-world commercial "
|
| 149 |
+
"operations. The telecom domain contains 114 programmatically generated tasks "
|
| 150 |
+
"with varying intents (service, mobile data, MMS). The outcome world-state "
|
| 151 |
+
"determines success — e.g. whether Data is functioning after agent completion."
|
| 152 |
+
),
|
| 153 |
+
"paper": "https://arxiv.org/abs/2506.07982",
|
| 154 |
+
},
|
| 155 |
+
{
|
| 156 |
+
"name": "ToolSandbox",
|
| 157 |
+
"category": "ACTIONS",
|
| 158 |
+
"calc_weight": 1.0,
|
| 159 |
+
"description": "Stateful dependencies + conversational tool calling",
|
| 160 |
+
"summary": (
|
| 161 |
+
"Apple's stateful, conversational, interactive evaluation benchmark for LLM tool "
|
| 162 |
+
"use. Includes stateful tool execution with implicit state dependencies between "
|
| 163 |
+
"tools, a built-in user simulator supporting on-policy conversational evaluation, "
|
| 164 |
+
"and dynamic evaluation of intermediate and final milestones. Tests canonicalization, "
|
| 165 |
+
"insufficient information handling, and complex state management."
|
| 166 |
+
),
|
| 167 |
+
"paper": "https://arxiv.org/abs/2408.04682",
|
| 168 |
+
},
|
| 169 |
+
{
|
| 170 |
+
"name": "When2Call",
|
| 171 |
+
"category": "ACTIONS",
|
| 172 |
+
"calc_weight": 1.0,
|
| 173 |
+
"description": "Tool call timing \u2014 when to trigger, when to follow-up, etc.",
|
| 174 |
+
"summary": (
|
| 175 |
+
"Evaluates tool-call timing decisions: knowing when to invoke a tool, when to ask "
|
| 176 |
+
"for clarification first, and when to provide a direct answer without tools. Tests "
|
| 177 |
+
"the critical judgment of whether a function call is appropriate given the current "
|
| 178 |
+
"conversational context and available information."
|
| 179 |
+
),
|
| 180 |
+
"paper": None,
|
| 181 |
+
},
|
| 182 |
+
{
|
| 183 |
+
"name": "GRM-Bench \u2014 Prompt Robustness",
|
| 184 |
+
"category": "ACTIONS",
|
| 185 |
+
"calc_weight": 1.0,
|
| 186 |
+
"description": "Same prompt expressed differently still invoking intended tools",
|
| 187 |
+
"summary": (
|
| 188 |
+
"Nvidia-authored benchmark testing whether semantically equivalent prompts "
|
| 189 |
+
"expressed in different phrasings, formality levels, and syntactic structures "
|
| 190 |
+
"still correctly invoke the intended tools and actions. Measures robustness of "
|
| 191 |
+
"tool-call intent recognition against natural language variation."
|
| 192 |
+
),
|
| 193 |
+
"paper": None,
|
| 194 |
+
},
|
| 195 |
+
{
|
| 196 |
+
"name": "BFCLv4",
|
| 197 |
+
"category": "ACTIONS",
|
| 198 |
+
"calc_weight": 0.5,
|
| 199 |
+
"description": "Adds memory into the loop, and tests format sensitivity",
|
| 200 |
+
"summary": (
|
| 201 |
+
"Berkeley Function-Calling Leaderboard v4 extends v3 with memory-augmented tool "
|
| 202 |
+
"calling scenarios and tests sensitivity to format variations in function schemas. "
|
| 203 |
+
"Evaluates how well models handle evolving context windows and maintain tool-call "
|
| 204 |
+
"accuracy when schema formats shift."
|
| 205 |
+
),
|
| 206 |
+
"paper": "https://gorilla.cs.berkeley.edu/blogs/12_bfcl_v3_multi_turn.html",
|
| 207 |
+
},
|
| 208 |
+
{
|
| 209 |
+
"name": "T-Eval",
|
| 210 |
+
"category": "ACTIONS",
|
| 211 |
+
"calc_weight": 0.5,
|
| 212 |
+
"description": "Step-by-step tool use and orchestration, logical tool decomposition",
|
| 213 |
+
"summary": (
|
| 214 |
+
"T-Eval evaluates step-by-step tool use and orchestration capabilities. Tests "
|
| 215 |
+
"logical decomposition of complex tasks into tool-calling sequences, including "
|
| 216 |
+
"plan generation, tool selection, argument filling, and response summarization. "
|
| 217 |
+
"Provides fine-grained analysis of where in the tool-use pipeline models fail."
|
| 218 |
+
),
|
| 219 |
+
"paper": "https://arxiv.org/abs/2312.14033",
|
| 220 |
+
},
|
| 221 |
+
|
| 222 |
+
# ── GENERAL (33%) ──────────────────────────────────────────────
|
| 223 |
+
{
|
| 224 |
+
"name": "RULER",
|
| 225 |
+
"category": "GENERAL",
|
| 226 |
+
"calc_weight": 1.0,
|
| 227 |
+
"description": "Needle-in-haystack + polluted state stress test",
|
| 228 |
+
"summary": (
|
| 229 |
+
"RULER (Real-world Understanding of Long-context and Evaluation through Retrieval) "
|
| 230 |
+
"extends needle-in-a-haystack testing with multiple retrieval types, multi-hop "
|
| 231 |
+
"composition, and aggregation tasks at varying context lengths. Includes polluted "
|
| 232 |
+
"state and distractor injection to stress-test long-context faithfulness."
|
| 233 |
+
),
|
| 234 |
+
"paper": "https://arxiv.org/abs/2404.06654",
|
| 235 |
+
},
|
| 236 |
+
{
|
| 237 |
+
"name": "GaRAGe",
|
| 238 |
+
"category": "GENERAL",
|
| 239 |
+
"calc_weight": 1.0,
|
| 240 |
+
"description": "Deflect/refuse action when state is insufficient/corrupted",
|
| 241 |
+
"summary": (
|
| 242 |
+
"GaRAGe (Grounded and Attributed RAG Evaluation) tests a model's ability to "
|
| 243 |
+
"deflect or refuse action when the provided retrieval context is insufficient, "
|
| 244 |
+
"corrupted, or contradictory. Evaluates robustness against adversarial or low-"
|
| 245 |
+
"quality retrieved passages and the model's capacity to say 'I don't know' rather "
|
| 246 |
+
"than hallucinate an answer."
|
| 247 |
+
),
|
| 248 |
+
"paper": None,
|
| 249 |
+
},
|
| 250 |
+
{
|
| 251 |
+
"name": "IFBench",
|
| 252 |
+
"category": "GENERAL",
|
| 253 |
+
"calc_weight": 0.5,
|
| 254 |
+
"description": "Generic instruction following, not as prone to overfit vs IFEval",
|
| 255 |
+
"summary": (
|
| 256 |
+
"IFBench from AllenAI evaluates precise instruction following with 294 single-turn "
|
| 257 |
+
"questions testing counting, formatting, and sentence manipulation. Uses a loose "
|
| 258 |
+
"evaluation mode that accounts for extraneous text or formatting. Designed to be "
|
| 259 |
+
"less prone to overfitting compared to IFEval."
|
| 260 |
+
),
|
| 261 |
+
"paper": "https://arxiv.org/abs/2507.02833",
|
| 262 |
+
},
|
| 263 |
+
{
|
| 264 |
+
"name": "AA LCR",
|
| 265 |
+
"category": "GENERAL",
|
| 266 |
+
"calc_weight": 0.5,
|
| 267 |
+
"description": "Complex reasoning across long contexts",
|
| 268 |
+
"summary": (
|
| 269 |
+
"Artificial Analysis Long Context Reasoning benchmark with 100 hard text-based "
|
| 270 |
+
"questions spanning 7 document categories (Company Reports, Industry Reports, "
|
| 271 |
+
"Government Consultations, Academia, Legal, Marketing, Surveys). Requires ~100K "
|
| 272 |
+
"tokens of input per question, demanding reasoning across multiple long documents."
|
| 273 |
+
),
|
| 274 |
+
"paper": "https://artificialanalysis.ai/methodology/intelligence-benchmarking",
|
| 275 |
+
},
|
| 276 |
+
{
|
| 277 |
+
"name": "StructEval-T",
|
| 278 |
+
"category": "GENERAL",
|
| 279 |
+
"calc_weight": 0.5,
|
| 280 |
+
"description": "Format-following",
|
| 281 |
+
"summary": (
|
| 282 |
+
"StructEval-T evaluates structured output and format-following capabilities. "
|
| 283 |
+
"Tests whether models can adhere to specified output templates, formatting "
|
| 284 |
+
"constraints (JSON, XML, Markdown, tables), and structural requirements while "
|
| 285 |
+
"maintaining content accuracy."
|
| 286 |
+
),
|
| 287 |
+
"paper": None,
|
| 288 |
+
},
|
| 289 |
+
{
|
| 290 |
+
"name": "InverseIFEval",
|
| 291 |
+
"category": "GENERAL",
|
| 292 |
+
"calc_weight": 0.5,
|
| 293 |
+
"description": "Unconventional instruction following",
|
| 294 |
+
"summary": (
|
| 295 |
+
"Tests unconventional and counter-intuitive instruction following where models "
|
| 296 |
+
"must comply with unusual or inverted constraints. Evaluates whether models can "
|
| 297 |
+
"faithfully execute instructions that go against typical patterns, such as "
|
| 298 |
+
"intentionally producing specific error formats or following negative constraints."
|
| 299 |
+
),
|
| 300 |
+
"paper": None,
|
| 301 |
+
},
|
| 302 |
+
{
|
| 303 |
+
"name": "RAGTruth",
|
| 304 |
+
"category": "GENERAL",
|
| 305 |
+
"calc_weight": 0.5,
|
| 306 |
+
"description": "Hallucinations relative to retrieved context",
|
| 307 |
+
"summary": (
|
| 308 |
+
"RAGTruth benchmarks hallucination detection and prevention in RAG pipelines. "
|
| 309 |
+
"Evaluates whether models faithfully ground responses in provided retrieved "
|
| 310 |
+
"documents rather than generating unsupported claims. Covers summary-level and "
|
| 311 |
+
"sentence-level faithfulness across diverse document types."
|
| 312 |
+
),
|
| 313 |
+
"paper": "https://arxiv.org/abs/2401.00396",
|
| 314 |
+
},
|
| 315 |
+
{
|
| 316 |
+
"name": "SpatialText",
|
| 317 |
+
"category": "GENERAL",
|
| 318 |
+
"calc_weight": 0.5,
|
| 319 |
+
"description": "Text-based spatial cognition",
|
| 320 |
+
"summary": (
|
| 321 |
+
"SpatialText tests text-based spatial cognition — understanding spatial "
|
| 322 |
+
"relationships, positions, orientations, and arrangements described purely through "
|
| 323 |
+
"natural language. Critical for gaming scenarios involving navigation, object "
|
| 324 |
+
"placement, and environmental descriptions."
|
| 325 |
+
),
|
| 326 |
+
"paper": None,
|
| 327 |
+
},
|
| 328 |
+
{
|
| 329 |
+
"name": "SpartQA",
|
| 330 |
+
"category": "GENERAL",
|
| 331 |
+
"calc_weight": 0.5,
|
| 332 |
+
"description": "Spatial reasoning with textual spatial descriptions and Q&A",
|
| 333 |
+
"summary": (
|
| 334 |
+
"SpartQA is a question-answering benchmark for spatial reasoning from textual "
|
| 335 |
+
"descriptions. Models must understand object positions, relative locations, "
|
| 336 |
+
"containment, and spatial logic described in natural language passages, then answer "
|
| 337 |
+
"questions requiring spatial inference. Includes FindRelation, FindBlock, and "
|
| 338 |
+
"YesNo question types."
|
| 339 |
+
),
|
| 340 |
+
"paper": "https://arxiv.org/abs/2104.05832",
|
| 341 |
+
},
|
| 342 |
+
{
|
| 343 |
+
"name": "COPA",
|
| 344 |
+
"category": "GENERAL",
|
| 345 |
+
"calc_weight": 0.5,
|
| 346 |
+
"description": "Premise + choose plausible cause/effect",
|
| 347 |
+
"summary": (
|
| 348 |
+
"Choice of Plausible Alternatives (COPA) is a classic commonsense causal reasoning "
|
| 349 |
+
"benchmark. Given a premise, the model must select the more plausible cause or "
|
| 350 |
+
"effect from two options. Tests open-domain commonsense causal reasoning critical "
|
| 351 |
+
"for narrative coherence in game dialogue."
|
| 352 |
+
),
|
| 353 |
+
"paper": "https://people.ict.usc.edu/~gordon/copa.html",
|
| 354 |
+
},
|
| 355 |
+
{
|
| 356 |
+
"name": "PIQA",
|
| 357 |
+
"category": "GENERAL",
|
| 358 |
+
"calc_weight": 0.5,
|
| 359 |
+
"description": "Physical interaction common sense",
|
| 360 |
+
"summary": (
|
| 361 |
+
"Physical Interaction Question Answering tests physical commonsense knowledge "
|
| 362 |
+
"about everyday objects, their properties, affordances, and interactions. Each "
|
| 363 |
+
"question presents a goal and two solutions; the model must select the physically "
|
| 364 |
+
"plausible one. Relevant for game NPCs reasoning about physical world interactions."
|
| 365 |
+
),
|
| 366 |
+
"paper": "https://arxiv.org/abs/1911.11641",
|
| 367 |
+
},
|
| 368 |
+
]
|
| 369 |
+
|
| 370 |
+
CATEGORIES = ["ROLEPLAY", "ACTIONS", "GENERAL"]
|
| 371 |
+
CATEGORY_WEIGHT = 1 / 3 # Each category contributes 33.3%
|
| 372 |
+
|
| 373 |
+
CATEGORY_DISPLAY = {
|
| 374 |
+
"ROLEPLAY": "Roleplay",
|
| 375 |
+
"ACTIONS": "Actions",
|
| 376 |
+
"GENERAL": "General",
|
| 377 |
+
}
|
| 378 |
+
|
| 379 |
+
|
| 380 |
+
def get_benchmarks_by_category(category: str) -> list[dict]:
|
| 381 |
+
return [b for b in BENCHMARKS if b["category"] == category]
|
| 382 |
+
|
| 383 |
+
|
| 384 |
+
def get_all_benchmark_names() -> list[str]:
|
| 385 |
+
return [b["name"] for b in BENCHMARKS]
|
pyproject.toml
DELETED
|
@@ -1,13 +0,0 @@
|
|
| 1 |
-
[tool.ruff]
|
| 2 |
-
# Enable pycodestyle (`E`) and Pyflakes (`F`) codes by default.
|
| 3 |
-
select = ["E", "F"]
|
| 4 |
-
ignore = ["E501"] # line too long (black is taking care of this)
|
| 5 |
-
line-length = 119
|
| 6 |
-
fixable = ["A", "B", "C", "D", "E", "F", "G", "I", "N", "Q", "S", "T", "W", "ANN", "ARG", "BLE", "COM", "DJ", "DTZ", "EM", "ERA", "EXE", "FBT", "ICN", "INP", "ISC", "NPY", "PD", "PGH", "PIE", "PL", "PT", "PTH", "PYI", "RET", "RSE", "RUF", "SIM", "SLF", "TCH", "TID", "TRY", "UP", "YTT"]
|
| 7 |
-
|
| 8 |
-
[tool.isort]
|
| 9 |
-
profile = "black"
|
| 10 |
-
line_length = 119
|
| 11 |
-
|
| 12 |
-
[tool.black]
|
| 13 |
-
line-length = 119
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ref/Coherence_Summary
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Coherence
|
| 2 |
+
Above all other factors that can break immersion is and the character/NPC interaction experience is instances in which the model response seem illogical or irrelevant. Incoherence can occur at any time for any number of reasons ranging from outright hallucinations to confusion of their role, or contradicting a previous statement/event/world state.
|
| 3 |
+
|
| 4 |
+
Test Methodology
|
| 5 |
+
Since incoherence can occur in any instance it means we need a wide range of detection to mark them, but it means almost any testing structure could still be useful. Our strategy however, is to craft test scenarios that are likely to invoke a common failure, then test a model's resilience to it.
|
| 6 |
+
|
| 7 |
+
Detection Scope:
|
| 8 |
+
Factual / Logical - Objectively false or contradicted by the system prompt, gamestate (e.g. hallucinates details, refers to an entity that doesn't exist, invents a rule not established anywhere in context).
|
| 9 |
+
Cause/ Effect - Fails to understand simple logical state transitions (e.g. “just drank whole glass of water therefore glass is empty”)
|
| 10 |
+
Contradiction - The response contradicts something the assistant previously said or did in an earlier turn without any in-world justification.
|
| 11 |
+
Personality / Background Violation - The response has the character do or claim something that directly violates a trait, limitation, or background fact established.
|
| 12 |
+
Role Confusion - Confuses facts, details, actions, or motivations of who or what entities and identity.
|
| 13 |
+
Irrelevance - The response does not seem relevant and/or confuses the subject matter or topic in discussion.
|
| 14 |
+
Knowledge Boundary - Invents knowledge it cannot possibly have, instead of clearly separating observation from speculation
|
| 15 |
+
False Premise - Incorrectly accepts a user’s smuggled-in premise about something that never happened
|
| 16 |
+
|
| 17 |
+
Test Samples
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
TEST_001
|
| 21 |
+
Tag(s): Role Confusion
|
| 22 |
+
Summary: Tests identity confusion between two companions with opposite elemental specialties across 9 rounds of dialogue with heavy noun saturation. Kael (fire mage, Cinderforge Academy, Emberchill Ring) and Lyra (ice mage, Glacial Sanctum, Frostbane Amulet). The final user message deliberately misattributes all key facts. Model must correct every error without confusing the details itself.
|
| 23 |
+
Expectation: Must correct all misattributions: Kael=Cinderforge (not Glacial Sanctum), Lyra=Glacial Sanctum (not Cinderforge), Kael wears Emberchill Ring (ice resist), Lyra wears Frostbane Amulet (fire resist)
|
| 24 |
+
Messages:
|
| 25 |
+
[
|
| 26 |
+
{
|
| 27 |
+
"role": "system",
|
| 28 |
+
"content": "You are Kael Emberheart, a fire mage and combat specialist who serves as one of the player's two companions in the Shadowveil Dungeon. You are accompanied by Lyra Frostweaver, an ice mage and defensive specialist.\n\nYOUR IDENTITY — KAEL EMBERHEART:\n- Class: Fire Mage…"
|
| 29 |
+
},
|
| 30 |
+
{
|
| 31 |
+
"role": "user",
|
| 32 |
+
"content": "Kael, quick question — what element do you specialize in? And what's your companion Lyra's element?"
|
| 33 |
+
},
|
| 34 |
+
{
|
| 35 |
+
"role": "assistant",
|
| 36 |
+
"content": "Ha! Fire, obviously — I'm Kael Emberheart, pyromancer supreme! Lyra's the frost mage. She handles the cold stuff, I bring the heat."
|
| 37 |
+
},
|
| 38 |
+
{
|
| 39 |
+
"role": "user",
|
| 40 |
+
"content": "There's an ice wraith ahead. Who's better suited to fight it — you or Lyra?"
|
| 41 |
+
},
|
| 42 |
+
… (8x turns)
|
| 43 |
+
{
|
| 44 |
+
"role": "assistant",
|
| 45 |
+
"content": "The Convergence Blast! I launch a concentrated fireball and Lyra intercepts it mid-flight with a frost bolt. Extreme heat meets extreme cold — massive steam explosion that devastates everything in the blast zone. I bring the fire, she brings the ice, and the result is pure chaos. One of my favorite moves."
|
| 46 |
+
},
|
| 47 |
+
{
|
| 48 |
+
"role": "user",
|
| 49 |
+
"content": "Okay, one last thing before we push into the Embervault. I want to make sure I've got everyone straight. You're the one who trained at the Glacial Sanctum, right? And Lyra graduated from Cinderforge Academy? Also remind me — you're wearing the Frostbane Amulet for the fire resistance, and Lyra has the Emberchill Ring?"
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
TEST_021
|
| 54 |
+
Summary: Tests whether the model invents contents of an unread document. Eleanor found a sealed letter with an unbroken wax seal. She explicitly confirmed she has NOT opened or read it. The inspector pressures her to state exactly what it says. Model must refuse to fabricate contents.
|
| 55 |
+
Tag(s): Knowledge Boundary
|
| 56 |
+
Expectation: Must NOT claim to know the letter's contents; should restate the seal is unbroken and she has not read it
|
| 57 |
+
Messages:
|
| 58 |
+
[
|
| 59 |
+
{
|
| 60 |
+
{
|
| 61 |
+
"role": "system",
|
| 62 |
+
"content": "You are Eleanor Vale, lady's maid in a gothic mystery set at Blackthorne Manor.\n\n# PERSONALITY\n- Composed, discreet, observant\n- Speaks carefully and avoids exaggeration\n\n# CURRENT FACTS\n- You found a sealed letter on the late master's desk…"
|
| 63 |
+
},
|
| 64 |
+
{
|
| 65 |
+
"role": "user",
|
| 66 |
+
"content": "What did you find in the study?"
|
| 67 |
+
},
|
| 68 |
+
{
|
| 69 |
+
"role": "assistant",
|
| 70 |
+
"content": "A sealed letter addressed to Lady Whitcomb. The wax is intact, so I have not read what is inside."
|
| 71 |
+
},
|
| 72 |
+
{
|
| 73 |
+
"role": "user",
|
| 74 |
+
"content": "Can you tell whether it's important?"
|
| 75 |
+
},
|
| 76 |
+
{
|
| 77 |
+
"role": "assistant",
|
| 78 |
+
"content": "Only that it was hidden carefully, which suggests it may matter. But that is only an inference. I do not know its contents."
|
| 79 |
+
},
|
| 80 |
+
{
|
| 81 |
+
"role": "user",
|
| 82 |
+
"content": "The inspector is impatient. Tell him exactly what the letter says."
|
| 83 |
+
}
|
| 84 |
+
}
|
ref/Overview
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Nvidia Game Ready Model Score (GRM) is an aggregated quality metric designed to assess LLM capabilites in gaming use cases.
|
| 2 |
+
|
| 3 |
+
General state-of-the-art language models are optimized for broad benchmarks such as math, code, and general knowledge. That does not reliably translate to in-game performance, and it does not reliably predict NPC quality, gameplay actions, or immersion.
|
| 4 |
+
|
| 5 |
+
With game model evaluation, game developers can accelerate AI integration pipelines by reducing time spent on model evaluation and narrowing model choice earlier. The overall score is the average of Roleplay, Actions, and General, while benchmarks inside each category are combined with weighted averaging using core weights of 1.0 and supplementary weights of 0.5.
|
| 6 |
+
|
| 7 |
+
GRM Score = (Roleplay + Actions + General) / 3
|
| 8 |
+
|
| 9 |
+
Category Score = sum(score x weight) / sum(weight)
|
requirements.txt
CHANGED
|
@@ -1,16 +1 @@
|
|
| 1 |
-
|
| 2 |
-
black
|
| 3 |
-
datasets
|
| 4 |
-
gradio==4.43
|
| 5 |
-
gradio[oauth]
|
| 6 |
-
gradio_leaderboard==0.0.13
|
| 7 |
-
gradio_client
|
| 8 |
-
huggingface-hub>=0.18.0
|
| 9 |
-
matplotlib
|
| 10 |
-
numpy
|
| 11 |
-
pandas
|
| 12 |
-
python-dateutil
|
| 13 |
-
tqdm
|
| 14 |
-
transformers
|
| 15 |
-
tokenizers>=0.15.0
|
| 16 |
-
sentencepiece
|
|
|
|
| 1 |
+
gradio
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
scores.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
GRM Evaluation Suite — Placeholder Model Scores
|
| 3 |
+
|
| 4 |
+
Scores are on a 0.0–1.0 scale (will be displayed as 0–100%).
|
| 5 |
+
All benchmarks have synthetic placeholder values for now.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
# fmt: off
|
| 9 |
+
MODEL_SCORES: dict[str, dict[str, float]] = {
|
| 10 |
+
"GPT-5.4": {
|
| 11 |
+
# ROLEPLAY
|
| 12 |
+
"MultiChallenge": 0.91, "RoleBench": 0.89, "GRM-Bench \u2014 Coherence": 0.87,
|
| 13 |
+
"GRM-Bench \u2014 Response Diversity": 0.85, "GRM-Bench \u2014 Context Adaption": 0.88,
|
| 14 |
+
"DialogueNLI": 0.90, "RoleMRC": 0.86, "EQBench v3": 0.88,
|
| 15 |
+
# ACTIONS
|
| 16 |
+
"BFCLv3": 0.91, "Tau2-Bench": 0.88, "ToolSandbox": 0.86,
|
| 17 |
+
"When2Call": 0.89, "GRM-Bench \u2014 Prompt Robustness": 0.84,
|
| 18 |
+
"BFCLv4": 0.89, "T-Eval": 0.87,
|
| 19 |
+
# GENERAL
|
| 20 |
+
"RULER": 0.93, "GaRAGe": 0.85, "IFBench": 0.87, "AA LCR": 0.85,
|
| 21 |
+
"StructEval-T": 0.86, "InverseIFEval": 0.82, "RAGTruth": 0.88,
|
| 22 |
+
"SpatialText": 0.80, "SpartQA": 0.81, "COPA": 0.94, "PIQA": 0.93,
|
| 23 |
+
},
|
| 24 |
+
"GPT-oss20b": {
|
| 25 |
+
"MultiChallenge": 0.76, "RoleBench": 0.74, "GRM-Bench \u2014 Coherence": 0.71,
|
| 26 |
+
"GRM-Bench \u2014 Response Diversity": 0.69, "GRM-Bench \u2014 Context Adaption": 0.73,
|
| 27 |
+
"DialogueNLI": 0.75, "RoleMRC": 0.70, "EQBench v3": 0.72,
|
| 28 |
+
"BFCLv3": 0.78, "Tau2-Bench": 0.72, "ToolSandbox": 0.70,
|
| 29 |
+
"When2Call": 0.74, "GRM-Bench \u2014 Prompt Robustness": 0.68,
|
| 30 |
+
"BFCLv4": 0.75, "T-Eval": 0.71,
|
| 31 |
+
"RULER": 0.80, "GaRAGe": 0.69, "IFBench": 0.71, "AA LCR": 0.68,
|
| 32 |
+
"StructEval-T": 0.70, "InverseIFEval": 0.65, "RAGTruth": 0.72,
|
| 33 |
+
"SpatialText": 0.63, "SpartQA": 0.64, "COPA": 0.82, "PIQA": 0.80,
|
| 34 |
+
},
|
| 35 |
+
"Gemini 2.5 Pro": {
|
| 36 |
+
"MultiChallenge": 0.89, "RoleBench": 0.87, "GRM-Bench \u2014 Coherence": 0.85,
|
| 37 |
+
"GRM-Bench \u2014 Response Diversity": 0.83, "GRM-Bench \u2014 Context Adaption": 0.86,
|
| 38 |
+
"DialogueNLI": 0.88, "RoleMRC": 0.84, "EQBench v3": 0.86,
|
| 39 |
+
"BFCLv3": 0.89, "Tau2-Bench": 0.86, "ToolSandbox": 0.84,
|
| 40 |
+
"When2Call": 0.87, "GRM-Bench \u2014 Prompt Robustness": 0.82,
|
| 41 |
+
"BFCLv4": 0.87, "T-Eval": 0.85,
|
| 42 |
+
"RULER": 0.91, "GaRAGe": 0.83, "IFBench": 0.84, "AA LCR": 0.82,
|
| 43 |
+
"StructEval-T": 0.84, "InverseIFEval": 0.80, "RAGTruth": 0.86,
|
| 44 |
+
"SpatialText": 0.78, "SpartQA": 0.79, "COPA": 0.92, "PIQA": 0.91,
|
| 45 |
+
},
|
| 46 |
+
"Nemotron 3 Super 120B A12B [BF16]": {
|
| 47 |
+
"MultiChallenge": 0.86, "RoleBench": 0.84, "GRM-Bench \u2014 Coherence": 0.82,
|
| 48 |
+
"GRM-Bench \u2014 Response Diversity": 0.80, "GRM-Bench \u2014 Context Adaption": 0.83,
|
| 49 |
+
"DialogueNLI": 0.85, "RoleMRC": 0.81, "EQBench v3": 0.83,
|
| 50 |
+
"BFCLv3": 0.86, "Tau2-Bench": 0.83, "ToolSandbox": 0.81,
|
| 51 |
+
"When2Call": 0.84, "GRM-Bench \u2014 Prompt Robustness": 0.79,
|
| 52 |
+
"BFCLv4": 0.84, "T-Eval": 0.82,
|
| 53 |
+
"RULER": 0.89, "GaRAGe": 0.80, "IFBench": 0.82, "AA LCR": 0.80,
|
| 54 |
+
"StructEval-T": 0.81, "InverseIFEval": 0.77, "RAGTruth": 0.83,
|
| 55 |
+
"SpatialText": 0.75, "SpartQA": 0.76, "COPA": 0.90, "PIQA": 0.89,
|
| 56 |
+
},
|
| 57 |
+
"Nemotron 3 Nano 30B A3B [BF16]": {
|
| 58 |
+
"MultiChallenge": 0.73, "RoleBench": 0.71, "GRM-Bench \u2014 Coherence": 0.68,
|
| 59 |
+
"GRM-Bench \u2014 Response Diversity": 0.66, "GRM-Bench \u2014 Context Adaption": 0.70,
|
| 60 |
+
"DialogueNLI": 0.72, "RoleMRC": 0.67, "EQBench v3": 0.69,
|
| 61 |
+
"BFCLv3": 0.74, "Tau2-Bench": 0.70, "ToolSandbox": 0.68,
|
| 62 |
+
"When2Call": 0.71, "GRM-Bench \u2014 Prompt Robustness": 0.65,
|
| 63 |
+
"BFCLv4": 0.72, "T-Eval": 0.68,
|
| 64 |
+
"RULER": 0.77, "GaRAGe": 0.67, "IFBench": 0.69, "AA LCR": 0.66,
|
| 65 |
+
"StructEval-T": 0.68, "InverseIFEval": 0.63, "RAGTruth": 0.70,
|
| 66 |
+
"SpatialText": 0.61, "SpartQA": 0.62, "COPA": 0.79, "PIQA": 0.78,
|
| 67 |
+
},
|
| 68 |
+
"Nemotron 3 Nano 4B": {
|
| 69 |
+
"MultiChallenge": 0.57, "RoleBench": 0.54, "GRM-Bench \u2014 Coherence": 0.51,
|
| 70 |
+
"GRM-Bench \u2014 Response Diversity": 0.49, "GRM-Bench \u2014 Context Adaption": 0.53,
|
| 71 |
+
"DialogueNLI": 0.56, "RoleMRC": 0.50, "EQBench v3": 0.52,
|
| 72 |
+
"BFCLv3": 0.58, "Tau2-Bench": 0.52, "ToolSandbox": 0.50,
|
| 73 |
+
"When2Call": 0.54, "GRM-Bench \u2014 Prompt Robustness": 0.47,
|
| 74 |
+
"BFCLv4": 0.55, "T-Eval": 0.51,
|
| 75 |
+
"RULER": 0.62, "GaRAGe": 0.50, "IFBench": 0.54, "AA LCR": 0.48,
|
| 76 |
+
"StructEval-T": 0.51, "InverseIFEval": 0.46, "RAGTruth": 0.53,
|
| 77 |
+
"SpatialText": 0.44, "SpartQA": 0.45, "COPA": 0.65, "PIQA": 0.64,
|
| 78 |
+
},
|
| 79 |
+
"Qwen 3.5 27B": {
|
| 80 |
+
"MultiChallenge": 0.81, "RoleBench": 0.79, "GRM-Bench \u2014 Coherence": 0.76,
|
| 81 |
+
"GRM-Bench \u2014 Response Diversity": 0.74, "GRM-Bench \u2014 Context Adaption": 0.78,
|
| 82 |
+
"DialogueNLI": 0.80, "RoleMRC": 0.75, "EQBench v3": 0.77,
|
| 83 |
+
"BFCLv3": 0.82, "Tau2-Bench": 0.79, "ToolSandbox": 0.77,
|
| 84 |
+
"When2Call": 0.80, "GRM-Bench \u2014 Prompt Robustness": 0.74,
|
| 85 |
+
"BFCLv4": 0.80, "T-Eval": 0.76,
|
| 86 |
+
"RULER": 0.85, "GaRAGe": 0.75, "IFBench": 0.77, "AA LCR": 0.74,
|
| 87 |
+
"StructEval-T": 0.76, "InverseIFEval": 0.71, "RAGTruth": 0.78,
|
| 88 |
+
"SpatialText": 0.69, "SpartQA": 0.70, "COPA": 0.87, "PIQA": 0.86,
|
| 89 |
+
},
|
| 90 |
+
"Nemotron Nano 9B v2 [BF16]": {
|
| 91 |
+
"MultiChallenge": 0.66, "RoleBench": 0.63, "GRM-Bench \u2014 Coherence": 0.60,
|
| 92 |
+
"GRM-Bench \u2014 Response Diversity": 0.58, "GRM-Bench \u2014 Context Adaption": 0.62,
|
| 93 |
+
"DialogueNLI": 0.65, "RoleMRC": 0.59, "EQBench v3": 0.61,
|
| 94 |
+
"BFCLv3": 0.67, "Tau2-Bench": 0.63, "ToolSandbox": 0.61,
|
| 95 |
+
"When2Call": 0.64, "GRM-Bench \u2014 Prompt Robustness": 0.57,
|
| 96 |
+
"BFCLv4": 0.65, "T-Eval": 0.60,
|
| 97 |
+
"RULER": 0.71, "GaRAGe": 0.60, "IFBench": 0.62, "AA LCR": 0.58,
|
| 98 |
+
"StructEval-T": 0.60, "InverseIFEval": 0.55, "RAGTruth": 0.63,
|
| 99 |
+
"SpatialText": 0.53, "SpartQA": 0.54, "COPA": 0.74, "PIQA": 0.73,
|
| 100 |
+
},
|
| 101 |
+
"Qwen 3.5 9B [BF16]": {
|
| 102 |
+
"MultiChallenge": 0.64, "RoleBench": 0.61, "GRM-Bench \u2014 Coherence": 0.58,
|
| 103 |
+
"GRM-Bench \u2014 Response Diversity": 0.56, "GRM-Bench \u2014 Context Adaption": 0.60,
|
| 104 |
+
"DialogueNLI": 0.63, "RoleMRC": 0.57, "EQBench v3": 0.59,
|
| 105 |
+
"BFCLv3": 0.65, "Tau2-Bench": 0.61, "ToolSandbox": 0.59,
|
| 106 |
+
"When2Call": 0.62, "GRM-Bench \u2014 Prompt Robustness": 0.55,
|
| 107 |
+
"BFCLv4": 0.63, "T-Eval": 0.58,
|
| 108 |
+
"RULER": 0.69, "GaRAGe": 0.58, "IFBench": 0.60, "AA LCR": 0.56,
|
| 109 |
+
"StructEval-T": 0.58, "InverseIFEval": 0.53, "RAGTruth": 0.61,
|
| 110 |
+
"SpatialText": 0.51, "SpartQA": 0.52, "COPA": 0.72, "PIQA": 0.71,
|
| 111 |
+
},
|
| 112 |
+
"Qwen 3.5 4B [BF16]": {
|
| 113 |
+
"MultiChallenge": 0.55, "RoleBench": 0.52, "GRM-Bench \u2014 Coherence": 0.49,
|
| 114 |
+
"GRM-Bench \u2014 Response Diversity": 0.47, "GRM-Bench \u2014 Context Adaption": 0.51,
|
| 115 |
+
"DialogueNLI": 0.54, "RoleMRC": 0.48, "EQBench v3": 0.50,
|
| 116 |
+
"BFCLv3": 0.56, "Tau2-Bench": 0.50, "ToolSandbox": 0.48,
|
| 117 |
+
"When2Call": 0.52, "GRM-Bench \u2014 Prompt Robustness": 0.45,
|
| 118 |
+
"BFCLv4": 0.53, "T-Eval": 0.49,
|
| 119 |
+
"RULER": 0.60, "GaRAGe": 0.48, "IFBench": 0.52, "AA LCR": 0.46,
|
| 120 |
+
"StructEval-T": 0.49, "InverseIFEval": 0.44, "RAGTruth": 0.51,
|
| 121 |
+
"SpatialText": 0.42, "SpartQA": 0.43, "COPA": 0.63, "PIQA": 0.62,
|
| 122 |
+
},
|
| 123 |
+
"Qwen 3.5 2B [BF16]": {
|
| 124 |
+
"MultiChallenge": 0.46, "RoleBench": 0.43, "GRM-Bench \u2014 Coherence": 0.40,
|
| 125 |
+
"GRM-Bench \u2014 Response Diversity": 0.38, "GRM-Bench \u2014 Context Adaption": 0.42,
|
| 126 |
+
"DialogueNLI": 0.45, "RoleMRC": 0.39, "EQBench v3": 0.41,
|
| 127 |
+
"BFCLv3": 0.47, "Tau2-Bench": 0.41, "ToolSandbox": 0.39,
|
| 128 |
+
"When2Call": 0.43, "GRM-Bench \u2014 Prompt Robustness": 0.36,
|
| 129 |
+
"BFCLv4": 0.44, "T-Eval": 0.40,
|
| 130 |
+
"RULER": 0.51, "GaRAGe": 0.39, "IFBench": 0.43, "AA LCR": 0.38,
|
| 131 |
+
"StructEval-T": 0.40, "InverseIFEval": 0.35, "RAGTruth": 0.42,
|
| 132 |
+
"SpatialText": 0.33, "SpartQA": 0.34, "COPA": 0.54, "PIQA": 0.53,
|
| 133 |
+
},
|
| 134 |
+
}
|
| 135 |
+
# fmt: on
|
scoring.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
GRM Evaluation Suite — Scoring Logic
|
| 3 |
+
|
| 4 |
+
GRM Score = mean(Roleplay_score, Actions_score, General_score)
|
| 5 |
+
|
| 6 |
+
Each category score is a weighted average of its benchmarks:
|
| 7 |
+
category_score = sum(score_i * calc_weight_i) / sum(calc_weight_i)
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
from benchmarks import BENCHMARKS, CATEGORIES, get_benchmarks_by_category
|
| 11 |
+
from scores import MODEL_SCORES
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def compute_category_score(
|
| 15 |
+
model_scores: dict[str, float | None],
|
| 16 |
+
category: str,
|
| 17 |
+
) -> float | None:
|
| 18 |
+
"""Weighted average of available benchmark scores in a category."""
|
| 19 |
+
benchmarks = get_benchmarks_by_category(category)
|
| 20 |
+
total_weight = 0.0
|
| 21 |
+
total_value = 0.0
|
| 22 |
+
for b in benchmarks:
|
| 23 |
+
score = model_scores.get(b["name"])
|
| 24 |
+
if score is not None:
|
| 25 |
+
total_weight += b["calc_weight"]
|
| 26 |
+
total_value += score * b["calc_weight"]
|
| 27 |
+
if total_weight == 0:
|
| 28 |
+
return None
|
| 29 |
+
return total_value / total_weight
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def compute_grm_score(model_scores: dict[str, float | None]) -> dict:
|
| 33 |
+
"""Return GRM Score and per-category scores for a single model.
|
| 34 |
+
|
| 35 |
+
All returned values are on a 0–100 scale, or None if no data.
|
| 36 |
+
"""
|
| 37 |
+
cat_scores = {}
|
| 38 |
+
for cat in CATEGORIES:
|
| 39 |
+
raw = compute_category_score(model_scores, cat)
|
| 40 |
+
cat_scores[cat] = round(raw * 100, 1) if raw is not None else None
|
| 41 |
+
|
| 42 |
+
available = [v for v in cat_scores.values() if v is not None]
|
| 43 |
+
grm = round(sum(available) / len(available), 1) if available else None
|
| 44 |
+
|
| 45 |
+
return {
|
| 46 |
+
"GRM Score": grm,
|
| 47 |
+
"Roleplay (33%)": cat_scores["ROLEPLAY"],
|
| 48 |
+
"Actions (33%)": cat_scores["ACTIONS"],
|
| 49 |
+
"General (33%)": cat_scores["GENERAL"],
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def build_leaderboard() -> list[dict]:
|
| 54 |
+
"""Compute scores for all models and return sorted rows."""
|
| 55 |
+
rows = []
|
| 56 |
+
for model_name, model_scores in MODEL_SCORES.items():
|
| 57 |
+
result = compute_grm_score(model_scores)
|
| 58 |
+
result["Model"] = model_name
|
| 59 |
+
rows.append(result)
|
| 60 |
+
|
| 61 |
+
rows.sort(key=lambda r: r["GRM Score"] if r["GRM Score"] is not None else -1, reverse=True)
|
| 62 |
+
|
| 63 |
+
for i, row in enumerate(rows, start=1):
|
| 64 |
+
row["Rank"] = i
|
| 65 |
+
|
| 66 |
+
return rows
|
src/about.py
DELETED
|
@@ -1,89 +0,0 @@
|
|
| 1 |
-
from dataclasses import dataclass
|
| 2 |
-
from enum import Enum
|
| 3 |
-
|
| 4 |
-
@dataclass
|
| 5 |
-
class Task:
|
| 6 |
-
benchmark: str
|
| 7 |
-
metric: str
|
| 8 |
-
col_name: str
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
# Select your tasks here
|
| 12 |
-
# ---------------------------------------------------
|
| 13 |
-
class Tasks(Enum):
|
| 14 |
-
# task_key in the json file, metric_key in the json file, name to display in the leaderboard
|
| 15 |
-
task0 = Task("anli_r1", "acc", "ANLI")
|
| 16 |
-
task1 = Task("logiqa", "acc_norm", "LogiQA")
|
| 17 |
-
|
| 18 |
-
NUM_FEWSHOT = 0 # Change with your few shot
|
| 19 |
-
# ---------------------------------------------------
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
# Your leaderboard name
|
| 24 |
-
TITLE = """<h1 align="center" id="space-title">Game Model Leaderboard</h1>"""
|
| 25 |
-
|
| 26 |
-
# What does your leaderboard evaluate?
|
| 27 |
-
INTRODUCTION_TEXT = """<h3 align="center">Best LLMs for Gaming</h3>"""
|
| 28 |
-
|
| 29 |
-
# Which evaluations are you running? how can people reproduce what you have?
|
| 30 |
-
LLM_BENCHMARKS_TEXT = f"""
|
| 31 |
-
## How it works
|
| 32 |
-
|
| 33 |
-
## Reproducibility
|
| 34 |
-
To reproduce our results, here is the commands you can run:
|
| 35 |
-
|
| 36 |
-
"""
|
| 37 |
-
|
| 38 |
-
METRICS_TEXT = """
|
| 39 |
-
## Metrics placeholder
|
| 40 |
-
"""
|
| 41 |
-
|
| 42 |
-
PERFORMANCE_TEXT = """
|
| 43 |
-
## Performance placeholder
|
| 44 |
-
"""
|
| 45 |
-
|
| 46 |
-
ABOUT_TEXT = """
|
| 47 |
-
## About placeholder
|
| 48 |
-
"""
|
| 49 |
-
|
| 50 |
-
EVALUATION_QUEUE_TEXT = """
|
| 51 |
-
## Some good practices before submitting a model
|
| 52 |
-
|
| 53 |
-
### 1) Make sure you can load your model and tokenizer using AutoClasses:
|
| 54 |
-
```python
|
| 55 |
-
from transformers import AutoConfig, AutoModel, AutoTokenizer
|
| 56 |
-
config = AutoConfig.from_pretrained("your model name", revision=revision)
|
| 57 |
-
model = AutoModel.from_pretrained("your model name", revision=revision)
|
| 58 |
-
tokenizer = AutoTokenizer.from_pretrained("your model name", revision=revision)
|
| 59 |
-
```
|
| 60 |
-
If this step fails, follow the error messages to debug your model before submitting it. It's likely your model has been improperly uploaded.
|
| 61 |
-
|
| 62 |
-
Note: make sure your model is public!
|
| 63 |
-
Note: if your model needs `use_remote_code=True`, we do not support this option yet but we are working on adding it, stay posted!
|
| 64 |
-
|
| 65 |
-
### 2) Convert your model weights to [safetensors](https://huggingface.co/docs/safetensors/index)
|
| 66 |
-
It's a new format for storing weights which is safer and faster to load and use. It will also allow us to add the number of parameters of your model to the `Extended Viewer`!
|
| 67 |
-
|
| 68 |
-
### 3) Make sure your model has an open license!
|
| 69 |
-
This is a leaderboard for Open LLMs, and we'd love for as many people as possible to know they can use your model 🤗
|
| 70 |
-
|
| 71 |
-
### 4) Fill up your model card
|
| 72 |
-
When we add extra information about models to the leaderboard, it will be automatically taken from the model card
|
| 73 |
-
|
| 74 |
-
## In case of model failure
|
| 75 |
-
If your model is displayed in the `FAILED` category, its execution stopped.
|
| 76 |
-
Make sure you have followed the above steps first.
|
| 77 |
-
If everything is done, check you can launch the EleutherAIHarness on your model locally, using the above command without modifications (you can add `--limit` to limit the number of examples per task).
|
| 78 |
-
"""
|
| 79 |
-
|
| 80 |
-
CITATION_BUTTON_LABEL = "Copy the following snippet to cite these results"
|
| 81 |
-
CITATION_BUTTON_TEXT = r"""
|
| 82 |
-
@misc{game_ready_models_leaderboard,
|
| 83 |
-
author = {Maria Bagdasarova, Jani Joki, Warren Drones, Irina Filatova},
|
| 84 |
-
title = {Game Ready Model Leaderboard},
|
| 85 |
-
year = {2026},
|
| 86 |
-
publisher = {Hugging Face},
|
| 87 |
-
howpublished = "\url{https://huggingface.co/spaces/nvidia/GRM}"
|
| 88 |
-
}
|
| 89 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/display/css_html_js.py
DELETED
|
@@ -1,105 +0,0 @@
|
|
| 1 |
-
custom_css = """
|
| 2 |
-
|
| 3 |
-
.markdown-text {
|
| 4 |
-
font-size: 16px !important;
|
| 5 |
-
}
|
| 6 |
-
|
| 7 |
-
#models-to-add-text {
|
| 8 |
-
font-size: 18px !important;
|
| 9 |
-
}
|
| 10 |
-
|
| 11 |
-
#citation-button span {
|
| 12 |
-
font-size: 16px !important;
|
| 13 |
-
}
|
| 14 |
-
|
| 15 |
-
#citation-button textarea {
|
| 16 |
-
font-size: 16px !important;
|
| 17 |
-
}
|
| 18 |
-
|
| 19 |
-
#citation-button > label > button {
|
| 20 |
-
margin: 6px;
|
| 21 |
-
transform: scale(1.3);
|
| 22 |
-
}
|
| 23 |
-
|
| 24 |
-
#leaderboard-table {
|
| 25 |
-
margin-top: 15px
|
| 26 |
-
}
|
| 27 |
-
|
| 28 |
-
#leaderboard-table-lite {
|
| 29 |
-
margin-top: 15px
|
| 30 |
-
}
|
| 31 |
-
|
| 32 |
-
#search-bar-table-box > div:first-child {
|
| 33 |
-
background: none;
|
| 34 |
-
border: none;
|
| 35 |
-
}
|
| 36 |
-
|
| 37 |
-
#search-bar {
|
| 38 |
-
padding: 0px;
|
| 39 |
-
}
|
| 40 |
-
|
| 41 |
-
/* Limit the width of the first AutoEvalColumn so that names don't expand too much */
|
| 42 |
-
#leaderboard-table td:nth-child(2),
|
| 43 |
-
#leaderboard-table th:nth-child(2) {
|
| 44 |
-
max-width: 400px;
|
| 45 |
-
overflow: auto;
|
| 46 |
-
white-space: nowrap;
|
| 47 |
-
}
|
| 48 |
-
|
| 49 |
-
.tab-buttons button {
|
| 50 |
-
font-size: 20px;
|
| 51 |
-
}
|
| 52 |
-
|
| 53 |
-
#scale-logo {
|
| 54 |
-
border-style: none !important;
|
| 55 |
-
box-shadow: none;
|
| 56 |
-
display: block;
|
| 57 |
-
margin-left: auto;
|
| 58 |
-
margin-right: auto;
|
| 59 |
-
max-width: 600px;
|
| 60 |
-
}
|
| 61 |
-
|
| 62 |
-
#scale-logo .download {
|
| 63 |
-
display: none;
|
| 64 |
-
}
|
| 65 |
-
#filter_type{
|
| 66 |
-
border: 0;
|
| 67 |
-
padding-left: 0;
|
| 68 |
-
padding-top: 0;
|
| 69 |
-
}
|
| 70 |
-
#filter_type label {
|
| 71 |
-
display: flex;
|
| 72 |
-
}
|
| 73 |
-
#filter_type label > span{
|
| 74 |
-
margin-top: var(--spacing-lg);
|
| 75 |
-
margin-right: 0.5em;
|
| 76 |
-
}
|
| 77 |
-
#filter_type label > .wrap{
|
| 78 |
-
width: 103px;
|
| 79 |
-
}
|
| 80 |
-
#filter_type label > .wrap .wrap-inner{
|
| 81 |
-
padding: 2px;
|
| 82 |
-
}
|
| 83 |
-
#filter_type label > .wrap .wrap-inner input{
|
| 84 |
-
width: 1px
|
| 85 |
-
}
|
| 86 |
-
#filter-columns-type{
|
| 87 |
-
border:0;
|
| 88 |
-
padding:0.5;
|
| 89 |
-
}
|
| 90 |
-
#filter-columns-size{
|
| 91 |
-
border:0;
|
| 92 |
-
padding:0.5;
|
| 93 |
-
}
|
| 94 |
-
#box-filter > .form{
|
| 95 |
-
border: 0
|
| 96 |
-
}
|
| 97 |
-
"""
|
| 98 |
-
|
| 99 |
-
get_window_url_params = """
|
| 100 |
-
function(url_params) {
|
| 101 |
-
const params = new URLSearchParams(window.location.search);
|
| 102 |
-
url_params = Object.fromEntries(params);
|
| 103 |
-
return url_params;
|
| 104 |
-
}
|
| 105 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/display/formatting.py
DELETED
|
@@ -1,27 +0,0 @@
|
|
| 1 |
-
def model_hyperlink(link, model_name):
|
| 2 |
-
return f'<a target="_blank" href="{link}" style="color: var(--link-text-color); text-decoration: underline;text-decoration-style: dotted;">{model_name}</a>'
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
def make_clickable_model(model_name):
|
| 6 |
-
link = f"https://huggingface.co/{model_name}"
|
| 7 |
-
return model_hyperlink(link, model_name)
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
def styled_error(error):
|
| 11 |
-
return f"<p style='color: red; font-size: 20px; text-align: center;'>{error}</p>"
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
def styled_warning(warn):
|
| 15 |
-
return f"<p style='color: orange; font-size: 20px; text-align: center;'>{warn}</p>"
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
def styled_message(message):
|
| 19 |
-
return f"<p style='color: green; font-size: 20px; text-align: center;'>{message}</p>"
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
def has_no_nan_values(df, columns):
|
| 23 |
-
return df[columns].notna().all(axis=1)
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
def has_nan_values(df, columns):
|
| 27 |
-
return df[columns].isna().any(axis=1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/display/utils.py
DELETED
|
@@ -1,110 +0,0 @@
|
|
| 1 |
-
from dataclasses import dataclass, make_dataclass
|
| 2 |
-
from enum import Enum
|
| 3 |
-
|
| 4 |
-
import pandas as pd
|
| 5 |
-
|
| 6 |
-
from src.about import Tasks
|
| 7 |
-
|
| 8 |
-
def fields(raw_class):
|
| 9 |
-
return [v for k, v in raw_class.__dict__.items() if k[:2] != "__" and k[-2:] != "__"]
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
# These classes are for user facing column names,
|
| 13 |
-
# to avoid having to change them all around the code
|
| 14 |
-
# when a modif is needed
|
| 15 |
-
@dataclass
|
| 16 |
-
class ColumnContent:
|
| 17 |
-
name: str
|
| 18 |
-
type: str
|
| 19 |
-
displayed_by_default: bool
|
| 20 |
-
hidden: bool = False
|
| 21 |
-
never_hidden: bool = False
|
| 22 |
-
|
| 23 |
-
## Leaderboard columns
|
| 24 |
-
auto_eval_column_dict = []
|
| 25 |
-
# Init
|
| 26 |
-
auto_eval_column_dict.append(["model_type_symbol", ColumnContent, ColumnContent("T", "str", True, never_hidden=True)])
|
| 27 |
-
auto_eval_column_dict.append(["model", ColumnContent, ColumnContent("Model", "markdown", True, never_hidden=True)])
|
| 28 |
-
#Scores
|
| 29 |
-
auto_eval_column_dict.append(["average", ColumnContent, ColumnContent("Average ⬆️", "number", True)])
|
| 30 |
-
for task in Tasks:
|
| 31 |
-
auto_eval_column_dict.append([task.name, ColumnContent, ColumnContent(task.value.col_name, "number", True)])
|
| 32 |
-
# Model information
|
| 33 |
-
auto_eval_column_dict.append(["model_type", ColumnContent, ColumnContent("Type", "str", False)])
|
| 34 |
-
auto_eval_column_dict.append(["architecture", ColumnContent, ColumnContent("Architecture", "str", False)])
|
| 35 |
-
auto_eval_column_dict.append(["weight_type", ColumnContent, ColumnContent("Weight type", "str", False, True)])
|
| 36 |
-
auto_eval_column_dict.append(["precision", ColumnContent, ColumnContent("Precision", "str", False)])
|
| 37 |
-
auto_eval_column_dict.append(["license", ColumnContent, ColumnContent("Hub License", "str", False)])
|
| 38 |
-
auto_eval_column_dict.append(["params", ColumnContent, ColumnContent("#Params (B)", "number", False)])
|
| 39 |
-
auto_eval_column_dict.append(["likes", ColumnContent, ColumnContent("Hub ❤️", "number", False)])
|
| 40 |
-
auto_eval_column_dict.append(["still_on_hub", ColumnContent, ColumnContent("Available on the hub", "bool", False)])
|
| 41 |
-
auto_eval_column_dict.append(["revision", ColumnContent, ColumnContent("Model sha", "str", False, False)])
|
| 42 |
-
|
| 43 |
-
# We use make dataclass to dynamically fill the scores from Tasks
|
| 44 |
-
AutoEvalColumn = make_dataclass("AutoEvalColumn", auto_eval_column_dict, frozen=True)
|
| 45 |
-
|
| 46 |
-
## For the queue columns in the submission tab
|
| 47 |
-
@dataclass(frozen=True)
|
| 48 |
-
class EvalQueueColumn: # Queue column
|
| 49 |
-
model = ColumnContent("model", "markdown", True)
|
| 50 |
-
revision = ColumnContent("revision", "str", True)
|
| 51 |
-
private = ColumnContent("private", "bool", True)
|
| 52 |
-
precision = ColumnContent("precision", "str", True)
|
| 53 |
-
weight_type = ColumnContent("weight_type", "str", "Original")
|
| 54 |
-
status = ColumnContent("status", "str", True)
|
| 55 |
-
|
| 56 |
-
## All the model information that we might need
|
| 57 |
-
@dataclass
|
| 58 |
-
class ModelDetails:
|
| 59 |
-
name: str
|
| 60 |
-
display_name: str = ""
|
| 61 |
-
symbol: str = "" # emoji
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
class ModelType(Enum):
|
| 65 |
-
PT = ModelDetails(name="pretrained", symbol="🟢")
|
| 66 |
-
FT = ModelDetails(name="fine-tuned", symbol="🔶")
|
| 67 |
-
IFT = ModelDetails(name="instruction-tuned", symbol="⭕")
|
| 68 |
-
RL = ModelDetails(name="RL-tuned", symbol="🟦")
|
| 69 |
-
Unknown = ModelDetails(name="", symbol="?")
|
| 70 |
-
|
| 71 |
-
def to_str(self, separator=" "):
|
| 72 |
-
return f"{self.value.symbol}{separator}{self.value.name}"
|
| 73 |
-
|
| 74 |
-
@staticmethod
|
| 75 |
-
def from_str(type):
|
| 76 |
-
if "fine-tuned" in type or "🔶" in type:
|
| 77 |
-
return ModelType.FT
|
| 78 |
-
if "pretrained" in type or "🟢" in type:
|
| 79 |
-
return ModelType.PT
|
| 80 |
-
if "RL-tuned" in type or "🟦" in type:
|
| 81 |
-
return ModelType.RL
|
| 82 |
-
if "instruction-tuned" in type or "⭕" in type:
|
| 83 |
-
return ModelType.IFT
|
| 84 |
-
return ModelType.Unknown
|
| 85 |
-
|
| 86 |
-
class WeightType(Enum):
|
| 87 |
-
Adapter = ModelDetails("Adapter")
|
| 88 |
-
Original = ModelDetails("Original")
|
| 89 |
-
Delta = ModelDetails("Delta")
|
| 90 |
-
|
| 91 |
-
class Precision(Enum):
|
| 92 |
-
float16 = ModelDetails("float16")
|
| 93 |
-
bfloat16 = ModelDetails("bfloat16")
|
| 94 |
-
Unknown = ModelDetails("?")
|
| 95 |
-
|
| 96 |
-
def from_str(precision):
|
| 97 |
-
if precision in ["torch.float16", "float16"]:
|
| 98 |
-
return Precision.float16
|
| 99 |
-
if precision in ["torch.bfloat16", "bfloat16"]:
|
| 100 |
-
return Precision.bfloat16
|
| 101 |
-
return Precision.Unknown
|
| 102 |
-
|
| 103 |
-
# Column selection
|
| 104 |
-
COLS = [c.name for c in fields(AutoEvalColumn) if not c.hidden]
|
| 105 |
-
|
| 106 |
-
EVAL_COLS = [c.name for c in fields(EvalQueueColumn)]
|
| 107 |
-
EVAL_TYPES = [c.type for c in fields(EvalQueueColumn)]
|
| 108 |
-
|
| 109 |
-
BENCHMARK_COLS = [t.value.col_name for t in Tasks]
|
| 110 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/envs.py
DELETED
|
@@ -1,25 +0,0 @@
|
|
| 1 |
-
import os
|
| 2 |
-
|
| 3 |
-
from huggingface_hub import HfApi
|
| 4 |
-
|
| 5 |
-
# Info to change for your repository
|
| 6 |
-
# ----------------------------------
|
| 7 |
-
TOKEN = os.environ.get("HF_TOKEN") # A read/write token for your org
|
| 8 |
-
|
| 9 |
-
OWNER = "demo-leaderboard-backend" # Change to your org - don't forget to create a results and request dataset, with the correct format!
|
| 10 |
-
# ----------------------------------
|
| 11 |
-
|
| 12 |
-
REPO_ID = f"{OWNER}/leaderboard"
|
| 13 |
-
QUEUE_REPO = f"{OWNER}/requests"
|
| 14 |
-
RESULTS_REPO = f"{OWNER}/results"
|
| 15 |
-
|
| 16 |
-
# If you setup a cache later, just change HF_HOME
|
| 17 |
-
CACHE_PATH=os.getenv("HF_HOME", ".")
|
| 18 |
-
|
| 19 |
-
# Local caches
|
| 20 |
-
EVAL_REQUESTS_PATH = os.path.join(CACHE_PATH, "eval-queue")
|
| 21 |
-
EVAL_RESULTS_PATH = os.path.join(CACHE_PATH, "eval-results")
|
| 22 |
-
EVAL_REQUESTS_PATH_BACKEND = os.path.join(CACHE_PATH, "eval-queue-bk")
|
| 23 |
-
EVAL_RESULTS_PATH_BACKEND = os.path.join(CACHE_PATH, "eval-results-bk")
|
| 24 |
-
|
| 25 |
-
API = HfApi(token=TOKEN)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/leaderboard/read_evals.py
DELETED
|
@@ -1,196 +0,0 @@
|
|
| 1 |
-
import glob
|
| 2 |
-
import json
|
| 3 |
-
import math
|
| 4 |
-
import os
|
| 5 |
-
from dataclasses import dataclass
|
| 6 |
-
|
| 7 |
-
import dateutil
|
| 8 |
-
import numpy as np
|
| 9 |
-
|
| 10 |
-
from src.display.formatting import make_clickable_model
|
| 11 |
-
from src.display.utils import AutoEvalColumn, ModelType, Tasks, Precision, WeightType
|
| 12 |
-
from src.submission.check_validity import is_model_on_hub
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
@dataclass
|
| 16 |
-
class EvalResult:
|
| 17 |
-
"""Represents one full evaluation. Built from a combination of the result and request file for a given run.
|
| 18 |
-
"""
|
| 19 |
-
eval_name: str # org_model_precision (uid)
|
| 20 |
-
full_model: str # org/model (path on hub)
|
| 21 |
-
org: str
|
| 22 |
-
model: str
|
| 23 |
-
revision: str # commit hash, "" if main
|
| 24 |
-
results: dict
|
| 25 |
-
precision: Precision = Precision.Unknown
|
| 26 |
-
model_type: ModelType = ModelType.Unknown # Pretrained, fine tuned, ...
|
| 27 |
-
weight_type: WeightType = WeightType.Original # Original or Adapter
|
| 28 |
-
architecture: str = "Unknown"
|
| 29 |
-
license: str = "?"
|
| 30 |
-
likes: int = 0
|
| 31 |
-
num_params: int = 0
|
| 32 |
-
date: str = "" # submission date of request file
|
| 33 |
-
still_on_hub: bool = False
|
| 34 |
-
|
| 35 |
-
@classmethod
|
| 36 |
-
def init_from_json_file(self, json_filepath):
|
| 37 |
-
"""Inits the result from the specific model result file"""
|
| 38 |
-
with open(json_filepath) as fp:
|
| 39 |
-
data = json.load(fp)
|
| 40 |
-
|
| 41 |
-
config = data.get("config")
|
| 42 |
-
|
| 43 |
-
# Precision
|
| 44 |
-
precision = Precision.from_str(config.get("model_dtype"))
|
| 45 |
-
|
| 46 |
-
# Get model and org
|
| 47 |
-
org_and_model = config.get("model_name", config.get("model_args", None))
|
| 48 |
-
org_and_model = org_and_model.split("/", 1)
|
| 49 |
-
|
| 50 |
-
if len(org_and_model) == 1:
|
| 51 |
-
org = None
|
| 52 |
-
model = org_and_model[0]
|
| 53 |
-
result_key = f"{model}_{precision.value.name}"
|
| 54 |
-
else:
|
| 55 |
-
org = org_and_model[0]
|
| 56 |
-
model = org_and_model[1]
|
| 57 |
-
result_key = f"{org}_{model}_{precision.value.name}"
|
| 58 |
-
full_model = "/".join(org_and_model)
|
| 59 |
-
|
| 60 |
-
still_on_hub, _, model_config = is_model_on_hub(
|
| 61 |
-
full_model, config.get("model_sha", "main"), trust_remote_code=True, test_tokenizer=False
|
| 62 |
-
)
|
| 63 |
-
architecture = "?"
|
| 64 |
-
if model_config is not None:
|
| 65 |
-
architectures = getattr(model_config, "architectures", None)
|
| 66 |
-
if architectures:
|
| 67 |
-
architecture = ";".join(architectures)
|
| 68 |
-
|
| 69 |
-
# Extract results available in this file (some results are split in several files)
|
| 70 |
-
results = {}
|
| 71 |
-
for task in Tasks:
|
| 72 |
-
task = task.value
|
| 73 |
-
|
| 74 |
-
# We average all scores of a given metric (not all metrics are present in all files)
|
| 75 |
-
accs = np.array([v.get(task.metric, None) for k, v in data["results"].items() if task.benchmark == k])
|
| 76 |
-
if accs.size == 0 or any([acc is None for acc in accs]):
|
| 77 |
-
continue
|
| 78 |
-
|
| 79 |
-
mean_acc = np.mean(accs) * 100.0
|
| 80 |
-
results[task.benchmark] = mean_acc
|
| 81 |
-
|
| 82 |
-
return self(
|
| 83 |
-
eval_name=result_key,
|
| 84 |
-
full_model=full_model,
|
| 85 |
-
org=org,
|
| 86 |
-
model=model,
|
| 87 |
-
results=results,
|
| 88 |
-
precision=precision,
|
| 89 |
-
revision= config.get("model_sha", ""),
|
| 90 |
-
still_on_hub=still_on_hub,
|
| 91 |
-
architecture=architecture
|
| 92 |
-
)
|
| 93 |
-
|
| 94 |
-
def update_with_request_file(self, requests_path):
|
| 95 |
-
"""Finds the relevant request file for the current model and updates info with it"""
|
| 96 |
-
request_file = get_request_file_for_model(requests_path, self.full_model, self.precision.value.name)
|
| 97 |
-
|
| 98 |
-
try:
|
| 99 |
-
with open(request_file, "r") as f:
|
| 100 |
-
request = json.load(f)
|
| 101 |
-
self.model_type = ModelType.from_str(request.get("model_type", ""))
|
| 102 |
-
self.weight_type = WeightType[request.get("weight_type", "Original")]
|
| 103 |
-
self.license = request.get("license", "?")
|
| 104 |
-
self.likes = request.get("likes", 0)
|
| 105 |
-
self.num_params = request.get("params", 0)
|
| 106 |
-
self.date = request.get("submitted_time", "")
|
| 107 |
-
except Exception:
|
| 108 |
-
print(f"Could not find request file for {self.org}/{self.model} with precision {self.precision.value.name}")
|
| 109 |
-
|
| 110 |
-
def to_dict(self):
|
| 111 |
-
"""Converts the Eval Result to a dict compatible with our dataframe display"""
|
| 112 |
-
average = sum([v for v in self.results.values() if v is not None]) / len(Tasks)
|
| 113 |
-
data_dict = {
|
| 114 |
-
"eval_name": self.eval_name, # not a column, just a save name,
|
| 115 |
-
AutoEvalColumn.precision.name: self.precision.value.name,
|
| 116 |
-
AutoEvalColumn.model_type.name: self.model_type.value.name,
|
| 117 |
-
AutoEvalColumn.model_type_symbol.name: self.model_type.value.symbol,
|
| 118 |
-
AutoEvalColumn.weight_type.name: self.weight_type.value.name,
|
| 119 |
-
AutoEvalColumn.architecture.name: self.architecture,
|
| 120 |
-
AutoEvalColumn.model.name: make_clickable_model(self.full_model),
|
| 121 |
-
AutoEvalColumn.revision.name: self.revision,
|
| 122 |
-
AutoEvalColumn.average.name: average,
|
| 123 |
-
AutoEvalColumn.license.name: self.license,
|
| 124 |
-
AutoEvalColumn.likes.name: self.likes,
|
| 125 |
-
AutoEvalColumn.params.name: self.num_params,
|
| 126 |
-
AutoEvalColumn.still_on_hub.name: self.still_on_hub,
|
| 127 |
-
}
|
| 128 |
-
|
| 129 |
-
for task in Tasks:
|
| 130 |
-
data_dict[task.value.col_name] = self.results[task.value.benchmark]
|
| 131 |
-
|
| 132 |
-
return data_dict
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
def get_request_file_for_model(requests_path, model_name, precision):
|
| 136 |
-
"""Selects the correct request file for a given model. Only keeps runs tagged as FINISHED"""
|
| 137 |
-
request_files = os.path.join(
|
| 138 |
-
requests_path,
|
| 139 |
-
f"{model_name}_eval_request_*.json",
|
| 140 |
-
)
|
| 141 |
-
request_files = glob.glob(request_files)
|
| 142 |
-
|
| 143 |
-
# Select correct request file (precision)
|
| 144 |
-
request_file = ""
|
| 145 |
-
request_files = sorted(request_files, reverse=True)
|
| 146 |
-
for tmp_request_file in request_files:
|
| 147 |
-
with open(tmp_request_file, "r") as f:
|
| 148 |
-
req_content = json.load(f)
|
| 149 |
-
if (
|
| 150 |
-
req_content["status"] in ["FINISHED"]
|
| 151 |
-
and req_content["precision"] == precision.split(".")[-1]
|
| 152 |
-
):
|
| 153 |
-
request_file = tmp_request_file
|
| 154 |
-
return request_file
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
def get_raw_eval_results(results_path: str, requests_path: str) -> list[EvalResult]:
|
| 158 |
-
"""From the path of the results folder root, extract all needed info for results"""
|
| 159 |
-
model_result_filepaths = []
|
| 160 |
-
|
| 161 |
-
for root, _, files in os.walk(results_path):
|
| 162 |
-
# We should only have json files in model results
|
| 163 |
-
if len(files) == 0 or any([not f.endswith(".json") for f in files]):
|
| 164 |
-
continue
|
| 165 |
-
|
| 166 |
-
# Sort the files by date
|
| 167 |
-
try:
|
| 168 |
-
files.sort(key=lambda x: x.removesuffix(".json").removeprefix("results_")[:-7])
|
| 169 |
-
except dateutil.parser._parser.ParserError:
|
| 170 |
-
files = [files[-1]]
|
| 171 |
-
|
| 172 |
-
for file in files:
|
| 173 |
-
model_result_filepaths.append(os.path.join(root, file))
|
| 174 |
-
|
| 175 |
-
eval_results = {}
|
| 176 |
-
for model_result_filepath in model_result_filepaths:
|
| 177 |
-
# Creation of result
|
| 178 |
-
eval_result = EvalResult.init_from_json_file(model_result_filepath)
|
| 179 |
-
eval_result.update_with_request_file(requests_path)
|
| 180 |
-
|
| 181 |
-
# Store results of same eval together
|
| 182 |
-
eval_name = eval_result.eval_name
|
| 183 |
-
if eval_name in eval_results.keys():
|
| 184 |
-
eval_results[eval_name].results.update({k: v for k, v in eval_result.results.items() if v is not None})
|
| 185 |
-
else:
|
| 186 |
-
eval_results[eval_name] = eval_result
|
| 187 |
-
|
| 188 |
-
results = []
|
| 189 |
-
for v in eval_results.values():
|
| 190 |
-
try:
|
| 191 |
-
v.to_dict() # we test if the dict version is complete
|
| 192 |
-
results.append(v)
|
| 193 |
-
except KeyError: # not all eval values present
|
| 194 |
-
continue
|
| 195 |
-
|
| 196 |
-
return results
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/populate.py
DELETED
|
@@ -1,58 +0,0 @@
|
|
| 1 |
-
import json
|
| 2 |
-
import os
|
| 3 |
-
|
| 4 |
-
import pandas as pd
|
| 5 |
-
|
| 6 |
-
from src.display.formatting import has_no_nan_values, make_clickable_model
|
| 7 |
-
from src.display.utils import AutoEvalColumn, EvalQueueColumn
|
| 8 |
-
from src.leaderboard.read_evals import get_raw_eval_results
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
def get_leaderboard_df(results_path: str, requests_path: str, cols: list, benchmark_cols: list) -> pd.DataFrame:
|
| 12 |
-
"""Creates a dataframe from all the individual experiment results"""
|
| 13 |
-
raw_data = get_raw_eval_results(results_path, requests_path)
|
| 14 |
-
all_data_json = [v.to_dict() for v in raw_data]
|
| 15 |
-
|
| 16 |
-
df = pd.DataFrame.from_records(all_data_json)
|
| 17 |
-
df = df.sort_values(by=[AutoEvalColumn.average.name], ascending=False)
|
| 18 |
-
df = df[cols].round(decimals=2)
|
| 19 |
-
|
| 20 |
-
# filter out if any of the benchmarks have not been produced
|
| 21 |
-
df = df[has_no_nan_values(df, benchmark_cols)]
|
| 22 |
-
return df
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
def get_evaluation_queue_df(save_path: str, cols: list) -> list[pd.DataFrame]:
|
| 26 |
-
"""Creates the different dataframes for the evaluation queues requestes"""
|
| 27 |
-
entries = [entry for entry in os.listdir(save_path) if not entry.startswith(".")]
|
| 28 |
-
all_evals = []
|
| 29 |
-
|
| 30 |
-
for entry in entries:
|
| 31 |
-
if ".json" in entry:
|
| 32 |
-
file_path = os.path.join(save_path, entry)
|
| 33 |
-
with open(file_path) as fp:
|
| 34 |
-
data = json.load(fp)
|
| 35 |
-
|
| 36 |
-
data[EvalQueueColumn.model.name] = make_clickable_model(data["model"])
|
| 37 |
-
data[EvalQueueColumn.revision.name] = data.get("revision", "main")
|
| 38 |
-
|
| 39 |
-
all_evals.append(data)
|
| 40 |
-
elif ".md" not in entry:
|
| 41 |
-
# this is a folder
|
| 42 |
-
sub_entries = [e for e in os.listdir(f"{save_path}/{entry}") if os.path.isfile(e) and not e.startswith(".")]
|
| 43 |
-
for sub_entry in sub_entries:
|
| 44 |
-
file_path = os.path.join(save_path, entry, sub_entry)
|
| 45 |
-
with open(file_path) as fp:
|
| 46 |
-
data = json.load(fp)
|
| 47 |
-
|
| 48 |
-
data[EvalQueueColumn.model.name] = make_clickable_model(data["model"])
|
| 49 |
-
data[EvalQueueColumn.revision.name] = data.get("revision", "main")
|
| 50 |
-
all_evals.append(data)
|
| 51 |
-
|
| 52 |
-
pending_list = [e for e in all_evals if e["status"] in ["PENDING", "RERUN"]]
|
| 53 |
-
running_list = [e for e in all_evals if e["status"] == "RUNNING"]
|
| 54 |
-
finished_list = [e for e in all_evals if e["status"].startswith("FINISHED") or e["status"] == "PENDING_NEW_EVAL"]
|
| 55 |
-
df_pending = pd.DataFrame.from_records(pending_list, columns=cols)
|
| 56 |
-
df_running = pd.DataFrame.from_records(running_list, columns=cols)
|
| 57 |
-
df_finished = pd.DataFrame.from_records(finished_list, columns=cols)
|
| 58 |
-
return df_finished[cols], df_running[cols], df_pending[cols]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/submission/check_validity.py
DELETED
|
@@ -1,99 +0,0 @@
|
|
| 1 |
-
import json
|
| 2 |
-
import os
|
| 3 |
-
import re
|
| 4 |
-
from collections import defaultdict
|
| 5 |
-
from datetime import datetime, timedelta, timezone
|
| 6 |
-
|
| 7 |
-
import huggingface_hub
|
| 8 |
-
from huggingface_hub import ModelCard
|
| 9 |
-
from huggingface_hub.hf_api import ModelInfo
|
| 10 |
-
from transformers import AutoConfig
|
| 11 |
-
from transformers.models.auto.tokenization_auto import AutoTokenizer
|
| 12 |
-
|
| 13 |
-
def check_model_card(repo_id: str) -> tuple[bool, str]:
|
| 14 |
-
"""Checks if the model card and license exist and have been filled"""
|
| 15 |
-
try:
|
| 16 |
-
card = ModelCard.load(repo_id)
|
| 17 |
-
except huggingface_hub.utils.EntryNotFoundError:
|
| 18 |
-
return False, "Please add a model card to your model to explain how you trained/fine-tuned it."
|
| 19 |
-
|
| 20 |
-
# Enforce license metadata
|
| 21 |
-
if card.data.license is None:
|
| 22 |
-
if not ("license_name" in card.data and "license_link" in card.data):
|
| 23 |
-
return False, (
|
| 24 |
-
"License not found. Please add a license to your model card using the `license` metadata or a"
|
| 25 |
-
" `license_name`/`license_link` pair."
|
| 26 |
-
)
|
| 27 |
-
|
| 28 |
-
# Enforce card content
|
| 29 |
-
if len(card.text) < 200:
|
| 30 |
-
return False, "Please add a description to your model card, it is too short."
|
| 31 |
-
|
| 32 |
-
return True, ""
|
| 33 |
-
|
| 34 |
-
def is_model_on_hub(model_name: str, revision: str, token: str = None, trust_remote_code=False, test_tokenizer=False) -> tuple[bool, str]:
|
| 35 |
-
"""Checks if the model model_name is on the hub, and whether it (and its tokenizer) can be loaded with AutoClasses."""
|
| 36 |
-
try:
|
| 37 |
-
config = AutoConfig.from_pretrained(model_name, revision=revision, trust_remote_code=trust_remote_code, token=token)
|
| 38 |
-
if test_tokenizer:
|
| 39 |
-
try:
|
| 40 |
-
tk = AutoTokenizer.from_pretrained(model_name, revision=revision, trust_remote_code=trust_remote_code, token=token)
|
| 41 |
-
except ValueError as e:
|
| 42 |
-
return (
|
| 43 |
-
False,
|
| 44 |
-
f"uses a tokenizer which is not in a transformers release: {e}",
|
| 45 |
-
None
|
| 46 |
-
)
|
| 47 |
-
except Exception as e:
|
| 48 |
-
return (False, "'s tokenizer cannot be loaded. Is your tokenizer class in a stable transformers release, and correctly configured?", None)
|
| 49 |
-
return True, None, config
|
| 50 |
-
|
| 51 |
-
except ValueError:
|
| 52 |
-
return (
|
| 53 |
-
False,
|
| 54 |
-
"needs to be launched with `trust_remote_code=True`. For safety reason, we do not allow these models to be automatically submitted to the leaderboard.",
|
| 55 |
-
None
|
| 56 |
-
)
|
| 57 |
-
|
| 58 |
-
except Exception as e:
|
| 59 |
-
return False, "was not found on hub!", None
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
def get_model_size(model_info: ModelInfo, precision: str):
|
| 63 |
-
"""Gets the model size from the configuration, or the model name if the configuration does not contain the information."""
|
| 64 |
-
try:
|
| 65 |
-
model_size = round(model_info.safetensors["total"] / 1e9, 3)
|
| 66 |
-
except (AttributeError, TypeError):
|
| 67 |
-
return 0 # Unknown model sizes are indicated as 0, see NUMERIC_INTERVALS in app.py
|
| 68 |
-
|
| 69 |
-
size_factor = 8 if (precision == "GPTQ" or "gptq" in model_info.modelId.lower()) else 1
|
| 70 |
-
model_size = size_factor * model_size
|
| 71 |
-
return model_size
|
| 72 |
-
|
| 73 |
-
def get_model_arch(model_info: ModelInfo):
|
| 74 |
-
"""Gets the model architecture from the configuration"""
|
| 75 |
-
return model_info.config.get("architectures", "Unknown")
|
| 76 |
-
|
| 77 |
-
def already_submitted_models(requested_models_dir: str) -> set[str]:
|
| 78 |
-
"""Gather a list of already submitted models to avoid duplicates"""
|
| 79 |
-
depth = 1
|
| 80 |
-
file_names = []
|
| 81 |
-
users_to_submission_dates = defaultdict(list)
|
| 82 |
-
|
| 83 |
-
for root, _, files in os.walk(requested_models_dir):
|
| 84 |
-
current_depth = root.count(os.sep) - requested_models_dir.count(os.sep)
|
| 85 |
-
if current_depth == depth:
|
| 86 |
-
for file in files:
|
| 87 |
-
if not file.endswith(".json"):
|
| 88 |
-
continue
|
| 89 |
-
with open(os.path.join(root, file), "r") as f:
|
| 90 |
-
info = json.load(f)
|
| 91 |
-
file_names.append(f"{info['model']}_{info['revision']}_{info['precision']}")
|
| 92 |
-
|
| 93 |
-
# Select organisation
|
| 94 |
-
if info["model"].count("/") == 0 or "submitted_time" not in info:
|
| 95 |
-
continue
|
| 96 |
-
organisation, _ = info["model"].split("/")
|
| 97 |
-
users_to_submission_dates[organisation].append(info["submitted_time"])
|
| 98 |
-
|
| 99 |
-
return set(file_names), users_to_submission_dates
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/submission/submit.py
DELETED
|
@@ -1,119 +0,0 @@
|
|
| 1 |
-
import json
|
| 2 |
-
import os
|
| 3 |
-
from datetime import datetime, timezone
|
| 4 |
-
|
| 5 |
-
from src.display.formatting import styled_error, styled_message, styled_warning
|
| 6 |
-
from src.envs import API, EVAL_REQUESTS_PATH, TOKEN, QUEUE_REPO
|
| 7 |
-
from src.submission.check_validity import (
|
| 8 |
-
already_submitted_models,
|
| 9 |
-
check_model_card,
|
| 10 |
-
get_model_size,
|
| 11 |
-
is_model_on_hub,
|
| 12 |
-
)
|
| 13 |
-
|
| 14 |
-
REQUESTED_MODELS = None
|
| 15 |
-
USERS_TO_SUBMISSION_DATES = None
|
| 16 |
-
|
| 17 |
-
def add_new_eval(
|
| 18 |
-
model: str,
|
| 19 |
-
base_model: str,
|
| 20 |
-
revision: str,
|
| 21 |
-
precision: str,
|
| 22 |
-
weight_type: str,
|
| 23 |
-
model_type: str,
|
| 24 |
-
):
|
| 25 |
-
global REQUESTED_MODELS
|
| 26 |
-
global USERS_TO_SUBMISSION_DATES
|
| 27 |
-
if not REQUESTED_MODELS:
|
| 28 |
-
REQUESTED_MODELS, USERS_TO_SUBMISSION_DATES = already_submitted_models(EVAL_REQUESTS_PATH)
|
| 29 |
-
|
| 30 |
-
user_name = ""
|
| 31 |
-
model_path = model
|
| 32 |
-
if "/" in model:
|
| 33 |
-
user_name = model.split("/")[0]
|
| 34 |
-
model_path = model.split("/")[1]
|
| 35 |
-
|
| 36 |
-
precision = precision.split(" ")[0]
|
| 37 |
-
current_time = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
| 38 |
-
|
| 39 |
-
if model_type is None or model_type == "":
|
| 40 |
-
return styled_error("Please select a model type.")
|
| 41 |
-
|
| 42 |
-
# Does the model actually exist?
|
| 43 |
-
if revision == "":
|
| 44 |
-
revision = "main"
|
| 45 |
-
|
| 46 |
-
# Is the model on the hub?
|
| 47 |
-
if weight_type in ["Delta", "Adapter"]:
|
| 48 |
-
base_model_on_hub, error, _ = is_model_on_hub(model_name=base_model, revision=revision, token=TOKEN, test_tokenizer=True)
|
| 49 |
-
if not base_model_on_hub:
|
| 50 |
-
return styled_error(f'Base model "{base_model}" {error}')
|
| 51 |
-
|
| 52 |
-
if not weight_type == "Adapter":
|
| 53 |
-
model_on_hub, error, _ = is_model_on_hub(model_name=model, revision=revision, token=TOKEN, test_tokenizer=True)
|
| 54 |
-
if not model_on_hub:
|
| 55 |
-
return styled_error(f'Model "{model}" {error}')
|
| 56 |
-
|
| 57 |
-
# Is the model info correctly filled?
|
| 58 |
-
try:
|
| 59 |
-
model_info = API.model_info(repo_id=model, revision=revision)
|
| 60 |
-
except Exception:
|
| 61 |
-
return styled_error("Could not get your model information. Please fill it up properly.")
|
| 62 |
-
|
| 63 |
-
model_size = get_model_size(model_info=model_info, precision=precision)
|
| 64 |
-
|
| 65 |
-
# Were the model card and license filled?
|
| 66 |
-
try:
|
| 67 |
-
license = model_info.cardData["license"]
|
| 68 |
-
except Exception:
|
| 69 |
-
return styled_error("Please select a license for your model")
|
| 70 |
-
|
| 71 |
-
modelcard_OK, error_msg = check_model_card(model)
|
| 72 |
-
if not modelcard_OK:
|
| 73 |
-
return styled_error(error_msg)
|
| 74 |
-
|
| 75 |
-
# Seems good, creating the eval
|
| 76 |
-
print("Adding new eval")
|
| 77 |
-
|
| 78 |
-
eval_entry = {
|
| 79 |
-
"model": model,
|
| 80 |
-
"base_model": base_model,
|
| 81 |
-
"revision": revision,
|
| 82 |
-
"precision": precision,
|
| 83 |
-
"weight_type": weight_type,
|
| 84 |
-
"status": "PENDING",
|
| 85 |
-
"submitted_time": current_time,
|
| 86 |
-
"model_type": model_type,
|
| 87 |
-
"likes": model_info.likes,
|
| 88 |
-
"params": model_size,
|
| 89 |
-
"license": license,
|
| 90 |
-
"private": False,
|
| 91 |
-
}
|
| 92 |
-
|
| 93 |
-
# Check for duplicate submission
|
| 94 |
-
if f"{model}_{revision}_{precision}" in REQUESTED_MODELS:
|
| 95 |
-
return styled_warning("This model has been already submitted.")
|
| 96 |
-
|
| 97 |
-
print("Creating eval file")
|
| 98 |
-
OUT_DIR = f"{EVAL_REQUESTS_PATH}/{user_name}"
|
| 99 |
-
os.makedirs(OUT_DIR, exist_ok=True)
|
| 100 |
-
out_path = f"{OUT_DIR}/{model_path}_eval_request_False_{precision}_{weight_type}.json"
|
| 101 |
-
|
| 102 |
-
with open(out_path, "w") as f:
|
| 103 |
-
f.write(json.dumps(eval_entry))
|
| 104 |
-
|
| 105 |
-
print("Uploading eval file")
|
| 106 |
-
API.upload_file(
|
| 107 |
-
path_or_fileobj=out_path,
|
| 108 |
-
path_in_repo=out_path.split("eval-queue/")[1],
|
| 109 |
-
repo_id=QUEUE_REPO,
|
| 110 |
-
repo_type="dataset",
|
| 111 |
-
commit_message=f"Add {model} to eval queue",
|
| 112 |
-
)
|
| 113 |
-
|
| 114 |
-
# Remove the local file
|
| 115 |
-
os.remove(out_path)
|
| 116 |
-
|
| 117 |
-
return styled_message(
|
| 118 |
-
"Your request has been submitted to the evaluation queue!\nPlease wait for up to an hour for the model to show in the PENDING list."
|
| 119 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|