Minette Kaunismäki commited on
Commit
465cf7a
·
1 Parent(s): 6aca0c6

video-to-video model

Browse files
Files changed (4) hide show
  1. app.py +99 -2
  2. data/video-editing-leaderboard.csv +12 -0
  3. model_display.py +8 -1
  4. ui.py +300 -47
app.py CHANGED
@@ -2301,6 +2301,62 @@ def load_qwen_combined_dataframe(path):
2301
  return df.reset_index(drop=True)
2302
 
2303
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2304
  df = load_oneig_dataframe(oneig_path)
2305
 
2306
  oneig_metric_columns = [
@@ -2360,10 +2416,15 @@ arena_path = _resolve_data_path(
2360
  data_dir / "arena_ai_text_to_image_leaderboard.csv",
2361
  space_root.parent / "arena_ai_text_to_image_leaderboard.csv",
2362
  )
 
 
 
 
2363
 
2364
  qwen_df = load_qwen_combined_dataframe(qwen_path)
2365
  aa_df = load_artificial_analysis_dataframe(aa_path)
2366
  arena_df = load_arena_ai_dataframe(arena_path)
 
2367
  qwen_display_columns = [
2368
  col
2369
  for col in [
@@ -2395,6 +2456,19 @@ arena_display_columns = [
2395
  ]
2396
  if col in arena_df.columns
2397
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
2398
 
2399
  oneig_samples = load_sample_comparison_data(oneig_combined_dir)
2400
  qwen_samples = load_sample_comparison_data(qwen_combined_dir)
@@ -2416,6 +2490,7 @@ metrics = [
2416
  {"id": "arena_art", "column": "Arena Art Elo"},
2417
  {"id": "arena_portraits", "column": "Arena Portraits Elo"},
2418
  {"id": "arena_text", "column": "Arena Text Rendering Elo"},
 
2419
  ]
2420
 
2421
 
@@ -2456,11 +2531,27 @@ arena_metric_ids = _metric_ids_for(
2456
  "arena_text",
2457
  ],
2458
  )
 
2459
 
2460
  datasets = [
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2461
  {
2462
  "id": "qwen",
2463
  "name": "Qwen Image Dataset",
 
2464
  "data": qwen_df,
2465
  "columns": qwen_display_columns,
2466
  "metric_ids": qwen_metric_ids,
@@ -2470,6 +2561,7 @@ datasets = [
2470
  {
2471
  "id": "oneig",
2472
  "name": "OneIG Alignment Dataset",
 
2473
  "data": oneig_df,
2474
  "columns": oneig_display_columns,
2475
  "metric_ids": oneig_metric_ids,
@@ -2482,6 +2574,7 @@ datasets = [
2482
  {
2483
  "id": "artificial_analysis",
2484
  "name": "Artificial Analysis Dataset",
 
2485
  "data": aa_df,
2486
  "columns": aa_display_columns,
2487
  "metric_ids": aa_metric_ids,
@@ -2491,6 +2584,7 @@ datasets = [
2491
  {
2492
  "id": "arena_ai",
2493
  "name": "Arena AI Dataset",
 
2494
  "data": arena_df,
2495
  "columns": arena_display_columns,
2496
  "metric_ids": arena_metric_ids,
@@ -2501,8 +2595,11 @@ datasets = [
2501
  datasets = [dataset for dataset in datasets if dataset["metric_ids"]]
2502
 
2503
  DEFAULT_DATASET_ID = next(
2504
- (dataset["id"] for dataset in datasets if dataset["id"] == "qwen"),
2505
- datasets[0]["id"] if datasets else None,
 
 
 
2506
  )
2507
  DEFAULT_METRIC_ID = None
2508
 
 
2301
  return df.reset_index(drop=True)
2302
 
2303
 
2304
+ def load_video_editing_dataframe(path):
2305
+ """Load the video-to-video editing leaderboard."""
2306
+ df = pd.read_csv(path, na_values=["N/A", "n/a", ""])
2307
+ df = df.rename(
2308
+ columns={
2309
+ "display_name": "Model",
2310
+ "elo": "Video Edit Elo",
2311
+ "n_generations": "Generations",
2312
+ "min_generation_s": "Min Generation Time (s)",
2313
+ "median_generation_s": "Median Generation Time (s)",
2314
+ "p20_generation_s": "P20 Generation Time (s)",
2315
+ "generation_s_per_output_video_s": "Time / Output Video Second (s)",
2316
+ "predict_time_s_per_output_video_s": "Predict Time / Output Video Second (s)",
2317
+ "model_execution_time_s_per_output_video_s": (
2318
+ "Execution Time / Output Video Second (s)"
2319
+ ),
2320
+ "price": "Price / Video (USD)",
2321
+ }
2322
+ )
2323
+ df = df.drop(columns=["wandb_run_ids"], errors="ignore")
2324
+ df["Model"] = df["Model"].astype(str).str.strip()
2325
+ df = _as_numeric(
2326
+ df,
2327
+ [
2328
+ "Video Edit Elo",
2329
+ "Generations",
2330
+ "Min Generation Time (s)",
2331
+ "Median Generation Time (s)",
2332
+ "P20 Generation Time (s)",
2333
+ "Time / Output Video Second (s)",
2334
+ "Predict Time / Output Video Second (s)",
2335
+ "Execution Time / Output Video Second (s)",
2336
+ "Price / Video (USD)",
2337
+ ],
2338
+ )
2339
+ end_to_end = df.get("Time / Output Video Second (s)")
2340
+ execution = df.get("Execution Time / Output Video Second (s)")
2341
+ if end_to_end is not None:
2342
+ if "model_id" in df.columns:
2343
+ is_ours = df["model_id"].astype(str).str.lower().str.startswith(
2344
+ "p_video_edit"
2345
+ )
2346
+ else:
2347
+ is_ours = df["Model"].astype(str).str.casefold().str.startswith(
2348
+ "p-video-edit"
2349
+ )
2350
+ if execution is None:
2351
+ pareto_time = end_to_end
2352
+ else:
2353
+ ours_time = execution.where(execution.notna(), end_to_end)
2354
+ pareto_time = end_to_end.where(~is_ours, ours_time)
2355
+ df["Pareto Time / Output Video Second (s)"] = pareto_time
2356
+ df = df.drop(columns=["model_id"], errors="ignore")
2357
+ return df.reset_index(drop=True)
2358
+
2359
+
2360
  df = load_oneig_dataframe(oneig_path)
2361
 
2362
  oneig_metric_columns = [
 
2416
  data_dir / "arena_ai_text_to_image_leaderboard.csv",
2417
  space_root.parent / "arena_ai_text_to_image_leaderboard.csv",
2418
  )
2419
+ video_path = _resolve_data_path(
2420
+ data_dir / "video-editing-leaderboard.csv",
2421
+ space_root.parent / "video-editing-leaderboard.csv",
2422
+ )
2423
 
2424
  qwen_df = load_qwen_combined_dataframe(qwen_path)
2425
  aa_df = load_artificial_analysis_dataframe(aa_path)
2426
  arena_df = load_arena_ai_dataframe(arena_path)
2427
+ video_df = load_video_editing_dataframe(video_path)
2428
  qwen_display_columns = [
2429
  col
2430
  for col in [
 
2456
  ]
2457
  if col in arena_df.columns
2458
  ]
2459
+ video_display_columns = [
2460
+ col
2461
+ for col in [
2462
+ "Model",
2463
+ "Video Edit Elo",
2464
+ "Generations",
2465
+ "Median Generation Time (s)",
2466
+ "Min Generation Time (s)",
2467
+ "Time / Output Video Second (s)",
2468
+ "Price / Video (USD)",
2469
+ ]
2470
+ if col in video_df.columns
2471
+ ]
2472
 
2473
  oneig_samples = load_sample_comparison_data(oneig_combined_dir)
2474
  qwen_samples = load_sample_comparison_data(qwen_combined_dir)
 
2490
  {"id": "arena_art", "column": "Arena Art Elo"},
2491
  {"id": "arena_portraits", "column": "Arena Portraits Elo"},
2492
  {"id": "arena_text", "column": "Arena Text Rendering Elo"},
2493
+ {"id": "video_edit_elo", "column": "Video Edit Elo"},
2494
  ]
2495
 
2496
 
 
2531
  "arena_text",
2532
  ],
2533
  )
2534
+ video_metric_ids = _metric_ids_for(video_df, ["video_edit_elo"])
2535
 
2536
  datasets = [
2537
+ {
2538
+ "id": "video_editing",
2539
+ "name": "Video Editing Dataset",
2540
+ "modality": "video_to_video",
2541
+ "data": video_df,
2542
+ "columns": video_display_columns,
2543
+ "metric_ids": video_metric_ids,
2544
+ "note": (
2545
+ "Elo from pairwise video-edit preference. Price is USD per output "
2546
+ "video. Seconds for 1s of video is how long it takes to produce "
2547
+ "one second of output video (end-to-end wall time)."
2548
+ ),
2549
+ "samples": None,
2550
+ },
2551
  {
2552
  "id": "qwen",
2553
  "name": "Qwen Image Dataset",
2554
+ "modality": "text_to_image",
2555
  "data": qwen_df,
2556
  "columns": qwen_display_columns,
2557
  "metric_ids": qwen_metric_ids,
 
2561
  {
2562
  "id": "oneig",
2563
  "name": "OneIG Alignment Dataset",
2564
+ "modality": "text_to_image",
2565
  "data": oneig_df,
2566
  "columns": oneig_display_columns,
2567
  "metric_ids": oneig_metric_ids,
 
2574
  {
2575
  "id": "artificial_analysis",
2576
  "name": "Artificial Analysis Dataset",
2577
+ "modality": "text_to_image",
2578
  "data": aa_df,
2579
  "columns": aa_display_columns,
2580
  "metric_ids": aa_metric_ids,
 
2584
  {
2585
  "id": "arena_ai",
2586
  "name": "Arena AI Dataset",
2587
+ "modality": "text_to_image",
2588
  "data": arena_df,
2589
  "columns": arena_display_columns,
2590
  "metric_ids": arena_metric_ids,
 
2595
  datasets = [dataset for dataset in datasets if dataset["metric_ids"]]
2596
 
2597
  DEFAULT_DATASET_ID = next(
2598
+ (dataset["id"] for dataset in datasets if dataset["id"] == "video_editing"),
2599
+ next(
2600
+ (dataset["id"] for dataset in datasets if dataset["id"] == "qwen"),
2601
+ datasets[0]["id"] if datasets else None,
2602
+ ),
2603
  )
2604
  DEFAULT_METRIC_ID = None
2605
 
data/video-editing-leaderboard.csv ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ model_id,display_name,elo,wandb_run_ids,n_generations,min_generation_s,median_generation_s,p20_generation_s,generation_s_per_output_video_s,predict_time_s_per_output_video_s,model_execution_time_s_per_output_video_s,price
2
+ gemini_omni_flash_edit__fal,Gemini Omni Flash Edit,1054,3iuctwj9,56,32.574746752001374,58.2327566820004,50.44402900500063,13.78,,,0.13
3
+ grok_imagine_video__replicate,Grok Imagine Video,974,q0ogvekx,59,31.640928319000523,43.75649321700257,32.88782280539963,11.04,,,0.05
4
+ happyhorse_1_0__wavespeed,HappyHorse 1.0,1048,k4zj5wqk,71,106.5824136010051,201.2431439649954,141.46595527700265,46.73,,,0.14
5
+ ltx_2_3_quality_reference_video_to_video__fal,LTX 2.3 Video Edit,911,p19ks8ad,73,60.768441981999786,73.62109239100027,70.20137865180223,15.83,,,0.054
6
+ lucy_edit_pro__fal,Lucy Edit Pro,879,na6bz9xx,72,118.3459930579993,136.44442766549764,124.13885582720104,27.18,,,0.15
7
+ minimax_h3_reference_to_video__fal,MiniMax H3 Reference-to-Video,1060,639xmakz,63,180.8385945170012,308.29198316100155,248.2394383729996,58.31,,,0.06
8
+ p_video_edit_preview__replicate_final,P-Video-Edit,1000,m8irpsa6,73,31.41135125700021,86.86665409700072,57.21095684959946,23.18,17.99,12.06,0.045
9
+ p_video_edit_preview__replicate_final__draft,P-Video-Edit Draft,994,6f88e6mx,73,23.04809326099712,42.9797806409988,33.19193872759861,11.48,11.28,4.46,0.025
10
+ seedance_2_5_video_edit_turbo__wavespeed,Seedance 2.5 Video Edit Turbo,1063,2nr274vp,68,142.49452170499717,293.7401115540015,223.11569286320045,59.93,,,0.24
11
+ wan_2_7_video_edit__wavespeed,Wan 2.7 Video Edit,1055,9lq803be,66,134.3744560209998,313.957433804002,219.2491260079987,68.04,,,0.2
12
+
model_display.py CHANGED
@@ -88,6 +88,13 @@ MODEL_DISPLAY_NAMES = {
88
  "p_image_2_ideogram_high_2k": "P-Image-Ideogram High 2K",
89
  "P-Image-Ideogram (High)": "P-Image-Ideogram High",
90
  "p_image_2_ideogram_very_high_1k": "P-Image-Ideogram Very High 1K",
 
 
 
 
 
 
 
91
  # Others overlapping P-Bench
92
  "z_image": "Z-Image",
93
  "glm_image": "GLM-Image",
@@ -210,6 +217,6 @@ def display_model_name(model_id) -> str:
210
  if raw in MODEL_DISPLAY_NAMES:
211
  return MODEL_DISPLAY_NAMES[raw]
212
  # Already a human label (spaces / punctuation) — keep as-is.
213
- if re.search(r"[\s.\[\]()]", raw):
214
  return raw
215
  return _prettify_snake_case(raw)
 
88
  "p_image_2_ideogram_high_2k": "P-Image-Ideogram High 2K",
89
  "P-Image-Ideogram (High)": "P-Image-Ideogram High",
90
  "p_image_2_ideogram_very_high_1k": "P-Image-Ideogram Very High 1K",
91
+ # P-Video-Edit
92
+ "P-Video-Edit": "P-Video-Edit",
93
+ "P-Video-Edit Draft": "P-Video-Edit Draft",
94
+ "P-Video Edit Final": "P-Video-Edit",
95
+ "P-Video Edit Final (draft)": "P-Video-Edit Draft",
96
+ "p_video_edit_preview__replicate_final": "P-Video-Edit",
97
+ "p_video_edit_preview__replicate_final__draft": "P-Video-Edit Draft",
98
  # Others overlapping P-Bench
99
  "z_image": "Z-Image",
100
  "glm_image": "GLM-Image",
 
217
  if raw in MODEL_DISPLAY_NAMES:
218
  return MODEL_DISPLAY_NAMES[raw]
219
  # Already a human label (spaces / punctuation) — keep as-is.
220
+ if re.search(r"[\s.\[\]()-]", raw):
221
  return raw
222
  return _prettify_snake_case(raw)
ui.py CHANGED
@@ -25,19 +25,41 @@ MAX_COMPARE_PROMPTS = 8
25
  MAX_PARETO_METRICS = 8
26
  _PARETO_SLOT_COUNT = 1 + MAX_PARETO_METRICS * 8
27
  _PARETO_PRICE_COLUMN = "Price / Image (USD)"
 
 
28
  _PARETO_TIME_COLUMN = "Min Generation Time (s)"
 
 
 
 
 
 
 
 
 
 
29
  _PARETO_SCALE_CHOICES = [
30
  ("Log", "Logarithmic"),
31
  ("Linear", "Linear"),
32
  ]
33
  _PARETO_SCALE_VALUES = {value for _, value in _PARETO_SCALE_CHOICES}
34
  _PARETO_SCALE_DEFAULT = "Logarithmic"
 
 
 
35
 
36
  TAB_LEADERBOARDS = "leaderboards"
37
  TAB_PARETO = "pareto"
38
  TAB_SAMPLES = "samples"
39
  TAB_ABOUT = "about"
40
 
 
 
 
 
 
 
 
41
  _MODEL_CHOICES_CACHE = {}
42
  _VIEW_EVENTS = {
43
  "show_progress": "hidden",
@@ -49,14 +71,15 @@ _VIEW_EVENTS = {
49
  ABOUT_OVERVIEW_CONTENT = """
50
  # About P-Bench
51
 
52
- P-Bench compares **text-to-image models**, including optimized or accelerated
53
- endpoints, on **quality, speed, and price**. Each view is a **dataset** scored
54
- with a **metric**, written as `Dataset | Metric`. There is no single score
55
- across P-Bench.
56
 
57
  ## How to read it
58
 
59
- 1. Pick a **dataset** and a **metric**.
 
60
  2. **Leaderboards**: ranked by that metric. Price and generation time sit in
61
  the same table when the source publishes them.
62
  3. **Pareto plots**: mark models that are not beaten on both higher score
@@ -82,6 +105,10 @@ prompt suites, so samples are not shown.
82
 
83
  ## Current datasets
84
 
 
 
 
 
85
  ### Qwen Image Dataset
86
  100 prompts from the 1,000-prompt Qwen Image Bench set, sampled for coverage
87
  across its fine-grained (L3) categories. Metrics include Datapoint Elo,
@@ -222,7 +249,7 @@ def render_header():
222
  </svg>
223
  </button>
224
  </div>
225
- <p class="app-header-tagline">Compare text-to-image models on quality, speed, and price</p>
226
  </header>
227
  """,
228
  padding=False,
@@ -237,10 +264,42 @@ def _item(items, item_id):
237
  return items[0] if items else None
238
 
239
 
240
- def _dataset_choices(datasets, *, require_samples=False, require_pareto=False):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241
  return [
242
  (dataset["name"], dataset["id"])
243
- for dataset in datasets
244
  if (not require_samples or dataset.get("samples"))
245
  and (not require_pareto or _dataset_has_pareto(datasets, dataset["id"]))
246
  ]
@@ -259,17 +318,39 @@ def _sample_model_ids(datasets, dataset_id):
259
  return set(samples.get("models") or [])
260
 
261
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
262
  def _dataset_has_pareto(datasets, dataset_id):
263
  dataset = _item(datasets, dataset_id)
264
- columns = getattr(dataset.get("data") if dataset else None, "columns", [])
265
- return _PARETO_PRICE_COLUMN in columns or _PARETO_TIME_COLUMN in columns
 
 
 
266
 
267
 
268
- def _dataset_dropdown_update(datasets, tab, dataset_id):
269
  """Limit the dataset list to what the current tab can show."""
 
 
270
  return gr.update(
271
  choices=_dataset_choices(
272
  datasets,
 
273
  require_samples=tab == TAB_SAMPLES
274
  and _dataset_has_samples(datasets, dataset_id),
275
  require_pareto=tab == TAB_PARETO
@@ -377,7 +458,10 @@ _LEADERBOARD_IDENTITY_COLUMNS = [
377
  _LEADERBOARD_META_COLUMNS = [
378
  "Median Generation Time (s)",
379
  "Min Generation Time (s)",
 
380
  "Price / Image (USD)",
 
 
381
  "Evaluation Date (UTC)",
382
  "Date",
383
  ]
@@ -429,7 +513,7 @@ def _format_leaderboard_cell(column, value):
429
  if pd.isna(value) or value is None or value == "":
430
  return "-"
431
  label = str(column).lower()
432
- if label == "rank":
433
  return str(int(value))
434
  if "price" in label:
435
  return _format_price(value)
@@ -604,7 +688,11 @@ def _display_label(column):
604
  "Arena Text Rendering Elo": "Text Rendering",
605
  "Median Generation Time (s)": "Median generation time",
606
  "Min Generation Time (s)": "Min generation time",
 
607
  "Price / Image (USD)": "Price per image",
 
 
 
608
  "Evaluation Date (UTC)": "Date",
609
  "Date": "Date",
610
  }
@@ -679,6 +767,23 @@ def _applied_key(view_state):
679
  )
680
 
681
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
682
  def _build_pareto_figure(
683
  data,
684
  score_column,
@@ -703,6 +808,8 @@ def _build_pareto_figure(
703
 
704
  dominated = scatter.loc[[not flag for flag in on_frontier]].copy()
705
  frontier = scatter.loc[on_frontier].sort_values(x_column).copy()
 
 
706
  if not dominated.empty:
707
  dominated["Model"] = dominated["Model"].map(display_model_name)
708
  if not frontier.empty:
@@ -723,10 +830,11 @@ def _build_pareto_figure(
723
  name="Below frontier",
724
  text=dominated["Model"],
725
  hovertemplate=hover,
 
726
  marker={
727
  "size": 9,
728
- "color": "#d8b4fe",
729
- "opacity": 0.8,
730
  "line": {"width": 0},
731
  },
732
  )
@@ -740,14 +848,51 @@ def _build_pareto_figure(
740
  name="On frontier",
741
  text=frontier["Model"],
742
  hovertemplate=hover,
743
- line={"color": "#69a45c", "width": 2.5},
 
744
  marker={
745
  "size": 12,
746
- "color": "#69a45c",
747
- "line": {"width": 1.5, "color": "#86c077"},
748
  },
749
  )
750
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
751
 
752
  score_label = _display_label(score_column)
753
  fig.update_layout(
@@ -887,23 +1032,43 @@ def _pareto_pair(
887
  if data is None or not score_column or score_column not in data.columns:
888
  return None, score_missing, None, score_missing
889
 
 
 
 
 
 
 
 
890
  price_fig, price_message = _pareto_axis(
891
  data,
892
  score_column,
893
- _PARETO_PRICE_COLUMN,
894
- "Price per image (USD)",
895
- "Price per image isn't available for this dataset.",
896
  "No models have both a score and a price for this metric.",
897
  x_hover_prefix="$",
898
  x_axis_type=_pareto_axis_type(price_scale),
899
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
900
  time_fig, time_message = _pareto_axis(
901
  data,
902
  score_column,
903
- _PARETO_TIME_COLUMN,
904
- "Min generation time (s)",
905
- "Min generation time isn't available for this dataset.",
906
- "No models have both a score and a min generation time for this metric.",
907
  x_hover_suffix="s",
908
  x_axis_type=_pareto_axis_type(latency_scale),
909
  )
@@ -911,29 +1076,29 @@ def _pareto_pair(
911
 
912
 
913
  def _pareto_dataset_message(data):
914
- has_price = data is not None and _PARETO_PRICE_COLUMN in data.columns
915
- has_time = data is not None and _PARETO_TIME_COLUMN in data.columns
916
  if has_price or has_time:
917
  return None
918
  return (
919
- "Price per image and min generation time aren't available for "
920
  "this dataset, so these plots can't be drawn."
921
  )
922
 
923
 
924
  def _pareto_slot_note(price_fig, price_message, time_fig, time_message, data):
925
- has_price = data is not None and _PARETO_PRICE_COLUMN in data.columns
926
- has_time = data is not None and _PARETO_TIME_COLUMN in data.columns
927
  notes = []
928
  if has_price and not has_time:
929
  notes.append(
930
- "Min generation time isn't available for this dataset, so only "
931
  "price vs score is shown."
932
  )
933
  elif has_time and not has_price:
934
  notes.append(
935
- "Price per image isn't available for this dataset, so only min "
936
- "generation time vs score is shown."
937
  )
938
  if price_fig is None and has_price:
939
  notes.append(price_message)
@@ -954,8 +1119,8 @@ def _pareto_slot_updates(
954
  score_columns = [column for column in (score_columns or []) if column]
955
  price_scales = _normalize_pareto_scales(price_scales)
956
  time_scales = _normalize_pareto_scales(time_scales)
957
- has_price = data is not None and _PARETO_PRICE_COLUMN in data.columns
958
- has_time = data is not None and _PARETO_TIME_COLUMN in data.columns
959
  dataset_note = _pareto_dataset_message(data)
960
  updates = [_pareto_note_update(dataset_note)]
961
  hide_all_slots = not has_price and not has_time
@@ -1123,9 +1288,19 @@ def _filter_row(datasets, metrics, default_dataset_id, default_metric_id=None):
1123
  metric_id = _coerce_metric(
1124
  datasets, metrics, default_dataset_id, default_metric_id
1125
  )
 
1126
  with gr.Row(elem_classes="view-filters"):
 
 
 
 
 
 
 
 
 
1127
  dataset_dd = gr.Dropdown(
1128
- choices=_dataset_choices(datasets),
1129
  value=default_dataset_id,
1130
  label="Dataset",
1131
  type="value",
@@ -1157,7 +1332,7 @@ def _filter_row(datasets, metrics, default_dataset_id, default_metric_id=None):
1157
  min_width=180,
1158
  elem_classes="filter-chips",
1159
  )
1160
- return dataset_dd, metric_dd, models_dd
1161
 
1162
 
1163
  def render_image_workspace(datasets, metrics, default_dataset_id, default_metric_id):
@@ -1172,15 +1347,16 @@ def render_image_workspace(datasets, metrics, default_dataset_id, default_metric
1172
  with gr.Column(elem_classes="workspace-filters") as filters_host:
1173
  gr.Markdown(
1174
  "<p class='filter-help'>"
1175
- "These filters apply to Leaderboards, Pareto plots, and Samples. "
1176
- "On Samples, only datasets and models we have generations for "
1177
- "are listed. On Pareto plots, only datasets with price or "
1178
- "generation time are listed. Search in Models, or leave it "
1179
- "empty to include every model."
 
1180
  "</p>",
1181
  elem_classes="filter-help-host",
1182
  )
1183
- dataset_dd, metric_dd, models_dd = _filter_row(
1184
  datasets, metrics, default_dataset_id, None
1185
  )
1186
  with gr.Tabs(elem_classes="main-tabs") as main_tabs:
@@ -1306,7 +1482,7 @@ def render_image_workspace(datasets, metrics, default_dataset_id, default_metric
1306
  elem_classes="pareto-col",
1307
  ) as slot_time_col:
1308
  slot_time_scale = _pareto_plot_heading(
1309
- "Min generation time vs score"
1310
  )
1311
  slot_time = gr.Plot(
1312
  value=None,
@@ -1460,8 +1636,14 @@ def render_image_workspace(datasets, metrics, default_dataset_id, default_metric
1460
  ):
1461
  prev = dict(view_state or {})
1462
  extras = extras or {}
 
 
 
 
1463
  return {
1464
  "dataset_id": dataset_id,
 
 
1465
  "metric_id": metric_id,
1466
  "models": list(models or []),
1467
  "current_tab": tab,
@@ -1599,13 +1781,14 @@ def render_image_workspace(datasets, metrics, default_dataset_id, default_metric
1599
  tab = view_state.get("current_tab") or TAB_LEADERBOARDS
1600
  selected_raw = _normalize_metric_ids(metric_id)
1601
  incoming_models = list(models or [])
1602
- dataset_changed = source == "dataset" and dataset_id != view_state.get(
 
1603
  "dataset_id"
1604
  )
1605
  can_pareto = _dataset_has_pareto(datasets, dataset_id)
1606
  can_samples = _dataset_has_samples(datasets, dataset_id)
1607
  selected_tab = tab
1608
- if source == "dataset":
1609
  if tab == TAB_SAMPLES and not can_samples:
1610
  selected_tab = TAB_LEADERBOARDS
1611
  elif tab == TAB_PARETO and not can_pareto:
@@ -1614,7 +1797,7 @@ def render_image_workspace(datasets, metrics, default_dataset_id, default_metric
1614
  dataset_id,
1615
  metric_id,
1616
  models,
1617
- clear_metric=dataset_changed,
1618
  require_samples=selected_tab == TAB_SAMPLES,
1619
  )
1620
  dataset_id, metric_id, models = synced[:3]
@@ -1659,7 +1842,7 @@ def render_image_workspace(datasets, metrics, default_dataset_id, default_metric
1659
  list(optimized_value or []),
1660
  )
1661
  extra_updates = None
1662
- if source == "dataset":
1663
  view = resolve_view(datasets, metrics, dataset_id, metric_id)
1664
  extra_updates = _leaderboard_extras(
1665
  view["data"] if view else None,
@@ -1712,6 +1895,67 @@ def render_image_workspace(datasets, metrics, default_dataset_id, default_metric
1712
  ),
1713
  }
1714
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1715
  def on_dataset(
1716
  dataset_id,
1717
  metric_id,
@@ -2061,9 +2305,12 @@ def render_image_workspace(datasets, metrics, default_dataset_id, default_metric
2061
  handler.__name__ = f"on_tab_{tab}"
2062
  return handler
2063
 
 
2064
  view_state = gr.State(
2065
  {
2066
  "dataset_id": default_dataset_id,
 
 
2067
  "metric_id": None,
2068
  "models": [],
2069
  "current_tab": TAB_LEADERBOARDS,
@@ -2145,6 +2392,12 @@ def render_image_workspace(datasets, metrics, default_dataset_id, default_metric
2145
  main_tabs,
2146
  view_state,
2147
  ]
 
 
 
 
 
 
2148
  dataset_dd.change(
2149
  on_dataset,
2150
  inputs=filter_inputs,
 
25
  MAX_PARETO_METRICS = 8
26
  _PARETO_SLOT_COUNT = 1 + MAX_PARETO_METRICS * 8
27
  _PARETO_PRICE_COLUMN = "Price / Image (USD)"
28
+ _PARETO_VIDEO_PRICE_COLUMN = "Price / Video (USD)"
29
+ _PARETO_PRICE_COLUMNS = (_PARETO_PRICE_COLUMN, _PARETO_VIDEO_PRICE_COLUMN)
30
  _PARETO_TIME_COLUMN = "Min Generation Time (s)"
31
+ _PARETO_VIDEO_TIME_COLUMN = "Pareto Time / Output Video Second (s)"
32
+ _PARETO_TIME_COLUMNS = (_PARETO_VIDEO_TIME_COLUMN, _PARETO_TIME_COLUMN)
33
+ _PARETO_PRICE_TITLES = {
34
+ _PARETO_PRICE_COLUMN: "Price per image (USD)",
35
+ _PARETO_VIDEO_PRICE_COLUMN: "Price per video (USD)",
36
+ }
37
+ _PARETO_TIME_TITLES = {
38
+ _PARETO_TIME_COLUMN: "Min generation time (s)",
39
+ _PARETO_VIDEO_TIME_COLUMN: "Seconds needed for 1s of output video",
40
+ }
41
  _PARETO_SCALE_CHOICES = [
42
  ("Log", "Logarithmic"),
43
  ("Linear", "Linear"),
44
  ]
45
  _PARETO_SCALE_VALUES = {value for _, value in _PARETO_SCALE_CHOICES}
46
  _PARETO_SCALE_DEFAULT = "Logarithmic"
47
+ _PARETO_PRUNA_COLOR = "#c084fc"
48
+ _PARETO_OTHER_COLOR = "#9aa3b5"
49
+ _PARETO_FRONTIER_OUTLINE = "#3fa87e"
50
 
51
  TAB_LEADERBOARDS = "leaderboards"
52
  TAB_PARETO = "pareto"
53
  TAB_SAMPLES = "samples"
54
  TAB_ABOUT = "about"
55
 
56
+ MODALITY_VIDEO_TO_VIDEO = "video_to_video"
57
+ MODALITY_TEXT_TO_IMAGE = "text_to_image"
58
+ MODALITY_CHOICES = [
59
+ ("Video to Video", MODALITY_VIDEO_TO_VIDEO),
60
+ ("Text to Image", MODALITY_TEXT_TO_IMAGE),
61
+ ]
62
+
63
  _MODEL_CHOICES_CACHE = {}
64
  _VIEW_EVENTS = {
65
  "show_progress": "hidden",
 
71
  ABOUT_OVERVIEW_CONTENT = """
72
  # About P-Bench
73
 
74
+ P-Bench compares **text-to-image** and **video-to-video** models, including
75
+ optimized or accelerated endpoints, on **quality, speed, and price**. Each
76
+ view is a **dataset** scored with a **metric**, written as `Dataset | Metric`.
77
+ There is no single score across P-Bench.
78
 
79
  ## How to read it
80
 
81
+ 1. Pick a **type** (Video to Video or Text to Image), then a **dataset**
82
+ and a **metric**.
83
  2. **Leaderboards**: ranked by that metric. Price and generation time sit in
84
  the same table when the source publishes them.
85
  3. **Pareto plots**: mark models that are not beaten on both higher score
 
105
 
106
  ## Current datasets
107
 
108
+ ### Video Editing Dataset
109
+ Video-to-video edit Elo from pairwise preference, with price per output
110
+ video and generation time. Samples are not shown yet.
111
+
112
  ### Qwen Image Dataset
113
  100 prompts from the 1,000-prompt Qwen Image Bench set, sampled for coverage
114
  across its fine-grained (L3) categories. Metrics include Datapoint Elo,
 
249
  </svg>
250
  </button>
251
  </div>
252
+ <p class="app-header-tagline">Compare models on quality, speed, and price</p>
253
  </header>
254
  """,
255
  padding=False,
 
264
  return items[0] if items else None
265
 
266
 
267
+ def _dataset_modality(dataset):
268
+ return (dataset or {}).get("modality") or MODALITY_TEXT_TO_IMAGE
269
+
270
+
271
+ def _datasets_for_modality(datasets, modality):
272
+ if not modality:
273
+ return list(datasets)
274
+ scoped = [
275
+ dataset
276
+ for dataset in datasets
277
+ if _dataset_modality(dataset) == modality
278
+ ]
279
+ return scoped or list(datasets)
280
+
281
+
282
+ def _modality_choices(datasets):
283
+ present = {_dataset_modality(dataset) for dataset in datasets}
284
+ return [
285
+ (label, value) for label, value in MODALITY_CHOICES if value in present
286
+ ]
287
+
288
+
289
+ def _default_dataset_id(datasets, modality, preferred=None):
290
+ scoped = _datasets_for_modality(datasets, modality)
291
+ if preferred and any(dataset["id"] == preferred for dataset in scoped):
292
+ return preferred
293
+ return scoped[0]["id"] if scoped else None
294
+
295
+
296
+ def _dataset_choices(
297
+ datasets, *, modality=None, require_samples=False, require_pareto=False
298
+ ):
299
+ scoped = _datasets_for_modality(datasets, modality)
300
  return [
301
  (dataset["name"], dataset["id"])
302
+ for dataset in scoped
303
  if (not require_samples or dataset.get("samples"))
304
  and (not require_pareto or _dataset_has_pareto(datasets, dataset["id"]))
305
  ]
 
318
  return set(samples.get("models") or [])
319
 
320
 
321
+ def _pareto_price_column(data):
322
+ columns = getattr(data, "columns", []) if data is not None else []
323
+ for column in _PARETO_PRICE_COLUMNS:
324
+ if column in columns:
325
+ return column
326
+ return None
327
+
328
+
329
+ def _pareto_time_column(data):
330
+ columns = getattr(data, "columns", []) if data is not None else []
331
+ for column in _PARETO_TIME_COLUMNS:
332
+ if column in columns:
333
+ return column
334
+ return None
335
+
336
+
337
  def _dataset_has_pareto(datasets, dataset_id):
338
  dataset = _item(datasets, dataset_id)
339
+ data = dataset.get("data") if dataset else None
340
+ return (
341
+ _pareto_price_column(data) is not None
342
+ or _pareto_time_column(data) is not None
343
+ )
344
 
345
 
346
+ def _dataset_dropdown_update(datasets, tab, dataset_id, modality=None):
347
  """Limit the dataset list to what the current tab can show."""
348
+ if modality is None:
349
+ modality = _dataset_modality(_item(datasets, dataset_id))
350
  return gr.update(
351
  choices=_dataset_choices(
352
  datasets,
353
+ modality=modality,
354
  require_samples=tab == TAB_SAMPLES
355
  and _dataset_has_samples(datasets, dataset_id),
356
  require_pareto=tab == TAB_PARETO
 
458
  _LEADERBOARD_META_COLUMNS = [
459
  "Median Generation Time (s)",
460
  "Min Generation Time (s)",
461
+ "Time / Output Video Second (s)",
462
  "Price / Image (USD)",
463
+ "Price / Video (USD)",
464
+ "Generations",
465
  "Evaluation Date (UTC)",
466
  "Date",
467
  ]
 
513
  if pd.isna(value) or value is None or value == "":
514
  return "-"
515
  label = str(column).lower()
516
+ if label in {"rank", "generations"}:
517
  return str(int(value))
518
  if "price" in label:
519
  return _format_price(value)
 
688
  "Arena Text Rendering Elo": "Text Rendering",
689
  "Median Generation Time (s)": "Median generation time",
690
  "Min Generation Time (s)": "Min generation time",
691
+ "Time / Output Video Second (s)": "Seconds for 1s of video",
692
  "Price / Image (USD)": "Price per image",
693
+ "Price / Video (USD)": "Price per video",
694
+ "Video Edit Elo": "Elo",
695
+ "Generations": "Generations",
696
  "Evaluation Date (UTC)": "Date",
697
  "Date": "Date",
698
  }
 
767
  )
768
 
769
 
770
+ def _is_pruna_model(model_id) -> bool:
771
+ raw = str(model_id or "").casefold()
772
+ label = display_model_name(model_id).casefold()
773
+ return any(
774
+ value.startswith(prefix)
775
+ for value in (raw, label)
776
+ for prefix in ("p-image", "p_image", "p-video", "p_video")
777
+ )
778
+
779
+
780
+ def _pareto_fill_colors(models):
781
+ return [
782
+ _PARETO_PRUNA_COLOR if _is_pruna_model(model) else _PARETO_OTHER_COLOR
783
+ for model in models
784
+ ]
785
+
786
+
787
  def _build_pareto_figure(
788
  data,
789
  score_column,
 
808
 
809
  dominated = scatter.loc[[not flag for flag in on_frontier]].copy()
810
  frontier = scatter.loc[on_frontier].sort_values(x_column).copy()
811
+ dominated_colors = _pareto_fill_colors(dominated["Model"]) if not dominated.empty else []
812
+ frontier_colors = _pareto_fill_colors(frontier["Model"]) if not frontier.empty else []
813
  if not dominated.empty:
814
  dominated["Model"] = dominated["Model"].map(display_model_name)
815
  if not frontier.empty:
 
830
  name="Below frontier",
831
  text=dominated["Model"],
832
  hovertemplate=hover,
833
+ showlegend=False,
834
  marker={
835
  "size": 9,
836
+ "color": dominated_colors,
837
+ "opacity": 0.85,
838
  "line": {"width": 0},
839
  },
840
  )
 
848
  name="On frontier",
849
  text=frontier["Model"],
850
  hovertemplate=hover,
851
+ showlegend=False,
852
+ line={"color": _PARETO_FRONTIER_OUTLINE, "width": 2.5},
853
  marker={
854
  "size": 12,
855
+ "color": frontier_colors,
856
+ "line": {"width": 2.5, "color": _PARETO_FRONTIER_OUTLINE},
857
  },
858
  )
859
  )
860
+ for name, marker in (
861
+ (
862
+ "Pruna",
863
+ {
864
+ "size": 10,
865
+ "color": _PARETO_PRUNA_COLOR,
866
+ "line": {"width": 0},
867
+ },
868
+ ),
869
+ (
870
+ "Other models",
871
+ {
872
+ "size": 10,
873
+ "color": _PARETO_OTHER_COLOR,
874
+ "line": {"width": 0},
875
+ },
876
+ ),
877
+ (
878
+ "On frontier",
879
+ {
880
+ "size": 12,
881
+ "color": "rgba(0,0,0,0)",
882
+ "line": {"width": 2.5, "color": _PARETO_FRONTIER_OUTLINE},
883
+ },
884
+ ),
885
+ ):
886
+ fig.add_trace(
887
+ go.Scatter(
888
+ x=[None],
889
+ y=[None],
890
+ mode="markers",
891
+ name=name,
892
+ marker=marker,
893
+ hoverinfo="skip",
894
+ )
895
+ )
896
 
897
  score_label = _display_label(score_column)
898
  fig.update_layout(
 
1032
  if data is None or not score_column or score_column not in data.columns:
1033
  return None, score_missing, None, score_missing
1034
 
1035
+ price_column = _pareto_price_column(data) or _PARETO_PRICE_COLUMN
1036
+ price_title = _PARETO_PRICE_TITLES.get(price_column, "Price (USD)")
1037
+ price_missing = (
1038
+ "Price per video isn't available for this dataset."
1039
+ if price_column == _PARETO_VIDEO_PRICE_COLUMN
1040
+ else "Price per image isn't available for this dataset."
1041
+ )
1042
  price_fig, price_message = _pareto_axis(
1043
  data,
1044
  score_column,
1045
+ price_column,
1046
+ price_title,
1047
+ price_missing,
1048
  "No models have both a score and a price for this metric.",
1049
  x_hover_prefix="$",
1050
  x_axis_type=_pareto_axis_type(price_scale),
1051
  )
1052
+ time_column = _pareto_time_column(data) or _PARETO_TIME_COLUMN
1053
+ time_title = _PARETO_TIME_TITLES.get(time_column, "Generation time (s)")
1054
+ time_missing = (
1055
+ "Seconds needed for 1s of output video isn't available for this dataset."
1056
+ if time_column == _PARETO_VIDEO_TIME_COLUMN
1057
+ else "Min generation time isn't available for this dataset."
1058
+ )
1059
+ time_empty = (
1060
+ "No models have both a score and seconds needed for 1s of output "
1061
+ "video for this metric."
1062
+ if time_column == _PARETO_VIDEO_TIME_COLUMN
1063
+ else "No models have both a score and a min generation time for this metric."
1064
+ )
1065
  time_fig, time_message = _pareto_axis(
1066
  data,
1067
  score_column,
1068
+ time_column,
1069
+ time_title,
1070
+ time_missing,
1071
+ time_empty,
1072
  x_hover_suffix="s",
1073
  x_axis_type=_pareto_axis_type(latency_scale),
1074
  )
 
1076
 
1077
 
1078
  def _pareto_dataset_message(data):
1079
+ has_price = _pareto_price_column(data) is not None
1080
+ has_time = _pareto_time_column(data) is not None
1081
  if has_price or has_time:
1082
  return None
1083
  return (
1084
+ "Price and generation time aren't available for "
1085
  "this dataset, so these plots can't be drawn."
1086
  )
1087
 
1088
 
1089
  def _pareto_slot_note(price_fig, price_message, time_fig, time_message, data):
1090
+ has_price = _pareto_price_column(data) is not None
1091
+ has_time = _pareto_time_column(data) is not None
1092
  notes = []
1093
  if has_price and not has_time:
1094
  notes.append(
1095
+ "Generation time isn't available for this dataset, so only "
1096
  "price vs score is shown."
1097
  )
1098
  elif has_time and not has_price:
1099
  notes.append(
1100
+ "Price isn't available for this dataset, so only "
1101
+ "time vs score is shown."
1102
  )
1103
  if price_fig is None and has_price:
1104
  notes.append(price_message)
 
1119
  score_columns = [column for column in (score_columns or []) if column]
1120
  price_scales = _normalize_pareto_scales(price_scales)
1121
  time_scales = _normalize_pareto_scales(time_scales)
1122
+ has_price = _pareto_price_column(data) is not None
1123
+ has_time = _pareto_time_column(data) is not None
1124
  dataset_note = _pareto_dataset_message(data)
1125
  updates = [_pareto_note_update(dataset_note)]
1126
  hide_all_slots = not has_price and not has_time
 
1288
  metric_id = _coerce_metric(
1289
  datasets, metrics, default_dataset_id, default_metric_id
1290
  )
1291
+ default_modality = _dataset_modality(_item(datasets, default_dataset_id))
1292
  with gr.Row(elem_classes="view-filters"):
1293
+ modality_dd = gr.Dropdown(
1294
+ choices=_modality_choices(datasets),
1295
+ value=default_modality,
1296
+ label="Type",
1297
+ type="value",
1298
+ filterable=False,
1299
+ scale=1,
1300
+ min_width=150,
1301
+ )
1302
  dataset_dd = gr.Dropdown(
1303
+ choices=_dataset_choices(datasets, modality=default_modality),
1304
  value=default_dataset_id,
1305
  label="Dataset",
1306
  type="value",
 
1332
  min_width=180,
1333
  elem_classes="filter-chips",
1334
  )
1335
+ return modality_dd, dataset_dd, metric_dd, models_dd
1336
 
1337
 
1338
  def render_image_workspace(datasets, metrics, default_dataset_id, default_metric_id):
 
1347
  with gr.Column(elem_classes="workspace-filters") as filters_host:
1348
  gr.Markdown(
1349
  "<p class='filter-help'>"
1350
+ "Start with Type to switch between Video to Video and Text "
1351
+ "to Image. The rest of the filters follow you across "
1352
+ "Leaderboards, Pareto plots, and Samples. Samples only "
1353
+ "lists datasets and models we have generations for; Pareto "
1354
+ "plots only lists datasets with price or generation time. "
1355
+ "Search in Models, or leave it empty to include every model."
1356
  "</p>",
1357
  elem_classes="filter-help-host",
1358
  )
1359
+ modality_dd, dataset_dd, metric_dd, models_dd = _filter_row(
1360
  datasets, metrics, default_dataset_id, None
1361
  )
1362
  with gr.Tabs(elem_classes="main-tabs") as main_tabs:
 
1482
  elem_classes="pareto-col",
1483
  ) as slot_time_col:
1484
  slot_time_scale = _pareto_plot_heading(
1485
+ "Time vs score"
1486
  )
1487
  slot_time = gr.Plot(
1488
  value=None,
 
1636
  ):
1637
  prev = dict(view_state or {})
1638
  extras = extras or {}
1639
+ modality = _dataset_modality(_item(datasets, dataset_id))
1640
+ last_by_modality = dict(prev.get("dataset_by_modality") or {})
1641
+ if dataset_id:
1642
+ last_by_modality[modality] = dataset_id
1643
  return {
1644
  "dataset_id": dataset_id,
1645
+ "modality": modality,
1646
+ "dataset_by_modality": last_by_modality,
1647
  "metric_id": metric_id,
1648
  "models": list(models or []),
1649
  "current_tab": tab,
 
1781
  tab = view_state.get("current_tab") or TAB_LEADERBOARDS
1782
  selected_raw = _normalize_metric_ids(metric_id)
1783
  incoming_models = list(models or [])
1784
+ filter_changed = source in {"dataset", "modality"}
1785
+ dataset_changed = filter_changed and dataset_id != view_state.get(
1786
  "dataset_id"
1787
  )
1788
  can_pareto = _dataset_has_pareto(datasets, dataset_id)
1789
  can_samples = _dataset_has_samples(datasets, dataset_id)
1790
  selected_tab = tab
1791
+ if filter_changed:
1792
  if tab == TAB_SAMPLES and not can_samples:
1793
  selected_tab = TAB_LEADERBOARDS
1794
  elif tab == TAB_PARETO and not can_pareto:
 
1797
  dataset_id,
1798
  metric_id,
1799
  models,
1800
+ clear_metric=source == "modality" or dataset_changed,
1801
  require_samples=selected_tab == TAB_SAMPLES,
1802
  )
1803
  dataset_id, metric_id, models = synced[:3]
 
1842
  list(optimized_value or []),
1843
  )
1844
  extra_updates = None
1845
+ if filter_changed:
1846
  view = resolve_view(datasets, metrics, dataset_id, metric_id)
1847
  extra_updates = _leaderboard_extras(
1848
  view["data"] if view else None,
 
1895
  ),
1896
  }
1897
 
1898
+ def on_modality(
1899
+ modality,
1900
+ dataset_id,
1901
+ metric_id,
1902
+ models,
1903
+ platform_value,
1904
+ owner_value,
1905
+ optimized_value,
1906
+ num_prompts,
1907
+ seed,
1908
+ view_state,
1909
+ ):
1910
+ view_state = dict(view_state or {})
1911
+ last_by_modality = dict(view_state.get("dataset_by_modality") or {})
1912
+ current_modality = view_state.get("modality") or _dataset_modality(
1913
+ _item(datasets, dataset_id)
1914
+ )
1915
+ if dataset_id:
1916
+ last_by_modality[current_modality] = dataset_id
1917
+ dataset_id = _default_dataset_id(
1918
+ datasets, modality, last_by_modality.get(modality)
1919
+ )
1920
+ view_state["modality"] = modality
1921
+ view_state["dataset_by_modality"] = last_by_modality
1922
+ result = _apply_filter_change(
1923
+ "modality",
1924
+ dataset_id,
1925
+ metric_id,
1926
+ models,
1927
+ platform_value,
1928
+ owner_value,
1929
+ optimized_value,
1930
+ num_prompts,
1931
+ seed,
1932
+ view_state,
1933
+ )
1934
+ if result is None:
1935
+ return _skip_all(len(dataset_outputs))
1936
+ extras = result["extra_updates"]
1937
+ return (
1938
+ _dataset_dropdown_update(
1939
+ datasets,
1940
+ result["selected_tab"],
1941
+ result["dataset_id"],
1942
+ modality=modality,
1943
+ ),
1944
+ result["metric_update"],
1945
+ result["models_update"],
1946
+ extras[6],
1947
+ extras[0],
1948
+ extras[1],
1949
+ extras[2],
1950
+ *result["views"],
1951
+ gr.update(interactive=result["can_pareto"]),
1952
+ gr.update(interactive=result["can_samples"]),
1953
+ gr.update(selected=result["selected_tab"])
1954
+ if result["selected_tab"] != result["tab"]
1955
+ else gr.skip(),
1956
+ result["state"],
1957
+ )
1958
+
1959
  def on_dataset(
1960
  dataset_id,
1961
  metric_id,
 
2305
  handler.__name__ = f"on_tab_{tab}"
2306
  return handler
2307
 
2308
+ default_modality = _dataset_modality(_item(datasets, default_dataset_id))
2309
  view_state = gr.State(
2310
  {
2311
  "dataset_id": default_dataset_id,
2312
+ "modality": default_modality,
2313
+ "dataset_by_modality": {default_modality: default_dataset_id},
2314
  "metric_id": None,
2315
  "models": [],
2316
  "current_tab": TAB_LEADERBOARDS,
 
2392
  main_tabs,
2393
  view_state,
2394
  ]
2395
+ modality_dd.change(
2396
+ on_modality,
2397
+ inputs=[modality_dd, *filter_inputs],
2398
+ outputs=dataset_outputs,
2399
+ **_VIEW_EVENTS,
2400
+ )
2401
  dataset_dd.change(
2402
  on_dataset,
2403
  inputs=filter_inputs,