Spaces:
Running
Running
GitHub Copilot commited on
Commit ·
d6f6c95
1
Parent(s): 67393f9
Update leaderboard UX and GRM benchmark content
Browse files- app.py +319 -43
- ref/Coherence_Summary +84 -0
- ref/Overview +9 -0
app.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
"""Nvidia Game Ready Model Score (GRM Score) Gradio app."""
|
| 2 |
|
| 3 |
from html import escape
|
|
|
|
| 4 |
|
| 5 |
import gradio as gr
|
| 6 |
|
|
@@ -19,7 +20,20 @@ LEADERBOARD_COLUMNS = [
|
|
| 19 |
]
|
| 20 |
|
| 21 |
|
| 22 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
{
|
| 24 |
"title": "Coherence",
|
| 25 |
"summary": [
|
|
@@ -135,13 +149,181 @@ GRM_BENCH_SECTIONS = [
|
|
| 135 |
]
|
| 136 |
|
| 137 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
def _fmt(value: float | None) -> str:
|
| 139 |
return f"{value:.1f}" if value is not None else "-"
|
| 140 |
|
| 141 |
|
| 142 |
-
def build_html_table(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
class_attr = f' class="data-table {table_class}"' if table_class else ' class="data-table"'
|
| 144 |
-
|
|
|
|
| 145 |
|
| 146 |
for header in headers:
|
| 147 |
parts.append(f"<th>{escape(header)}</th>")
|
|
@@ -180,8 +362,14 @@ def get_ranked_model_names() -> list[str]:
|
|
| 180 |
|
| 181 |
def build_evaluation_suite_html() -> str:
|
| 182 |
parts = [
|
| 183 |
-
|
| 184 |
"<table class=\"data-table evaluation-suite-table\">",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 185 |
"<thead><tr>",
|
| 186 |
"<th>Category</th>",
|
| 187 |
"<th>Benchmark</th>",
|
|
@@ -209,7 +397,12 @@ def build_evaluation_suite_html() -> str:
|
|
| 209 |
|
| 210 |
|
| 211 |
def build_leaderboard_html() -> str:
|
| 212 |
-
return build_html_table(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 213 |
|
| 214 |
|
| 215 |
def build_category_score_table_html(category: str) -> str:
|
|
@@ -264,6 +457,28 @@ def build_benchmark_details_html() -> str:
|
|
| 264 |
return "".join(parts)
|
| 265 |
|
| 266 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 267 |
def build_grm_bench_section_html(section: dict) -> str:
|
| 268 |
parts = [
|
| 269 |
"<section class=\"grm-bench-section\">",
|
|
@@ -281,9 +496,14 @@ def build_grm_bench_section_html(section: dict) -> str:
|
|
| 281 |
build_html_table(["Category", "Description"], section["scope"], table_class="grm-bench-scope-table")
|
| 282 |
)
|
| 283 |
|
| 284 |
-
|
|
|
|
| 285 |
parts.append("<div class=\"grm-bench-subtitle\">Representative Samples</div>")
|
| 286 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 287 |
|
| 288 |
parts.append("</section>")
|
| 289 |
return "".join(parts)
|
|
@@ -315,34 +535,7 @@ HEADER_HTML = """
|
|
| 315 |
"""
|
| 316 |
|
| 317 |
|
| 318 |
-
OVERVIEW_HTML =
|
| 319 |
-
<div class="longform-copy">
|
| 320 |
-
<p>
|
| 321 |
-
<strong>Nvidia Game Ready Model Score (GRM)</strong> is a collection of tests oriented
|
| 322 |
-
to evaluating known gaps in practical gaming and edge use cases. The suite contains data
|
| 323 |
-
structures and game-state captures that emulate real-world scenarios developers actually
|
| 324 |
-
need to ship against.
|
| 325 |
-
</p>
|
| 326 |
-
<p>
|
| 327 |
-
General state-of-the-art language models are optimized for broad benchmarks such as math,
|
| 328 |
-
code, and general knowledge. That does not reliably translate to in-game performance, and
|
| 329 |
-
it does not reliably predict NPC quality, gameplay actions, or immersion.
|
| 330 |
-
</p>
|
| 331 |
-
<p>
|
| 332 |
-
With open game model evaluation, game developers can accelerate AI integration pipelines
|
| 333 |
-
by reducing time spent on model evaluation and narrowing model choice earlier. The overall
|
| 334 |
-
score is the average of <strong>Roleplay</strong>, <strong>Actions</strong>, and
|
| 335 |
-
<strong>General</strong>, while benchmarks inside each category are combined with weighted
|
| 336 |
-
averaging using core weights of 1.0 and supplementary weights of 0.5.
|
| 337 |
-
</p>
|
| 338 |
-
<p class="formula-line">
|
| 339 |
-
GRM Score = (Roleplay + Actions + General) / 3
|
| 340 |
-
</p>
|
| 341 |
-
<p class="formula-line subdued">
|
| 342 |
-
Category Score = sum(score x weight) / sum(weight)
|
| 343 |
-
</p>
|
| 344 |
-
</div>
|
| 345 |
-
"""
|
| 346 |
|
| 347 |
|
| 348 |
CUSTOM_CSS = """
|
|
@@ -511,9 +704,16 @@ body {
|
|
| 511 |
|
| 512 |
.table-shell {
|
| 513 |
width: 100%;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 514 |
overflow-x: auto;
|
| 515 |
overflow-y: visible;
|
| 516 |
-
|
|
|
|
|
|
|
|
|
|
| 517 |
}
|
| 518 |
|
| 519 |
.data-table {
|
|
@@ -554,20 +754,38 @@ body {
|
|
| 554 |
font-size: 0.78rem;
|
| 555 |
}
|
| 556 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 557 |
.evaluation-suite-table td {
|
| 558 |
padding: 7px 11px;
|
| 559 |
font-size: 0.9rem;
|
| 560 |
line-height: 1.3;
|
| 561 |
}
|
| 562 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 563 |
.evaluation-suite-table .weight-column,
|
| 564 |
.evaluation-suite-table .weight-cell {
|
| 565 |
font-variant-numeric: tabular-nums;
|
| 566 |
-
max-width:
|
| 567 |
-
min-width:
|
| 568 |
text-align: center;
|
| 569 |
white-space: nowrap;
|
| 570 |
-
width:
|
|
|
|
|
|
|
| 571 |
}
|
| 572 |
|
| 573 |
.evaluation-suite-table .category-cell {
|
|
@@ -581,11 +799,11 @@ body {
|
|
| 581 |
}
|
| 582 |
|
| 583 |
.evaluation-suite-table td:nth-child(2) {
|
| 584 |
-
|
| 585 |
}
|
| 586 |
|
| 587 |
.evaluation-suite-table td:nth-child(3) {
|
| 588 |
-
min-width:
|
| 589 |
}
|
| 590 |
|
| 591 |
.leaderboard-table tbody tr:first-child td {
|
|
@@ -706,6 +924,49 @@ body {
|
|
| 706 |
margin: 14px 0 6px 0;
|
| 707 |
}
|
| 708 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 709 |
.grm-bench-scope-table th:first-child,
|
| 710 |
.grm-bench-scope-table td:first-child {
|
| 711 |
min-width: 180px;
|
|
@@ -726,6 +987,11 @@ body {
|
|
| 726 |
padding: 10px 9px;
|
| 727 |
}
|
| 728 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 729 |
.evaluation-suite-table td:nth-child(2),
|
| 730 |
.evaluation-suite-table td:nth-child(3) {
|
| 731 |
min-width: unset;
|
|
@@ -734,7 +1000,13 @@ body {
|
|
| 734 |
"""
|
| 735 |
|
| 736 |
|
| 737 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 738 |
gr.HTML(HEADER_HTML)
|
| 739 |
|
| 740 |
with gr.Tabs():
|
|
@@ -776,4 +1048,8 @@ with gr.Blocks(title="GRM Score - Game Ready Leaderboard") as demo:
|
|
| 776 |
|
| 777 |
|
| 778 |
if __name__ == "__main__":
|
| 779 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
|
|
|
|
| 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 |
+
|
| 36 |
+
BASE_GRM_BENCH_SECTIONS = [
|
| 37 |
{
|
| 38 |
"title": "Coherence",
|
| 39 |
"summary": [
|
|
|
|
| 149 |
]
|
| 150 |
|
| 151 |
|
| 152 |
+
def _read_reference_file(name: str) -> str | None:
|
| 153 |
+
try:
|
| 154 |
+
return (REF_ROOT / name).read_text(encoding="utf-8").strip()
|
| 155 |
+
except OSError:
|
| 156 |
+
return None
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
def _split_reference_blocks(text: str) -> list[str]:
|
| 160 |
+
blocks = []
|
| 161 |
+
for chunk in text.split("\n\n"):
|
| 162 |
+
block = " ".join(line.strip() for line in chunk.splitlines() if line.strip())
|
| 163 |
+
if block:
|
| 164 |
+
blocks.append(block)
|
| 165 |
+
return blocks
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
def _build_overview_html(blocks: list[str]) -> str:
|
| 169 |
+
parts = ['<div class="longform-copy">']
|
| 170 |
+
title_prefix = "Nvidia Game Ready Model Score (GRM)"
|
| 171 |
+
|
| 172 |
+
for block in blocks:
|
| 173 |
+
if block.startswith("GRM Score ="):
|
| 174 |
+
parts.append(f'<p class="formula-line">{escape(block)}</p>')
|
| 175 |
+
continue
|
| 176 |
+
|
| 177 |
+
if block.startswith("Category Score ="):
|
| 178 |
+
parts.append(f'<p class="formula-line subdued">{escape(block)}</p>')
|
| 179 |
+
continue
|
| 180 |
+
|
| 181 |
+
if block.startswith(title_prefix):
|
| 182 |
+
suffix = block[len(title_prefix) :]
|
| 183 |
+
parts.append(f"<p><strong>{escape(title_prefix)}</strong>{escape(suffix)}</p>")
|
| 184 |
+
continue
|
| 185 |
+
|
| 186 |
+
parts.append(f"<p>{escape(block)}</p>")
|
| 187 |
+
|
| 188 |
+
parts.append("</div>")
|
| 189 |
+
return "".join(parts)
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
def _load_overview_html() -> str:
|
| 193 |
+
text = _read_reference_file("Overview")
|
| 194 |
+
blocks = _split_reference_blocks(text) if text else DEFAULT_OVERVIEW_BLOCKS
|
| 195 |
+
return _build_overview_html(blocks)
|
| 196 |
+
|
| 197 |
+
|
| 198 |
+
def _load_coherence_section() -> dict | None:
|
| 199 |
+
text = _read_reference_file("Coherence_Summary")
|
| 200 |
+
if not text:
|
| 201 |
+
return None
|
| 202 |
+
|
| 203 |
+
lines = text.splitlines()
|
| 204 |
+
index = 0
|
| 205 |
+
|
| 206 |
+
while index < len(lines) and not lines[index].strip():
|
| 207 |
+
index += 1
|
| 208 |
+
|
| 209 |
+
if index >= len(lines):
|
| 210 |
+
return None
|
| 211 |
+
|
| 212 |
+
title = lines[index].strip()
|
| 213 |
+
index += 1
|
| 214 |
+
|
| 215 |
+
summary_lines = []
|
| 216 |
+
while index < len(lines) and lines[index].strip() != "Test Methodology":
|
| 217 |
+
if lines[index].strip():
|
| 218 |
+
summary_lines.append(lines[index].strip())
|
| 219 |
+
index += 1
|
| 220 |
+
|
| 221 |
+
if index >= len(lines):
|
| 222 |
+
return None
|
| 223 |
+
|
| 224 |
+
index += 1
|
| 225 |
+
methodology_lines = []
|
| 226 |
+
while index < len(lines) and lines[index].strip() != "Detection Scope:":
|
| 227 |
+
if lines[index].strip():
|
| 228 |
+
methodology_lines.append(lines[index].strip())
|
| 229 |
+
index += 1
|
| 230 |
+
|
| 231 |
+
if index >= len(lines):
|
| 232 |
+
return None
|
| 233 |
+
|
| 234 |
+
index += 1
|
| 235 |
+
scope = []
|
| 236 |
+
while index < len(lines) and lines[index].strip() != "Test Samples":
|
| 237 |
+
line = lines[index].strip()
|
| 238 |
+
if line:
|
| 239 |
+
category, _, description = line.partition(" - ")
|
| 240 |
+
scope.append([category.strip(), description.strip()])
|
| 241 |
+
index += 1
|
| 242 |
+
|
| 243 |
+
samples = []
|
| 244 |
+
if index < len(lines) and lines[index].strip() == "Test Samples":
|
| 245 |
+
index += 1
|
| 246 |
+
|
| 247 |
+
while index < len(lines):
|
| 248 |
+
while index < len(lines) and not lines[index].strip():
|
| 249 |
+
index += 1
|
| 250 |
+
|
| 251 |
+
if index >= len(lines):
|
| 252 |
+
break
|
| 253 |
+
|
| 254 |
+
if not lines[index].strip().startswith("TEST_"):
|
| 255 |
+
index += 1
|
| 256 |
+
continue
|
| 257 |
+
|
| 258 |
+
sample_id = lines[index].strip()
|
| 259 |
+
index += 1
|
| 260 |
+
metadata = []
|
| 261 |
+
code_lines = []
|
| 262 |
+
|
| 263 |
+
while index < len(lines):
|
| 264 |
+
line = lines[index]
|
| 265 |
+
stripped = line.strip()
|
| 266 |
+
|
| 267 |
+
if stripped.startswith("TEST_"):
|
| 268 |
+
break
|
| 269 |
+
|
| 270 |
+
if stripped == "Messages:":
|
| 271 |
+
index += 1
|
| 272 |
+
while index < len(lines) and not lines[index].strip().startswith("TEST_"):
|
| 273 |
+
code_lines.append(lines[index].rstrip())
|
| 274 |
+
index += 1
|
| 275 |
+
break
|
| 276 |
+
|
| 277 |
+
if stripped and ":" in stripped:
|
| 278 |
+
label, value = stripped.split(":", 1)
|
| 279 |
+
metadata.append([label.strip(), value.strip()])
|
| 280 |
+
|
| 281 |
+
index += 1
|
| 282 |
+
|
| 283 |
+
samples.append(
|
| 284 |
+
{
|
| 285 |
+
"id": sample_id,
|
| 286 |
+
"metadata": metadata,
|
| 287 |
+
"code": "\n".join(code_lines).strip(),
|
| 288 |
+
}
|
| 289 |
+
)
|
| 290 |
+
|
| 291 |
+
if not summary_lines or not methodology_lines or not scope:
|
| 292 |
+
return None
|
| 293 |
+
|
| 294 |
+
return {
|
| 295 |
+
"title": title,
|
| 296 |
+
"summary": [" ".join(summary_lines)],
|
| 297 |
+
"methodology": " ".join(methodology_lines),
|
| 298 |
+
"scope": scope,
|
| 299 |
+
"samples": samples,
|
| 300 |
+
}
|
| 301 |
+
|
| 302 |
+
|
| 303 |
+
def _load_grm_bench_sections() -> list[dict]:
|
| 304 |
+
sections = list(BASE_GRM_BENCH_SECTIONS)
|
| 305 |
+
coherence_section = _load_coherence_section()
|
| 306 |
+
if coherence_section is not None:
|
| 307 |
+
sections[0] = coherence_section
|
| 308 |
+
return sections
|
| 309 |
+
|
| 310 |
+
|
| 311 |
+
GRM_BENCH_SECTIONS = _load_grm_bench_sections()
|
| 312 |
+
|
| 313 |
+
|
| 314 |
def _fmt(value: float | None) -> str:
|
| 315 |
return f"{value:.1f}" if value is not None else "-"
|
| 316 |
|
| 317 |
|
| 318 |
+
def build_html_table(
|
| 319 |
+
headers: list[str],
|
| 320 |
+
rows: list[list[str]],
|
| 321 |
+
table_class: str = "",
|
| 322 |
+
shell_class: str = "table-scroll-shell",
|
| 323 |
+
) -> str:
|
| 324 |
class_attr = f' class="data-table {table_class}"' if table_class else ' class="data-table"'
|
| 325 |
+
shell_classes = " ".join(part for part in ["table-shell", shell_class] if part)
|
| 326 |
+
parts = [f'<div class="{shell_classes}">', f"<table{class_attr}>", "<thead><tr>"]
|
| 327 |
|
| 328 |
for header in headers:
|
| 329 |
parts.append(f"<th>{escape(header)}</th>")
|
|
|
|
| 362 |
|
| 363 |
def build_evaluation_suite_html() -> str:
|
| 364 |
parts = [
|
| 365 |
+
'<div class="table-shell table-scroll-shell">',
|
| 366 |
"<table class=\"data-table evaluation-suite-table\">",
|
| 367 |
+
"<colgroup>",
|
| 368 |
+
'<col class="evaluation-suite-category-col">',
|
| 369 |
+
'<col class="evaluation-suite-benchmark-col">',
|
| 370 |
+
'<col class="evaluation-suite-description-col">',
|
| 371 |
+
'<col class="evaluation-suite-weight-col">',
|
| 372 |
+
"</colgroup>",
|
| 373 |
"<thead><tr>",
|
| 374 |
"<th>Category</th>",
|
| 375 |
"<th>Benchmark</th>",
|
|
|
|
| 397 |
|
| 398 |
|
| 399 |
def build_leaderboard_html() -> str:
|
| 400 |
+
return build_html_table(
|
| 401 |
+
LEADERBOARD_COLUMNS,
|
| 402 |
+
get_leaderboard_rows(),
|
| 403 |
+
table_class="leaderboard-table",
|
| 404 |
+
shell_class="leaderboard-shell",
|
| 405 |
+
)
|
| 406 |
|
| 407 |
|
| 408 |
def build_category_score_table_html(category: str) -> str:
|
|
|
|
| 457 |
return "".join(parts)
|
| 458 |
|
| 459 |
|
| 460 |
+
def _build_grm_bench_sample_html(sample: dict) -> str:
|
| 461 |
+
parts = [
|
| 462 |
+
'<article class="grm-bench-sample">',
|
| 463 |
+
f'<div class="grm-bench-sample-id">{escape(sample["id"])}</div>',
|
| 464 |
+
]
|
| 465 |
+
|
| 466 |
+
for label, value in sample.get("metadata", []):
|
| 467 |
+
parts.append(
|
| 468 |
+
'<p class="grm-bench-sample-meta">'
|
| 469 |
+
f'<span class="grm-bench-sample-label">{escape(label)}:</span> {escape(value)}'
|
| 470 |
+
"</p>"
|
| 471 |
+
)
|
| 472 |
+
|
| 473 |
+
if sample.get("code"):
|
| 474 |
+
parts.append('<pre class="grm-bench-sample-code"><code>')
|
| 475 |
+
parts.append(escape(sample["code"]))
|
| 476 |
+
parts.append("</code></pre>")
|
| 477 |
+
|
| 478 |
+
parts.append("</article>")
|
| 479 |
+
return "".join(parts)
|
| 480 |
+
|
| 481 |
+
|
| 482 |
def build_grm_bench_section_html(section: dict) -> str:
|
| 483 |
parts = [
|
| 484 |
"<section class=\"grm-bench-section\">",
|
|
|
|
| 496 |
build_html_table(["Category", "Description"], section["scope"], table_class="grm-bench-scope-table")
|
| 497 |
)
|
| 498 |
|
| 499 |
+
samples = section.get("samples")
|
| 500 |
+
if samples:
|
| 501 |
parts.append("<div class=\"grm-bench-subtitle\">Representative Samples</div>")
|
| 502 |
+
if isinstance(samples, str):
|
| 503 |
+
parts.append(f"<p>{escape(samples)}</p>")
|
| 504 |
+
else:
|
| 505 |
+
for sample in samples:
|
| 506 |
+
parts.append(_build_grm_bench_sample_html(sample))
|
| 507 |
|
| 508 |
parts.append("</section>")
|
| 509 |
return "".join(parts)
|
|
|
|
| 535 |
"""
|
| 536 |
|
| 537 |
|
| 538 |
+
OVERVIEW_HTML = _load_overview_html()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 539 |
|
| 540 |
|
| 541 |
CUSTOM_CSS = """
|
|
|
|
| 704 |
|
| 705 |
.table-shell {
|
| 706 |
width: 100%;
|
| 707 |
+
margin-top: 10px;
|
| 708 |
+
}
|
| 709 |
+
|
| 710 |
+
.table-scroll-shell {
|
| 711 |
overflow-x: auto;
|
| 712 |
overflow-y: visible;
|
| 713 |
+
}
|
| 714 |
+
|
| 715 |
+
.leaderboard-shell {
|
| 716 |
+
overflow: visible;
|
| 717 |
}
|
| 718 |
|
| 719 |
.data-table {
|
|
|
|
| 754 |
font-size: 0.78rem;
|
| 755 |
}
|
| 756 |
|
| 757 |
+
.evaluation-suite-table {
|
| 758 |
+
table-layout: fixed;
|
| 759 |
+
}
|
| 760 |
+
|
| 761 |
.evaluation-suite-table td {
|
| 762 |
padding: 7px 11px;
|
| 763 |
font-size: 0.9rem;
|
| 764 |
line-height: 1.3;
|
| 765 |
}
|
| 766 |
|
| 767 |
+
.evaluation-suite-category-col {
|
| 768 |
+
width: 120px;
|
| 769 |
+
}
|
| 770 |
+
|
| 771 |
+
.evaluation-suite-benchmark-col {
|
| 772 |
+
width: 220px;
|
| 773 |
+
}
|
| 774 |
+
|
| 775 |
+
.evaluation-suite-weight-col {
|
| 776 |
+
width: 46px;
|
| 777 |
+
}
|
| 778 |
+
|
| 779 |
.evaluation-suite-table .weight-column,
|
| 780 |
.evaluation-suite-table .weight-cell {
|
| 781 |
font-variant-numeric: tabular-nums;
|
| 782 |
+
max-width: 46px;
|
| 783 |
+
min-width: 46px;
|
| 784 |
text-align: center;
|
| 785 |
white-space: nowrap;
|
| 786 |
+
width: 46px;
|
| 787 |
+
padding-left: 6px;
|
| 788 |
+
padding-right: 6px;
|
| 789 |
}
|
| 790 |
|
| 791 |
.evaluation-suite-table .category-cell {
|
|
|
|
| 799 |
}
|
| 800 |
|
| 801 |
.evaluation-suite-table td:nth-child(2) {
|
| 802 |
+
width: 220px;
|
| 803 |
}
|
| 804 |
|
| 805 |
.evaluation-suite-table td:nth-child(3) {
|
| 806 |
+
min-width: 0;
|
| 807 |
}
|
| 808 |
|
| 809 |
.leaderboard-table tbody tr:first-child td {
|
|
|
|
| 924 |
margin: 14px 0 6px 0;
|
| 925 |
}
|
| 926 |
|
| 927 |
+
.grm-bench-sample {
|
| 928 |
+
background: #171b1f;
|
| 929 |
+
border: 1px solid var(--rule);
|
| 930 |
+
border-radius: 10px;
|
| 931 |
+
margin-top: 12px;
|
| 932 |
+
padding: 14px 16px;
|
| 933 |
+
}
|
| 934 |
+
|
| 935 |
+
.grm-bench-sample-id {
|
| 936 |
+
color: var(--text-main);
|
| 937 |
+
font-size: 0.84rem;
|
| 938 |
+
font-weight: 700;
|
| 939 |
+
letter-spacing: 0.08em;
|
| 940 |
+
text-transform: uppercase;
|
| 941 |
+
}
|
| 942 |
+
|
| 943 |
+
.grm-bench-sample-meta {
|
| 944 |
+
margin: 6px 0 0 0 !important;
|
| 945 |
+
}
|
| 946 |
+
|
| 947 |
+
.grm-bench-sample-label {
|
| 948 |
+
color: var(--text-main);
|
| 949 |
+
font-weight: 600;
|
| 950 |
+
}
|
| 951 |
+
|
| 952 |
+
.grm-bench-sample-code {
|
| 953 |
+
background: #0f1215;
|
| 954 |
+
border: 1px solid var(--rule-soft);
|
| 955 |
+
border-radius: 8px;
|
| 956 |
+
color: #d7dde3;
|
| 957 |
+
font-family: Consolas, "SFMono-Regular", monospace;
|
| 958 |
+
font-size: 0.84rem;
|
| 959 |
+
line-height: 1.55;
|
| 960 |
+
margin: 12px 0 0 0;
|
| 961 |
+
overflow-x: auto;
|
| 962 |
+
padding: 12px 14px;
|
| 963 |
+
white-space: pre-wrap;
|
| 964 |
+
}
|
| 965 |
+
|
| 966 |
+
.grm-bench-sample-code code {
|
| 967 |
+
font-family: inherit;
|
| 968 |
+
}
|
| 969 |
+
|
| 970 |
.grm-bench-scope-table th:first-child,
|
| 971 |
.grm-bench-scope-table td:first-child {
|
| 972 |
min-width: 180px;
|
|
|
|
| 987 |
padding: 10px 9px;
|
| 988 |
}
|
| 989 |
|
| 990 |
+
.leaderboard-shell {
|
| 991 |
+
overflow-x: auto;
|
| 992 |
+
overflow-y: visible;
|
| 993 |
+
}
|
| 994 |
+
|
| 995 |
.evaluation-suite-table td:nth-child(2),
|
| 996 |
.evaluation-suite-table td:nth-child(3) {
|
| 997 |
min-width: unset;
|
|
|
|
| 1000 |
"""
|
| 1001 |
|
| 1002 |
|
| 1003 |
+
blocks_kwargs = {"title": "GRM Score - Game Ready Leaderboard"}
|
| 1004 |
+
if GRADIO_MAJOR_VERSION < 6:
|
| 1005 |
+
blocks_kwargs["theme"] = gr.themes.Base()
|
| 1006 |
+
blocks_kwargs["css"] = CUSTOM_CSS
|
| 1007 |
+
|
| 1008 |
+
|
| 1009 |
+
with gr.Blocks(**blocks_kwargs) as demo:
|
| 1010 |
gr.HTML(HEADER_HTML)
|
| 1011 |
|
| 1012 |
with gr.Tabs():
|
|
|
|
| 1048 |
|
| 1049 |
|
| 1050 |
if __name__ == "__main__":
|
| 1051 |
+
launch_kwargs = {}
|
| 1052 |
+
if GRADIO_MAJOR_VERSION >= 6:
|
| 1053 |
+
launch_kwargs["theme"] = gr.themes.Base()
|
| 1054 |
+
launch_kwargs["css"] = CUSTOM_CSS
|
| 1055 |
+
demo.launch(**launch_kwargs)
|
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)
|