Commit ·
43eca18
1
Parent(s): 7e19265
Point the Router Benchmark at the white paper and lock ARI at β = 0.2.
Browse files- app.py +310 -238
- export_router_run.py +37 -2
- runs/router1_2026-10-26/metadata.json +4 -3
- runs/router1_2026-10-26/router_board.csv +24 -24
app.py
CHANGED
|
@@ -48,18 +48,17 @@ def discover_available_runs() -> List[Dict]:
|
|
| 48 |
try:
|
| 49 |
with open(metadata_path, 'r') as f:
|
| 50 |
metadata = json.load(f)
|
| 51 |
-
# Router runs have their own tab. Leaving them in this list
|
| 52 |
-
# would make the model dropdown try to load summary CSVs.
|
| 53 |
-
if metadata.get("run_type") == "router":
|
| 54 |
-
continue
|
| 55 |
metadata["path"] = run_path
|
| 56 |
runs.append(metadata)
|
| 57 |
except Exception as e:
|
| 58 |
print(f"Error loading metadata for {run_dir}: {e}")
|
| 59 |
|
| 60 |
-
#
|
|
|
|
| 61 |
runs.sort(key=lambda x: x.get("date", ""), reverse=True)
|
| 62 |
-
|
|
|
|
|
|
|
| 63 |
|
| 64 |
def load_run_metadata(run_id: str) -> Dict:
|
| 65 |
"""Load metadata for a specific run."""
|
|
@@ -392,27 +391,86 @@ def create_p99_latency_plot(data: Dict[str, pd.DataFrame]) -> tuple:
|
|
| 392 |
)
|
| 393 |
return fig_p99, ""
|
| 394 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 395 |
def update_leaderboard_data(selected_run_id: str) -> tuple:
|
| 396 |
-
"""Update
|
| 397 |
if not selected_run_id:
|
| 398 |
-
# Return empty/default values for all outputs
|
| 399 |
empty_df = pd.DataFrame()
|
| 400 |
return (
|
| 401 |
-
empty_df, empty_df, "", empty_df, empty_df, empty_df, empty_df,
|
| 402 |
-
None, "", None, "", None, "",
|
| 403 |
-
"No run selected", ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 404 |
)
|
| 405 |
-
|
| 406 |
-
# Load data for selected run
|
| 407 |
data = load_run_data(selected_run_id)
|
| 408 |
-
|
| 409 |
-
|
| 410 |
if not data:
|
| 411 |
empty_df = pd.DataFrame()
|
| 412 |
return (
|
| 413 |
empty_df, empty_df, "", empty_df, empty_df, empty_df, empty_df,
|
| 414 |
None, "Error loading data", None, "Error loading data", None, "Error loading data",
|
| 415 |
-
f"Error loading run: {selected_run_id}", ""
|
|
|
|
|
|
|
| 416 |
)
|
| 417 |
|
| 418 |
# Get DataFrames
|
|
@@ -452,46 +510,18 @@ def update_leaderboard_data(selected_run_id: str) -> tuple:
|
|
| 452 |
avg_latency_plot, avg_latency_msg = create_avg_latency_plot(data)
|
| 453 |
p99_latency_plot, p99_latency_msg = create_p99_latency_plot(data)
|
| 454 |
|
| 455 |
-
# Create info message
|
| 456 |
-
info_msg = f"**Current Run:** {run_metadata.get('title', 'Unknown')} ({run_metadata.get('date', 'Unknown date')})"
|
| 457 |
-
if 'model_count' in run_metadata:
|
| 458 |
-
info_msg += f" - {run_metadata['model_count']} models"
|
| 459 |
-
|
| 460 |
-
# Get correlation text and benchmark comparison intro
|
| 461 |
correlations_text = format_correlations_text(data.get("correlations", {}))
|
| 462 |
benchmark_intro_text = format_benchmark_comparison_intro(data, run_metadata)
|
| 463 |
-
|
| 464 |
return (
|
| 465 |
overall_rank_display, benchmark_display, benchmark_intro_text, cost_display, avg_latency_display, p99_latency_display, domain_display,
|
| 466 |
cost_plot, cost_msg, avg_latency_plot, avg_latency_msg, p99_latency_plot, p99_latency_msg,
|
| 467 |
-
|
|
|
|
|
|
|
| 468 |
)
|
| 469 |
|
| 470 |
# --- Build Gradio App ---
|
| 471 |
-
def discover_router_runs() -> List[Dict]:
|
| 472 |
-
"""Router runs are excluded from the model dropdown."""
|
| 473 |
-
routers = []
|
| 474 |
-
if not os.path.exists(RUNS_DIR):
|
| 475 |
-
return routers
|
| 476 |
-
for run_dir in os.listdir(RUNS_DIR):
|
| 477 |
-
run_path = os.path.join(RUNS_DIR, run_dir)
|
| 478 |
-
metadata_path = os.path.join(run_path, "metadata.json")
|
| 479 |
-
if not os.path.isdir(run_path) or not os.path.exists(metadata_path):
|
| 480 |
-
continue
|
| 481 |
-
try:
|
| 482 |
-
with open(metadata_path, "r", encoding="utf-8") as handle:
|
| 483 |
-
metadata = json.load(handle)
|
| 484 |
-
except Exception as exc:
|
| 485 |
-
print(f"Error loading router metadata for {run_dir}: {exc}")
|
| 486 |
-
continue
|
| 487 |
-
if metadata.get("run_type") != "router":
|
| 488 |
-
continue
|
| 489 |
-
metadata["path"] = run_path
|
| 490 |
-
routers.append(metadata)
|
| 491 |
-
routers.sort(key=lambda item: item.get("date", ""), reverse=True)
|
| 492 |
-
return routers
|
| 493 |
-
|
| 494 |
-
|
| 495 |
def router_quality_cost_figure(board: pd.DataFrame):
|
| 496 |
headline = board[board["is_baseline"] == 0].copy()
|
| 497 |
headline["group"] = headline["collapsed"].map(
|
|
@@ -574,10 +604,13 @@ def router_same_pick_figure(same: pd.DataFrame, axis: List[str]):
|
|
| 574 |
|
| 575 |
|
| 576 |
def router_board_display(board: pd.DataFrame) -> pd.DataFrame:
|
| 577 |
-
view = board.sort_values(
|
|
|
|
|
|
|
| 578 |
displayed = pd.DataFrame(
|
| 579 |
{
|
| 580 |
"Pass": view["label"],
|
|
|
|
| 581 |
"Vendor": view["vendor"],
|
| 582 |
"Setting": view["setting"],
|
| 583 |
"n": view["n"],
|
|
@@ -619,7 +652,22 @@ We graded **{facts.get("commercial_routers")} commercial routers** ({facts.get("
|
|
| 619 |
- {facts.get("collapsed_passes")} of {facts.get("headline_passes")} passes collapsed, sending at least {threshold:.0f}% of questions to one model.
|
| 620 |
- Median cross-vendor same-pick is {median:g} across {facts.get("cross_vendor_pairs")} pairs.
|
| 621 |
|
| 622 |
-
The board is
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 623 |
"""
|
| 624 |
|
| 625 |
|
|
@@ -632,6 +680,24 @@ def open_blocks():
|
|
| 632 |
return gr.Blocks()
|
| 633 |
|
| 634 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 635 |
with open_blocks() as app:
|
| 636 |
gr.Markdown("# AutoBench LLM Leaderboard")
|
| 637 |
gr.Markdown(
|
|
@@ -647,209 +713,194 @@ with open_blocks() as app:
|
|
| 647 |
run_choices = [(f"{run['date']} - {run['title']}", run['run_id']) for run in available_runs]
|
| 648 |
run_selector = gr.Dropdown(
|
| 649 |
choices=run_choices,
|
| 650 |
-
value=
|
| 651 |
label="📊 Select AutoBench Run",
|
| 652 |
-
info="
|
| 653 |
)
|
| 654 |
with gr.Column(scale=2):
|
| 655 |
-
current_run_info = gr.Markdown(
|
| 656 |
-
f"**Current Run:** {latest_run['title']} ({latest_run['date']})" +
|
| 657 |
-
(f" - {latest_run['model_count']} models" if 'model_count' in latest_run else "")
|
| 658 |
-
)
|
| 659 |
|
| 660 |
gr.Markdown("---")
|
| 661 |
|
| 662 |
-
|
| 663 |
-
|
| 664 |
-
|
| 665 |
-
|
| 666 |
-
|
| 667 |
-
|
| 668 |
-
|
| 669 |
-
|
| 670 |
-
|
| 671 |
-
|
| 672 |
-
|
| 673 |
-
|
| 674 |
-
|
| 675 |
-
|
| 676 |
-
|
| 677 |
-
|
| 678 |
-
with gr.Tab("Benchmark Comparison"):
|
| 679 |
-
gr.Markdown("## Benchmark Comparison")
|
| 680 |
-
benchmark_comparison_intro = gr.Markdown(
|
| 681 |
-
value=format_benchmark_comparison_intro(current_data, latest_run)
|
| 682 |
-
)
|
| 683 |
-
|
| 684 |
-
benchmark_comparison_table = gr.DataFrame(
|
| 685 |
-
current_data.get("benchmark_display", pd.DataFrame()),
|
| 686 |
-
interactive=True,
|
| 687 |
-
label="Benchmark Comparison"
|
| 688 |
-
)
|
| 689 |
-
|
| 690 |
-
# --- Tab 3: Performance Plots ---
|
| 691 |
-
with gr.Tab("Performance Plots"):
|
| 692 |
-
gr.Markdown("## Performance Visualizations")
|
| 693 |
-
gr.Markdown("Exploring relationships between AutoBench Rank, Latency, and Cost.")
|
| 694 |
-
|
| 695 |
-
# Scatter Plot 1: Cost vs Rank
|
| 696 |
-
gr.Markdown("### Rank vs. Average Cost")
|
| 697 |
-
initial_cost_plot, initial_cost_msg = create_cost_scatter_plot(current_data)
|
| 698 |
-
cost_plot = gr.Plot(value=initial_cost_plot)
|
| 699 |
-
cost_plot_msg = gr.Markdown(value=initial_cost_msg)
|
| 700 |
-
|
| 701 |
-
# Plot 2: Rank vs Average Latency
|
| 702 |
-
gr.Markdown("### Rank vs. Average Latency")
|
| 703 |
-
initial_avg_latency_plot, initial_avg_latency_msg = create_avg_latency_plot(current_data)
|
| 704 |
-
avg_latency_plot = gr.Plot(value=initial_avg_latency_plot)
|
| 705 |
-
avg_latency_plot_msg = gr.Markdown(value=initial_avg_latency_msg)
|
| 706 |
-
|
| 707 |
-
# Plot 3: Rank vs P99 Latency
|
| 708 |
-
gr.Markdown("### Rank vs. P99 Latency")
|
| 709 |
-
initial_p99_latency_plot, initial_p99_latency_msg = create_p99_latency_plot(current_data)
|
| 710 |
-
p99_latency_plot = gr.Plot(value=initial_p99_latency_plot)
|
| 711 |
-
p99_latency_plot_msg = gr.Markdown(value=initial_p99_latency_msg)
|
| 712 |
-
|
| 713 |
-
# --- Tab 4: Cost & Latency Analysis ---
|
| 714 |
-
with gr.Tab("Cost & Latency Analysis"):
|
| 715 |
-
gr.Markdown("## Performance vs. Cost/Latency Trade-offs")
|
| 716 |
-
|
| 717 |
-
# Cost Breakdown Table
|
| 718 |
-
gr.Markdown("### Cost Breakdown per Domain ($ Cents/Response)")
|
| 719 |
-
cost_df = current_data.get("cost", pd.DataFrame())
|
| 720 |
-
if not cost_df.empty and 'model_name' in cost_df.columns:
|
| 721 |
-
cols = ['model_name'] + [col for col in cost_df.columns if col != 'model_name']
|
| 722 |
-
initial_cost_display = cost_df[cols]
|
| 723 |
-
else:
|
| 724 |
-
initial_cost_display = cost_df
|
| 725 |
-
cost_breakdown_table = gr.DataFrame(
|
| 726 |
-
value=initial_cost_display,
|
| 727 |
-
interactive=True,
|
| 728 |
-
label="Cost Breakdown"
|
| 729 |
-
)
|
| 730 |
-
|
| 731 |
-
# Latency Breakdown Tables
|
| 732 |
-
gr.Markdown("### Average Latency Breakdown per Domain (Seconds)")
|
| 733 |
-
avg_latency_df = current_data.get("avg_latency", pd.DataFrame())
|
| 734 |
-
if not avg_latency_df.empty and 'model_name' in avg_latency_df.columns:
|
| 735 |
-
cols = ['model_name'] + [col for col in avg_latency_df.columns if col != 'model_name']
|
| 736 |
-
initial_avg_latency_display = avg_latency_df[cols]
|
| 737 |
-
else:
|
| 738 |
-
initial_avg_latency_display = avg_latency_df
|
| 739 |
-
avg_latency_breakdown_table = gr.DataFrame(
|
| 740 |
-
value=initial_avg_latency_display,
|
| 741 |
-
interactive=True,
|
| 742 |
-
label="Average Latency Breakdown"
|
| 743 |
-
)
|
| 744 |
-
|
| 745 |
-
gr.Markdown("### P99 Latency Breakdown per Domain (Seconds)")
|
| 746 |
-
p99_latency_df = current_data.get("p99_latency", pd.DataFrame())
|
| 747 |
-
if not p99_latency_df.empty and 'model_name' in p99_latency_df.columns:
|
| 748 |
-
cols = ['model_name'] + [col for col in p99_latency_df.columns if col != 'model_name']
|
| 749 |
-
initial_p99_latency_display = p99_latency_df[cols]
|
| 750 |
-
else:
|
| 751 |
-
initial_p99_latency_display = p99_latency_df
|
| 752 |
-
p99_latency_breakdown_table = gr.DataFrame(
|
| 753 |
-
value=initial_p99_latency_display,
|
| 754 |
-
interactive=True,
|
| 755 |
-
label="P99 Latency Breakdown"
|
| 756 |
-
)
|
| 757 |
-
|
| 758 |
-
|
| 759 |
-
# --- Tab 5: Domain Performance ---
|
| 760 |
-
with gr.Tab("Domain Performance"):
|
| 761 |
-
gr.Markdown("## Performance Across Different Domains")
|
| 762 |
-
gr.Markdown("Model ranks within specific knowledge or task areas. Higher is better.")
|
| 763 |
-
|
| 764 |
-
domain_df = current_data.get("domain", pd.DataFrame())
|
| 765 |
-
if not domain_df.empty and 'Model Name' in domain_df.columns:
|
| 766 |
-
cols = ['Model Name'] + [col for col in domain_df.columns if col != 'Model Name']
|
| 767 |
-
initial_domain_display = domain_df[cols]
|
| 768 |
-
else:
|
| 769 |
-
initial_domain_display = domain_df
|
| 770 |
-
domain_performance_table = gr.DataFrame(
|
| 771 |
-
value=initial_domain_display,
|
| 772 |
-
interactive=True,
|
| 773 |
-
label="Domain Performance"
|
| 774 |
-
)
|
| 775 |
|
| 776 |
-
|
| 777 |
-
|
| 778 |
-
|
| 779 |
-
|
| 780 |
-
|
| 781 |
-
else:
|
| 782 |
-
router_meta = router_runs[0]
|
| 783 |
-
router_board = pd.read_csv(os.path.join(router_meta["path"], "router_board.csv"))
|
| 784 |
-
router_same = pd.read_csv(os.path.join(router_meta["path"], "same_pick.csv"))
|
| 785 |
-
gr.Markdown(router_intro_markdown(router_meta))
|
| 786 |
-
gr.Markdown("### Board")
|
| 787 |
-
gr.DataFrame(value=router_board_display(router_board), interactive=False)
|
| 788 |
-
gr.Markdown(
|
| 789 |
-
"### Quality versus cost\n\n"
|
| 790 |
-
"Dashed lines are Always-Sol, Always-Luna, and Random. "
|
| 791 |
-
"Amber points collapsed onto one model."
|
| 792 |
-
)
|
| 793 |
-
gr.Plot(value=router_quality_cost_figure(router_board))
|
| 794 |
-
gr.Markdown("### Top-model share\n\nPasses at or above 90% are collapsed.")
|
| 795 |
-
gr.Plot(value=router_collapse_figure(router_board))
|
| 796 |
-
gr.Markdown(
|
| 797 |
-
"### Same-pick\n\n"
|
| 798 |
-
"Share of questions on which two passes named the same model. "
|
| 799 |
-
"This grid is the 12-pass comparison from the report. "
|
| 800 |
-
"The median of 0 is across the full set of cross-vendor pairs, not this grid alone."
|
| 801 |
-
)
|
| 802 |
-
gr.Plot(
|
| 803 |
-
value=router_same_pick_figure(
|
| 804 |
-
router_same, router_meta.get("same_pick_axis", [])
|
| 805 |
)
|
| 806 |
-
)
|
| 807 |
-
gr.Markdown(
|
| 808 |
-
f"""
|
| 809 |
-
### Methodology
|
| 810 |
-
- The answer matrix is frozen: {router_meta.get("questions")} questions, {router_meta.get("matrix_models")} models, dated {router_meta.get("matrix_date")}.
|
| 811 |
-
- Scoring rules were locked before any router was called. Each router choice is scored by lookup on that matrix. Routers are not re-judged.
|
| 812 |
-
- **Accuracy** is the share of questions where the chosen model is statistically as good as the best available answer.
|
| 813 |
-
- A pass is collapsed when its top model receives at least 90% of questions.
|
| 814 |
-
- Same-pick is the share of questions on which two passes named the same model. The published median is the median across cross-vendor pairs.
|
| 815 |
-
- [Read the report]({router_meta.get("report_url", "")})
|
| 816 |
-
"""
|
| 817 |
-
)
|
| 818 |
|
| 819 |
-
|
| 820 |
-
|
| 821 |
-
|
| 822 |
-
|
|
|
|
| 823 |
|
| 824 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 825 |
|
| 826 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 827 |
|
| 828 |
-
|
| 829 |
-
|
| 830 |
-
|
| 831 |
-
|
| 832 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 833 |
|
| 834 |
-
### Metrics
|
| 835 |
-
* **AutoBench Score (AB):** The average rank received by a model's responses across all questions/domains (higher is better).
|
| 836 |
-
* **Avg Cost (USD Cents/response):** Estimated average cost to generate one response based on model provider pricing (input+output tokens). Lower is better.
|
| 837 |
-
* **Avg Latency (s):** Average time taken by the model to generate a response. Lower is better.
|
| 838 |
-
* **P99 Latency (s):** The 99th percentile of response time, indicating worst-case latency. Lower is better.
|
| 839 |
-
* **Chatbot Arena / Artificial Analysis Intelligence Index / MMLU:** Scores from other well-known benchmarks for comparison (where available).
|
| 840 |
|
| 841 |
-
|
| 842 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 843 |
|
| 844 |
-
|
| 845 |
-
|
| 846 |
-
|
| 847 |
-
|
| 848 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 849 |
|
| 850 |
-
**Disclaimer:** Benchmark results provide one perspective on model capabilities. Performance can vary based on specific tasks, prompts, and API conditions. Costs are estimates and subject to change by providers. Latency depends on server load and geographic location.
|
| 851 |
-
""")
|
| 852 |
-
|
| 853 |
# --- Event Handlers ---
|
| 854 |
# Set up reactive data loading when run selection changes
|
| 855 |
run_selector.change(
|
|
@@ -870,7 +921,28 @@ with open_blocks() as app:
|
|
| 870 |
p99_latency_plot,
|
| 871 |
p99_latency_plot_msg,
|
| 872 |
current_run_info,
|
| 873 |
-
correlations_display
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 874 |
]
|
| 875 |
)
|
| 876 |
|
|
|
|
| 48 |
try:
|
| 49 |
with open(metadata_path, 'r') as f:
|
| 50 |
metadata = json.load(f)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
metadata["path"] = run_path
|
| 52 |
runs.append(metadata)
|
| 53 |
except Exception as e:
|
| 54 |
print(f"Error loading metadata for {run_dir}: {e}")
|
| 55 |
|
| 56 |
+
# Model runs stay newest-first. Router benchmarks are separate runs and
|
| 57 |
+
# are listed after every model run, so they are never the default.
|
| 58 |
runs.sort(key=lambda x: x.get("date", ""), reverse=True)
|
| 59 |
+
model_runs = [run for run in runs if run.get("run_type") != "router"]
|
| 60 |
+
router_runs = [run for run in runs if run.get("run_type") == "router"]
|
| 61 |
+
return model_runs + router_runs
|
| 62 |
|
| 63 |
def load_run_metadata(run_id: str) -> Dict:
|
| 64 |
"""Load metadata for a specific run."""
|
|
|
|
| 391 |
)
|
| 392 |
return fig_p99, ""
|
| 393 |
|
| 394 |
+
def benchmark_visibility(show_router: bool) -> tuple:
|
| 395 |
+
"""Show either the model tabs or the router tabs.
|
| 396 |
+
|
| 397 |
+
Hiding the wrapping column is not enough in Gradio 6: a column that
|
| 398 |
+
contains tabs stays on screen. Each tab is toggled as well.
|
| 399 |
+
"""
|
| 400 |
+
model_on = not show_router
|
| 401 |
+
return (
|
| 402 |
+
gr.update(visible=model_on),
|
| 403 |
+
gr.update(visible=show_router),
|
| 404 |
+
gr.update(visible=model_on),
|
| 405 |
+
gr.update(visible=show_router),
|
| 406 |
+
*(gr.update(visible=model_on) for _ in range(6)),
|
| 407 |
+
*(gr.update(visible=show_router) for _ in range(5)),
|
| 408 |
+
)
|
| 409 |
+
|
| 410 |
+
|
| 411 |
+
def format_run_info(run_metadata: Dict) -> str:
|
| 412 |
+
"""One-line description of the run currently selected in the dropdown."""
|
| 413 |
+
info = (
|
| 414 |
+
f"**Current Run:** {run_metadata.get('title', 'Unknown')} "
|
| 415 |
+
f"({run_metadata.get('date', 'Unknown date')})"
|
| 416 |
+
)
|
| 417 |
+
if run_metadata.get("run_type") == "router":
|
| 418 |
+
routers = run_metadata.get("facts", {}).get("commercial_routers")
|
| 419 |
+
if routers:
|
| 420 |
+
info += f" - {routers} commercial routers"
|
| 421 |
+
return info
|
| 422 |
+
if "model_count" in run_metadata:
|
| 423 |
+
info += f" - {run_metadata['model_count']} models"
|
| 424 |
+
return info
|
| 425 |
+
|
| 426 |
+
|
| 427 |
+
def load_router_views(run_metadata: Dict) -> Dict:
|
| 428 |
+
"""Tables and figures for one router-benchmark run."""
|
| 429 |
+
board = pd.read_csv(os.path.join(run_metadata["path"], "router_board.csv"))
|
| 430 |
+
same = pd.read_csv(os.path.join(run_metadata["path"], "same_pick.csv"))
|
| 431 |
+
return {
|
| 432 |
+
"intro": router_intro_markdown(run_metadata),
|
| 433 |
+
"board": router_board_display(board),
|
| 434 |
+
"quality": router_quality_cost_figure(board),
|
| 435 |
+
"collapse": router_collapse_figure(board),
|
| 436 |
+
"same": router_same_pick_figure(same, run_metadata.get("same_pick_axis", [])),
|
| 437 |
+
"method": router_method_markdown(run_metadata),
|
| 438 |
+
}
|
| 439 |
+
|
| 440 |
+
|
| 441 |
def update_leaderboard_data(selected_run_id: str) -> tuple:
|
| 442 |
+
"""Update the visible benchmark when the run selection changes."""
|
| 443 |
if not selected_run_id:
|
|
|
|
| 444 |
empty_df = pd.DataFrame()
|
| 445 |
return (
|
| 446 |
+
empty_df, empty_df, "", empty_df, empty_df, empty_df, empty_df,
|
| 447 |
+
None, "", None, "", None, "",
|
| 448 |
+
"No run selected", "",
|
| 449 |
+
*benchmark_visibility(False),
|
| 450 |
+
gr.skip(), gr.skip(), gr.skip(), gr.skip(), gr.skip(), gr.skip(),
|
| 451 |
+
)
|
| 452 |
+
|
| 453 |
+
run_metadata = load_run_metadata(selected_run_id)
|
| 454 |
+
if run_metadata.get("run_type") == "router":
|
| 455 |
+
views = load_router_views(run_metadata)
|
| 456 |
+
return (
|
| 457 |
+
gr.skip(), gr.skip(), gr.skip(), gr.skip(), gr.skip(), gr.skip(), gr.skip(),
|
| 458 |
+
gr.skip(), gr.skip(), gr.skip(), gr.skip(), gr.skip(), gr.skip(),
|
| 459 |
+
format_run_info(run_metadata), gr.skip(),
|
| 460 |
+
*benchmark_visibility(True),
|
| 461 |
+
views["intro"], views["board"], views["quality"], views["collapse"], views["same"], views["method"],
|
| 462 |
)
|
| 463 |
+
|
|
|
|
| 464 |
data = load_run_data(selected_run_id)
|
| 465 |
+
|
|
|
|
| 466 |
if not data:
|
| 467 |
empty_df = pd.DataFrame()
|
| 468 |
return (
|
| 469 |
empty_df, empty_df, "", empty_df, empty_df, empty_df, empty_df,
|
| 470 |
None, "Error loading data", None, "Error loading data", None, "Error loading data",
|
| 471 |
+
f"Error loading run: {selected_run_id}", "",
|
| 472 |
+
*benchmark_visibility(False),
|
| 473 |
+
gr.skip(), gr.skip(), gr.skip(), gr.skip(), gr.skip(), gr.skip(),
|
| 474 |
)
|
| 475 |
|
| 476 |
# Get DataFrames
|
|
|
|
| 510 |
avg_latency_plot, avg_latency_msg = create_avg_latency_plot(data)
|
| 511 |
p99_latency_plot, p99_latency_msg = create_p99_latency_plot(data)
|
| 512 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 513 |
correlations_text = format_correlations_text(data.get("correlations", {}))
|
| 514 |
benchmark_intro_text = format_benchmark_comparison_intro(data, run_metadata)
|
| 515 |
+
|
| 516 |
return (
|
| 517 |
overall_rank_display, benchmark_display, benchmark_intro_text, cost_display, avg_latency_display, p99_latency_display, domain_display,
|
| 518 |
cost_plot, cost_msg, avg_latency_plot, avg_latency_msg, p99_latency_plot, p99_latency_msg,
|
| 519 |
+
format_run_info(run_metadata), correlations_text,
|
| 520 |
+
*benchmark_visibility(False),
|
| 521 |
+
gr.skip(), gr.skip(), gr.skip(), gr.skip(), gr.skip(), gr.skip(),
|
| 522 |
)
|
| 523 |
|
| 524 |
# --- Build Gradio App ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 525 |
def router_quality_cost_figure(board: pd.DataFrame):
|
| 526 |
headline = board[board["is_baseline"] == 0].copy()
|
| 527 |
headline["group"] = headline["collapsed"].map(
|
|
|
|
| 604 |
|
| 605 |
|
| 606 |
def router_board_display(board: pd.DataFrame) -> pd.DataFrame:
|
| 607 |
+
view = board.sort_values(
|
| 608 |
+
["ari", "cost", "display_order"], ascending=[False, True, True]
|
| 609 |
+
).copy()
|
| 610 |
displayed = pd.DataFrame(
|
| 611 |
{
|
| 612 |
"Pass": view["label"],
|
| 613 |
+
"ARI": view["ari"].map(lambda value: f"{value:.1f}"),
|
| 614 |
"Vendor": view["vendor"],
|
| 615 |
"Setting": view["setting"],
|
| 616 |
"n": view["n"],
|
|
|
|
| 652 |
- {facts.get("collapsed_passes")} of {facts.get("headline_passes")} passes collapsed, sending at least {threshold:.0f}% of questions to one model.
|
| 653 |
- Median cross-vendor same-pick is {median:g} across {facts.get("cross_vendor_pairs")} pairs.
|
| 654 |
|
| 655 |
+
The board is ordered by **ARI** (AutoBench Router Index) at β = 0.2, highest first. ARI runs from 0 to 100 and is higher when a pass is both accurate and cheap. A pass no better than random scores 0. Quality is the share of the gap from the random floor to a perfect answer. Cost is a log score between $0.00001 and $1 per question. β = 0.2 is the weight on cost. Each row shows how many questions that pass actually answered (`n`). Read the report: [{metadata.get("report_url", "")}]({metadata.get("report_url", "")}).
|
| 656 |
+
"""
|
| 657 |
+
|
| 658 |
+
|
| 659 |
+
def router_method_markdown(metadata: Dict) -> str:
|
| 660 |
+
facts = metadata.get("facts", {})
|
| 661 |
+
return f"""
|
| 662 |
+
## Methodology
|
| 663 |
+
|
| 664 |
+
- The answer matrix is frozen: {facts.get("questions", metadata.get("questions"))} questions, {facts.get("matrix_models", metadata.get("matrix_models"))} models, dated {facts.get("matrix_date", metadata.get("matrix_date"))}.
|
| 665 |
+
- Scoring rules were locked before any router was called. Each router choice is scored by lookup on that matrix. Routers are not re-judged.
|
| 666 |
+
- **Accuracy** is the share of questions where the chosen model is statistically as good as the best available answer.
|
| 667 |
+
- **ARI** (AutoBench Router Index) ranks the board at β = 0.2. It is a 0–100 weighted harmonic mean of quality and cost. Quality is the share of the gap from the random floor (64.7% on this matrix) to a perfect answer. Cost is a log score between the fixed anchors $0.00001 and $1 per question. A pass at or below the random floor scores 0.
|
| 668 |
+
- A pass is collapsed when its top model receives at least 90% of questions.
|
| 669 |
+
- Same-pick is the share of questions on which two passes named the same model. The published median is the median across cross-vendor pairs. The same-pick tab shows the 12-pass grid from the report, which is not that full pair set.
|
| 670 |
+
- [Read the report]({metadata.get("report_url", "")})
|
| 671 |
"""
|
| 672 |
|
| 673 |
|
|
|
|
| 680 |
return gr.Blocks()
|
| 681 |
|
| 682 |
|
| 683 |
+
router_catalog = [run for run in available_runs if run.get("run_type") == "router"]
|
| 684 |
+
if router_catalog:
|
| 685 |
+
initial_router = load_router_views(router_catalog[0])
|
| 686 |
+
opening_run = router_catalog[0]
|
| 687 |
+
else:
|
| 688 |
+
initial_router = {
|
| 689 |
+
"intro": "No router benchmark run is in this checkout.",
|
| 690 |
+
"board": pd.DataFrame(),
|
| 691 |
+
"quality": None,
|
| 692 |
+
"collapse": None,
|
| 693 |
+
"same": None,
|
| 694 |
+
"method": "",
|
| 695 |
+
}
|
| 696 |
+
opening_run = latest_run
|
| 697 |
+
opens_on_router = opening_run.get("run_type") == "router"
|
| 698 |
+
model_visible = not opens_on_router
|
| 699 |
+
print(f"Opening on: {opening_run['title']}")
|
| 700 |
+
|
| 701 |
with open_blocks() as app:
|
| 702 |
gr.Markdown("# AutoBench LLM Leaderboard")
|
| 703 |
gr.Markdown(
|
|
|
|
| 713 |
run_choices = [(f"{run['date']} - {run['title']}", run['run_id']) for run in available_runs]
|
| 714 |
run_selector = gr.Dropdown(
|
| 715 |
choices=run_choices,
|
| 716 |
+
value=opening_run["run_id"],
|
| 717 |
label="📊 Select AutoBench Run",
|
| 718 |
+
info="Opens on the Router Benchmark. Model runs are listed above it."
|
| 719 |
)
|
| 720 |
with gr.Column(scale=2):
|
| 721 |
+
current_run_info = gr.Markdown(format_run_info(opening_run))
|
|
|
|
|
|
|
|
|
|
| 722 |
|
| 723 |
gr.Markdown("---")
|
| 724 |
|
| 725 |
+
with gr.Column(visible=model_visible) as model_view:
|
| 726 |
+
with gr.Tabs(visible=model_visible) as model_tabs:
|
| 727 |
+
# --- Tab 1: Overall Ranking ---
|
| 728 |
+
with gr.Tab("Overall Ranking", visible=model_visible) as tab_overall:
|
| 729 |
+
gr.Markdown("## Overall Model Performance")
|
| 730 |
+
gr.Markdown("Models ranked by AutoBench score. Lower cost ($ Cents), latency (s), and fail rate (%) are better. Iterations shows the number of evaluations per model.")
|
| 731 |
+
|
| 732 |
+
# Add correlations display
|
| 733 |
+
initial_correlations = format_correlations_text(current_data.get("correlations", {}))
|
| 734 |
+
correlations_display = gr.Markdown(value=initial_correlations)
|
| 735 |
+
|
| 736 |
+
overall_ranking_table = gr.DataFrame(
|
| 737 |
+
current_data.get("summary_display", pd.DataFrame()).copy().rename(columns={'AutoBench': 'Rank'}) if 'AutoBench' in current_data.get("summary_display", pd.DataFrame()).columns else current_data.get("summary_display", pd.DataFrame()),
|
| 738 |
+
interactive=True,
|
| 739 |
+
label="Overall Rankings"
|
| 740 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 741 |
|
| 742 |
+
# --- Tab 2: Benchmark Comparison ---
|
| 743 |
+
with gr.Tab("Benchmark Comparison", visible=model_visible) as tab_compare:
|
| 744 |
+
gr.Markdown("## Benchmark Comparison")
|
| 745 |
+
benchmark_comparison_intro = gr.Markdown(
|
| 746 |
+
value=format_benchmark_comparison_intro(current_data, latest_run)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 747 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 748 |
|
| 749 |
+
benchmark_comparison_table = gr.DataFrame(
|
| 750 |
+
current_data.get("benchmark_display", pd.DataFrame()),
|
| 751 |
+
interactive=True,
|
| 752 |
+
label="Benchmark Comparison"
|
| 753 |
+
)
|
| 754 |
|
| 755 |
+
# --- Tab 3: Performance Plots ---
|
| 756 |
+
with gr.Tab("Performance Plots", visible=model_visible) as tab_plots:
|
| 757 |
+
gr.Markdown("## Performance Visualizations")
|
| 758 |
+
gr.Markdown("Exploring relationships between AutoBench Rank, Latency, and Cost.")
|
| 759 |
+
|
| 760 |
+
# Scatter Plot 1: Cost vs Rank
|
| 761 |
+
gr.Markdown("### Rank vs. Average Cost")
|
| 762 |
+
initial_cost_plot, initial_cost_msg = create_cost_scatter_plot(current_data)
|
| 763 |
+
cost_plot = gr.Plot(value=initial_cost_plot)
|
| 764 |
+
cost_plot_msg = gr.Markdown(value=initial_cost_msg)
|
| 765 |
+
|
| 766 |
+
# Plot 2: Rank vs Average Latency
|
| 767 |
+
gr.Markdown("### Rank vs. Average Latency")
|
| 768 |
+
initial_avg_latency_plot, initial_avg_latency_msg = create_avg_latency_plot(current_data)
|
| 769 |
+
avg_latency_plot = gr.Plot(value=initial_avg_latency_plot)
|
| 770 |
+
avg_latency_plot_msg = gr.Markdown(value=initial_avg_latency_msg)
|
| 771 |
+
|
| 772 |
+
# Plot 3: Rank vs P99 Latency
|
| 773 |
+
gr.Markdown("### Rank vs. P99 Latency")
|
| 774 |
+
initial_p99_latency_plot, initial_p99_latency_msg = create_p99_latency_plot(current_data)
|
| 775 |
+
p99_latency_plot = gr.Plot(value=initial_p99_latency_plot)
|
| 776 |
+
p99_latency_plot_msg = gr.Markdown(value=initial_p99_latency_msg)
|
| 777 |
+
|
| 778 |
+
# --- Tab 4: Cost & Latency Analysis ---
|
| 779 |
+
with gr.Tab("Cost & Latency Analysis", visible=model_visible) as tab_cost:
|
| 780 |
+
gr.Markdown("## Performance vs. Cost/Latency Trade-offs")
|
| 781 |
+
|
| 782 |
+
# Cost Breakdown Table
|
| 783 |
+
gr.Markdown("### Cost Breakdown per Domain ($ Cents/Response)")
|
| 784 |
+
cost_df = current_data.get("cost", pd.DataFrame())
|
| 785 |
+
if not cost_df.empty and 'model_name' in cost_df.columns:
|
| 786 |
+
cols = ['model_name'] + [col for col in cost_df.columns if col != 'model_name']
|
| 787 |
+
initial_cost_display = cost_df[cols]
|
| 788 |
+
else:
|
| 789 |
+
initial_cost_display = cost_df
|
| 790 |
+
cost_breakdown_table = gr.DataFrame(
|
| 791 |
+
value=initial_cost_display,
|
| 792 |
+
interactive=True,
|
| 793 |
+
label="Cost Breakdown"
|
| 794 |
+
)
|
| 795 |
|
| 796 |
+
# Latency Breakdown Tables
|
| 797 |
+
gr.Markdown("### Average Latency Breakdown per Domain (Seconds)")
|
| 798 |
+
avg_latency_df = current_data.get("avg_latency", pd.DataFrame())
|
| 799 |
+
if not avg_latency_df.empty and 'model_name' in avg_latency_df.columns:
|
| 800 |
+
cols = ['model_name'] + [col for col in avg_latency_df.columns if col != 'model_name']
|
| 801 |
+
initial_avg_latency_display = avg_latency_df[cols]
|
| 802 |
+
else:
|
| 803 |
+
initial_avg_latency_display = avg_latency_df
|
| 804 |
+
avg_latency_breakdown_table = gr.DataFrame(
|
| 805 |
+
value=initial_avg_latency_display,
|
| 806 |
+
interactive=True,
|
| 807 |
+
label="Average Latency Breakdown"
|
| 808 |
+
)
|
| 809 |
|
| 810 |
+
gr.Markdown("### P99 Latency Breakdown per Domain (Seconds)")
|
| 811 |
+
p99_latency_df = current_data.get("p99_latency", pd.DataFrame())
|
| 812 |
+
if not p99_latency_df.empty and 'model_name' in p99_latency_df.columns:
|
| 813 |
+
cols = ['model_name'] + [col for col in p99_latency_df.columns if col != 'model_name']
|
| 814 |
+
initial_p99_latency_display = p99_latency_df[cols]
|
| 815 |
+
else:
|
| 816 |
+
initial_p99_latency_display = p99_latency_df
|
| 817 |
+
p99_latency_breakdown_table = gr.DataFrame(
|
| 818 |
+
value=initial_p99_latency_display,
|
| 819 |
+
interactive=True,
|
| 820 |
+
label="P99 Latency Breakdown"
|
| 821 |
+
)
|
| 822 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 823 |
|
| 824 |
+
# --- Tab 5: Domain Performance ---
|
| 825 |
+
with gr.Tab("Domain Performance", visible=model_visible) as tab_domain:
|
| 826 |
+
gr.Markdown("## Performance Across Different Domains")
|
| 827 |
+
gr.Markdown("Model ranks within specific knowledge or task areas. Higher is better.")
|
| 828 |
+
|
| 829 |
+
domain_df = current_data.get("domain", pd.DataFrame())
|
| 830 |
+
if not domain_df.empty and 'Model Name' in domain_df.columns:
|
| 831 |
+
cols = ['Model Name'] + [col for col in domain_df.columns if col != 'Model Name']
|
| 832 |
+
initial_domain_display = domain_df[cols]
|
| 833 |
+
else:
|
| 834 |
+
initial_domain_display = domain_df
|
| 835 |
+
domain_performance_table = gr.DataFrame(
|
| 836 |
+
value=initial_domain_display,
|
| 837 |
+
interactive=True,
|
| 838 |
+
label="Domain Performance"
|
| 839 |
+
)
|
| 840 |
|
| 841 |
+
# --- Tab 5: About ---
|
| 842 |
+
with gr.Tab("About AutoBench", visible=model_visible) as tab_about:
|
| 843 |
+
gr.Markdown("""
|
| 844 |
+
## About AutoBench
|
| 845 |
+
|
| 846 |
+
AutoBench is the evaluation engine for the agentic era. A committee of language models writes questions, answers them, and scores the answers, so quality, cost, and speed are measured together. The same evidence grades models and grades routers. Choose the Router Benchmark from the run dropdown; it is listed last and opens its own tabs.
|
| 847 |
+
|
| 848 |
+
We don't build base models. We measure them. Neutrality is the product.
|
| 849 |
+
|
| 850 |
+
### Methodology
|
| 851 |
+
1. **Question Generation:** High-quality questions across various domains (Coding, History, Science, etc.) are generated by selected LLMs.
|
| 852 |
+
2. **Response Generation:** The models being benchmarked generate answers to these questions.
|
| 853 |
+
3. **Ranking:** Ranking LLMs rank the responses from different models for each question, on a 1-5 scale.
|
| 854 |
+
4. **Aggregation:** Scores are averaged across multiple questions and domains to produce the final AutoBench rank.
|
| 855 |
+
|
| 856 |
+
### Metrics
|
| 857 |
+
* **AutoBench Score (AB):** The average rank received by a model's responses across all questions/domains (higher is better).
|
| 858 |
+
* **Avg Cost (USD Cents/response):** Estimated average cost to generate one response based on model provider pricing (input+output tokens). Lower is better.
|
| 859 |
+
* **Avg Latency (s):** Average time taken by the model to generate a response. Lower is better.
|
| 860 |
+
* **P99 Latency (s):** The 99th percentile of response time, indicating worst-case latency. Lower is better.
|
| 861 |
+
* **Chatbot Arena / Artificial Analysis Intelligence Index / MMLU:** Scores from other well-known benchmarks for comparison (where available).
|
| 862 |
+
|
| 863 |
+
### Data
|
| 864 |
+
The space opens on the Router Benchmark. Model runs are listed above it in the dropdown and keep these tabs. Routers are scored by lookup on a frozen answer matrix: the rules were locked before any router was called.
|
| 865 |
+
|
| 866 |
+
### Links
|
| 867 |
+
* [Router Benchmark report](https://autobench.org/reports/do-llm-routers-pay-for-themselves)
|
| 868 |
+
* [AutoBench Run 2 Results](https://huggingface.co/blog/PeterKruger/autobench-2nd-run)
|
| 869 |
+
* [AutoBench Blog Post](https://huggingface.co/blog/PeterKruger/autobench)
|
| 870 |
+
* [Autobench Repositories](https://huggingface.co/AutoBench)
|
| 871 |
+
|
| 872 |
+
**Disclaimer:** Benchmark results provide one perspective on model capabilities. Performance can vary based on specific tasks, prompts, and API conditions. Costs are estimates and subject to change by providers. Latency depends on server load and geographic location.
|
| 873 |
+
""")
|
| 874 |
+
|
| 875 |
+
|
| 876 |
+
with gr.Column(visible=opens_on_router) as router_view:
|
| 877 |
+
with gr.Tabs(visible=opens_on_router) as router_tabs:
|
| 878 |
+
with gr.Tab("Board", visible=opens_on_router) as tab_board:
|
| 879 |
+
router_intro = gr.Markdown(value=initial_router["intro"])
|
| 880 |
+
router_board_table = gr.DataFrame(
|
| 881 |
+
value=initial_router["board"],
|
| 882 |
+
interactive=True,
|
| 883 |
+
label="Router board",
|
| 884 |
+
)
|
| 885 |
+
with gr.Tab("Quality versus cost", visible=opens_on_router) as tab_quality:
|
| 886 |
+
gr.Markdown(
|
| 887 |
+
"Dashed lines are Always-Sol, Always-Luna, and Random. "
|
| 888 |
+
"Amber points collapsed onto one model."
|
| 889 |
+
)
|
| 890 |
+
router_quality_plot = gr.Plot(value=initial_router["quality"])
|
| 891 |
+
with gr.Tab("Top-model share", visible=opens_on_router) as tab_share:
|
| 892 |
+
gr.Markdown("Passes at or above 90% are collapsed.")
|
| 893 |
+
router_collapse_plot = gr.Plot(value=initial_router["collapse"])
|
| 894 |
+
with gr.Tab("Same-pick", visible=opens_on_router) as tab_same:
|
| 895 |
+
gr.Markdown(
|
| 896 |
+
"Share of questions on which two passes named the same model. "
|
| 897 |
+
"This grid is the 12-pass comparison from the report. "
|
| 898 |
+
"The median of 0 is across the full set of cross-vendor pairs, not this grid alone."
|
| 899 |
+
)
|
| 900 |
+
router_same_plot = gr.Plot(value=initial_router["same"])
|
| 901 |
+
with gr.Tab("Methodology", visible=opens_on_router) as tab_method:
|
| 902 |
+
router_method = gr.Markdown(value=initial_router["method"])
|
| 903 |
|
|
|
|
|
|
|
|
|
|
| 904 |
# --- Event Handlers ---
|
| 905 |
# Set up reactive data loading when run selection changes
|
| 906 |
run_selector.change(
|
|
|
|
| 921 |
p99_latency_plot,
|
| 922 |
p99_latency_plot_msg,
|
| 923 |
current_run_info,
|
| 924 |
+
correlations_display,
|
| 925 |
+
model_view,
|
| 926 |
+
router_view,
|
| 927 |
+
model_tabs,
|
| 928 |
+
router_tabs,
|
| 929 |
+
tab_overall,
|
| 930 |
+
tab_compare,
|
| 931 |
+
tab_plots,
|
| 932 |
+
tab_cost,
|
| 933 |
+
tab_domain,
|
| 934 |
+
tab_about,
|
| 935 |
+
tab_board,
|
| 936 |
+
tab_quality,
|
| 937 |
+
tab_share,
|
| 938 |
+
tab_same,
|
| 939 |
+
tab_method,
|
| 940 |
+
router_intro,
|
| 941 |
+
router_board_table,
|
| 942 |
+
router_quality_plot,
|
| 943 |
+
router_collapse_plot,
|
| 944 |
+
router_same_plot,
|
| 945 |
+
router_method,
|
| 946 |
]
|
| 947 |
)
|
| 948 |
|
export_router_run.py
CHANGED
|
@@ -13,7 +13,9 @@ from __future__ import annotations
|
|
| 13 |
import argparse
|
| 14 |
import csv
|
| 15 |
import json
|
|
|
|
| 16 |
import re
|
|
|
|
| 17 |
from pathlib import Path
|
| 18 |
|
| 19 |
|
|
@@ -23,6 +25,14 @@ MATRIX_DATE = "2026-08-29"
|
|
| 23 |
MATRIX_MODELS = 20
|
| 24 |
COLLAPSE_THRESHOLD = 0.90
|
| 25 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
# key, public label, vendor, setting, is_baseline, collapse.dat label
|
| 27 |
PASSES = [
|
| 28 |
("oracle", "Oracle", "Baseline", "oracle", 1, None),
|
|
@@ -66,9 +76,32 @@ BOARD_FIELDS = [
|
|
| 66 |
"collapsed",
|
| 67 |
"is_baseline",
|
| 68 |
"display_order",
|
|
|
|
| 69 |
]
|
| 70 |
|
| 71 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
def parse_collapse(path: Path) -> dict[str, float]:
|
| 73 |
shares: dict[str, float] = {}
|
| 74 |
for line in path.read_text(encoding="utf-8").splitlines():
|
|
@@ -163,6 +196,7 @@ def main() -> None:
|
|
| 163 |
"collapsed": collapsed,
|
| 164 |
"is_baseline": is_baseline,
|
| 165 |
"display_order": order,
|
|
|
|
| 166 |
}
|
| 167 |
)
|
| 168 |
|
|
@@ -183,8 +217,8 @@ def main() -> None:
|
|
| 183 |
"Accuracy is the share of questions where the chosen model is statistically "
|
| 184 |
"as good as the best available answer."
|
| 185 |
),
|
| 186 |
-
"report_url": "https://autobench.org/reports/
|
| 187 |
-
"blog_url": "https://autobench.org/reports/
|
| 188 |
"matrix_date": MATRIX_DATE,
|
| 189 |
"matrix_models": MATRIX_MODELS,
|
| 190 |
"questions": questions,
|
|
@@ -199,6 +233,7 @@ def main() -> None:
|
|
| 199 |
"questions": questions,
|
| 200 |
"matrix_models": MATRIX_MODELS,
|
| 201 |
"matrix_date": MATRIX_DATE,
|
|
|
|
| 202 |
},
|
| 203 |
}
|
| 204 |
|
|
|
|
| 13 |
import argparse
|
| 14 |
import csv
|
| 15 |
import json
|
| 16 |
+
import math
|
| 17 |
import re
|
| 18 |
+
from decimal import Decimal, ROUND_HALF_UP
|
| 19 |
from pathlib import Path
|
| 20 |
|
| 21 |
|
|
|
|
| 25 |
MATRIX_MODELS = 20
|
| 26 |
COLLAPSE_THRESHOLD = 0.90
|
| 27 |
|
| 28 |
+
# Official AutoBench Router Index. beta is locked at 0.2.
|
| 29 |
+
# Quality is the share of the gap from the seed-7 random floor to a perfect answer.
|
| 30 |
+
# Cost is a log score between fixed anchors of $0.00001 and $1 per question.
|
| 31 |
+
ARI_BETA = 0.2
|
| 32 |
+
ARI_DH_RAND = 0.646825
|
| 33 |
+
ARI_C_MIN = 1e-5
|
| 34 |
+
ARI_C_MAX = 1.0
|
| 35 |
+
|
| 36 |
# key, public label, vendor, setting, is_baseline, collapse.dat label
|
| 37 |
PASSES = [
|
| 38 |
("oracle", "Oracle", "Baseline", "oracle", 1, None),
|
|
|
|
| 76 |
"collapsed",
|
| 77 |
"is_baseline",
|
| 78 |
"display_order",
|
| 79 |
+
"ari",
|
| 80 |
]
|
| 81 |
|
| 82 |
|
| 83 |
+
def router_index(signal: float, cost: float) -> float:
|
| 84 |
+
"""ARI at the official beta. Returns one decimal, matching the published table."""
|
| 85 |
+
quality = max(0.0, (signal - ARI_DH_RAND) / (1.0 - ARI_DH_RAND))
|
| 86 |
+
if cost <= 0 or quality == 0:
|
| 87 |
+
return 0.0
|
| 88 |
+
span = math.log2(ARI_C_MAX) - math.log2(ARI_C_MIN)
|
| 89 |
+
cost_score = (math.log2(ARI_C_MAX) - math.log2(cost)) / span
|
| 90 |
+
cost_score = min(1.0, max(0.0, cost_score))
|
| 91 |
+
if cost_score == 0:
|
| 92 |
+
return 0.0
|
| 93 |
+
value = (
|
| 94 |
+
100.0
|
| 95 |
+
* (1.0 + ARI_BETA)
|
| 96 |
+
* quality
|
| 97 |
+
* cost_score
|
| 98 |
+
/ (ARI_BETA * quality + cost_score)
|
| 99 |
+
)
|
| 100 |
+
return float(
|
| 101 |
+
Decimal(f"{value:.10f}").quantize(Decimal("0.1"), rounding=ROUND_HALF_UP)
|
| 102 |
+
)
|
| 103 |
+
|
| 104 |
+
|
| 105 |
def parse_collapse(path: Path) -> dict[str, float]:
|
| 106 |
shares: dict[str, float] = {}
|
| 107 |
for line in path.read_text(encoding="utf-8").splitlines():
|
|
|
|
| 196 |
"collapsed": collapsed,
|
| 197 |
"is_baseline": is_baseline,
|
| 198 |
"display_order": order,
|
| 199 |
+
"ari": router_index(float(row["dh"]), float(row["cost"])),
|
| 200 |
}
|
| 201 |
)
|
| 202 |
|
|
|
|
| 217 |
"Accuracy is the share of questions where the chosen model is statistically "
|
| 218 |
"as good as the best available answer."
|
| 219 |
),
|
| 220 |
+
"report_url": "https://autobench.org/reports/do-llm-routers-pay-for-themselves",
|
| 221 |
+
"blog_url": "https://autobench.org/reports/do-llm-routers-pay-for-themselves.pdf",
|
| 222 |
"matrix_date": MATRIX_DATE,
|
| 223 |
"matrix_models": MATRIX_MODELS,
|
| 224 |
"questions": questions,
|
|
|
|
| 233 |
"questions": questions,
|
| 234 |
"matrix_models": MATRIX_MODELS,
|
| 235 |
"matrix_date": MATRIX_DATE,
|
| 236 |
+
"ari_beta": ARI_BETA,
|
| 237 |
},
|
| 238 |
}
|
| 239 |
|
runs/router1_2026-10-26/metadata.json
CHANGED
|
@@ -6,8 +6,8 @@
|
|
| 6 |
"run_version": "2026-10-26",
|
| 7 |
"is_latest": false,
|
| 8 |
"description": "Commercial routers and baselines graded on a frozen answer matrix. Accuracy is the share of questions where the chosen model is statistically as good as the best available answer.",
|
| 9 |
-
"report_url": "https://autobench.org/reports/
|
| 10 |
-
"blog_url": "https://autobench.org/reports/
|
| 11 |
"matrix_date": "2026-08-29",
|
| 12 |
"matrix_models": 20,
|
| 13 |
"questions": 252,
|
|
@@ -34,6 +34,7 @@
|
|
| 34 |
"median_same_pick": 0.0,
|
| 35 |
"questions": 252,
|
| 36 |
"matrix_models": 20,
|
| 37 |
-
"matrix_date": "2026-08-29"
|
|
|
|
| 38 |
}
|
| 39 |
}
|
|
|
|
| 6 |
"run_version": "2026-10-26",
|
| 7 |
"is_latest": false,
|
| 8 |
"description": "Commercial routers and baselines graded on a frozen answer matrix. Accuracy is the share of questions where the chosen model is statistically as good as the best available answer.",
|
| 9 |
+
"report_url": "https://autobench.org/reports/do-llm-routers-pay-for-themselves",
|
| 10 |
+
"blog_url": "https://autobench.org/reports/do-llm-routers-pay-for-themselves.pdf",
|
| 11 |
"matrix_date": "2026-08-29",
|
| 12 |
"matrix_models": 20,
|
| 13 |
"questions": 252,
|
|
|
|
| 34 |
"median_same_pick": 0.0,
|
| 35 |
"questions": 252,
|
| 36 |
"matrix_models": 20,
|
| 37 |
+
"matrix_date": "2026-08-29",
|
| 38 |
+
"ari_beta": 0.2
|
| 39 |
}
|
| 40 |
}
|
runs/router1_2026-10-26/router_board.csv
CHANGED
|
@@ -1,24 +1,24 @@
|
|
| 1 |
-
pass_key,label,vendor,setting,n,accuracy,ci_low,ci_high,cost,cost_ci_low,cost_ci_high,top_share,collapsed,is_baseline,display_order
|
| 2 |
-
oracle,Oracle,Baseline,oracle,252,100.0,100.0,100.0,0.03406342571592063,0.023090085942222026,0.04675808716741607,,0,1,0
|
| 3 |
-
cheapest_adequate,Cheapest-adequate,Baseline,cheapest adequate,252,100.0,100.0,100.0,0.0017775440658730159,0.0012147553282936506,0.0024296202333019836,,0,1,1
|
| 4 |
-
best_static_single,Always-Sol,Baseline,always Sol,248,87.5,83.5,91.5,0.07865474395161291,0.06454585211693548,0.09954083326612903,,0,1,2
|
| 5 |
-
always_luna,Always-Luna,Baseline,always Luna,251,85.3,80.9,89.6,0.004334039103585658,0.0037202327948207175,0.005072818161354581,,0,1,3
|
| 6 |
-
random,Random,Baseline,random,252,64.7,58.7,70.6,0.011535058849650793,0.007392945504305159,0.017047359445582142,0.0714,0,1,4
|
| 7 |
-
always_cheapest,Always-cheapest,Baseline,always cheapest,252,44.4,38.5,50.4,0.00016450185134920633,0.00014635133277579365,0.0001831236469801587,,0,1,5
|
| 8 |
-
orca_constrained_cheapest,Orca cheapest,OrcaRouter,cheapest,252,39.3,33.3,45.2,0.00022013774206349209,0.00019737690396825396,0.00024262488363095238,0.9921,1,0,6
|
| 9 |
-
orca_constrained_balanced,Orca balanced,OrcaRouter,balanced,252,39.3,33.3,45.2,0.0002193627023809524,0.00019667757688492064,0.0002418150847222222,0.996,1,0,7
|
| 10 |
-
orca_constrained_quality,Orca quality,OrcaRouter,quality,245,65.7,59.2,71.0,0.0005173967175510203,0.00042429937727755105,0.0006060875131632653,1.0,1,0,8
|
| 11 |
-
azure_cost,Azure cost,Azure Model Router,cost,74,45.9,33.8,56.8,0.00912647891891892,0.00708104558918919,0.01094242530135135,0.5913,0,0,9
|
| 12 |
-
azure_balanced,Azure balanced,Azure Model Router,balanced,179,82.7,78.8,89.4,0.020369913128491617,0.013021539309357542,0.03823974199790503,0.4802,0,0,10
|
| 13 |
-
azure_quality,Azure quality,Azure Model Router,quality,238,85.3,80.3,89.1,0.064655818697479,0.054270396591386556,0.08091888025735294,0.4087,0,0,11
|
| 14 |
-
nadirclaw,NadirClaw,NadirClaw,as shipped,249,70.3,64.3,75.9,0.05136010172289157,0.04020215791164659,0.0689510219376506,0.5873,0,0,12
|
| 15 |
-
brick,Brick,Brick,as shipped,241,63.9,57.7,69.7,0.00310902156506224,0.002298716507154565,0.005002192490113071,0.8016,0,0,13
|
| 16 |
-
cascade,Cascade,Cascade,as shipped,235,60.9,53.2,66.0,0.01035301790706383,0.007934312614846808,0.012938480779023402,0.5826,0,0,14
|
| 17 |
-
nd_subset_k0,ND k0,Not Diamond,k0,248,87.5,83.5,91.5,0.07865474395161291,0.06454585211693548,0.09954083326612903,1.0,1,0,15
|
| 18 |
-
nd_subset_k4,ND k4,Not Diamond,k4,249,80.3,75.5,85.2,0.05996616918433735,0.04669444507111647,0.07957475177908833,0.7698,0,0,16
|
| 19 |
-
nd_subset_k5,ND k5,Not Diamond,k5,251,46.6,40.2,52.6,0.000941773053625498,0.0006677803593406375,0.0012532932769940238,0.8294,0,0,17
|
| 20 |
-
nd_native_k5,ND native,Not Diamond,native,250,72.0,66.0,77.2,0.059749960000000005,0.0475751486,0.07856580617999999,0.6389,0,0,18
|
| 21 |
-
or_auto,Auto low,OpenRouter,low,245,65.7,59.2,71.0,0.0005501390004897959,0.0004446207167918367,0.0006506275904183674,0.9841,1,0,19
|
| 22 |
-
or_auto_medium,Auto medium,OpenRouter,medium,246,73.6,67.1,78.0,0.013130098215934962,0.008043599401597561,0.019080980965126015,0.7817,0,0,20
|
| 23 |
-
or_auto_high,Auto high,OpenRouter,high,244,79.1,73.4,83.6,0.023005027726557378,0.018076698892231558,0.02858057378719467,0.5794,0,0,21
|
| 24 |
-
or_auto_max,Auto max,OpenRouter,max,240,78.3,72.9,83.3,0.028915902712999996,0.020327376228814583,0.043068421132057284,0.7817,0,0,22
|
|
|
|
| 1 |
+
pass_key,label,vendor,setting,n,accuracy,ci_low,ci_high,cost,cost_ci_low,cost_ci_high,top_share,collapsed,is_baseline,display_order,ari
|
| 2 |
+
oracle,Oracle,Baseline,oracle,252,100.0,100.0,100.0,0.03406342571592063,0.023090085942222026,0.04675808716741607,,0,1,0,71.4
|
| 3 |
+
cheapest_adequate,Cheapest-adequate,Baseline,cheapest adequate,252,100.0,100.0,100.0,0.0017775440658730159,0.0012147553282936506,0.0024296202333019836,,0,1,1,88.0
|
| 4 |
+
best_static_single,Always-Sol,Baseline,always Sol,248,87.5,83.5,91.5,0.07865474395161291,0.06454585211693548,0.09954083326612903,,0,1,2,48.9
|
| 5 |
+
always_luna,Always-Luna,Baseline,always Luna,251,85.3,80.9,89.6,0.004334039103585658,0.0037202327948207175,0.005072818161354581,,0,1,3,56.1
|
| 6 |
+
random,Random,Baseline,random,252,64.7,58.7,70.6,0.011535058849650793,0.007392945504305159,0.017047359445582142,0.0714,0,1,4,0.0
|
| 7 |
+
always_cheapest,Always-cheapest,Baseline,always cheapest,252,44.4,38.5,50.4,0.00016450185134920633,0.00014635133277579365,0.0001831236469801587,,0,1,5,0.0
|
| 8 |
+
orca_constrained_cheapest,Orca cheapest,OrcaRouter,cheapest,252,39.3,33.3,45.2,0.00022013774206349209,0.00019737690396825396,0.00024262488363095238,0.9921,1,0,6,0.0
|
| 9 |
+
orca_constrained_balanced,Orca balanced,OrcaRouter,balanced,252,39.3,33.3,45.2,0.0002193627023809524,0.00019667757688492064,0.0002418150847222222,0.996,1,0,7,0.0
|
| 10 |
+
orca_constrained_quality,Orca quality,OrcaRouter,quality,245,65.7,59.2,71.0,0.0005173967175510203,0.00042429937727755105,0.0006060875131632653,1.0,1,0,8,3.5
|
| 11 |
+
azure_cost,Azure cost,Azure Model Router,cost,74,45.9,33.8,56.8,0.00912647891891892,0.00708104558918919,0.01094242530135135,0.5913,0,0,9,0.0
|
| 12 |
+
azure_balanced,Azure balanced,Azure Model Router,balanced,179,82.7,78.8,89.4,0.020369913128491617,0.013021539309357542,0.03823974199790503,0.4802,0,0,10,47.0
|
| 13 |
+
azure_quality,Azure quality,Azure Model Router,quality,238,85.3,80.3,89.1,0.064655818697479,0.054270396591386556,0.08091888025735294,0.4087,0,0,11,47.0
|
| 14 |
+
nadirclaw,NadirClaw,NadirClaw,as shipped,249,70.3,64.3,75.9,0.05136010172289157,0.04020215791164659,0.0689510219376506,0.5873,0,0,12,16.9
|
| 15 |
+
brick,Brick,Brick,as shipped,241,63.9,57.7,69.7,0.00310902156506224,0.002298716507154565,0.005002192490113071,0.8016,0,0,13,0.0
|
| 16 |
+
cascade,Cascade,Cascade,as shipped,235,60.9,53.2,66.0,0.01035301790706383,0.007934312614846808,0.012938480779023402,0.5826,0,0,14,0.0
|
| 17 |
+
nd_subset_k0,ND k0,Not Diamond,k0,248,87.5,83.5,91.5,0.07865474395161291,0.06454585211693548,0.09954083326612903,1.0,1,0,15,48.9
|
| 18 |
+
nd_subset_k4,ND k4,Not Diamond,k4,249,80.3,75.5,85.2,0.05996616918433735,0.04669444507111647,0.07957475177908833,0.7698,0,0,16,39.0
|
| 19 |
+
nd_subset_k5,ND k5,Not Diamond,k5,251,46.6,40.2,52.6,0.000941773053625498,0.0006677803593406375,0.0012532932769940238,0.8294,0,0,17,0.0
|
| 20 |
+
nd_native_k5,ND native,Not Diamond,native,250,72.0,66.0,77.2,0.059749960000000005,0.0475751486,0.07856580617999999,0.6389,0,0,18,21.3
|
| 21 |
+
or_auto,Auto low,OpenRouter,low,245,65.7,59.2,71.0,0.0005501390004897959,0.0004446207167918367,0.0006506275904183674,0.9841,1,0,19,3.5
|
| 22 |
+
or_auto_medium,Auto medium,OpenRouter,medium,246,73.6,67.1,78.0,0.013130098215934962,0.008043599401597561,0.019080980965126015,0.7817,0,0,20,26.7
|
| 23 |
+
or_auto_high,Auto high,OpenRouter,high,244,79.1,73.4,83.6,0.023005027726557378,0.018076698892231558,0.02858057378719467,0.5794,0,0,21,39.2
|
| 24 |
+
or_auto_max,Auto max,OpenRouter,max,240,78.3,72.9,83.3,0.028915902712999996,0.020327376228814583,0.043068421132057284,0.7817,0,0,22,37.1
|