Minette Kaunismäki commited on
Commit
6d8f26e
·
2 Parent(s): e680130a8fa348

Merge style/ui-update into pr/3

Browse files
Files changed (2) hide show
  1. app.py +293 -21
  2. ui.py +296 -14
app.py CHANGED
@@ -62,6 +62,9 @@ custom_css = """
62
  --pruna-accordion-bg: rgba(255, 255, 255, 0.02);
63
  --pruna-accordion-border: rgba(216, 180, 254, 0.15);
64
  --pruna-dropdown-hover: #2a1844;
 
 
 
65
  color-scheme: dark;
66
  }
67
 
@@ -105,13 +108,27 @@ custom_css = """
105
  --pruna-accordion-bg: var(--pruna-bg-card);
106
  --pruna-accordion-border: var(--pruna-border);
107
  --pruna-dropdown-hover: #f3e8ff;
 
 
 
108
  color-scheme: light;
109
  }
110
 
111
- html, body {
112
  width: 100% !important;
113
  max-width: 100% !important;
114
- overflow-x: clip;
 
 
 
 
 
 
 
 
 
 
 
115
  -webkit-tap-highlight-color: transparent;
116
  }
117
  html, body, .gradio-container, .main {
@@ -134,18 +151,25 @@ button, a, label, input, select, textarea,
134
  /* Subtle depth — not a marketing-site hero glow */
135
  body, .gradio-container {
136
  background-image: var(--pruna-glow) !important;
137
- background-attachment: fixed !important;
 
 
 
 
 
 
138
  }
139
 
140
  .gradio-container {
141
  width: 100% !important;
142
  max-width: 1200px !important;
 
143
  margin: 0 auto !important;
144
  padding-top: 0 !important;
145
  padding-left: 20px !important;
146
  padding-right: 20px !important;
147
  box-sizing: border-box !important;
148
- overflow-x: clip;
149
  }
150
  .gradio-container .main,
151
  .gradio-container .wrap,
@@ -166,6 +190,7 @@ body, .gradio-container {
166
  .workspace-filters,
167
  .view-filters {
168
  max-width: 100% !important;
 
169
  }
170
 
171
  /* —— App header (P-Bench only) —— */
@@ -300,6 +325,9 @@ button.theme-toggle[data-mode="light"] .theme-icon-moon { display: block !import
300
  background: transparent !important;
301
  box-shadow: none !important;
302
  }
 
 
 
303
  .workspace-shell > .tabs,
304
  .workspace-shell > .main-tabs,
305
  .workspace-shell > .block:not(.workspace-filters),
@@ -323,12 +351,49 @@ button.theme-toggle[data-mode="light"] .theme-icon-moon { display: block !import
323
  margin: 0 0 16px !important;
324
  justify-content: center !important;
325
  width: 100% !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
  }
327
  .main-tabs .tab-container {
328
  height: auto !important;
 
329
  justify-content: center !important;
330
  flex-wrap: wrap !important;
331
- overflow: hidden !important;
332
  max-width: 100% !important;
333
  gap: 2px;
334
  }
@@ -445,6 +510,8 @@ button.theme-toggle[data-mode="light"] .theme-icon-moon { display: block !import
445
  .app-header-brand h1,
446
  .gradio-container .app-header-brand h1 {
447
  font-size: 1.55rem !important;
 
 
448
  }
449
  .app-header-tagline {
450
  font-size: 0.88rem !important;
@@ -521,17 +588,29 @@ button.theme-toggle[data-mode="light"] .theme-icon-moon { display: block !import
521
  left: 0 !important;
522
  width: 2.4rem;
523
  min-width: 2.4rem;
 
524
  }
525
  .ranking-table .model-cell,
526
- .prose .ranking-table .model-cell,
527
- .ranking-table th.model-cell {
 
 
 
 
 
 
 
 
528
  position: sticky !important;
529
- left: 2.4rem !important;
530
- min-width: 108px;
531
- max-width: 36vw;
 
 
 
532
  }
533
- .ranking-table .model-cell strong {
534
- white-space: nowrap;
535
  }
536
  .compare-controls,
537
  .compare-controls.row,
@@ -604,7 +683,7 @@ button.theme-toggle[data-mode="light"] .theme-icon-moon { display: block !import
604
  .view-filters {
605
  display: flex !important;
606
  flex-wrap: wrap !important;
607
- align-items: end !important;
608
  gap: 12px !important;
609
  margin: 0;
610
  overflow: visible !important;
@@ -612,7 +691,7 @@ button.theme-toggle[data-mode="light"] .theme-icon-moon { display: block !import
612
  .view-filters > div,
613
  .view-filters > .block,
614
  .view-filters > .form {
615
- flex: 1 1 0 !important;
616
  min-width: 0 !important;
617
  }
618
  .view-filters > .block,
@@ -764,6 +843,10 @@ button.theme-toggle[data-mode="light"] .theme-icon-moon { display: block !import
764
  line-height: 1.45 !important;
765
  font-weight: 400 !important;
766
  }
 
 
 
 
767
  .view-filters span[data-testid="block-info"],
768
  .view-filters .info,
769
  .view-filters .block-info {
@@ -901,7 +984,7 @@ button.theme-toggle[data-mode="light"] .theme-icon-moon { display: block !import
901
  .leaderboard-controls {
902
  display: flex !important;
903
  flex-wrap: wrap !important;
904
- align-items: end !important;
905
  gap: 10px !important;
906
  margin-bottom: 12px;
907
  overflow: visible !important;
@@ -1317,7 +1400,9 @@ button.theme-toggle[data-mode="light"] .theme-icon-moon { display: block !import
1317
  box-shadow: none !important;
1318
  }
1319
  .compare-controls .compare-prompt-count .head {
1320
- display: contents;
 
 
1321
  margin: 0 !important;
1322
  }
1323
  .compare-controls .compare-prompt-count .head label {
@@ -1491,7 +1576,182 @@ button.theme-toggle[data-mode="light"] .theme-icon-moon { display: block !import
1491
  margin: 0 !important;
1492
  }
1493
  .compare-row { display: grid; gap: 12px; min-width: 0; width: 100%; }
1494
- .compare-prompt-text { overflow-wrap: anywhere; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1495
  .pareto-layout,
1496
  .pareto-layout.row,
1497
  .pareto-layout .form {
@@ -1549,7 +1809,7 @@ button.theme-toggle[data-mode="light"] .theme-icon-moon { display: block !import
1549
  .app-header .app-header-brand h1 {
1550
  display: block !important;
1551
  width: max-content !important;
1552
- max-width: none !important;
1553
  flex: 0 0 auto !important;
1554
  margin: 0 !important;
1555
  padding: 0 !important;
@@ -2317,11 +2577,23 @@ custom_head = """
2317
  return found;
2318
  };
2319
 
2320
- const restylePlots = (mode) => {
2321
  const layout = PLOT_LAYOUT[mode];
2322
- if (!layout || typeof Plotly === "undefined") return;
 
 
 
 
 
 
 
 
 
 
 
2323
  queryAll(".js-plotly-plot").forEach((gd) => {
2324
- try { Plotly.relayout(gd, layout); } catch (e) {}
 
2325
  });
2326
  };
2327
 
 
62
  --pruna-accordion-bg: rgba(255, 255, 255, 0.02);
63
  --pruna-accordion-border: rgba(216, 180, 254, 0.15);
64
  --pruna-dropdown-hover: #2a1844;
65
+ --pruna-toggle-track: var(--pruna-bg-header);
66
+ --pruna-toggle-thumb: var(--pruna-bg-elevated);
67
+ --pruna-toggle-thumb-shadow: 0 1px 2px rgba(0, 0, 0, 0.45), inset 0 1px rgba(255, 255, 255, 0.06);
68
  color-scheme: dark;
69
  }
70
 
 
108
  --pruna-accordion-bg: var(--pruna-bg-card);
109
  --pruna-accordion-border: var(--pruna-border);
110
  --pruna-dropdown-hover: #f3e8ff;
111
+ --pruna-toggle-track: var(--pruna-bg-header);
112
+ --pruna-toggle-thumb: var(--pruna-bg-card);
113
+ --pruna-toggle-thumb-shadow: 0 1px 2px rgba(88, 28, 135, 0.12);
114
  color-scheme: light;
115
  }
116
 
117
+ html {
118
  width: 100% !important;
119
  max-width: 100% !important;
120
+ min-width: 0 !important;
121
+ overflow-x: hidden;
122
+ overflow-y: auto;
123
+ -webkit-text-size-adjust: 100%;
124
+ text-size-adjust: 100%;
125
+ -webkit-tap-highlight-color: transparent;
126
+ }
127
+ body, gradio-app {
128
+ width: 100% !important;
129
+ max-width: 100% !important;
130
+ min-width: 0 !important;
131
+ overflow: visible;
132
  -webkit-tap-highlight-color: transparent;
133
  }
134
  html, body, .gradio-container, .main {
 
151
  /* Subtle depth — not a marketing-site hero glow */
152
  body, .gradio-container {
153
  background-image: var(--pruna-glow) !important;
154
+ background-repeat: no-repeat !important;
155
+ background-attachment: scroll !important;
156
+ }
157
+ @media (min-width: 701px) and (hover: hover) and (pointer: fine) {
158
+ body, .gradio-container {
159
+ background-attachment: fixed !important;
160
+ }
161
  }
162
 
163
  .gradio-container {
164
  width: 100% !important;
165
  max-width: 1200px !important;
166
+ min-width: 0 !important;
167
  margin: 0 auto !important;
168
  padding-top: 0 !important;
169
  padding-left: 20px !important;
170
  padding-right: 20px !important;
171
  box-sizing: border-box !important;
172
+ overflow-x: hidden;
173
  }
174
  .gradio-container .main,
175
  .gradio-container .wrap,
 
190
  .workspace-filters,
191
  .view-filters {
192
  max-width: 100% !important;
193
+ min-width: 0 !important;
194
  }
195
 
196
  /* —— App header (P-Bench only) —— */
 
325
  background: transparent !important;
326
  box-shadow: none !important;
327
  }
328
+ /* Flatten tabs so the bar sits above shared filters. display:contents is
329
+ the fallback; Safari can drop or mis-order those children, so browsers
330
+ with subgrid use the grid layout below instead. */
331
  .workspace-shell > .tabs,
332
  .workspace-shell > .main-tabs,
333
  .workspace-shell > .block:not(.workspace-filters),
 
351
  margin: 0 0 16px !important;
352
  justify-content: center !important;
353
  width: 100% !important;
354
+ overflow: visible !important;
355
+ }
356
+ @supports (grid-template-rows: subgrid) {
357
+ .workspace-shell,
358
+ .workspace-shell.block,
359
+ .workspace-shell.column,
360
+ .workspace-shell.gap {
361
+ display: grid !important;
362
+ grid-template-columns: minmax(0, 1fr) !important;
363
+ grid-template-rows: auto auto auto !important;
364
+ align-content: start !important;
365
+ }
366
+ .workspace-shell > .tabs,
367
+ .workspace-shell > .main-tabs,
368
+ .workspace-shell .tabs.main-tabs {
369
+ display: grid !important;
370
+ grid-template-columns: minmax(0, 1fr) !important;
371
+ grid-template-rows: subgrid !important;
372
+ grid-column: 1 !important;
373
+ grid-row: 1 / 4 !important;
374
+ position: static !important;
375
+ }
376
+ .main-tabs > .tab-wrapper {
377
+ grid-row: 1 !important;
378
+ order: 0 !important;
379
+ }
380
+ .workspace-filters {
381
+ grid-column: 1 !important;
382
+ grid-row: 2 !important;
383
+ order: 0 !important;
384
+ }
385
+ .main-tabs .tabitem {
386
+ grid-row: 3 !important;
387
+ order: 0 !important;
388
+ min-width: 0 !important;
389
+ }
390
  }
391
  .main-tabs .tab-container {
392
  height: auto !important;
393
+ min-height: 0 !important;
394
  justify-content: center !important;
395
  flex-wrap: wrap !important;
396
+ overflow: visible !important;
397
  max-width: 100% !important;
398
  gap: 2px;
399
  }
 
510
  .app-header-brand h1,
511
  .gradio-container .app-header-brand h1 {
512
  font-size: 1.55rem !important;
513
+ width: auto !important;
514
+ max-width: 100% !important;
515
  }
516
  .app-header-tagline {
517
  font-size: 0.88rem !important;
 
588
  left: 0 !important;
589
  width: 2.4rem;
590
  min-width: 2.4rem;
591
+ box-shadow: 6px 0 8px -6px rgba(0, 0, 0, 0.45);
592
  }
593
  .ranking-table .model-cell,
594
+ .prose .ranking-table .model-cell {
595
+ position: static !important;
596
+ left: auto !important;
597
+ z-index: auto;
598
+ min-width: 140px;
599
+ max-width: none;
600
+ background: transparent !important;
601
+ }
602
+ .ranking-table th.model-cell,
603
+ .prose .ranking-table th.model-cell {
604
  position: sticky !important;
605
+ top: 0 !important;
606
+ left: auto !important;
607
+ z-index: 3;
608
+ min-width: 140px;
609
+ max-width: none;
610
+ background: var(--pruna-bg-header) !important;
611
  }
612
+ .ranking-table tbody tr:hover .model-cell {
613
+ background: var(--pruna-table-hover) !important;
614
  }
615
  .compare-controls,
616
  .compare-controls.row,
 
683
  .view-filters {
684
  display: flex !important;
685
  flex-wrap: wrap !important;
686
+ align-items: flex-end !important;
687
  gap: 12px !important;
688
  margin: 0;
689
  overflow: visible !important;
 
691
  .view-filters > div,
692
  .view-filters > .block,
693
  .view-filters > .form {
694
+ flex: 1 1 0% !important;
695
  min-width: 0 !important;
696
  }
697
  .view-filters > .block,
 
843
  line-height: 1.45 !important;
844
  font-weight: 400 !important;
845
  }
846
+ .view-help + .view-help,
847
+ .prose .view-help + .view-help {
848
+ margin-top: 0.45rem !important;
849
+ }
850
  .view-filters span[data-testid="block-info"],
851
  .view-filters .info,
852
  .view-filters .block-info {
 
984
  .leaderboard-controls {
985
  display: flex !important;
986
  flex-wrap: wrap !important;
987
+ align-items: flex-end !important;
988
  gap: 10px !important;
989
  margin-bottom: 12px;
990
  overflow: visible !important;
 
1400
  box-shadow: none !important;
1401
  }
1402
  .compare-controls .compare-prompt-count .head {
1403
+ display: block !important;
1404
+ grid-column: 1 / -1;
1405
+ grid-row: 1;
1406
  margin: 0 !important;
1407
  }
1408
  .compare-controls .compare-prompt-count .head label {
 
1576
  margin: 0 !important;
1577
  }
1578
  .compare-row { display: grid; gap: 12px; min-width: 0; width: 100%; }
1579
+ .compare-prompt-text { overflow-wrap: anywhere; word-break: break-word; }
1580
+ .pareto-heading-row,
1581
+ .pareto-heading-row.row,
1582
+ .pareto-heading-row .form {
1583
+ display: flex !important;
1584
+ flex-wrap: wrap !important;
1585
+ align-items: center !important;
1586
+ gap: 8px 12px !important;
1587
+ width: 100% !important;
1588
+ margin-bottom: 0.4rem !important;
1589
+ }
1590
+ .pareto-heading-row .pareto-subhead,
1591
+ .pareto-heading-row > div:first-child,
1592
+ .pareto-heading-row .form > div:first-child {
1593
+ flex: 1 1 240px !important;
1594
+ min-width: 0 !important;
1595
+ margin: 0 !important;
1596
+ }
1597
+ .pareto-heading-row .pareto-scale-control {
1598
+ display: flex !important;
1599
+ flex-direction: row !important;
1600
+ align-items: center !important;
1601
+ justify-content: flex-end !important;
1602
+ flex: 0 0 auto !important;
1603
+ gap: 0 !important;
1604
+ margin-left: auto !important;
1605
+ max-width: 168px !important;
1606
+ padding: 0 !important;
1607
+ }
1608
+ .pareto-scale-all-row,
1609
+ .pareto-scale-all-row.row,
1610
+ .pareto-scale-all-row .form {
1611
+ display: flex !important;
1612
+ flex-direction: row !important;
1613
+ flex-wrap: wrap !important;
1614
+ align-items: center !important;
1615
+ justify-content: flex-start !important;
1616
+ gap: 8px 12px !important;
1617
+ width: 100% !important;
1618
+ margin: 2px 0 14px !important;
1619
+ }
1620
+ .pareto-scale-all-row .html-container,
1621
+ .pareto-scale-all-row .block {
1622
+ border: none !important;
1623
+ background: transparent !important;
1624
+ box-shadow: none !important;
1625
+ padding: 0 !important;
1626
+ margin: 0 !important;
1627
+ width: auto !important;
1628
+ flex: 0 0 auto !important;
1629
+ }
1630
+ .pareto-scale-all-row .html-container {
1631
+ flex: 1 1 auto !important;
1632
+ min-width: 0 !important;
1633
+ }
1634
+ .pareto-scale-all-label {
1635
+ color: var(--pruna-text-muted);
1636
+ font-size: 0.8rem;
1637
+ font-weight: 500;
1638
+ white-space: nowrap;
1639
+ }
1640
+ .pareto-scale-all-row .pareto-scale-toggle {
1641
+ margin-left: auto !important;
1642
+ }
1643
+ .pareto-scale-toggle,
1644
+ .pareto-scale-toggle.block {
1645
+ min-width: 0 !important;
1646
+ width: auto !important;
1647
+ border: none !important;
1648
+ background: transparent !important;
1649
+ box-shadow: none !important;
1650
+ padding: 0 !important;
1651
+ margin: 0 !important;
1652
+ }
1653
+ .pareto-scale-toggle .wrap,
1654
+ .pareto-scale-toggle .form {
1655
+ display: block !important;
1656
+ width: auto !important;
1657
+ margin: 0 !important;
1658
+ padding: 0 !important;
1659
+ border: none !important;
1660
+ background: transparent !important;
1661
+ box-shadow: none !important;
1662
+ }
1663
+ .pareto-scale-toggle legend {
1664
+ display: none !important;
1665
+ }
1666
+ .pareto-scale-toggle fieldset,
1667
+ .pareto-scale-toggle .wrap:has(> label),
1668
+ .pareto-scale-toggle .form:has(> label) {
1669
+ position: relative !important;
1670
+ display: grid !important;
1671
+ grid-template-columns: 1fr 1fr !important;
1672
+ align-items: stretch !important;
1673
+ isolation: isolate;
1674
+ box-sizing: border-box !important;
1675
+ width: max-content !important;
1676
+ min-width: 0 !important;
1677
+ padding: 4px !important;
1678
+ gap: 4px !important;
1679
+ border: 1px solid var(--pruna-input-border) !important;
1680
+ border-radius: 10px !important;
1681
+ background: var(--pruna-toggle-track) !important;
1682
+ box-shadow: none !important;
1683
+ }
1684
+ .pareto-scale-toggle fieldset::before,
1685
+ .pareto-scale-toggle .wrap:has(> label)::before,
1686
+ .pareto-scale-toggle .form:has(> label)::before {
1687
+ content: none !important;
1688
+ }
1689
+ .pareto-scale-toggle label {
1690
+ position: relative !important;
1691
+ z-index: 1 !important;
1692
+ display: flex !important;
1693
+ flex: 1 1 auto !important;
1694
+ align-items: center !important;
1695
+ justify-content: center !important;
1696
+ gap: 0 !important;
1697
+ box-sizing: border-box !important;
1698
+ min-width: 58px !important;
1699
+ min-height: 26px !important;
1700
+ margin: 0 !important;
1701
+ padding: 5px 12px !important;
1702
+ border: none !important;
1703
+ border-radius: 6px !important;
1704
+ background: transparent !important;
1705
+ box-shadow: none !important;
1706
+ color: var(--pruna-text-body) !important;
1707
+ font-size: 0.72rem !important;
1708
+ font-weight: 600 !important;
1709
+ line-height: 1.2 !important;
1710
+ letter-spacing: 0.01em;
1711
+ white-space: nowrap;
1712
+ cursor: pointer !important;
1713
+ }
1714
+ .pareto-scale-toggle-all label {
1715
+ min-width: 68px !important;
1716
+ min-height: 30px !important;
1717
+ padding: 6px 14px !important;
1718
+ font-size: 0.85rem !important;
1719
+ }
1720
+ .pareto-scale-toggle label span {
1721
+ margin: 0 !important;
1722
+ padding: 0 !important;
1723
+ color: inherit !important;
1724
+ opacity: 1 !important;
1725
+ }
1726
+ .pareto-scale-toggle label > * + * {
1727
+ margin-left: 0 !important;
1728
+ }
1729
+ .pareto-scale-toggle label + label::before,
1730
+ .pareto-scale-toggle label + label {
1731
+ content: none !important;
1732
+ border-left: none !important;
1733
+ }
1734
+ .pareto-scale-toggle input[type="radio"] {
1735
+ position: absolute !important;
1736
+ appearance: none !important;
1737
+ opacity: 0 !important;
1738
+ width: 0 !important;
1739
+ height: 0 !important;
1740
+ margin: 0 !important;
1741
+ pointer-events: none !important;
1742
+ }
1743
+ .pareto-scale-toggle label:hover {
1744
+ background: transparent !important;
1745
+ color: var(--pruna-text-primary) !important;
1746
+ }
1747
+ .pareto-scale-toggle label.selected,
1748
+ .pareto-scale-toggle label:has(input:checked) {
1749
+ background: var(--pruna-toggle-thumb) !important;
1750
+ color: var(--pruna-lavender) !important;
1751
+ font-weight: 700 !important;
1752
+ border-color: transparent !important;
1753
+ box-shadow: var(--pruna-toggle-thumb-shadow) !important;
1754
+ }
1755
  .pareto-layout,
1756
  .pareto-layout.row,
1757
  .pareto-layout .form {
 
1809
  .app-header .app-header-brand h1 {
1810
  display: block !important;
1811
  width: max-content !important;
1812
+ max-width: 100% !important;
1813
  flex: 0 0 auto !important;
1814
  margin: 0 !important;
1815
  padding: 0 !important;
 
2577
  return found;
2578
  };
2579
 
2580
+ const applyPlotTheme = (gd, mode) => {
2581
  const layout = PLOT_LAYOUT[mode];
2582
+ if (!layout || typeof Plotly === "undefined" || !gd) return;
2583
+ if (gd.layout && gd.layout.paper_bgcolor === layout.paper_bgcolor) return;
2584
+ try { Plotly.relayout(gd, layout); } catch (e) {}
2585
+ };
2586
+
2587
+ const watchPlotTheme = (gd) => {
2588
+ if (!gd || gd.__inferbenchThemeBound) return;
2589
+ gd.__inferbenchThemeBound = true;
2590
+ gd.addEventListener("plotly_afterplot", () => applyPlotTheme(gd, currentMode()));
2591
+ };
2592
+
2593
+ const restylePlots = (mode) => {
2594
  queryAll(".js-plotly-plot").forEach((gd) => {
2595
+ watchPlotTheme(gd);
2596
+ applyPlotTheme(gd, mode);
2597
  });
2598
  };
2599
 
ui.py CHANGED
@@ -1,4 +1,5 @@
1
  from html import escape
 
2
  from pathlib import Path
3
  import base64
4
  import random
@@ -25,6 +26,12 @@ MAX_PARETO_METRICS = 8
25
  _PARETO_SLOT_COUNT = 1 + MAX_PARETO_METRICS * 8
26
  _PARETO_PRICE_COLUMN = "Price / Image (USD)"
27
  _PARETO_TIME_COLUMN = "Min Generation Time (s)"
 
 
 
 
 
 
28
 
29
  TAB_LEADERBOARDS = "leaderboards"
30
  TAB_PARETO = "pareto"
@@ -670,6 +677,7 @@ def _build_pareto_figure(
670
  x_title,
671
  x_hover_prefix="",
672
  x_hover_suffix="",
 
673
  ):
674
  scatter = (
675
  data[["Model", score_column, x_column]]
@@ -755,7 +763,29 @@ def _build_pareto_figure(
755
  )
756
  axis_font = {"color": "#fafafa", "size": 13}
757
  tick_font = {"color": "#a3a3a3", "size": 12}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
758
  fig.update_xaxes(
 
 
759
  showgrid=True,
760
  gridcolor="rgba(74, 57, 98, 0.55)",
761
  zeroline=False,
@@ -774,6 +804,55 @@ def _build_pareto_figure(
774
  return fig
775
 
776
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
777
  def _pareto_axis(data, score_column, x_column, x_title, missing_message, empty_message, **hover):
778
  if x_column not in data.columns:
779
  return None, missing_message
@@ -789,7 +868,12 @@ def _pareto_axis(data, score_column, x_column, x_title, missing_message, empty_m
789
  return fig, None
790
 
791
 
792
- def _pareto_pair(data, score_column):
 
 
 
 
 
793
  score_missing = "No score data is available for this metric."
794
  if data is None or not score_column or score_column not in data.columns:
795
  return None, score_missing, None, score_missing
@@ -802,6 +886,7 @@ def _pareto_pair(data, score_column):
802
  "Price per image isn't available for this dataset.",
803
  "No models have both a score and a price for this metric.",
804
  x_hover_prefix="$",
 
805
  )
806
  time_fig, time_message = _pareto_axis(
807
  data,
@@ -811,6 +896,7 @@ def _pareto_pair(data, score_column):
811
  "Min generation time isn't available for this dataset.",
812
  "No models have both a score and a min generation time for this metric.",
813
  x_hover_suffix="s",
 
814
  )
815
  return price_fig, price_message, time_fig, time_message
816
 
@@ -849,9 +935,16 @@ def _pareto_slot_note(price_fig, price_message, time_fig, time_message, data):
849
  return " ".join(notes)
850
 
851
 
852
- def _pareto_slot_updates(data, score_columns):
 
 
 
 
 
853
  """Updates for a fixed bank of Gradio Plot slots (visible/hidden)."""
854
  score_columns = [column for column in (score_columns or []) if column]
 
 
855
  has_price = data is not None and _PARETO_PRICE_COLUMN in data.columns
856
  has_time = data is not None and _PARETO_TIME_COLUMN in data.columns
857
  dataset_note = _pareto_dataset_message(data)
@@ -873,7 +966,10 @@ def _pareto_slot_updates(data, score_columns):
873
  continue
874
  score_column = score_columns[index]
875
  price_fig, price_message, time_fig, time_message = _pareto_pair(
876
- data, score_column
 
 
 
877
  )
878
  show_price = price_fig is not None
879
  show_time = time_fig is not None
@@ -900,6 +996,30 @@ def _pareto_slot_updates(data, score_columns):
900
  return updates
901
 
902
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
903
  def _samples_html(samples, selected_models, num_prompts, seed=0):
904
  if not samples:
905
  return _pareto_unavailable_html(
@@ -1117,12 +1237,27 @@ def render_image_workspace(datasets, metrics, default_dataset_id, default_metric
1117
  ) as pp_tab:
1118
  gr.Markdown(
1119
  "<p class='view-help'>"
1120
- "Score against price and generation time. Green points are on the "
1121
- "frontier; lavender points sit below it. Hover a point to see "
1122
- "which model it is."
 
 
 
 
1123
  "</p>",
1124
  elem_classes="view-help-host",
1125
  )
 
 
 
 
 
 
 
 
 
 
 
1126
  pareto_dataset_note = gr.HTML(
1127
  "",
1128
  padding=False,
@@ -1148,9 +1283,8 @@ def render_image_workspace(datasets, metrics, default_dataset_id, default_metric
1148
  min_width=320,
1149
  elem_classes="pareto-col",
1150
  ) as slot_price_col:
1151
- gr.Markdown(
1152
- "#### Price vs score",
1153
- elem_classes="pareto-subhead",
1154
  )
1155
  slot_price = gr.Plot(
1156
  value=None,
@@ -1162,9 +1296,8 @@ def render_image_workspace(datasets, metrics, default_dataset_id, default_metric
1162
  min_width=320,
1163
  elem_classes="pareto-col",
1164
  ) as slot_time_col:
1165
- gr.Markdown(
1166
- "#### Min generation time vs score",
1167
- elem_classes="pareto-subhead",
1168
  )
1169
  slot_time = gr.Plot(
1170
  value=None,
@@ -1185,8 +1318,10 @@ def render_image_workspace(datasets, metrics, default_dataset_id, default_metric
1185
  slot_layout,
1186
  slot_price_col,
1187
  slot_price,
 
1188
  slot_time_col,
1189
  slot_time,
 
1190
  )
1191
  )
1192
 
@@ -1324,6 +1459,8 @@ def render_image_workspace(datasets, metrics, default_dataset_id, default_metric
1324
  "optimized": list(
1325
  extras.get("optimized", prev.get("optimized") or [])
1326
  ),
 
 
1327
  "stale": {
1328
  TAB_LEADERBOARDS: not flags["include_leaderboard"],
1329
  TAB_PARETO: not flags["include_pareto"],
@@ -1383,6 +1520,8 @@ def render_image_workspace(datasets, metrics, default_dataset_id, default_metric
1383
  include_leaderboard=True,
1384
  include_pareto=False,
1385
  include_samples=False,
 
 
1386
  ):
1387
  view = resolve_view(datasets, metrics, dataset_id, metric_id)
1388
  data = view["data"]
@@ -1403,7 +1542,12 @@ def render_image_workspace(datasets, metrics, default_dataset_id, default_metric
1403
  ranking_html = gr.skip()
1404
  if include_pareto:
1405
  pareto_data = _filter_leaderboard(data, [], [], [], models=models)
1406
- pareto_updates = _pareto_slot_updates(pareto_data, view["score_columns"])
 
 
 
 
 
1407
  else:
1408
  pareto_updates = _pareto_skip_updates()
1409
  if include_samples:
@@ -1537,6 +1681,8 @@ def render_image_workspace(datasets, metrics, default_dataset_id, default_metric
1537
  extras[2],
1538
  num_prompts,
1539
  seed,
 
 
1540
  **flags,
1541
  ),
1542
  "state": _commit_state(
@@ -1739,6 +1885,8 @@ def render_image_workspace(datasets, metrics, default_dataset_id, default_metric
1739
  optimized_value,
1740
  num_prompts,
1741
  seed,
 
 
1742
  **flags,
1743
  )
1744
  stale[tab] = False
@@ -1795,6 +1943,70 @@ def render_image_workspace(datasets, metrics, default_dataset_id, default_metric
1795
  next_seed,
1796
  )
1797
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1798
  def _on_tab(tab):
1799
  def handler(
1800
  dataset_id,
@@ -1832,6 +2044,8 @@ def render_image_workspace(datasets, metrics, default_dataset_id, default_metric
1832
  "platform": [],
1833
  "owner": [],
1834
  "optimized": [],
 
 
1835
  "stale": {
1836
  TAB_LEADERBOARDS: False,
1837
  TAB_PARETO: True,
@@ -1843,7 +2057,7 @@ def render_image_workspace(datasets, metrics, default_dataset_id, default_metric
1843
  pareto_dataset_note,
1844
  *[
1845
  component
1846
- for slot_group, slot_title, slot_note, slot_layout, slot_price_col, slot_price, slot_time_col, slot_time in pareto_slots
1847
  for component in (
1848
  slot_group,
1849
  slot_title,
@@ -1856,6 +2070,24 @@ def render_image_workspace(datasets, metrics, default_dataset_id, default_metric
1856
  )
1857
  ],
1858
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1859
  view_inputs = [
1860
  platform,
1861
  owner,
@@ -1960,6 +2192,56 @@ def render_image_workspace(datasets, metrics, default_dataset_id, default_metric
1960
  show_progress="hidden",
1961
  )
1962
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1963
  prompt_count.change(
1964
  on_samples_controls,
1965
  inputs=[dataset_dd, models_dd, prompt_count, seed_state],
 
1
  from html import escape
2
+ from math import ceil, floor, log10
3
  from pathlib import Path
4
  import base64
5
  import random
 
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
+ ("Linear", "Linear"),
31
+ ("Log", "Logarithmic"),
32
+ ]
33
+ _PARETO_SCALE_VALUES = {value for _, value in _PARETO_SCALE_CHOICES}
34
+ _PARETO_SCALE_DEFAULT = "Linear"
35
 
36
  TAB_LEADERBOARDS = "leaderboards"
37
  TAB_PARETO = "pareto"
 
677
  x_title,
678
  x_hover_prefix="",
679
  x_hover_suffix="",
680
+ x_axis_type="linear",
681
  ):
682
  scatter = (
683
  data[["Model", score_column, x_column]]
 
763
  )
764
  axis_font = {"color": "#fafafa", "size": 13}
765
  tick_font = {"color": "#a3a3a3", "size": 12}
766
+ x_axis_ticks = {}
767
+ if x_axis_type == "log":
768
+ positive_x = scatter.loc[scatter[x_column] > 0, x_column].astype(float)
769
+ if not positive_x.empty:
770
+ minimum = positive_x.min()
771
+ maximum = positive_x.max()
772
+ tick_values = [
773
+ factor * (10**exponent)
774
+ for exponent in range(
775
+ floor(log10(minimum)),
776
+ ceil(log10(maximum)) + 1,
777
+ )
778
+ for factor in (1, 2, 5)
779
+ if minimum * 0.8 <= factor * (10**exponent) <= maximum * 1.2
780
+ ]
781
+ x_axis_ticks = {
782
+ "tickmode": "array",
783
+ "tickvals": tick_values,
784
+ "ticktext": [f"{value:g}" for value in tick_values],
785
+ }
786
  fig.update_xaxes(
787
+ type=x_axis_type,
788
+ **x_axis_ticks,
789
  showgrid=True,
790
  gridcolor="rgba(74, 57, 98, 0.55)",
791
  zeroline=False,
 
804
  return fig
805
 
806
 
807
+ def _is_log_scale(scale):
808
+ return scale == "Logarithmic"
809
+
810
+
811
+ def _pareto_axis_type(scale):
812
+ return "log" if _is_log_scale(scale) else "linear"
813
+
814
+
815
+ def _pareto_scale_radio(*extra_classes):
816
+ return gr.Radio(
817
+ choices=_PARETO_SCALE_CHOICES,
818
+ value=_PARETO_SCALE_DEFAULT,
819
+ show_label=False,
820
+ container=False,
821
+ elem_classes=["pareto-scale-toggle", *extra_classes],
822
+ )
823
+
824
+
825
+ def _pareto_plot_heading(title):
826
+ with gr.Row(equal_height=False, elem_classes="pareto-heading-row"):
827
+ gr.Markdown(f"#### {title}", elem_classes="pareto-subhead")
828
+ with gr.Column(min_width=140, elem_classes="pareto-scale-control"):
829
+ return _pareto_scale_radio()
830
+
831
+
832
+ def _default_pareto_scales():
833
+ return [_PARETO_SCALE_DEFAULT] * MAX_PARETO_METRICS
834
+
835
+
836
+ def _normalize_pareto_scales(scales):
837
+ values = list(scales or [])
838
+ if len(values) < MAX_PARETO_METRICS:
839
+ values.extend(
840
+ [_PARETO_SCALE_DEFAULT] * (MAX_PARETO_METRICS - len(values))
841
+ )
842
+ return values[:MAX_PARETO_METRICS]
843
+
844
+
845
+ def _uniform_pareto_scales(scale):
846
+ return [scale] * MAX_PARETO_METRICS
847
+
848
+
849
+ def _pareto_master_scale_update(price_scales, time_scales):
850
+ values = list(price_scales) + list(time_scales)
851
+ if values and all(value == values[0] for value in values):
852
+ return gr.update(value=values[0])
853
+ return gr.update(value=None)
854
+
855
+
856
  def _pareto_axis(data, score_column, x_column, x_title, missing_message, empty_message, **hover):
857
  if x_column not in data.columns:
858
  return None, missing_message
 
868
  return fig, None
869
 
870
 
871
+ def _pareto_pair(
872
+ data,
873
+ score_column,
874
+ latency_scale=_PARETO_SCALE_DEFAULT,
875
+ price_scale=_PARETO_SCALE_DEFAULT,
876
+ ):
877
  score_missing = "No score data is available for this metric."
878
  if data is None or not score_column or score_column not in data.columns:
879
  return None, score_missing, None, score_missing
 
886
  "Price per image isn't available for this dataset.",
887
  "No models have both a score and a price for this metric.",
888
  x_hover_prefix="$",
889
+ x_axis_type=_pareto_axis_type(price_scale),
890
  )
891
  time_fig, time_message = _pareto_axis(
892
  data,
 
896
  "Min generation time isn't available for this dataset.",
897
  "No models have both a score and a min generation time for this metric.",
898
  x_hover_suffix="s",
899
+ x_axis_type=_pareto_axis_type(latency_scale),
900
  )
901
  return price_fig, price_message, time_fig, time_message
902
 
 
935
  return " ".join(notes)
936
 
937
 
938
+ def _pareto_slot_updates(
939
+ data,
940
+ score_columns,
941
+ price_scales=None,
942
+ time_scales=None,
943
+ ):
944
  """Updates for a fixed bank of Gradio Plot slots (visible/hidden)."""
945
  score_columns = [column for column in (score_columns or []) if column]
946
+ price_scales = _normalize_pareto_scales(price_scales)
947
+ time_scales = _normalize_pareto_scales(time_scales)
948
  has_price = data is not None and _PARETO_PRICE_COLUMN in data.columns
949
  has_time = data is not None and _PARETO_TIME_COLUMN in data.columns
950
  dataset_note = _pareto_dataset_message(data)
 
966
  continue
967
  score_column = score_columns[index]
968
  price_fig, price_message, time_fig, time_message = _pareto_pair(
969
+ data,
970
+ score_column,
971
+ latency_scale=time_scales[index],
972
+ price_scale=price_scales[index],
973
  )
974
  show_price = price_fig is not None
975
  show_time = time_fig is not None
 
996
  return updates
997
 
998
 
999
+ def _pareto_all_scale_updates(data, score_columns, scale):
1000
+ """Apply one scale to every Pareto plot and radio."""
1001
+ score_columns = [column for column in (score_columns or []) if column]
1002
+ price_updates = []
1003
+ time_updates = []
1004
+ for index in range(MAX_PARETO_METRICS):
1005
+ if index >= len(score_columns):
1006
+ price_updates.append(gr.skip())
1007
+ time_updates.append(gr.skip())
1008
+ continue
1009
+ price_fig, _, time_fig, _ = _pareto_pair(
1010
+ data,
1011
+ score_columns[index],
1012
+ latency_scale=scale,
1013
+ price_scale=scale,
1014
+ )
1015
+ price_updates.append(_pareto_plot_update(price_fig))
1016
+ time_updates.append(_pareto_plot_update(time_fig))
1017
+ radio_updates = [
1018
+ gr.update(value=scale) for _ in range(MAX_PARETO_METRICS * 2)
1019
+ ]
1020
+ return price_updates + time_updates + radio_updates
1021
+
1022
+
1023
  def _samples_html(samples, selected_models, num_prompts, seed=0):
1024
  if not samples:
1025
  return _pareto_unavailable_html(
 
1237
  ) as pp_tab:
1238
  gr.Markdown(
1239
  "<p class='view-help'>"
1240
+ "Score against price and generation time. Green points are on "
1241
+ "the frontier; lavender points sit below it. Hover a point to "
1242
+ "see which model it is."
1243
+ "</p>"
1244
+ "<p class='view-help'>"
1245
+ "You can switch between linear and logarithmic scale for all "
1246
+ "the plots, or individually for each plots."
1247
  "</p>",
1248
  elem_classes="view-help-host",
1249
  )
1250
+ with gr.Row(
1251
+ equal_height=False,
1252
+ elem_classes="pareto-scale-all-row",
1253
+ ):
1254
+ gr.HTML(
1255
+ "<span class='pareto-scale-all-label'>All plots</span>",
1256
+ padding=False,
1257
+ )
1258
+ pareto_all_scale = _pareto_scale_radio(
1259
+ "pareto-scale-toggle-all",
1260
+ )
1261
  pareto_dataset_note = gr.HTML(
1262
  "",
1263
  padding=False,
 
1283
  min_width=320,
1284
  elem_classes="pareto-col",
1285
  ) as slot_price_col:
1286
+ slot_price_scale = _pareto_plot_heading(
1287
+ "Price vs score"
 
1288
  )
1289
  slot_price = gr.Plot(
1290
  value=None,
 
1296
  min_width=320,
1297
  elem_classes="pareto-col",
1298
  ) as slot_time_col:
1299
+ slot_time_scale = _pareto_plot_heading(
1300
+ "Min generation time vs score"
 
1301
  )
1302
  slot_time = gr.Plot(
1303
  value=None,
 
1318
  slot_layout,
1319
  slot_price_col,
1320
  slot_price,
1321
+ slot_price_scale,
1322
  slot_time_col,
1323
  slot_time,
1324
+ slot_time_scale,
1325
  )
1326
  )
1327
 
 
1459
  "optimized": list(
1460
  extras.get("optimized", prev.get("optimized") or [])
1461
  ),
1462
+ "price_scales": _normalize_pareto_scales(prev.get("price_scales")),
1463
+ "time_scales": _normalize_pareto_scales(prev.get("time_scales")),
1464
  "stale": {
1465
  TAB_LEADERBOARDS: not flags["include_leaderboard"],
1466
  TAB_PARETO: not flags["include_pareto"],
 
1520
  include_leaderboard=True,
1521
  include_pareto=False,
1522
  include_samples=False,
1523
+ price_scales=None,
1524
+ time_scales=None,
1525
  ):
1526
  view = resolve_view(datasets, metrics, dataset_id, metric_id)
1527
  data = view["data"]
 
1542
  ranking_html = gr.skip()
1543
  if include_pareto:
1544
  pareto_data = _filter_leaderboard(data, [], [], [], models=models)
1545
+ pareto_updates = _pareto_slot_updates(
1546
+ pareto_data,
1547
+ view["score_columns"],
1548
+ price_scales=price_scales,
1549
+ time_scales=time_scales,
1550
+ )
1551
  else:
1552
  pareto_updates = _pareto_skip_updates()
1553
  if include_samples:
 
1681
  extras[2],
1682
  num_prompts,
1683
  seed,
1684
+ price_scales=view_state.get("price_scales"),
1685
+ time_scales=view_state.get("time_scales"),
1686
  **flags,
1687
  ),
1688
  "state": _commit_state(
 
1885
  optimized_value,
1886
  num_prompts,
1887
  seed,
1888
+ price_scales=view_state.get("price_scales"),
1889
+ time_scales=view_state.get("time_scales"),
1890
  **flags,
1891
  )
1892
  stale[tab] = False
 
1943
  next_seed,
1944
  )
1945
 
1946
+ def _on_pareto_plot_scale(slot_index, axis):
1947
+ def handler(dataset_id, metric_id, models, scale, view_state):
1948
+ view_state = dict(view_state or {})
1949
+ price_scales = _normalize_pareto_scales(
1950
+ view_state.get("price_scales")
1951
+ )
1952
+ time_scales = _normalize_pareto_scales(
1953
+ view_state.get("time_scales")
1954
+ )
1955
+ if axis == "price":
1956
+ if price_scales[slot_index] == scale:
1957
+ return gr.skip(), gr.skip(), gr.skip()
1958
+ price_scales[slot_index] = scale
1959
+ else:
1960
+ if time_scales[slot_index] == scale:
1961
+ return gr.skip(), gr.skip(), gr.skip()
1962
+ time_scales[slot_index] = scale
1963
+ view_state["price_scales"] = price_scales
1964
+ view_state["time_scales"] = time_scales
1965
+ master_scale = _pareto_master_scale_update(
1966
+ price_scales, time_scales
1967
+ )
1968
+ view = resolve_view(datasets, metrics, dataset_id, metric_id)
1969
+ score_columns = [
1970
+ column for column in (view["score_columns"] or []) if column
1971
+ ]
1972
+ if slot_index >= len(score_columns):
1973
+ return gr.skip(), master_scale, view_state
1974
+ data = _filter_leaderboard(
1975
+ view["data"], [], [], [], models=list(models or [])
1976
+ )
1977
+ price_fig, _, time_fig, _ = _pareto_pair(
1978
+ data,
1979
+ score_columns[slot_index],
1980
+ latency_scale=time_scales[slot_index],
1981
+ price_scale=price_scales[slot_index],
1982
+ )
1983
+ fig = price_fig if axis == "price" else time_fig
1984
+ return _pareto_plot_update(fig), master_scale, view_state
1985
+
1986
+ handler.__name__ = f"on_pareto_{axis}_scale_{slot_index}"
1987
+ return handler
1988
+
1989
+ def on_pareto_all_scale(dataset_id, metric_id, models, scale, view_state):
1990
+ if scale not in _PARETO_SCALE_VALUES:
1991
+ return (*_skip_all(MAX_PARETO_METRICS * 4), gr.skip())
1992
+ view_state = dict(view_state or {})
1993
+ scales = _uniform_pareto_scales(scale)
1994
+ if (
1995
+ _normalize_pareto_scales(view_state.get("price_scales")) == scales
1996
+ and _normalize_pareto_scales(view_state.get("time_scales")) == scales
1997
+ ):
1998
+ return (*_skip_all(MAX_PARETO_METRICS * 4), gr.skip())
1999
+ view_state["price_scales"] = scales
2000
+ view_state["time_scales"] = scales
2001
+ view = resolve_view(datasets, metrics, dataset_id, metric_id)
2002
+ data = _filter_leaderboard(
2003
+ view["data"], [], [], [], models=list(models or [])
2004
+ )
2005
+ return (
2006
+ *_pareto_all_scale_updates(data, view["score_columns"], scale),
2007
+ view_state,
2008
+ )
2009
+
2010
  def _on_tab(tab):
2011
  def handler(
2012
  dataset_id,
 
2044
  "platform": [],
2045
  "owner": [],
2046
  "optimized": [],
2047
+ "price_scales": _default_pareto_scales(),
2048
+ "time_scales": _default_pareto_scales(),
2049
  "stale": {
2050
  TAB_LEADERBOARDS: False,
2051
  TAB_PARETO: True,
 
2057
  pareto_dataset_note,
2058
  *[
2059
  component
2060
+ for slot_group, slot_title, slot_note, slot_layout, slot_price_col, slot_price, slot_price_scale, slot_time_col, slot_time, slot_time_scale in pareto_slots
2061
  for component in (
2062
  slot_group,
2063
  slot_title,
 
2070
  )
2071
  ],
2072
  ]
2073
+ pareto_all_scale_outputs = [
2074
+ *[
2075
+ slot_price
2076
+ for _, _, _, _, _, slot_price, _, _, _, _ in pareto_slots
2077
+ ],
2078
+ *[
2079
+ slot_time
2080
+ for _, _, _, _, _, _, _, _, slot_time, _ in pareto_slots
2081
+ ],
2082
+ *[
2083
+ slot_price_scale
2084
+ for _, _, _, _, _, _, slot_price_scale, _, _, _ in pareto_slots
2085
+ ],
2086
+ *[
2087
+ slot_time_scale
2088
+ for _, _, _, _, _, _, _, _, _, slot_time_scale in pareto_slots
2089
+ ],
2090
+ ]
2091
  view_inputs = [
2092
  platform,
2093
  owner,
 
2192
  show_progress="hidden",
2193
  )
2194
 
2195
+ pareto_all_scale.change(
2196
+ on_pareto_all_scale,
2197
+ inputs=[
2198
+ dataset_dd,
2199
+ metric_dd,
2200
+ models_dd,
2201
+ pareto_all_scale,
2202
+ view_state,
2203
+ ],
2204
+ outputs=[*pareto_all_scale_outputs, view_state],
2205
+ **_VIEW_EVENTS,
2206
+ )
2207
+
2208
+ for slot_index, (
2209
+ _,
2210
+ _,
2211
+ _,
2212
+ _,
2213
+ _,
2214
+ slot_price,
2215
+ slot_price_scale,
2216
+ _,
2217
+ slot_time,
2218
+ slot_time_scale,
2219
+ ) in enumerate(pareto_slots):
2220
+ slot_price_scale.change(
2221
+ _on_pareto_plot_scale(slot_index, "price"),
2222
+ inputs=[
2223
+ dataset_dd,
2224
+ metric_dd,
2225
+ models_dd,
2226
+ slot_price_scale,
2227
+ view_state,
2228
+ ],
2229
+ outputs=[slot_price, pareto_all_scale, view_state],
2230
+ **_VIEW_EVENTS,
2231
+ )
2232
+ slot_time_scale.change(
2233
+ _on_pareto_plot_scale(slot_index, "time"),
2234
+ inputs=[
2235
+ dataset_dd,
2236
+ metric_dd,
2237
+ models_dd,
2238
+ slot_time_scale,
2239
+ view_state,
2240
+ ],
2241
+ outputs=[slot_time, pareto_all_scale, view_state],
2242
+ **_VIEW_EVENTS,
2243
+ )
2244
+
2245
  prompt_count.change(
2246
  on_samples_controls,
2247
  inputs=[dataset_dd, models_dd, prompt_count, seed_state],