CP Legendre commited on
Commit ·
52073ff
1
Parent(s): 4fd20a0
Finalize efficiency review updates
Browse files- README.md +54 -50
- app.py +56 -107
- src/charts.py +119 -55
- src/leaderboard.py +87 -35
- tests/test_token_efficiency.py +237 -85
README.md
CHANGED
|
@@ -19,77 +19,45 @@ Compare coding-agent models and harnesses across benchmark performance, cost, la
|
|
| 19 |
|
| 20 |
## Leaderboard views
|
| 21 |
|
| 22 |
-
###
|
| 23 |
|
| 24 |
-
The
|
|
|
|
| 25 |
|
| 26 |
-
|
| 27 |
|
| 28 |
-
|
|
|
|
|
|
|
| 29 |
|
| 30 |
-
|
| 31 |
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
- benchmark filtering, including **All benchmarks**
|
| 35 |
-
- total, input, output, or solved-task-normalized token metrics
|
| 36 |
-
- linear or logarithmic token axes
|
| 37 |
-
- coloring by model, harness, or benchmark
|
| 38 |
-
- optional point labels
|
| 39 |
-
- an optional token-efficiency Pareto frontier
|
| 40 |
-
- a sortable token-efficiency ranking table
|
| 41 |
-
|
| 42 |
-
#### Metric definitions
|
| 43 |
-
|
| 44 |
-
Scores are stored internally as fractions from 0 to 1 and displayed as percentages.
|
| 45 |
-
|
| 46 |
-
- **Total Tokens Per Task**: mean total token usage for one task.
|
| 47 |
-
- **Input Tokens Per Task**: mean input token usage for one task.
|
| 48 |
-
- **Output Tokens Per Task**: mean output token usage for one task.
|
| 49 |
-
- **Cache Tokens Per Task**: mean cached token usage for one task.
|
| 50 |
-
- **Tokens Per Solved Task**: `Total Tokens Per Task / Score`, where Score is the fractional value from 0 to 1.
|
| 51 |
-
|
| 52 |
-
For example, a run using 10,000 total tokens per task with a score of 0.5 has 20,000 Tokens Per Solved Task.
|
| 53 |
-
|
| 54 |
-
#### Missing and invalid token data
|
| 55 |
-
|
| 56 |
-
A token measurement is available only when its value is present and greater than zero. Missing, zero, and negative token values are excluded from token-efficiency plots rather than treated as perfect efficiency.
|
| 57 |
-
|
| 58 |
-
Tokens Per Solved Task is also unavailable when the score is zero or negative, which avoids division by zero. The UI reports how many runs were omitted because the selected token metric was missing or non-positive.
|
| 59 |
|
| 60 |
#### Pareto frontier
|
| 61 |
|
| 62 |
-
A run is
|
| 63 |
|
| 64 |
-
- uses
|
| 65 |
- achieves an equal or higher score,
|
| 66 |
|
| 67 |
-
with at least one
|
| 68 |
|
| 69 |
-
|
| 70 |
|
| 71 |
-
The
|
| 72 |
-
|
| 73 |
-
#### Color palettes and themes
|
| 74 |
|
| 75 |
-
|
| 76 |
|
| 77 |
-
|
| 78 |
-
- Pastel
|
| 79 |
-
- Bold
|
| 80 |
-
- Safe
|
| 81 |
-
- Grayscale
|
| 82 |
-
- Viridis
|
| 83 |
-
- Plasma
|
| 84 |
-
- Cividis
|
| 85 |
|
| 86 |
-
|
| 87 |
|
| 88 |
## Adding a New Leaderboard Entry
|
| 89 |
|
| 90 |
Create a pull request adding a new entry to the `results/` folder. See [`results/qwen3-6-35b-nvfp4-claude-code.json`](./results/qwen3-6-35b-nvfp4-claude-code.json) for an example result.
|
| 91 |
|
| 92 |
-
Do not change the result JSON schema for
|
| 93 |
|
| 94 |
## Development
|
| 95 |
|
|
@@ -115,3 +83,39 @@ Do not change the result JSON schema for token-efficiency analysis. The feature
|
|
| 115 |
```sh
|
| 116 |
pytest
|
| 117 |
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
## Leaderboard views
|
| 21 |
|
| 22 |
+
### Efficiency
|
| 23 |
|
| 24 |
+
The **Efficiency** tab compares benchmark score with resource use for one selected benchmark at a time. Keeping the view benchmark-specific avoids misleading comparisons when model and harness combinations have incomplete benchmark coverage.
|
| 25 |
+
The generalized performance-vs-resource chart covers tokens, cost, and agent time in one benchmark-specific view.
|
| 26 |
|
| 27 |
+
The scatter plot supports three resource metrics on the x-axis:
|
| 28 |
|
| 29 |
+
- **Total tokens**: mean total token usage per task.
|
| 30 |
+
- **Cost per task**: the repository's reported `mean_cost_usd_per_task` value, in USD.
|
| 31 |
+
- **Agent time per task**: the repository's reported `mean_agent_time_seconds_per_task` value, in seconds.
|
| 32 |
|
| 33 |
+
Only positive, available values for the selected resource are plotted. Missing, zero, and negative values are treated as unavailable rather than as perfect efficiency. The UI reports how many runs for the selected benchmark were excluded for the chosen resource metric.
|
| 34 |
|
| 35 |
+
Scores are stored internally as fractions from 0 to 1 and displayed as percentages. Point labels are optional and coloring can be grouped by **Model** or **Harness**. Linear and logarithmic resource axes are available.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
|
| 37 |
#### Pareto frontier
|
| 38 |
|
| 39 |
+
For all three resource metrics, lower resource use and higher score are better. A displayed run is Pareto-efficient when no other valid run on the selected benchmark:
|
| 40 |
|
| 41 |
+
- uses an equal or lower amount of the selected resource, and
|
| 42 |
- achieves an equal or higher score,
|
| 43 |
|
| 44 |
+
with at least one strict improvement. Tied nondominated points are preserved. The dashed line connects the Pareto-efficient observations.
|
| 45 |
|
| 46 |
+
#### Ranking table
|
| 47 |
|
| 48 |
+
The ranking table remains benchmark-specific and begins with **Model**, **Harness**, and **Benchmark**. Displayed scores are rounded to one decimal place without reducing the precision used for calculations.
|
|
|
|
|
|
|
| 49 |
|
| 50 |
+
**Tokens Per Solved Task** remains available in the table as a reference metric even though it is no longer an Efficiency scatter x-axis option. It is defined as `Total Tokens Per Task / Score`, where Score is the fractional value from 0 to 1. It is unavailable when total token data is missing/non-positive or when Score is zero or negative.
|
| 51 |
|
| 52 |
+
#### Color palettes and themes
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
|
| 54 |
+
The Efficiency chart uses the shared palette registry and supports Citrus, Okabe-Ito, High contrast, Rainbow, Grayscale, Viridis, Plasma, and Cividis palettes with light and dark chart backgrounds.
|
| 55 |
|
| 56 |
## Adding a New Leaderboard Entry
|
| 57 |
|
| 58 |
Create a pull request adding a new entry to the `results/` folder. See [`results/qwen3-6-35b-nvfp4-claude-code.json`](./results/qwen3-6-35b-nvfp4-claude-code.json) for an example result.
|
| 59 |
|
| 60 |
+
Do not change the result JSON schema for Efficiency analysis. The feature uses resource and performance metrics already present in the existing result model.
|
| 61 |
|
| 62 |
## Development
|
| 63 |
|
|
|
|
| 83 |
```sh
|
| 84 |
pytest
|
| 85 |
```
|
| 86 |
+
|
| 87 |
+
## Manual validation
|
| 88 |
+
|
| 89 |
+
After automated checks pass, launch the app from a clean process with `python app.py` and verify the Efficiency view in a real browser. Unit tests and figure-level smoke tests do not replace this browser validation.
|
| 90 |
+
|
| 91 |
+
### Efficiency behavior
|
| 92 |
+
|
| 93 |
+
- Confirm there is no **All benchmarks** option and that a valid benchmark is selected by default.
|
| 94 |
+
- Confirm **Color By** offers only **Model** and **Harness**.
|
| 95 |
+
- Confirm the resource selector offers **Total tokens**, **Cost per task**, and **Agent time per task**.
|
| 96 |
+
- Switch through all three resource metrics and verify axis labels and hover formatting.
|
| 97 |
+
- Verify the Pareto frontier for all three metrics.
|
| 98 |
+
- Verify linear and logarithmic scales where valid.
|
| 99 |
+
- Verify point labels off and on.
|
| 100 |
+
- Confirm the ranking table starts with **Model**, **Harness**, **Benchmark**, shows scores to one decimal place, and retains **Tokens Per Solved Task**.
|
| 101 |
+
- Confirm the existing Leaderboard and Benchmark Runs tabs still work without new terminal tracebacks.
|
| 102 |
+
|
| 103 |
+
### Responsive layout verification
|
| 104 |
+
|
| 105 |
+
- Restart the app from a clean launch.
|
| 106 |
+
- Open **Efficiency** as the first non-default tab and confirm the plot is not squished.
|
| 107 |
+
- Switch away from Efficiency and back several times.
|
| 108 |
+
- Resize the browser narrower and wider.
|
| 109 |
+
- Confirm the plot resizes correctly without requiring a control change.
|
| 110 |
+
- Verify there is no legend overlap or clipping.
|
| 111 |
+
- Verify the ranking table below does not force the plot into a narrow column.
|
| 112 |
+
- Repeat the checks with point labels off and on, Pareto off and on, each resource metric, and light and dark plot backgrounds.
|
| 113 |
+
- Also switch **Leaderboard → Efficiency** and **Benchmark Runs → Efficiency** to confirm hidden-tab initialization does not collapse the chart.
|
| 114 |
+
|
| 115 |
+
### PR completion report
|
| 116 |
+
|
| 117 |
+
The final PR report should state the files changed, metric definitions, tests and results, data-quality limitations, and whether the app was manually verified. For the responsive-layout fix, it must additionally record:
|
| 118 |
+
|
| 119 |
+
- the root cause of the initial squished-chart issue;
|
| 120 |
+
- the exact responsive-layout fix used; and
|
| 121 |
+
- whether the behavior was manually verified from a fresh app launch.
|
app.py
CHANGED
|
@@ -50,8 +50,7 @@ from huggingface_hub import HfApi
|
|
| 50 |
from src.charts import (
|
| 51 |
clean_markdown_link,
|
| 52 |
create_leaderboard_benchmark_plot,
|
| 53 |
-
|
| 54 |
-
create_score_vs_tokens_plot,
|
| 55 |
)
|
| 56 |
from src.display.text_blocks import (
|
| 57 |
HOW_TO_USE_TEXT,
|
|
@@ -59,13 +58,11 @@ from src.display.text_blocks import (
|
|
| 59 |
LLM_BENCHMARKS_TEXT,
|
| 60 |
)
|
| 61 |
from src.leaderboard import (
|
| 62 |
-
|
| 63 |
-
TOKEN_METRICS,
|
| 64 |
get_analysis_df,
|
| 65 |
get_benchmark_names,
|
| 66 |
get_benchmark_run_df,
|
| 67 |
-
|
| 68 |
-
get_token_efficiency_df,
|
| 69 |
get_token_efficiency_table_df,
|
| 70 |
)
|
| 71 |
|
|
@@ -73,7 +70,7 @@ REPO_ID = "taagarwa/coding-agent-leaderboard"
|
|
| 73 |
TOKEN = os.environ.get("HF_TOKEN")
|
| 74 |
API = HfApi(token=TOKEN)
|
| 75 |
COLOR_BY_CHOICES = ["Model", "Harness"]
|
| 76 |
-
EFFICIENCY_COLOR_BY_CHOICES = ["Model", "Harness"
|
| 77 |
COLOR_PALETTE_CHOICES = [
|
| 78 |
"Citrus",
|
| 79 |
"Okabe-Ito",
|
|
@@ -87,7 +84,24 @@ COLOR_PALETTE_CHOICES = [
|
|
| 87 |
DEFAULT_COLOR_PALETTE = "Citrus"
|
| 88 |
PLOT_BACKGROUND_CHOICES = ["Dark", "White"]
|
| 89 |
DEFAULT_PLOT_BACKGROUND = "Dark"
|
| 90 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
<script>
|
| 92 |
(() => {
|
| 93 |
const url = new URL(window.location.href);
|
|
@@ -97,10 +111,9 @@ FORCE_DARK_MODE_HEAD = """
|
|
| 97 |
}
|
| 98 |
})();
|
| 99 |
</script>
|
| 100 |
-
<style>
|
| 101 |
-
.responsive-plot { overflow-x: auto; }
|
| 102 |
-
</style>
|
| 103 |
"""
|
|
|
|
|
|
|
| 104 |
|
| 105 |
|
| 106 |
def restart_space():
|
|
@@ -110,7 +123,6 @@ def restart_space():
|
|
| 110 |
BENCHMARK_NAMES = get_benchmark_names()
|
| 111 |
DEFAULT_BENCHMARK = BENCHMARK_NAMES[0] if BENCHMARK_NAMES else None
|
| 112 |
BENCHMARK_RUN_DF = get_benchmark_run_df()
|
| 113 |
-
SCORE_VS_COST_DF = get_score_vs_cost_df()
|
| 114 |
ANALYSIS_DF = get_analysis_df()
|
| 115 |
|
| 116 |
|
|
@@ -129,24 +141,8 @@ def render_leaderboard_benchmark_plot(
|
|
| 129 |
)
|
| 130 |
|
| 131 |
|
| 132 |
-
def render_score_vs_cost_plot(
|
| 133 |
-
benchmark_name,
|
| 134 |
-
color_by,
|
| 135 |
-
show_labels=False,
|
| 136 |
-
color_palette=DEFAULT_COLOR_PALETTE,
|
| 137 |
-
plot_background=DEFAULT_PLOT_BACKGROUND,
|
| 138 |
-
):
|
| 139 |
-
return create_score_vs_cost_plot(
|
| 140 |
-
SCORE_VS_COST_DF,
|
| 141 |
-
benchmark_name=benchmark_name,
|
| 142 |
-
color_by=color_by,
|
| 143 |
-
show_labels=show_labels,
|
| 144 |
-
palette_name=color_palette,
|
| 145 |
-
background_name=plot_background,
|
| 146 |
-
)
|
| 147 |
-
|
| 148 |
|
| 149 |
-
def
|
| 150 |
benchmark_name,
|
| 151 |
token_metric,
|
| 152 |
color_by,
|
|
@@ -156,9 +152,9 @@ def render_token_efficiency(
|
|
| 156 |
color_palette=DEFAULT_COLOR_PALETTE,
|
| 157 |
plot_background=DEFAULT_PLOT_BACKGROUND,
|
| 158 |
):
|
| 159 |
-
plot_df =
|
| 160 |
benchmark_name=benchmark_name,
|
| 161 |
-
|
| 162 |
analysis_df=ANALYSIS_DF,
|
| 163 |
)
|
| 164 |
table_df = get_token_efficiency_table_df(
|
|
@@ -166,10 +162,10 @@ def render_token_efficiency(
|
|
| 166 |
analysis_df=ANALYSIS_DF,
|
| 167 |
)
|
| 168 |
exclusion_count = plot_df.attrs.get("exclusion_count", 0)
|
| 169 |
-
note = f"{exclusion_count} runs excluded because {token_metric.lower()} was missing or non-positive."
|
| 170 |
-
figure =
|
| 171 |
plot_df,
|
| 172 |
-
|
| 173 |
color_by=color_by,
|
| 174 |
x_scale=x_scale,
|
| 175 |
show_pareto_frontier=show_pareto_frontier,
|
|
@@ -302,81 +298,25 @@ with demo:
|
|
| 302 |
outputs=plot,
|
| 303 |
)
|
| 304 |
|
| 305 |
-
with gr.Tab("
|
| 306 |
-
cost_benchmark = gr.Dropdown(
|
| 307 |
-
choices=BENCHMARK_NAMES,
|
| 308 |
-
value=DEFAULT_BENCHMARK,
|
| 309 |
-
label="Benchmark",
|
| 310 |
-
)
|
| 311 |
-
with gr.Row():
|
| 312 |
-
cost_color_by = gr.Radio(
|
| 313 |
-
choices=COLOR_BY_CHOICES,
|
| 314 |
-
value="Model",
|
| 315 |
-
label="Color by",
|
| 316 |
-
elem_classes="color-control",
|
| 317 |
-
)
|
| 318 |
-
cost_palette = gr.Dropdown(
|
| 319 |
-
choices=COLOR_PALETTE_CHOICES,
|
| 320 |
-
value=DEFAULT_COLOR_PALETTE,
|
| 321 |
-
label="Color palette",
|
| 322 |
-
elem_classes="color-control",
|
| 323 |
-
)
|
| 324 |
-
cost_background = gr.Dropdown(
|
| 325 |
-
choices=PLOT_BACKGROUND_CHOICES,
|
| 326 |
-
value=DEFAULT_PLOT_BACKGROUND,
|
| 327 |
-
label="Image background",
|
| 328 |
-
elem_classes="color-control",
|
| 329 |
-
)
|
| 330 |
-
cost_labels = gr.Checkbox(
|
| 331 |
-
value=False,
|
| 332 |
-
label="Show point labels",
|
| 333 |
-
elem_classes="color-control",
|
| 334 |
-
)
|
| 335 |
-
score_vs_cost_plot = gr.Plot(
|
| 336 |
-
value=render_score_vs_cost_plot(
|
| 337 |
-
DEFAULT_BENCHMARK,
|
| 338 |
-
"Model",
|
| 339 |
-
False,
|
| 340 |
-
DEFAULT_COLOR_PALETTE,
|
| 341 |
-
DEFAULT_PLOT_BACKGROUND,
|
| 342 |
-
),
|
| 343 |
-
show_label=False,
|
| 344 |
-
elem_classes="responsive-plot",
|
| 345 |
-
)
|
| 346 |
-
cost_controls = [
|
| 347 |
-
cost_benchmark,
|
| 348 |
-
cost_color_by,
|
| 349 |
-
cost_labels,
|
| 350 |
-
cost_palette,
|
| 351 |
-
cost_background,
|
| 352 |
-
]
|
| 353 |
-
for control in cost_controls:
|
| 354 |
-
control.change(
|
| 355 |
-
fn=render_score_vs_cost_plot,
|
| 356 |
-
inputs=cost_controls,
|
| 357 |
-
outputs=score_vs_cost_plot,
|
| 358 |
-
)
|
| 359 |
-
|
| 360 |
-
with gr.Tab("⚡ Efficiency"):
|
| 361 |
gr.Markdown(
|
| 362 |
-
"###
|
| 363 |
-
"
|
| 364 |
-
"
|
| 365 |
-
"
|
| 366 |
-
"while achieving an equal or higher score
|
| 367 |
-
"
|
| 368 |
-
"Pareto membership and are not statistical thresholds."
|
| 369 |
)
|
| 370 |
with gr.Row():
|
| 371 |
efficiency_benchmark = gr.Dropdown(
|
| 372 |
-
choices=
|
| 373 |
-
value=
|
| 374 |
label="Benchmark",
|
| 375 |
)
|
| 376 |
efficiency_metric = gr.Dropdown(
|
| 377 |
-
choices=list(
|
| 378 |
-
value="
|
| 379 |
-
label="
|
| 380 |
)
|
| 381 |
efficiency_color_by = gr.Radio(
|
| 382 |
choices=EFFICIENCY_COLOR_BY_CHOICES,
|
|
@@ -398,9 +338,9 @@ with demo:
|
|
| 398 |
label="Image background",
|
| 399 |
)
|
| 400 |
|
| 401 |
-
initial_efficiency =
|
| 402 |
-
|
| 403 |
-
"
|
| 404 |
"Model",
|
| 405 |
"Log",
|
| 406 |
True,
|
|
@@ -414,7 +354,7 @@ with demo:
|
|
| 414 |
show_label=False,
|
| 415 |
elem_classes="responsive-plot",
|
| 416 |
)
|
| 417 |
-
gr.Markdown("####
|
| 418 |
efficiency_table = gr.Dataframe(
|
| 419 |
value=initial_efficiency[1],
|
| 420 |
interactive=False,
|
|
@@ -433,11 +373,20 @@ with demo:
|
|
| 433 |
]
|
| 434 |
for control in efficiency_controls:
|
| 435 |
control.change(
|
| 436 |
-
fn=
|
| 437 |
inputs=efficiency_controls,
|
| 438 |
outputs=[efficiency_plot, efficiency_table, efficiency_note],
|
| 439 |
)
|
| 440 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 441 |
with gr.Tab("🏃 Benchmark Runs"):
|
| 442 |
benchmark_runs = init_benchmark_runs(BENCHMARK_RUN_DF)
|
| 443 |
|
|
|
|
| 50 |
from src.charts import (
|
| 51 |
clean_markdown_link,
|
| 52 |
create_leaderboard_benchmark_plot,
|
| 53 |
+
create_performance_vs_resource_plot,
|
|
|
|
| 54 |
)
|
| 55 |
from src.display.text_blocks import (
|
| 56 |
HOW_TO_USE_TEXT,
|
|
|
|
| 58 |
LLM_BENCHMARKS_TEXT,
|
| 59 |
)
|
| 60 |
from src.leaderboard import (
|
| 61 |
+
EFFICIENCY_RESOURCE_METRICS,
|
|
|
|
| 62 |
get_analysis_df,
|
| 63 |
get_benchmark_names,
|
| 64 |
get_benchmark_run_df,
|
| 65 |
+
get_efficiency_df,
|
|
|
|
| 66 |
get_token_efficiency_table_df,
|
| 67 |
)
|
| 68 |
|
|
|
|
| 70 |
TOKEN = os.environ.get("HF_TOKEN")
|
| 71 |
API = HfApi(token=TOKEN)
|
| 72 |
COLOR_BY_CHOICES = ["Model", "Harness"]
|
| 73 |
+
EFFICIENCY_COLOR_BY_CHOICES = ["Model", "Harness"]
|
| 74 |
COLOR_PALETTE_CHOICES = [
|
| 75 |
"Citrus",
|
| 76 |
"Okabe-Ito",
|
|
|
|
| 84 |
DEFAULT_COLOR_PALETTE = "Citrus"
|
| 85 |
PLOT_BACKGROUND_CHOICES = ["Dark", "White"]
|
| 86 |
DEFAULT_PLOT_BACKGROUND = "Dark"
|
| 87 |
+
RESPONSIVE_PLOT_MIN_HEIGHT_PX = 420
|
| 88 |
+
RESPONSIVE_PLOT_CSS = f"""
|
| 89 |
+
<style>
|
| 90 |
+
.responsive-plot {{
|
| 91 |
+
overflow-x: auto;
|
| 92 |
+
width: 100%;
|
| 93 |
+
min-height: {RESPONSIVE_PLOT_MIN_HEIGHT_PX}px;
|
| 94 |
+
}}
|
| 95 |
+
.responsive-plot .plot-container,
|
| 96 |
+
.responsive-plot .js-plotly-plot,
|
| 97 |
+
.responsive-plot .plotly-graph-div {{
|
| 98 |
+
width: 100% !important;
|
| 99 |
+
min-height: {RESPONSIVE_PLOT_MIN_HEIGHT_PX}px;
|
| 100 |
+
}}
|
| 101 |
+
</style>
|
| 102 |
+
"""
|
| 103 |
+
FORCE_DARK_MODE_HEAD = (
|
| 104 |
+
"""
|
| 105 |
<script>
|
| 106 |
(() => {
|
| 107 |
const url = new URL(window.location.href);
|
|
|
|
| 111 |
}
|
| 112 |
})();
|
| 113 |
</script>
|
|
|
|
|
|
|
|
|
|
| 114 |
"""
|
| 115 |
+
+ RESPONSIVE_PLOT_CSS
|
| 116 |
+
)
|
| 117 |
|
| 118 |
|
| 119 |
def restart_space():
|
|
|
|
| 123 |
BENCHMARK_NAMES = get_benchmark_names()
|
| 124 |
DEFAULT_BENCHMARK = BENCHMARK_NAMES[0] if BENCHMARK_NAMES else None
|
| 125 |
BENCHMARK_RUN_DF = get_benchmark_run_df()
|
|
|
|
| 126 |
ANALYSIS_DF = get_analysis_df()
|
| 127 |
|
| 128 |
|
|
|
|
| 141 |
)
|
| 142 |
|
| 143 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
|
| 145 |
+
def render_efficiency(
|
| 146 |
benchmark_name,
|
| 147 |
token_metric,
|
| 148 |
color_by,
|
|
|
|
| 152 |
color_palette=DEFAULT_COLOR_PALETTE,
|
| 153 |
plot_background=DEFAULT_PLOT_BACKGROUND,
|
| 154 |
):
|
| 155 |
+
plot_df = get_efficiency_df(
|
| 156 |
benchmark_name=benchmark_name,
|
| 157 |
+
resource_metric=token_metric,
|
| 158 |
analysis_df=ANALYSIS_DF,
|
| 159 |
)
|
| 160 |
table_df = get_token_efficiency_table_df(
|
|
|
|
| 162 |
analysis_df=ANALYSIS_DF,
|
| 163 |
)
|
| 164 |
exclusion_count = plot_df.attrs.get("exclusion_count", 0)
|
| 165 |
+
note = f"{exclusion_count} runs excluded for this benchmark because {token_metric.lower()} was missing or non-positive."
|
| 166 |
+
figure = create_performance_vs_resource_plot(
|
| 167 |
plot_df,
|
| 168 |
+
resource_metric=token_metric,
|
| 169 |
color_by=color_by,
|
| 170 |
x_scale=x_scale,
|
| 171 |
show_pareto_frontier=show_pareto_frontier,
|
|
|
|
| 298 |
outputs=plot,
|
| 299 |
)
|
| 300 |
|
| 301 |
+
with gr.Tab("⚡ Efficiency") as efficiency_tab:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 302 |
gr.Markdown(
|
| 303 |
+
"### Efficiency\n"
|
| 304 |
+
"Compare score with resource use within one benchmark. The x-axis can show total tokens, "
|
| 305 |
+
"cost per task, or agent execution time per task. The dashed Pareto frontier connects runs "
|
| 306 |
+
"for which no other displayed run uses an equal or lower amount of the selected resource "
|
| 307 |
+
"while achieving an equal or higher score. Tokens Per Solved Task remains in the ranking "
|
| 308 |
+
"table as a reference metric."
|
|
|
|
| 309 |
)
|
| 310 |
with gr.Row():
|
| 311 |
efficiency_benchmark = gr.Dropdown(
|
| 312 |
+
choices=BENCHMARK_NAMES,
|
| 313 |
+
value=BENCHMARK_NAMES[0] if BENCHMARK_NAMES else None,
|
| 314 |
label="Benchmark",
|
| 315 |
)
|
| 316 |
efficiency_metric = gr.Dropdown(
|
| 317 |
+
choices=list(EFFICIENCY_RESOURCE_METRICS),
|
| 318 |
+
value="Total tokens",
|
| 319 |
+
label="Resource metric",
|
| 320 |
)
|
| 321 |
efficiency_color_by = gr.Radio(
|
| 322 |
choices=EFFICIENCY_COLOR_BY_CHOICES,
|
|
|
|
| 338 |
label="Image background",
|
| 339 |
)
|
| 340 |
|
| 341 |
+
initial_efficiency = render_efficiency(
|
| 342 |
+
BENCHMARK_NAMES[0] if BENCHMARK_NAMES else None,
|
| 343 |
+
"Total tokens",
|
| 344 |
"Model",
|
| 345 |
"Log",
|
| 346 |
True,
|
|
|
|
| 354 |
show_label=False,
|
| 355 |
elem_classes="responsive-plot",
|
| 356 |
)
|
| 357 |
+
gr.Markdown("#### Efficiency ranking")
|
| 358 |
efficiency_table = gr.Dataframe(
|
| 359 |
value=initial_efficiency[1],
|
| 360 |
interactive=False,
|
|
|
|
| 373 |
]
|
| 374 |
for control in efficiency_controls:
|
| 375 |
control.change(
|
| 376 |
+
fn=render_efficiency,
|
| 377 |
inputs=efficiency_controls,
|
| 378 |
outputs=[efficiency_plot, efficiency_table, efficiency_note],
|
| 379 |
)
|
| 380 |
|
| 381 |
+
# Gradio initially lays out hidden tabs without reliable dimensions. Re-render
|
| 382 |
+
# once Efficiency becomes visible so Plotly can autosize against the real
|
| 383 |
+
# container instead of keeping the hidden-tab geometry.
|
| 384 |
+
efficiency_tab.select(
|
| 385 |
+
fn=render_efficiency,
|
| 386 |
+
inputs=efficiency_controls,
|
| 387 |
+
outputs=[efficiency_plot, efficiency_table, efficiency_note],
|
| 388 |
+
)
|
| 389 |
+
|
| 390 |
with gr.Tab("🏃 Benchmark Runs"):
|
| 391 |
benchmark_runs = init_benchmark_runs(BENCHMARK_RUN_DF)
|
| 392 |
|
src/charts.py
CHANGED
|
@@ -103,31 +103,6 @@ PLOT_BACKGROUNDS: dict[PlotBackground, dict[str, str]] = {
|
|
| 103 |
|
| 104 |
|
| 105 |
|
| 106 |
-
CAPABILITY_TIER_BOUNDARIES = (70.0, 80.0)
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
def add_capability_tier_guides(fig: Figure, background_name: str | None = DEFAULT_BACKGROUND) -> None:
|
| 110 |
-
"""Add fixed score-band guides without affecting Pareto calculations."""
|
| 111 |
-
theme = get_plot_background(background_name)
|
| 112 |
-
for boundary in CAPABILITY_TIER_BOUNDARIES:
|
| 113 |
-
fig.add_hline(
|
| 114 |
-
y=boundary,
|
| 115 |
-
line={"width": 1.5, "dash": "dot", "color": theme["zero_line_color"]},
|
| 116 |
-
layer="below",
|
| 117 |
-
)
|
| 118 |
-
fig.add_annotation(
|
| 119 |
-
x=1.0,
|
| 120 |
-
xref="paper",
|
| 121 |
-
xanchor="right",
|
| 122 |
-
y=boundary,
|
| 123 |
-
yref="y",
|
| 124 |
-
yshift=8,
|
| 125 |
-
text=f"{boundary:.0f}% guide",
|
| 126 |
-
showarrow=False,
|
| 127 |
-
font={"size": 11, "color": theme["text_muted"]},
|
| 128 |
-
)
|
| 129 |
-
|
| 130 |
-
|
| 131 |
def clean_markdown_link(value: object) -> str:
|
| 132 |
"""Return human-readable text from Markdown links used in leaderboard tables."""
|
| 133 |
text = str(value).replace("<sup>*</sup>", "")
|
|
@@ -312,9 +287,7 @@ def prepare_benchmark_run_plot_df(dataframe: pd.DataFrame) -> pd.DataFrame:
|
|
| 312 |
plot_df["Model Label"] = plot_df["Model"].map(clean_markdown_link)
|
| 313 |
plot_df["Harness Label"] = plot_df["Harness"].map(clean_markdown_link)
|
| 314 |
plot_df["Benchmark Label"] = plot_df["Benchmark"].map(clean_markdown_link)
|
| 315 |
-
plot_df["Run Label"] =
|
| 316 |
-
plot_df["Model Label"] + " / " + plot_df["Harness Label"]
|
| 317 |
-
)
|
| 318 |
plot_df["Score"] = pd.to_numeric(plot_df["Score"], errors="coerce")
|
| 319 |
return plot_df
|
| 320 |
|
|
@@ -323,6 +296,7 @@ def create_leaderboard_benchmark_plot(
|
|
| 323 |
dataframe: pd.DataFrame,
|
| 324 |
benchmark_name: str,
|
| 325 |
color_by: ColorBy = "Model",
|
|
|
|
| 326 |
palette_name: str | None = DEFAULT_PALETTE,
|
| 327 |
background_name: str | None = DEFAULT_BACKGROUND,
|
| 328 |
) -> Figure:
|
|
@@ -451,36 +425,82 @@ def create_score_vs_cost_plot(
|
|
| 451 |
return apply_plot_theme(fig, background_name)
|
| 452 |
|
| 453 |
|
| 454 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 455 |
dataframe: pd.DataFrame,
|
| 456 |
-
|
| 457 |
-
color_by:
|
| 458 |
x_scale: Literal["Linear", "Log"] = "Log",
|
| 459 |
show_pareto_frontier: bool = True,
|
| 460 |
show_labels: bool = False,
|
| 461 |
palette_name: str | None = DEFAULT_PALETTE,
|
| 462 |
background_name: str | None = DEFAULT_BACKGROUND,
|
| 463 |
) -> Figure:
|
| 464 |
-
"""Plot score against
|
| 465 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 466 |
|
| 467 |
if dataframe is None or dataframe.empty:
|
| 468 |
-
return empty_figure("No valid
|
| 469 |
-
if
|
| 470 |
-
return empty_figure(f"
|
| 471 |
-
if color_by not in dataframe.columns:
|
| 472 |
return empty_figure(f"Color dimension not available: {color_by}.", background_name)
|
| 473 |
|
| 474 |
plot_df = dataframe.copy()
|
| 475 |
-
|
|
|
|
|
|
|
|
|
|
| 476 |
plot_df["Score (%)"] = pd.to_numeric(plot_df["Score (%)"], errors="coerce")
|
| 477 |
-
plot_df = plot_df.dropna(subset=[
|
| 478 |
-
plot_df = plot_df[plot_df[
|
| 479 |
if plot_df.empty:
|
| 480 |
-
return empty_figure("No valid
|
| 481 |
|
| 482 |
-
|
| 483 |
-
colors = color_map_for(plot_df[color_by], palette_color_by, palette_name)
|
| 484 |
theme = get_plot_background(background_name)
|
| 485 |
fig = go.Figure()
|
| 486 |
hover_columns = [
|
|
@@ -500,11 +520,17 @@ def create_score_vs_tokens_plot(
|
|
| 500 |
if column not in plot_df:
|
| 501 |
plot_df[column] = None
|
| 502 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 503 |
for group, group_df in plot_df.groupby(color_by, sort=True):
|
| 504 |
label_kwargs = scatter_label_kwargs(group_df, show_labels)
|
| 505 |
fig.add_trace(
|
| 506 |
go.Scatter(
|
| 507 |
-
x=group_df[
|
| 508 |
y=group_df["Score (%)"],
|
| 509 |
name=str(group),
|
| 510 |
**label_kwargs,
|
|
@@ -519,24 +545,30 @@ def create_score_vs_tokens_plot(
|
|
| 519 |
"Harness: %{customdata[1]}<br>"
|
| 520 |
"Benchmark: %{customdata[2]}<br>"
|
| 521 |
"Score: %{customdata[3]:.1f}%<br>"
|
|
|
|
| 522 |
"Input tokens/task: %{customdata[4]:,.0f}<br>"
|
| 523 |
"Output tokens/task: %{customdata[5]:,.0f}<br>"
|
| 524 |
"Cache tokens/task: %{customdata[6]:,.0f}<br>"
|
| 525 |
"Total tokens/task: %{customdata[7]:,.0f}<br>"
|
| 526 |
"Cost/task: $%{customdata[8]:.4f}<br>"
|
| 527 |
-
"Total time/task: %{customdata[9]:,.
|
| 528 |
-
"Agent time/task: %{customdata[10]:,.
|
| 529 |
"<extra></extra>"
|
| 530 |
),
|
| 531 |
)
|
| 532 |
)
|
| 533 |
|
| 534 |
if show_pareto_frontier:
|
| 535 |
-
frontier_df =
|
| 536 |
if not frontier_df.empty:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 537 |
fig.add_trace(
|
| 538 |
go.Scatter(
|
| 539 |
-
x=frontier_df[
|
| 540 |
y=frontier_df["Score (%)"],
|
| 541 |
mode="lines+markers",
|
| 542 |
name="Pareto frontier",
|
|
@@ -551,35 +583,67 @@ def create_score_vs_tokens_plot(
|
|
| 551 |
hovertemplate=(
|
| 552 |
"<b>Pareto frontier</b><br>"
|
| 553 |
"%{customdata[0]}<br>"
|
| 554 |
-
f"{
|
| 555 |
"Score: %{y:.1f}%<extra></extra>"
|
| 556 |
),
|
| 557 |
)
|
| 558 |
)
|
| 559 |
|
| 560 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 561 |
|
| 562 |
fig.update_layout(
|
| 563 |
title=None,
|
| 564 |
-
xaxis=
|
| 565 |
yaxis={"title": "Score (%)", "range": [0, 105]},
|
| 566 |
legend_title_text=color_by,
|
| 567 |
)
|
| 568 |
return apply_plot_theme(fig, background_name)
|
| 569 |
|
| 570 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 571 |
def create_token_pareto_frontier_plot(
|
| 572 |
dataframe: pd.DataFrame,
|
| 573 |
-
token_metric: str = "
|
| 574 |
-
color_by:
|
| 575 |
x_scale: Literal["Linear", "Log"] = "Log",
|
| 576 |
show_labels: bool = False,
|
| 577 |
palette_name: str | None = DEFAULT_PALETTE,
|
| 578 |
background_name: str | None = DEFAULT_BACKGROUND,
|
| 579 |
) -> Figure:
|
| 580 |
-
|
|
|
|
| 581 |
dataframe=dataframe,
|
| 582 |
-
|
| 583 |
color_by=color_by,
|
| 584 |
x_scale=x_scale,
|
| 585 |
show_pareto_frontier=True,
|
|
|
|
| 103 |
|
| 104 |
|
| 105 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
def clean_markdown_link(value: object) -> str:
|
| 107 |
"""Return human-readable text from Markdown links used in leaderboard tables."""
|
| 108 |
text = str(value).replace("<sup>*</sup>", "")
|
|
|
|
| 287 |
plot_df["Model Label"] = plot_df["Model"].map(clean_markdown_link)
|
| 288 |
plot_df["Harness Label"] = plot_df["Harness"].map(clean_markdown_link)
|
| 289 |
plot_df["Benchmark Label"] = plot_df["Benchmark"].map(clean_markdown_link)
|
| 290 |
+
plot_df["Run Label"] = plot_df["Model Label"] + "<br>" + plot_df["Harness Label"]
|
|
|
|
|
|
|
| 291 |
plot_df["Score"] = pd.to_numeric(plot_df["Score"], errors="coerce")
|
| 292 |
return plot_df
|
| 293 |
|
|
|
|
| 296 |
dataframe: pd.DataFrame,
|
| 297 |
benchmark_name: str,
|
| 298 |
color_by: ColorBy = "Model",
|
| 299 |
+
show_labels: bool = False,
|
| 300 |
palette_name: str | None = DEFAULT_PALETTE,
|
| 301 |
background_name: str | None = DEFAULT_BACKGROUND,
|
| 302 |
) -> Figure:
|
|
|
|
| 425 |
return apply_plot_theme(fig, background_name)
|
| 426 |
|
| 427 |
|
| 428 |
+
RESOURCE_AXIS_CONFIG = {
|
| 429 |
+
"Total tokens": {
|
| 430 |
+
"column": "Total Tokens Per Task",
|
| 431 |
+
"axis_title": "Total tokens per task",
|
| 432 |
+
"hover_label": "Total tokens/task",
|
| 433 |
+
"hover_format": ",.0f",
|
| 434 |
+
},
|
| 435 |
+
"Cost per task": {
|
| 436 |
+
"column": "Cost Per Task",
|
| 437 |
+
"axis_title": "Cost per task (USD)",
|
| 438 |
+
"hover_label": "Cost/task",
|
| 439 |
+
"hover_format": ".4f",
|
| 440 |
+
"tickprefix": "$",
|
| 441 |
+
},
|
| 442 |
+
"Agent time per task": {
|
| 443 |
+
"column": "Agent Time Per Task",
|
| 444 |
+
"axis_title": "Agent time per task (seconds)",
|
| 445 |
+
"hover_label": "Agent time/task",
|
| 446 |
+
"hover_format": ",.1f",
|
| 447 |
+
"ticksuffix": "s",
|
| 448 |
+
},
|
| 449 |
+
}
|
| 450 |
+
|
| 451 |
+
|
| 452 |
+
def _resource_axis_config(resource_metric: str) -> dict[str, str]:
|
| 453 |
+
"""Return display metadata for a supported Efficiency resource metric."""
|
| 454 |
+
if resource_metric in RESOURCE_AXIS_CONFIG:
|
| 455 |
+
return RESOURCE_AXIS_CONFIG[resource_metric]
|
| 456 |
+
for config in RESOURCE_AXIS_CONFIG.values():
|
| 457 |
+
if resource_metric == config["column"]:
|
| 458 |
+
return config
|
| 459 |
+
raise ValueError(f"Unsupported efficiency resource metric: {resource_metric}")
|
| 460 |
+
|
| 461 |
+
|
| 462 |
+
def create_performance_vs_resource_plot(
|
| 463 |
dataframe: pd.DataFrame,
|
| 464 |
+
resource_metric: str = "Total tokens",
|
| 465 |
+
color_by: ColorBy = "Model",
|
| 466 |
x_scale: Literal["Linear", "Log"] = "Log",
|
| 467 |
show_pareto_frontier: bool = True,
|
| 468 |
show_labels: bool = False,
|
| 469 |
palette_name: str | None = DEFAULT_PALETTE,
|
| 470 |
background_name: str | None = DEFAULT_BACKGROUND,
|
| 471 |
) -> Figure:
|
| 472 |
+
"""Plot benchmark score against one positive resource metric.
|
| 473 |
+
|
| 474 |
+
Lower resource use and higher score define the optional Pareto frontier.
|
| 475 |
+
The caller is expected to provide rows for one benchmark only.
|
| 476 |
+
"""
|
| 477 |
+
from src.leaderboard import get_resource_pareto_frontier_df
|
| 478 |
+
|
| 479 |
+
try:
|
| 480 |
+
resource_config = _resource_axis_config(resource_metric)
|
| 481 |
+
except ValueError:
|
| 482 |
+
return empty_figure(f"Resource metric not available: {resource_metric}.", background_name)
|
| 483 |
+
resource_column = resource_config["column"]
|
| 484 |
|
| 485 |
if dataframe is None or dataframe.empty:
|
| 486 |
+
return empty_figure("No valid resource data available for this benchmark.", background_name)
|
| 487 |
+
if resource_column not in dataframe.columns:
|
| 488 |
+
return empty_figure(f"Resource metric not available: {resource_column}.", background_name)
|
| 489 |
+
if color_by not in ("Model", "Harness") or color_by not in dataframe.columns:
|
| 490 |
return empty_figure(f"Color dimension not available: {color_by}.", background_name)
|
| 491 |
|
| 492 |
plot_df = dataframe.copy()
|
| 493 |
+
if "Benchmark" in plot_df.columns and plot_df["Benchmark"].dropna().nunique() > 1:
|
| 494 |
+
return empty_figure("Select one benchmark for the Efficiency view.", background_name)
|
| 495 |
+
|
| 496 |
+
plot_df[resource_column] = pd.to_numeric(plot_df[resource_column], errors="coerce")
|
| 497 |
plot_df["Score (%)"] = pd.to_numeric(plot_df["Score (%)"], errors="coerce")
|
| 498 |
+
plot_df = plot_df.dropna(subset=[resource_column, "Score (%)"])
|
| 499 |
+
plot_df = plot_df[plot_df[resource_column] > 0]
|
| 500 |
if plot_df.empty:
|
| 501 |
+
return empty_figure("No valid resource data available for this benchmark.", background_name)
|
| 502 |
|
| 503 |
+
colors = color_map_for(plot_df[color_by], color_by, palette_name)
|
|
|
|
| 504 |
theme = get_plot_background(background_name)
|
| 505 |
fig = go.Figure()
|
| 506 |
hover_columns = [
|
|
|
|
| 520 |
if column not in plot_df:
|
| 521 |
plot_df[column] = None
|
| 522 |
|
| 523 |
+
resource_hover = f"{resource_config['hover_label']}: %{{x:{resource_config['hover_format']}}}"
|
| 524 |
+
if resource_metric == "Cost per task" or resource_column == "Cost Per Task":
|
| 525 |
+
resource_hover = f"{resource_config['hover_label']}: $%{{x:{resource_config['hover_format']}}}"
|
| 526 |
+
elif resource_metric == "Agent time per task" or resource_column == "Agent Time Per Task":
|
| 527 |
+
resource_hover += "s"
|
| 528 |
+
|
| 529 |
for group, group_df in plot_df.groupby(color_by, sort=True):
|
| 530 |
label_kwargs = scatter_label_kwargs(group_df, show_labels)
|
| 531 |
fig.add_trace(
|
| 532 |
go.Scatter(
|
| 533 |
+
x=group_df[resource_column],
|
| 534 |
y=group_df["Score (%)"],
|
| 535 |
name=str(group),
|
| 536 |
**label_kwargs,
|
|
|
|
| 545 |
"Harness: %{customdata[1]}<br>"
|
| 546 |
"Benchmark: %{customdata[2]}<br>"
|
| 547 |
"Score: %{customdata[3]:.1f}%<br>"
|
| 548 |
+
f"{resource_hover}<br>"
|
| 549 |
"Input tokens/task: %{customdata[4]:,.0f}<br>"
|
| 550 |
"Output tokens/task: %{customdata[5]:,.0f}<br>"
|
| 551 |
"Cache tokens/task: %{customdata[6]:,.0f}<br>"
|
| 552 |
"Total tokens/task: %{customdata[7]:,.0f}<br>"
|
| 553 |
"Cost/task: $%{customdata[8]:.4f}<br>"
|
| 554 |
+
"Total time/task: %{customdata[9]:,.1f}s<br>"
|
| 555 |
+
"Agent time/task: %{customdata[10]:,.1f}s"
|
| 556 |
"<extra></extra>"
|
| 557 |
),
|
| 558 |
)
|
| 559 |
)
|
| 560 |
|
| 561 |
if show_pareto_frontier:
|
| 562 |
+
frontier_df = get_resource_pareto_frontier_df(plot_df, resource_column)
|
| 563 |
if not frontier_df.empty:
|
| 564 |
+
frontier_hover = f"{resource_config['hover_label']}: %{{x:{resource_config['hover_format']}}}"
|
| 565 |
+
if resource_column == "Cost Per Task":
|
| 566 |
+
frontier_hover = f"{resource_config['hover_label']}: $%{{x:{resource_config['hover_format']}}}"
|
| 567 |
+
elif resource_column == "Agent Time Per Task":
|
| 568 |
+
frontier_hover += "s"
|
| 569 |
fig.add_trace(
|
| 570 |
go.Scatter(
|
| 571 |
+
x=frontier_df[resource_column],
|
| 572 |
y=frontier_df["Score (%)"],
|
| 573 |
mode="lines+markers",
|
| 574 |
name="Pareto frontier",
|
|
|
|
| 583 |
hovertemplate=(
|
| 584 |
"<b>Pareto frontier</b><br>"
|
| 585 |
"%{customdata[0]}<br>"
|
| 586 |
+
f"{frontier_hover}<br>"
|
| 587 |
"Score: %{y:.1f}%<extra></extra>"
|
| 588 |
),
|
| 589 |
)
|
| 590 |
)
|
| 591 |
|
| 592 |
+
|
| 593 |
+
xaxis = {
|
| 594 |
+
"title": resource_config["axis_title"],
|
| 595 |
+
"type": "log" if x_scale == "Log" else "linear",
|
| 596 |
+
}
|
| 597 |
+
if "tickprefix" in resource_config:
|
| 598 |
+
xaxis["tickprefix"] = resource_config["tickprefix"]
|
| 599 |
+
if "ticksuffix" in resource_config:
|
| 600 |
+
xaxis["ticksuffix"] = resource_config["ticksuffix"]
|
| 601 |
|
| 602 |
fig.update_layout(
|
| 603 |
title=None,
|
| 604 |
+
xaxis=xaxis,
|
| 605 |
yaxis={"title": "Score (%)", "range": [0, 105]},
|
| 606 |
legend_title_text=color_by,
|
| 607 |
)
|
| 608 |
return apply_plot_theme(fig, background_name)
|
| 609 |
|
| 610 |
|
| 611 |
+
def create_score_vs_tokens_plot(
|
| 612 |
+
dataframe: pd.DataFrame,
|
| 613 |
+
token_metric: str = "Total tokens",
|
| 614 |
+
color_by: ColorBy = "Model",
|
| 615 |
+
x_scale: Literal["Linear", "Log"] = "Log",
|
| 616 |
+
show_pareto_frontier: bool = True,
|
| 617 |
+
show_labels: bool = False,
|
| 618 |
+
palette_name: str | None = DEFAULT_PALETTE,
|
| 619 |
+
background_name: str | None = DEFAULT_BACKGROUND,
|
| 620 |
+
) -> Figure:
|
| 621 |
+
"""Backward-compatible wrapper around the performance-vs-resource chart."""
|
| 622 |
+
return create_performance_vs_resource_plot(
|
| 623 |
+
dataframe=dataframe,
|
| 624 |
+
resource_metric=token_metric,
|
| 625 |
+
color_by=color_by,
|
| 626 |
+
x_scale=x_scale,
|
| 627 |
+
show_pareto_frontier=show_pareto_frontier,
|
| 628 |
+
show_labels=show_labels,
|
| 629 |
+
palette_name=palette_name,
|
| 630 |
+
background_name=background_name,
|
| 631 |
+
)
|
| 632 |
+
|
| 633 |
+
|
| 634 |
def create_token_pareto_frontier_plot(
|
| 635 |
dataframe: pd.DataFrame,
|
| 636 |
+
token_metric: str = "Total tokens",
|
| 637 |
+
color_by: ColorBy = "Model",
|
| 638 |
x_scale: Literal["Linear", "Log"] = "Log",
|
| 639 |
show_labels: bool = False,
|
| 640 |
palette_name: str | None = DEFAULT_PALETTE,
|
| 641 |
background_name: str | None = DEFAULT_BACKGROUND,
|
| 642 |
) -> Figure:
|
| 643 |
+
"""Backward-compatible convenience wrapper with the Pareto frontier enabled."""
|
| 644 |
+
return create_performance_vs_resource_plot(
|
| 645 |
dataframe=dataframe,
|
| 646 |
+
resource_metric=token_metric,
|
| 647 |
color_by=color_by,
|
| 648 |
x_scale=x_scale,
|
| 649 |
show_pareto_frontier=True,
|
src/leaderboard.py
CHANGED
|
@@ -12,13 +12,11 @@ BENCHMARK_SORT_ORDER = {
|
|
| 12 |
"SWE-Bench Pro -- Ansible": 1,
|
| 13 |
}
|
| 14 |
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
"
|
| 18 |
-
"
|
| 19 |
-
|
| 20 |
-
"Tokens Per Solved Task",
|
| 21 |
-
)
|
| 22 |
ANALYSIS_COLUMNS = [
|
| 23 |
"Benchmark",
|
| 24 |
"Model",
|
|
@@ -40,11 +38,13 @@ ANALYSIS_COLUMNS = [
|
|
| 40 |
"Token Data Available",
|
| 41 |
]
|
| 42 |
TOKEN_EFFICIENCY_TABLE_COLUMNS = [
|
| 43 |
-
"Benchmark",
|
| 44 |
"Model",
|
| 45 |
"Harness",
|
|
|
|
| 46 |
"Score (%)",
|
| 47 |
"Total Tokens Per Task",
|
|
|
|
|
|
|
| 48 |
"Input Tokens Per Task",
|
| 49 |
"Output Tokens Per Task",
|
| 50 |
"Cache Tokens Per Task",
|
|
@@ -81,6 +81,28 @@ def get_benchmark_names(results: list[Result] | None = None) -> list[str]:
|
|
| 81 |
return sorted({r.benchmark.name for r in results}, key=benchmark_sort_key)
|
| 82 |
|
| 83 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
def get_analysis_df(results: list[Result] | None = None) -> pd.DataFrame:
|
| 85 |
"""Return one normalized analysis row per benchmark result."""
|
| 86 |
if results is None:
|
|
@@ -90,8 +112,10 @@ def get_analysis_df(results: list[Result] | None = None) -> pd.DataFrame:
|
|
| 90 |
for result in results:
|
| 91 |
metrics = result.metrics
|
| 92 |
model_label = result.model.repo or result.model.name
|
| 93 |
-
total_tokens = metrics.mean_tokens_per_task
|
| 94 |
-
|
|
|
|
|
|
|
| 95 |
tokens_per_solved_task = (
|
| 96 |
total_tokens / metrics.score
|
| 97 |
if token_data_available and metrics.score > 0
|
|
@@ -113,9 +137,9 @@ def get_analysis_df(results: list[Result] | None = None) -> pd.DataFrame:
|
|
| 113 |
"Output Tokens Per Task": metrics.mean_output_tokens_per_task,
|
| 114 |
"Total Tokens Per Task": total_tokens,
|
| 115 |
"Tokens Per Solved Task": tokens_per_solved_task,
|
| 116 |
-
"Cost Per Task":
|
| 117 |
"Total Time Per Task": metrics.mean_total_time_seconds_per_task,
|
| 118 |
-
"Agent Time Per Task":
|
| 119 |
"Token Data Available": token_data_available,
|
| 120 |
}
|
| 121 |
)
|
|
@@ -123,61 +147,80 @@ def get_analysis_df(results: list[Result] | None = None) -> pd.DataFrame:
|
|
| 123 |
return pd.DataFrame(rows, columns=ANALYSIS_COLUMNS)
|
| 124 |
|
| 125 |
|
| 126 |
-
def
|
| 127 |
-
benchmark_name: str
|
| 128 |
-
|
| 129 |
analysis_df: pd.DataFrame | None = None,
|
| 130 |
) -> pd.DataFrame:
|
| 131 |
-
"""
|
| 132 |
|
| 133 |
-
The number of rows excluded because the selected
|
| 134 |
-
non-positive is
|
| 135 |
"""
|
| 136 |
-
|
| 137 |
-
raise ValueError(f"Unsupported token metric: {token_metric}")
|
| 138 |
-
|
| 139 |
dataframe = get_analysis_df() if analysis_df is None else analysis_df.copy()
|
| 140 |
-
|
| 141 |
-
dataframe = dataframe[dataframe["Benchmark"] == benchmark_name].copy()
|
| 142 |
|
| 143 |
if dataframe.empty:
|
| 144 |
dataframe.attrs["exclusion_count"] = 0
|
| 145 |
return dataframe
|
| 146 |
|
| 147 |
-
metric_values = pd.to_numeric(dataframe[
|
| 148 |
valid_mask = metric_values.notna() & (metric_values > 0)
|
| 149 |
exclusion_count = int((~valid_mask).sum())
|
| 150 |
dataframe = dataframe.loc[valid_mask].copy()
|
| 151 |
-
dataframe[
|
| 152 |
dataframe = dataframe.sort_values(
|
| 153 |
-
[
|
| 154 |
-
ascending=[True, False, True, True
|
| 155 |
na_position="last",
|
| 156 |
)
|
| 157 |
dataframe.attrs["exclusion_count"] = exclusion_count
|
| 158 |
return dataframe
|
| 159 |
|
| 160 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 161 |
def get_token_efficiency_table_df(
|
| 162 |
-
benchmark_name: str
|
| 163 |
analysis_df: pd.DataFrame | None = None,
|
| 164 |
) -> pd.DataFrame:
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 169 |
)
|
| 170 |
table_df = dataframe.reindex(columns=TOKEN_EFFICIENCY_TABLE_COLUMNS).copy()
|
| 171 |
-
table_df
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 172 |
return table_df
|
| 173 |
|
| 174 |
|
| 175 |
-
def
|
| 176 |
dataframe: pd.DataFrame,
|
| 177 |
token_metric: str,
|
| 178 |
score_column: str = "Score (%)",
|
| 179 |
) -> pd.DataFrame:
|
| 180 |
-
"""Return deterministic non-dominated points for lower
|
| 181 |
if token_metric not in dataframe.columns or score_column not in dataframe.columns:
|
| 182 |
return dataframe.iloc[0:0].copy()
|
| 183 |
|
|
@@ -211,6 +254,15 @@ def get_token_pareto_frontier_df(
|
|
| 211 |
)
|
| 212 |
|
| 213 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 214 |
def get_benchmark_run_df():
|
| 215 |
results = get_results()
|
| 216 |
|
|
|
|
| 12 |
"SWE-Bench Pro -- Ansible": 1,
|
| 13 |
}
|
| 14 |
|
| 15 |
+
EFFICIENCY_RESOURCE_METRICS = {
|
| 16 |
+
"Total tokens": "Total Tokens Per Task",
|
| 17 |
+
"Cost per task": "Cost Per Task",
|
| 18 |
+
"Agent time per task": "Agent Time Per Task",
|
| 19 |
+
}
|
|
|
|
|
|
|
| 20 |
ANALYSIS_COLUMNS = [
|
| 21 |
"Benchmark",
|
| 22 |
"Model",
|
|
|
|
| 38 |
"Token Data Available",
|
| 39 |
]
|
| 40 |
TOKEN_EFFICIENCY_TABLE_COLUMNS = [
|
|
|
|
| 41 |
"Model",
|
| 42 |
"Harness",
|
| 43 |
+
"Benchmark",
|
| 44 |
"Score (%)",
|
| 45 |
"Total Tokens Per Task",
|
| 46 |
+
"Cost Per Task",
|
| 47 |
+
"Agent Time Per Task",
|
| 48 |
"Input Tokens Per Task",
|
| 49 |
"Output Tokens Per Task",
|
| 50 |
"Cache Tokens Per Task",
|
|
|
|
| 81 |
return sorted({r.benchmark.name for r in results}, key=benchmark_sort_key)
|
| 82 |
|
| 83 |
|
| 84 |
+
def _positive_number_or_none(value: object) -> float | None:
|
| 85 |
+
"""Return a positive numeric value, otherwise ``None``."""
|
| 86 |
+
if value is None:
|
| 87 |
+
return None
|
| 88 |
+
try:
|
| 89 |
+
numeric = float(value)
|
| 90 |
+
except (TypeError, ValueError):
|
| 91 |
+
return None
|
| 92 |
+
if pd.isna(numeric) or numeric <= 0:
|
| 93 |
+
return None
|
| 94 |
+
return numeric
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
def get_efficiency_resource_column(metric_name: str) -> str:
|
| 98 |
+
"""Resolve an Efficiency UI metric name to its canonical analysis column."""
|
| 99 |
+
if metric_name in EFFICIENCY_RESOURCE_METRICS:
|
| 100 |
+
return EFFICIENCY_RESOURCE_METRICS[metric_name]
|
| 101 |
+
if metric_name in EFFICIENCY_RESOURCE_METRICS.values():
|
| 102 |
+
return metric_name
|
| 103 |
+
raise ValueError(f"Unsupported efficiency resource metric: {metric_name}")
|
| 104 |
+
|
| 105 |
+
|
| 106 |
def get_analysis_df(results: list[Result] | None = None) -> pd.DataFrame:
|
| 107 |
"""Return one normalized analysis row per benchmark result."""
|
| 108 |
if results is None:
|
|
|
|
| 112 |
for result in results:
|
| 113 |
metrics = result.metrics
|
| 114 |
model_label = result.model.repo or result.model.name
|
| 115 |
+
total_tokens = _positive_number_or_none(metrics.mean_tokens_per_task)
|
| 116 |
+
cost_per_task = _positive_number_or_none(metrics.mean_cost_usd_per_task)
|
| 117 |
+
agent_time_per_task = _positive_number_or_none(metrics.mean_agent_time_seconds_per_task)
|
| 118 |
+
token_data_available = total_tokens is not None
|
| 119 |
tokens_per_solved_task = (
|
| 120 |
total_tokens / metrics.score
|
| 121 |
if token_data_available and metrics.score > 0
|
|
|
|
| 137 |
"Output Tokens Per Task": metrics.mean_output_tokens_per_task,
|
| 138 |
"Total Tokens Per Task": total_tokens,
|
| 139 |
"Tokens Per Solved Task": tokens_per_solved_task,
|
| 140 |
+
"Cost Per Task": cost_per_task,
|
| 141 |
"Total Time Per Task": metrics.mean_total_time_seconds_per_task,
|
| 142 |
+
"Agent Time Per Task": agent_time_per_task,
|
| 143 |
"Token Data Available": token_data_available,
|
| 144 |
}
|
| 145 |
)
|
|
|
|
| 147 |
return pd.DataFrame(rows, columns=ANALYSIS_COLUMNS)
|
| 148 |
|
| 149 |
|
| 150 |
+
def get_efficiency_df(
|
| 151 |
+
benchmark_name: str,
|
| 152 |
+
resource_metric: str = "Total tokens",
|
| 153 |
analysis_df: pd.DataFrame | None = None,
|
| 154 |
) -> pd.DataFrame:
|
| 155 |
+
"""Return valid resource rows for one benchmark-specific Efficiency plot.
|
| 156 |
|
| 157 |
+
The number of rows excluded because the selected resource is missing or
|
| 158 |
+
non-positive is exposed via ``dataframe.attrs["exclusion_count"]``.
|
| 159 |
"""
|
| 160 |
+
metric_column = get_efficiency_resource_column(resource_metric)
|
|
|
|
|
|
|
| 161 |
dataframe = get_analysis_df() if analysis_df is None else analysis_df.copy()
|
| 162 |
+
dataframe = dataframe[dataframe["Benchmark"] == benchmark_name].copy()
|
|
|
|
| 163 |
|
| 164 |
if dataframe.empty:
|
| 165 |
dataframe.attrs["exclusion_count"] = 0
|
| 166 |
return dataframe
|
| 167 |
|
| 168 |
+
metric_values = pd.to_numeric(dataframe[metric_column], errors="coerce")
|
| 169 |
valid_mask = metric_values.notna() & (metric_values > 0)
|
| 170 |
exclusion_count = int((~valid_mask).sum())
|
| 171 |
dataframe = dataframe.loc[valid_mask].copy()
|
| 172 |
+
dataframe[metric_column] = metric_values.loc[valid_mask]
|
| 173 |
dataframe = dataframe.sort_values(
|
| 174 |
+
[metric_column, "Score (%)", "Model", "Harness"],
|
| 175 |
+
ascending=[True, False, True, True],
|
| 176 |
na_position="last",
|
| 177 |
)
|
| 178 |
dataframe.attrs["exclusion_count"] = exclusion_count
|
| 179 |
return dataframe
|
| 180 |
|
| 181 |
|
| 182 |
+
def get_token_efficiency_df(
|
| 183 |
+
benchmark_name: str,
|
| 184 |
+
token_metric: str = "Total tokens",
|
| 185 |
+
analysis_df: pd.DataFrame | None = None,
|
| 186 |
+
) -> pd.DataFrame:
|
| 187 |
+
"""Backward-compatible wrapper for :func:`get_efficiency_df`."""
|
| 188 |
+
return get_efficiency_df(benchmark_name, token_metric, analysis_df)
|
| 189 |
+
|
| 190 |
+
|
| 191 |
def get_token_efficiency_table_df(
|
| 192 |
+
benchmark_name: str,
|
| 193 |
analysis_df: pd.DataFrame | None = None,
|
| 194 |
) -> pd.DataFrame:
|
| 195 |
+
"""Return the benchmark-specific Efficiency ranking table for display."""
|
| 196 |
+
dataframe = get_analysis_df() if analysis_df is None else analysis_df.copy()
|
| 197 |
+
dataframe = dataframe[dataframe["Benchmark"] == benchmark_name].copy()
|
| 198 |
+
if dataframe.empty:
|
| 199 |
+
return pd.DataFrame(columns=TOKEN_EFFICIENCY_TABLE_COLUMNS)
|
| 200 |
+
|
| 201 |
+
dataframe["Score (%)"] = pd.to_numeric(dataframe["Score (%)"], errors="coerce")
|
| 202 |
+
dataframe = dataframe.sort_values(
|
| 203 |
+
["Tokens Per Solved Task", "Score (%)", "Model", "Harness"],
|
| 204 |
+
ascending=[True, False, True, True],
|
| 205 |
+
na_position="last",
|
| 206 |
)
|
| 207 |
table_df = dataframe.reindex(columns=TOKEN_EFFICIENCY_TABLE_COLUMNS).copy()
|
| 208 |
+
table_df["Score (%)"] = table_df["Score (%)"].round(1)
|
| 209 |
+
if "Cost Per Task" in table_df:
|
| 210 |
+
table_df["Cost Per Task"] = pd.to_numeric(table_df["Cost Per Task"], errors="coerce").round(4)
|
| 211 |
+
if "Agent Time Per Task" in table_df:
|
| 212 |
+
table_df["Agent Time Per Task"] = pd.to_numeric(
|
| 213 |
+
table_df["Agent Time Per Task"], errors="coerce"
|
| 214 |
+
).round(1)
|
| 215 |
return table_df
|
| 216 |
|
| 217 |
|
| 218 |
+
def get_resource_pareto_frontier_df(
|
| 219 |
dataframe: pd.DataFrame,
|
| 220 |
token_metric: str,
|
| 221 |
score_column: str = "Score (%)",
|
| 222 |
) -> pd.DataFrame:
|
| 223 |
+
"""Return deterministic non-dominated points for lower resource use/higher score."""
|
| 224 |
if token_metric not in dataframe.columns or score_column not in dataframe.columns:
|
| 225 |
return dataframe.iloc[0:0].copy()
|
| 226 |
|
|
|
|
| 254 |
)
|
| 255 |
|
| 256 |
|
| 257 |
+
def get_token_pareto_frontier_df(
|
| 258 |
+
dataframe: pd.DataFrame,
|
| 259 |
+
token_metric: str,
|
| 260 |
+
score_column: str = "Score (%)",
|
| 261 |
+
) -> pd.DataFrame:
|
| 262 |
+
"""Backward-compatible wrapper for the generic resource Pareto helper."""
|
| 263 |
+
return get_resource_pareto_frontier_df(dataframe, token_metric, score_column)
|
| 264 |
+
|
| 265 |
+
|
| 266 |
def get_benchmark_run_df():
|
| 267 |
results = get_results()
|
| 268 |
|
tests/test_token_efficiency.py
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
|
|
| 1 |
import pandas as pd
|
| 2 |
import plotly.graph_objects as go
|
| 3 |
|
| 4 |
-
from src.charts import
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
from src.leaderboard import (
|
| 6 |
-
ALL_BENCHMARKS,
|
| 7 |
ANALYSIS_COLUMNS,
|
|
|
|
|
|
|
| 8 |
get_analysis_df,
|
| 9 |
-
|
| 10 |
-
|
|
|
|
|
|
|
| 11 |
)
|
| 12 |
from src.models import Benchmark, Environment, Harness, Metrics, Model, Result
|
| 13 |
|
|
@@ -18,14 +26,29 @@ def make_result(
|
|
| 18 |
model: str = "model-a",
|
| 19 |
harness: str = "harness-a",
|
| 20 |
score: float = 0.5,
|
|
|
|
| 21 |
total_tokens: int | None = 100,
|
| 22 |
input_tokens: int | None = 60,
|
| 23 |
cache_tokens: int | None = 10,
|
| 24 |
output_tokens: int | None = 30,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
) -> Result:
|
| 26 |
return Result(
|
| 27 |
-
benchmark=Benchmark(
|
| 28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
model=Model(
|
| 30 |
name=model,
|
| 31 |
repo=None,
|
|
@@ -37,39 +60,58 @@ def make_result(
|
|
| 37 |
environment=Environment(name="env", url="https://example.com/env"),
|
| 38 |
metrics=Metrics(
|
| 39 |
score=score,
|
| 40 |
-
n_tasks=
|
| 41 |
n_errors=1,
|
| 42 |
mean_input_tokens_per_task=input_tokens,
|
| 43 |
mean_cache_tokens_per_task=cache_tokens,
|
| 44 |
mean_output_tokens_per_task=output_tokens,
|
| 45 |
mean_tokens_per_task=total_tokens,
|
| 46 |
-
mean_cost_usd_per_task=
|
| 47 |
mean_total_time_seconds_per_task=12,
|
| 48 |
-
mean_agent_time_seconds_per_task=
|
|
|
|
|
|
|
| 49 |
),
|
| 50 |
)
|
| 51 |
|
| 52 |
|
| 53 |
-
def
|
| 54 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
|
| 56 |
assert set(ANALYSIS_COLUMNS).issubset(dataframe.columns)
|
| 57 |
assert dataframe.loc[0, "Score (%)"] == 25
|
| 58 |
assert dataframe.loc[0, "Tokens Per Solved Task"] == 800
|
|
|
|
|
|
|
| 59 |
assert bool(dataframe.loc[0, "Token Data Available"]) is True
|
| 60 |
|
| 61 |
|
| 62 |
-
def
|
| 63 |
dataframe = get_analysis_df(
|
| 64 |
[
|
| 65 |
-
make_result(model="missing", total_tokens=None),
|
| 66 |
-
make_result(model="zero", total_tokens=0),
|
| 67 |
-
make_result(model="negative", total_tokens=-10),
|
| 68 |
]
|
| 69 |
)
|
| 70 |
|
| 71 |
assert dataframe["Token Data Available"].tolist() == [False, False, False]
|
| 72 |
assert dataframe["Tokens Per Solved Task"].isna().all()
|
|
|
|
|
|
|
| 73 |
|
| 74 |
|
| 75 |
def test_zero_score_does_not_divide_by_zero():
|
|
@@ -79,84 +121,205 @@ def test_zero_score_does_not_divide_by_zero():
|
|
| 79 |
assert bool(dataframe.loc[0, "Token Data Available"]) is True
|
| 80 |
|
| 81 |
|
| 82 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
analysis_df = get_analysis_df(
|
| 84 |
[
|
| 85 |
-
make_result(model="valid", total_tokens=100),
|
| 86 |
-
make_result(model="zero", total_tokens=0),
|
| 87 |
-
make_result(model="missing", total_tokens=None),
|
|
|
|
| 88 |
]
|
| 89 |
)
|
| 90 |
|
| 91 |
-
filtered =
|
| 92 |
-
|
|
|
|
| 93 |
analysis_df=analysis_df,
|
| 94 |
)
|
| 95 |
|
| 96 |
assert filtered["Model"].tolist() == ["valid"]
|
|
|
|
| 97 |
assert filtered.attrs["exclusion_count"] == 2
|
|
|
|
|
|
|
|
|
|
| 98 |
|
| 99 |
|
| 100 |
-
def
|
| 101 |
analysis_df = get_analysis_df(
|
| 102 |
[
|
| 103 |
-
make_result(
|
| 104 |
-
make_result(
|
| 105 |
]
|
| 106 |
)
|
| 107 |
|
| 108 |
-
|
| 109 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
|
| 111 |
-
assert
|
| 112 |
-
assert
|
|
|
|
|
|
|
|
|
|
| 113 |
|
| 114 |
|
| 115 |
-
def
|
| 116 |
dataframe = pd.DataFrame(
|
| 117 |
{
|
| 118 |
"Run Label": ["a", "b", "c", "d"],
|
|
|
|
| 119 |
"Total Tokens Per Task": [100, 200, 300, 400],
|
|
|
|
|
|
|
| 120 |
"Score (%)": [50, 60, 55, 80],
|
| 121 |
}
|
| 122 |
)
|
| 123 |
|
| 124 |
-
|
|
|
|
|
|
|
| 125 |
|
| 126 |
-
assert frontier["Run Label"].tolist() == ["a", "b", "d"]
|
| 127 |
|
| 128 |
-
|
| 129 |
-
def test_pareto_ties_are_deterministic_and_not_falsely_dominated():
|
| 130 |
dataframe = pd.DataFrame(
|
| 131 |
{
|
| 132 |
"Run Label": ["z", "a", "dominated", "higher"],
|
| 133 |
"Model": ["z", "a", "d", "h"],
|
| 134 |
-
"
|
| 135 |
"Score (%)": [50, 50, 40, 60],
|
| 136 |
}
|
| 137 |
)
|
| 138 |
|
| 139 |
-
frontier =
|
| 140 |
|
| 141 |
assert frontier["Run Label"].tolist() == ["a", "z", "higher"]
|
| 142 |
assert "dominated" not in frontier["Run Label"].tolist()
|
| 143 |
|
| 144 |
|
| 145 |
-
def
|
| 146 |
-
dataframe =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
|
| 148 |
-
scatter = create_score_vs_tokens_plot(dataframe, token_metric="Total Tokens Per Task")
|
| 149 |
-
pareto = create_token_pareto_frontier_plot(dataframe, token_metric="Total Tokens Per Task")
|
| 150 |
|
| 151 |
-
assert isinstance(scatter, go.Figure)
|
| 152 |
-
assert isinstance(pareto, go.Figure)
|
| 153 |
-
assert scatter.layout.xaxis.type == "log"
|
| 154 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
|
| 156 |
-
|
| 157 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
invalid_df = get_analysis_df([make_result(total_tokens=0)])
|
| 159 |
-
invalid =
|
| 160 |
|
| 161 |
assert isinstance(empty, go.Figure)
|
| 162 |
assert isinstance(invalid, go.Figure)
|
|
@@ -177,9 +340,7 @@ def test_palette_lookup_new_palettes_fallback_and_copy():
|
|
| 177 |
assert "#000000" not in COLOR_PALETTES["Citrus"]
|
| 178 |
|
| 179 |
|
| 180 |
-
def
|
| 181 |
-
from src.charts import create_score_vs_cost_plot
|
| 182 |
-
|
| 183 |
cost_df = pd.DataFrame(
|
| 184 |
{
|
| 185 |
"Benchmark": ["Benchmark A"],
|
|
@@ -190,52 +351,43 @@ def test_cost_and_token_scatter_labels_toggle_consistently():
|
|
| 190 |
"Label": ["model-a<br>harness-a"],
|
| 191 |
}
|
| 192 |
)
|
| 193 |
-
|
| 194 |
|
| 195 |
cost_without = create_score_vs_cost_plot(cost_df, "Benchmark A", show_labels=False)
|
| 196 |
cost_with = create_score_vs_cost_plot(cost_df, "Benchmark A", show_labels=True)
|
| 197 |
-
|
| 198 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 199 |
|
| 200 |
assert cost_without.data[0].mode == "markers"
|
| 201 |
assert cost_with.data[0].mode == "markers+text"
|
| 202 |
-
assert
|
| 203 |
-
assert
|
| 204 |
-
assert token_with.data[0].mode == "markers+text"
|
| 205 |
-
assert list(token_with.data[0].text) == ["model-a / harness-a"]
|
| 206 |
assert cost_without.data[0].hovertemplate == cost_with.data[0].hovertemplate
|
| 207 |
-
assert
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
def test_label_toggle_handles_empty_data():
|
| 211 |
-
from src.charts import create_score_vs_cost_plot
|
| 212 |
|
| 213 |
-
|
| 214 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 215 |
|
| 216 |
-
assert
|
| 217 |
-
assert
|
| 218 |
-
assert
|
| 219 |
-
assert len(tokens.layout.annotations) == 1
|
| 220 |
|
| 221 |
|
| 222 |
-
def
|
| 223 |
-
|
| 224 |
-
{
|
| 225 |
-
"Benchmark": ["bench"],
|
| 226 |
-
"Model": ["model"],
|
| 227 |
-
"Harness": ["harness"],
|
| 228 |
-
"Run Label": ["model / harness"],
|
| 229 |
-
"Score (%)": [75.0],
|
| 230 |
-
"Tokens Per Solved Task": [1000.0],
|
| 231 |
-
}
|
| 232 |
-
)
|
| 233 |
-
|
| 234 |
-
figure = create_score_vs_tokens_plot(dataframe, show_pareto_frontier=False)
|
| 235 |
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
if shape.type == "line" and shape.y0 == shape.y1
|
| 240 |
-
]
|
| 241 |
-
assert {shape.y0 for shape in horizontal_lines} == {70.0, 80.0}
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
import pandas as pd
|
| 3 |
import plotly.graph_objects as go
|
| 4 |
|
| 5 |
+
from src.charts import (
|
| 6 |
+
create_performance_vs_resource_plot,
|
| 7 |
+
create_score_vs_cost_plot,
|
| 8 |
+
create_score_vs_tokens_plot,
|
| 9 |
+
)
|
| 10 |
from src.leaderboard import (
|
|
|
|
| 11 |
ANALYSIS_COLUMNS,
|
| 12 |
+
EFFICIENCY_RESOURCE_METRICS,
|
| 13 |
+
TOKEN_EFFICIENCY_TABLE_COLUMNS,
|
| 14 |
get_analysis_df,
|
| 15 |
+
get_efficiency_resource_column,
|
| 16 |
+
get_efficiency_df,
|
| 17 |
+
get_token_efficiency_table_df,
|
| 18 |
+
get_resource_pareto_frontier_df,
|
| 19 |
)
|
| 20 |
from src.models import Benchmark, Environment, Harness, Metrics, Model, Result
|
| 21 |
|
|
|
|
| 26 |
model: str = "model-a",
|
| 27 |
harness: str = "harness-a",
|
| 28 |
score: float = 0.5,
|
| 29 |
+
n_tasks: int | None = 10,
|
| 30 |
total_tokens: int | None = 100,
|
| 31 |
input_tokens: int | None = 60,
|
| 32 |
cache_tokens: int | None = 10,
|
| 33 |
output_tokens: int | None = 30,
|
| 34 |
+
cost_per_task: float | None = 0.25,
|
| 35 |
+
agent_time_per_task: int | None = 10,
|
| 36 |
+
cost_usd: float | None = None,
|
| 37 |
+
agent_time_seconds: int | None = None,
|
| 38 |
) -> Result:
|
| 39 |
return Result(
|
| 40 |
+
benchmark=Benchmark(
|
| 41 |
+
name=benchmark,
|
| 42 |
+
repo="repo",
|
| 43 |
+
num_tasks=10,
|
| 44 |
+
url="https://example.com/benchmark",
|
| 45 |
+
),
|
| 46 |
+
harness=Harness(
|
| 47 |
+
name=harness,
|
| 48 |
+
skills=[],
|
| 49 |
+
is_oss=True,
|
| 50 |
+
url="https://example.com/harness",
|
| 51 |
+
),
|
| 52 |
model=Model(
|
| 53 |
name=model,
|
| 54 |
repo=None,
|
|
|
|
| 60 |
environment=Environment(name="env", url="https://example.com/env"),
|
| 61 |
metrics=Metrics(
|
| 62 |
score=score,
|
| 63 |
+
n_tasks=n_tasks,
|
| 64 |
n_errors=1,
|
| 65 |
mean_input_tokens_per_task=input_tokens,
|
| 66 |
mean_cache_tokens_per_task=cache_tokens,
|
| 67 |
mean_output_tokens_per_task=output_tokens,
|
| 68 |
mean_tokens_per_task=total_tokens,
|
| 69 |
+
mean_cost_usd_per_task=cost_per_task,
|
| 70 |
mean_total_time_seconds_per_task=12,
|
| 71 |
+
mean_agent_time_seconds_per_task=agent_time_per_task,
|
| 72 |
+
cost_usd=cost_usd,
|
| 73 |
+
agent_time_seconds=agent_time_seconds,
|
| 74 |
),
|
| 75 |
)
|
| 76 |
|
| 77 |
|
| 78 |
+
def test_cost_vs_performance_tab_removed_without_efficiency_cost_regression():
|
| 79 |
+
app_source = Path("app.py").read_text()
|
| 80 |
+
|
| 81 |
+
assert 'gr.Tab("💰 Cost vs Performance")' not in app_source
|
| 82 |
+
assert "cost_benchmark" not in app_source
|
| 83 |
+
assert "cost_controls" not in app_source
|
| 84 |
+
assert "render_score_vs_cost_plot" not in app_source
|
| 85 |
+
assert 'with gr.Tab("⚡ Efficiency")' in app_source
|
| 86 |
+
assert app_source.index('with gr.Tab("🏆 Leaderboard")') < app_source.index('with gr.Tab("⚡ Efficiency")')
|
| 87 |
+
assert app_source.index('with gr.Tab("⚡ Efficiency")') < app_source.index('with gr.Tab("🏃 Benchmark Runs")')
|
| 88 |
+
|
| 89 |
+
def test_analysis_df_columns_and_derived_metrics():
|
| 90 |
+
dataframe = get_analysis_df(
|
| 91 |
+
[make_result(score=0.25, total_tokens=200, cost_per_task=0.125, agent_time_per_task=7)]
|
| 92 |
+
)
|
| 93 |
|
| 94 |
assert set(ANALYSIS_COLUMNS).issubset(dataframe.columns)
|
| 95 |
assert dataframe.loc[0, "Score (%)"] == 25
|
| 96 |
assert dataframe.loc[0, "Tokens Per Solved Task"] == 800
|
| 97 |
+
assert dataframe.loc[0, "Cost Per Task"] == 0.125
|
| 98 |
+
assert dataframe.loc[0, "Agent Time Per Task"] == 7
|
| 99 |
assert bool(dataframe.loc[0, "Token Data Available"]) is True
|
| 100 |
|
| 101 |
|
| 102 |
+
def test_missing_zero_negative_resource_values_are_unavailable():
|
| 103 |
dataframe = get_analysis_df(
|
| 104 |
[
|
| 105 |
+
make_result(model="missing", total_tokens=None, cost_per_task=None, agent_time_per_task=None),
|
| 106 |
+
make_result(model="zero", total_tokens=0, cost_per_task=0, agent_time_per_task=0),
|
| 107 |
+
make_result(model="negative", total_tokens=-10, cost_per_task=-1, agent_time_per_task=-3),
|
| 108 |
]
|
| 109 |
)
|
| 110 |
|
| 111 |
assert dataframe["Token Data Available"].tolist() == [False, False, False]
|
| 112 |
assert dataframe["Tokens Per Solved Task"].isna().all()
|
| 113 |
+
assert dataframe["Cost Per Task"].isna().all()
|
| 114 |
+
assert dataframe["Agent Time Per Task"].isna().all()
|
| 115 |
|
| 116 |
|
| 117 |
def test_zero_score_does_not_divide_by_zero():
|
|
|
|
| 121 |
assert bool(dataframe.loc[0, "Token Data Available"]) is True
|
| 122 |
|
| 123 |
|
| 124 |
+
def test_invalid_task_denominator_does_not_trigger_total_metric_fallback():
|
| 125 |
+
dataframe = get_analysis_df(
|
| 126 |
+
[
|
| 127 |
+
make_result(
|
| 128 |
+
n_tasks=0,
|
| 129 |
+
cost_per_task=None,
|
| 130 |
+
agent_time_per_task=None,
|
| 131 |
+
cost_usd=1.5,
|
| 132 |
+
agent_time_seconds=30,
|
| 133 |
+
)
|
| 134 |
+
]
|
| 135 |
+
)
|
| 136 |
+
|
| 137 |
+
assert pd.isna(dataframe.loc[0, "Cost Per Task"])
|
| 138 |
+
assert pd.isna(dataframe.loc[0, "Agent Time Per Task"])
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
def test_efficiency_resource_metric_choices_are_exact():
|
| 142 |
+
assert list(EFFICIENCY_RESOURCE_METRICS) == [
|
| 143 |
+
"Total tokens",
|
| 144 |
+
"Cost per task",
|
| 145 |
+
"Agent time per task",
|
| 146 |
+
]
|
| 147 |
+
assert "Tokens Per Solved Task" not in EFFICIENCY_RESOURCE_METRICS
|
| 148 |
+
assert get_efficiency_resource_column("Total tokens") == "Total Tokens Per Task"
|
| 149 |
+
assert get_efficiency_resource_column("Cost per task") == "Cost Per Task"
|
| 150 |
+
assert get_efficiency_resource_column("Agent time per task") == "Agent Time Per Task"
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
def test_efficiency_filtering_is_benchmark_specific_and_counts_exclusions():
|
| 154 |
analysis_df = get_analysis_df(
|
| 155 |
[
|
| 156 |
+
make_result(benchmark="Benchmark A", model="valid", total_tokens=100),
|
| 157 |
+
make_result(benchmark="Benchmark A", model="zero", total_tokens=0),
|
| 158 |
+
make_result(benchmark="Benchmark A", model="missing", total_tokens=None),
|
| 159 |
+
make_result(benchmark="Benchmark B", model="other", total_tokens=100),
|
| 160 |
]
|
| 161 |
)
|
| 162 |
|
| 163 |
+
filtered = get_efficiency_df(
|
| 164 |
+
benchmark_name="Benchmark A",
|
| 165 |
+
resource_metric="Total tokens",
|
| 166 |
analysis_df=analysis_df,
|
| 167 |
)
|
| 168 |
|
| 169 |
assert filtered["Model"].tolist() == ["valid"]
|
| 170 |
+
assert filtered["Benchmark"].unique().tolist() == ["Benchmark A"]
|
| 171 |
assert filtered.attrs["exclusion_count"] == 2
|
| 172 |
+
assert get_efficiency_df(
|
| 173 |
+
"All benchmarks", "Total tokens", analysis_df
|
| 174 |
+
).empty
|
| 175 |
|
| 176 |
|
| 177 |
+
def test_cost_and_agent_time_filtering_use_positive_values_only():
|
| 178 |
analysis_df = get_analysis_df(
|
| 179 |
[
|
| 180 |
+
make_result(model="valid", cost_per_task=0.2, agent_time_per_task=9),
|
| 181 |
+
make_result(model="invalid", cost_per_task=0, agent_time_per_task=-1),
|
| 182 |
]
|
| 183 |
)
|
| 184 |
|
| 185 |
+
cost = get_efficiency_df("Benchmark A", "Cost per task", analysis_df)
|
| 186 |
+
agent_time = get_efficiency_df("Benchmark A", "Agent time per task", analysis_df)
|
| 187 |
+
|
| 188 |
+
assert cost["Model"].tolist() == ["valid"]
|
| 189 |
+
assert agent_time["Model"].tolist() == ["valid"]
|
| 190 |
+
assert cost.attrs["exclusion_count"] == 1
|
| 191 |
+
assert agent_time.attrs["exclusion_count"] == 1
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
def test_efficiency_table_keeps_tokens_per_solved_task_and_expected_order():
|
| 195 |
+
analysis_df = get_analysis_df(
|
| 196 |
+
[make_result(score=0.333333333333, total_tokens=120, cost_per_task=0.123456)]
|
| 197 |
+
)
|
| 198 |
+
|
| 199 |
+
table = get_token_efficiency_table_df("Benchmark A", analysis_df)
|
| 200 |
|
| 201 |
+
assert list(table.columns) == TOKEN_EFFICIENCY_TABLE_COLUMNS
|
| 202 |
+
assert list(table.columns[:3]) == ["Model", "Harness", "Benchmark"]
|
| 203 |
+
assert "Tokens Per Solved Task" in table.columns
|
| 204 |
+
assert table.loc[0, "Score (%)"] == 33.3
|
| 205 |
+
assert table.loc[0, "Cost Per Task"] == 0.1235
|
| 206 |
|
| 207 |
|
| 208 |
+
def test_pareto_frontier_for_all_resource_metrics():
|
| 209 |
dataframe = pd.DataFrame(
|
| 210 |
{
|
| 211 |
"Run Label": ["a", "b", "c", "d"],
|
| 212 |
+
"Model": ["a", "b", "c", "d"],
|
| 213 |
"Total Tokens Per Task": [100, 200, 300, 400],
|
| 214 |
+
"Cost Per Task": [0.1, 0.2, 0.3, 0.4],
|
| 215 |
+
"Agent Time Per Task": [10, 20, 30, 40],
|
| 216 |
"Score (%)": [50, 60, 55, 80],
|
| 217 |
}
|
| 218 |
)
|
| 219 |
|
| 220 |
+
for metric in ("Total Tokens Per Task", "Cost Per Task", "Agent Time Per Task"):
|
| 221 |
+
frontier = get_resource_pareto_frontier_df(dataframe, metric)
|
| 222 |
+
assert frontier["Run Label"].tolist() == ["a", "b", "d"]
|
| 223 |
|
|
|
|
| 224 |
|
| 225 |
+
def test_pareto_equal_x_equal_score_ties_are_preserved():
|
|
|
|
| 226 |
dataframe = pd.DataFrame(
|
| 227 |
{
|
| 228 |
"Run Label": ["z", "a", "dominated", "higher"],
|
| 229 |
"Model": ["z", "a", "d", "h"],
|
| 230 |
+
"Cost Per Task": [0.1, 0.1, 0.1, 0.2],
|
| 231 |
"Score (%)": [50, 50, 40, 60],
|
| 232 |
}
|
| 233 |
)
|
| 234 |
|
| 235 |
+
frontier = get_resource_pareto_frontier_df(dataframe, "Cost Per Task")
|
| 236 |
|
| 237 |
assert frontier["Run Label"].tolist() == ["a", "z", "higher"]
|
| 238 |
assert "dominated" not in frontier["Run Label"].tolist()
|
| 239 |
|
| 240 |
|
| 241 |
+
def test_pareto_excludes_missing_zero_and_negative_resources():
|
| 242 |
+
dataframe = pd.DataFrame(
|
| 243 |
+
{
|
| 244 |
+
"Run Label": ["valid", "missing", "zero", "negative"],
|
| 245 |
+
"Agent Time Per Task": [10, None, 0, -1],
|
| 246 |
+
"Score (%)": [50, 100, 100, 100],
|
| 247 |
+
}
|
| 248 |
+
)
|
| 249 |
+
|
| 250 |
+
frontier = get_resource_pareto_frontier_df(dataframe, "Agent Time Per Task")
|
| 251 |
+
|
| 252 |
+
assert frontier["Run Label"].tolist() == ["valid"]
|
| 253 |
+
|
| 254 |
+
|
| 255 |
+
def test_performance_resource_charts_construct_with_metric_specific_axes():
|
| 256 |
+
dataframe = get_analysis_df(
|
| 257 |
+
[
|
| 258 |
+
make_result(),
|
| 259 |
+
make_result(model="model-b", score=0.7, total_tokens=200, cost_per_task=0.4, agent_time_per_task=20),
|
| 260 |
+
]
|
| 261 |
+
)
|
| 262 |
+
|
| 263 |
+
expected_titles = {
|
| 264 |
+
"Total tokens": "Total tokens per task",
|
| 265 |
+
"Cost per task": "Cost per task (USD)",
|
| 266 |
+
"Agent time per task": "Agent time per task (seconds)",
|
| 267 |
+
}
|
| 268 |
+
for metric, title in expected_titles.items():
|
| 269 |
+
figure = create_performance_vs_resource_plot(dataframe, resource_metric=metric)
|
| 270 |
+
assert isinstance(figure, go.Figure)
|
| 271 |
+
assert figure.layout.xaxis.type == "log"
|
| 272 |
+
assert figure.layout.xaxis.title.text == title
|
| 273 |
+
assert any(trace.name == "Pareto frontier" for trace in figure.data)
|
| 274 |
+
|
| 275 |
+
compatibility = create_score_vs_tokens_plot(dataframe, token_metric="Total tokens")
|
| 276 |
+
assert isinstance(compatibility, go.Figure)
|
| 277 |
|
|
|
|
|
|
|
| 278 |
|
|
|
|
|
|
|
|
|
|
| 279 |
|
| 280 |
+
def test_performance_resource_chart_rejects_multiple_benchmarks():
|
| 281 |
+
dataframe = get_analysis_df(
|
| 282 |
+
[
|
| 283 |
+
make_result(benchmark="Benchmark A"),
|
| 284 |
+
make_result(benchmark="Benchmark B", model="model-b"),
|
| 285 |
+
]
|
| 286 |
+
)
|
| 287 |
+
figure = create_performance_vs_resource_plot(dataframe, resource_metric="Total tokens")
|
| 288 |
+
|
| 289 |
+
assert len(figure.layout.annotations) == 1
|
| 290 |
+
assert "Select one benchmark" in figure.layout.annotations[0].text
|
| 291 |
+
|
| 292 |
+
def test_linear_scale_and_point_labels_still_work():
|
| 293 |
+
dataframe = get_analysis_df([make_result()])
|
| 294 |
+
figure = create_performance_vs_resource_plot(
|
| 295 |
+
dataframe,
|
| 296 |
+
resource_metric="Cost per task",
|
| 297 |
+
x_scale="Linear",
|
| 298 |
+
show_labels=True,
|
| 299 |
+
show_pareto_frontier=False,
|
| 300 |
+
)
|
| 301 |
+
|
| 302 |
+
assert figure.layout.xaxis.type == "linear"
|
| 303 |
+
assert figure.data[0].mode == "markers+text"
|
| 304 |
+
assert list(figure.data[0].text) == ["model-a / harness-a"]
|
| 305 |
|
| 306 |
+
|
| 307 |
+
def test_color_by_benchmark_is_not_supported_in_efficiency_chart():
|
| 308 |
+
dataframe = get_analysis_df([make_result()])
|
| 309 |
+
figure = create_performance_vs_resource_plot(
|
| 310 |
+
dataframe,
|
| 311 |
+
resource_metric="Total tokens",
|
| 312 |
+
color_by="Benchmark", # type: ignore[arg-type]
|
| 313 |
+
)
|
| 314 |
+
|
| 315 |
+
assert len(figure.layout.annotations) == 1
|
| 316 |
+
assert "Color dimension not available" in figure.layout.annotations[0].text
|
| 317 |
+
|
| 318 |
+
|
| 319 |
+
def test_empty_and_fully_invalid_resource_chart_data_are_graceful():
|
| 320 |
+
empty = create_performance_vs_resource_plot(pd.DataFrame(), resource_metric="Total tokens")
|
| 321 |
invalid_df = get_analysis_df([make_result(total_tokens=0)])
|
| 322 |
+
invalid = create_performance_vs_resource_plot(invalid_df, resource_metric="Total tokens")
|
| 323 |
|
| 324 |
assert isinstance(empty, go.Figure)
|
| 325 |
assert isinstance(invalid, go.Figure)
|
|
|
|
| 340 |
assert "#000000" not in COLOR_PALETTES["Citrus"]
|
| 341 |
|
| 342 |
|
| 343 |
+
def test_cost_and_efficiency_scatter_labels_toggle_consistently():
|
|
|
|
|
|
|
| 344 |
cost_df = pd.DataFrame(
|
| 345 |
{
|
| 346 |
"Benchmark": ["Benchmark A"],
|
|
|
|
| 351 |
"Label": ["model-a<br>harness-a"],
|
| 352 |
}
|
| 353 |
)
|
| 354 |
+
efficiency_df = get_analysis_df([make_result()])
|
| 355 |
|
| 356 |
cost_without = create_score_vs_cost_plot(cost_df, "Benchmark A", show_labels=False)
|
| 357 |
cost_with = create_score_vs_cost_plot(cost_df, "Benchmark A", show_labels=True)
|
| 358 |
+
efficiency_without = create_performance_vs_resource_plot(
|
| 359 |
+
efficiency_df,
|
| 360 |
+
show_labels=False,
|
| 361 |
+
show_pareto_frontier=False,
|
| 362 |
+
)
|
| 363 |
+
efficiency_with = create_performance_vs_resource_plot(
|
| 364 |
+
efficiency_df,
|
| 365 |
+
show_labels=True,
|
| 366 |
+
show_pareto_frontier=False,
|
| 367 |
+
)
|
| 368 |
|
| 369 |
assert cost_without.data[0].mode == "markers"
|
| 370 |
assert cost_with.data[0].mode == "markers+text"
|
| 371 |
+
assert efficiency_without.data[0].mode == "markers"
|
| 372 |
+
assert efficiency_with.data[0].mode == "markers+text"
|
|
|
|
|
|
|
| 373 |
assert cost_without.data[0].hovertemplate == cost_with.data[0].hovertemplate
|
| 374 |
+
assert efficiency_without.data[0].hovertemplate == efficiency_with.data[0].hovertemplate
|
|
|
|
|
|
|
|
|
|
|
|
|
| 375 |
|
| 376 |
+
def test_efficiency_figure_uses_responsive_autosizing_without_fixed_width():
|
| 377 |
+
dataframe = get_analysis_df([make_result()])
|
| 378 |
+
figure = create_performance_vs_resource_plot(
|
| 379 |
+
dataframe,
|
| 380 |
+
resource_metric="Total tokens",
|
| 381 |
+
)
|
| 382 |
|
| 383 |
+
assert figure.layout.autosize is True
|
| 384 |
+
assert figure.layout.width is None
|
| 385 |
+
assert figure.layout.height is None
|
|
|
|
| 386 |
|
| 387 |
|
| 388 |
+
def test_shared_plot_container_has_minimum_height_and_efficiency_tab_refresh():
|
| 389 |
+
app_source = (Path(__file__).parents[1] / "app.py").read_text()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 390 |
|
| 391 |
+
assert "RESPONSIVE_PLOT_MIN_HEIGHT_PX = 420" in app_source
|
| 392 |
+
assert "min-height: {RESPONSIVE_PLOT_MIN_HEIGHT_PX}px" in app_source
|
| 393 |
+
assert "efficiency_tab.select(" in app_source
|
|
|
|
|
|
|
|
|