1919
2020import pandas as pd
2121import requests
22+
2223import streamlit as st
24+ import streamlit .components .v1 as components
2325
2426# Configuration
2527API_BASE_URL = os .getenv ("API_BASE_URL" , "http://localhost:8000" )
@@ -151,7 +153,6 @@ def format_display_name(raw_name: str) -> str:
151153
152154st .markdown ("""
153155<style>
154- /* Import Google Fonts - Qualifire & HuggingFace Inspired Typography */
155156 @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=Space+Grotesk:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&family=DM+Sans:wght@400;500;600;700&display=swap');
156157
157158 /* Global Styles */
@@ -444,8 +445,7 @@ def format_display_name(raw_name: str) -> str:
444445 outline: none;
445446 box-shadow: 0 0 0 3px rgba(56, 239, 125, 0.25);
446447 }
447-
448- /* TOP LEADERBOARD TABLE - HuggingFace/Qualifire Inspired */
448+
449449 .leaderboard-container {
450450 background: var(--bg-card);
451451 backdrop-filter: blur(12px);
@@ -1107,8 +1107,7 @@ def format_display_name(raw_name: str) -> str:
11071107 color: var(--text-secondary);
11081108 line-height: 1.6;
11091109 }
1110-
1111- /* Input Container - Qualifire-inspired clean design */
1110+
11121111 .input-container {
11131112 background: var(--bg-card);
11141113 padding: 2.5rem;
@@ -5115,10 +5114,10 @@ def render_options_list_inline():
51155114
51165115 # Define categories
51175116 categories = [
5118- ("balanced" , "Balanced" , "#EE0000" ),
5119- ("best_accuracy" , "Best Accuracy" , "#ffffff" ),
5120- ("lowest_cost" , "Lowest Cost" , "#f59e0b" ),
5121- ("lowest_latency" , "Lowest Latency" , "#ffffff" ),
5117+ ("balanced" , "Balanced" ),
5118+ ("best_accuracy" , "Best Accuracy" ),
5119+ ("lowest_cost" , "Lowest Cost" ),
5120+ ("lowest_latency" , "Lowest Latency" ),
51225121 ]
51235122
51245123 # Helper function to format GPU config
@@ -5131,37 +5130,168 @@ def format_gpu_config(gpu_config: dict) -> str:
51315130 replicas = gpu_config .get ("replicas" , 1 )
51325131 return f"{ gpu_count } x { gpu_type } (TP={ tp } , R={ replicas } )"
51335132
5134- # Build table rows
5135- all_rows = []
5136- for cat_key , cat_name , cat_color in categories :
5133+ # Build table data
5134+ table_data = []
5135+ for cat_key , cat_name in categories :
51375136 recs = ranked_response .get (cat_key , [])
5138- if not recs :
5139- all_rows .append (f'<tr style="border-bottom: 1px solid rgba(255,255,255,0.1);"><td style="padding: 0.75rem 0.5rem;"><span style="color: { cat_color } ; font-weight: 600;">{ cat_name } </span></td><td colspan="7" style="padding: 0.75rem 0.5rem; color: rgba(255,255,255,0.5); font-style: italic;">No configurations found</td></tr>' )
5140- else :
5141- for i , rec in enumerate (recs [:5 ]): # Show top 5 per category
5142- model_name = format_display_name (rec .get ("model_name" , "Unknown" ))
5143- gpu_config = rec .get ("gpu_config" , {})
5144- gpu_str = format_gpu_config (gpu_config )
5145- ttft = rec .get ("predicted_ttft_p95_ms" , 0 )
5146- cost = rec .get ("cost_per_month_usd" , 0 )
5147- scores = rec .get ("scores" , {}) or {}
5148- accuracy = scores .get ("accuracy_score" , 0 )
5149- balanced = scores .get ("balanced_score" , 0 )
5150- meets_slo = rec .get ("meets_slo" , False )
5151- slo_icon = "Yes" if meets_slo else "No"
5152-
5153- cat_display = f'<span style="color: { cat_color } ; font-weight: 600;">{ cat_name } </span> (+{ len (recs )- 1 } )' if i == 0 else ""
5154-
5155- row = f'<tr style="border-bottom: 1px solid rgba(255,255,255,0.1);"><td style="padding: 0.75rem 0.5rem;">{ cat_display } </td><td style="padding: 0.75rem 0.5rem; color: white; font-weight: 500;">{ model_name } </td><td style="padding: 0.75rem 0.5rem; color: rgba(255,255,255,0.7); font-size: 0.85rem;">{ gpu_str } </td><td style="padding: 0.75rem 0.5rem; text-align: right; color: #06b6d4;">{ ttft :.0f} ms</td><td style="padding: 0.75rem 0.5rem; text-align: right; color: #f59e0b;">${ cost :,.0f} </td><td style="padding: 0.75rem 0.5rem; text-align: center; color: #10b981;">{ accuracy :.0f} </td><td style="padding: 0.75rem 0.5rem; text-align: center; color: #8b5cf6;">{ balanced :.1f} </td><td style="padding: 0.75rem 0.5rem; text-align: center;">{ slo_icon } </td></tr>'
5156- all_rows .append (row )
5157-
5158- # Table header
5159- header = '<thead><tr style="border-bottom: 2px solid rgba(255,255,255,0.2);"><th style="text-align: left; padding: 0.75rem 0.5rem; color: rgba(255,255,255,0.7); font-size: 0.85rem; font-weight: 600;">Category</th><th style="text-align: left; padding: 0.75rem 0.5rem; color: rgba(255,255,255,0.7); font-size: 0.85rem; font-weight: 600;">Model</th><th style="text-align: left; padding: 0.75rem 0.5rem; color: rgba(255,255,255,0.7); font-size: 0.85rem; font-weight: 600;">GPU Config</th><th style="text-align: right; padding: 0.75rem 0.5rem; color: rgba(255,255,255,0.7); font-size: 0.85rem; font-weight: 600;">TTFT</th><th style="text-align: right; padding: 0.75rem 0.5rem; color: rgba(255,255,255,0.7); font-size: 0.85rem; font-weight: 600;">Cost/mo</th><th style="text-align: center; padding: 0.75rem 0.5rem; color: rgba(255,255,255,0.7); font-size: 0.85rem; font-weight: 600;">Acc</th><th style="text-align: center; padding: 0.75rem 0.5rem; color: rgba(255,255,255,0.7); font-size: 0.85rem; font-weight: 600;">Score</th><th style="text-align: center; padding: 0.75rem 0.5rem; color: rgba(255,255,255,0.7); font-size: 0.85rem; font-weight: 600;">SLO</th></tr></thead>'
5137+ for rec in recs [:5 ]: # Show top 5 per category
5138+ model_name = format_display_name (rec .get ("model_name" , "Unknown" ))
5139+ gpu_config = rec .get ("gpu_config" , {})
5140+ gpu_str = format_gpu_config (gpu_config )
5141+ ttft = rec .get ("predicted_ttft_p95_ms" , 0 )
5142+ cost = rec .get ("cost_per_month_usd" , 0 )
5143+ scores = rec .get ("scores" , {}) or {}
5144+ accuracy = scores .get ("accuracy_score" , 0 )
5145+ balanced = scores .get ("balanced_score" , 0 )
5146+ meets_slo = rec .get ("meets_slo" , False )
5147+ slo_value = 1 if meets_slo else 0 # Numeric value for sorting
5148+
5149+ table_data .append ({
5150+ "category" : cat_name ,
5151+ "model" : model_name ,
5152+ "gpu_config" : gpu_str ,
5153+ "ttft" : ttft ,
5154+ "cost" : cost ,
5155+ "accuracy" : accuracy ,
5156+ "balanced" : balanced ,
5157+ "slo" : "Yes" if meets_slo else "No" ,
5158+ "slo_value" : slo_value ,
5159+ })
51605160
5161- # Render table
5162- table_html = f'<table style="width: 100%; border-collapse: collapse; background: rgba(13, 17, 23, 0.95); border-radius: 8px;">{ header } <tbody>{ "" .join (all_rows )} </tbody></table>'
5161+ if table_data :
5162+ # Build table rows HTML
5163+ rows_html = []
5164+ for row in table_data :
5165+ rows_html .append (f'''
5166+ <tr style="border-bottom: 1px solid rgba(255,255,255,0.1);"
5167+ data-category="{ row ['category' ]} "
5168+ data-model="{ row ['model' ]} "
5169+ data-gpu="{ row ['gpu_config' ]} "
5170+ data-ttft="{ row ['ttft' ]} "
5171+ data-cost="{ row ['cost' ]} "
5172+ data-accuracy="{ row ['accuracy' ]} "
5173+ data-balanced="{ row ['balanced' ]} "
5174+ data-slo="{ row ['slo_value' ]} ">
5175+ <td style="padding: 0.75rem 0.5rem; color: white;">{ row ['category' ]} </td>
5176+ <td style="padding: 0.75rem 0.5rem; color: white; font-weight: 500;">{ row ['model' ]} </td>
5177+ <td style="padding: 0.75rem 0.5rem; color: rgba(255,255,255,0.7); font-size: 0.85rem;">{ row ['gpu_config' ]} </td>
5178+ <td style="padding: 0.75rem 0.5rem; text-align: right; color: #06b6d4;">{ row ['ttft' ]:.0f} ms</td>
5179+ <td style="padding: 0.75rem 0.5rem; text-align: right; color: #f59e0b;">${ row ['cost' ]:,.0f} </td>
5180+ <td style="padding: 0.75rem 0.5rem; text-align: center; color: #10b981;">{ row ['accuracy' ]:.0f} </td>
5181+ <td style="padding: 0.75rem 0.5rem; text-align: center; color: #8b5cf6;">{ row ['balanced' ]:.1f} </td>
5182+ <td style="padding: 0.75rem 0.5rem; text-align: center;">{ row ['slo' ]} </td>
5183+ </tr>
5184+ ''' )
5185+
5186+ # Table HTML with JavaScript sorting
5187+ table_html = f'''
5188+ <!DOCTYPE html>
5189+ <html>
5190+ <head>
5191+ <style>
5192+ body {{
5193+ margin: 0;
5194+ padding: 0;
5195+ background: transparent;
5196+ font-family: "Source Sans Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
5197+ }}
5198+ .sortable-table {{
5199+ font-family: "Source Sans Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
5200+ }}
5201+ .sortable-table th {{
5202+ cursor: pointer;
5203+ user-select: none;
5204+ position: relative;
5205+ }}
5206+ .sortable-table th:hover {{
5207+ background: rgba(50, 50, 50, 0.8) !important;
5208+ }}
5209+ .sortable-table th.sort-asc::after {{
5210+ content: " ▲";
5211+ font-size: 0.7em;
5212+ color: #06b6d4;
5213+ }}
5214+ .sortable-table th.sort-desc::after {{
5215+ content: " ▼";
5216+ font-size: 0.7em;
5217+ color: #06b6d4;
5218+ }}
5219+ .sortable-table tbody tr:hover {{
5220+ background: rgba(30, 30, 30, 0.6) !important;
5221+ }}
5222+ </style>
5223+ </head>
5224+ <body>
5225+ <table class="sortable-table" id="recsTableInline" style="width: 100%; border-collapse: collapse; background: rgba(13, 17, 23, 0.95); border-radius: 8px;">
5226+ <thead>
5227+ <tr style="border-bottom: 2px solid rgba(255,255,255,0.2);">
5228+ <th onclick="sortTableInline(0, 'string')" style="text-align: left; padding: 0.75rem 0.5rem; color: rgba(255,255,255,0.7); font-size: 0.85rem; font-weight: 600;">Category</th>
5229+ <th onclick="sortTableInline(1, 'string')" style="text-align: left; padding: 0.75rem 0.5rem; color: rgba(255,255,255,0.7); font-size: 0.85rem; font-weight: 600;">Model</th>
5230+ <th onclick="sortTableInline(2, 'string')" style="text-align: left; padding: 0.75rem 0.5rem; color: rgba(255,255,255,0.7); font-size: 0.85rem; font-weight: 600;">GPU Config</th>
5231+ <th onclick="sortTableInline(3, 'number')" style="text-align: right; padding: 0.75rem 0.5rem; color: rgba(255,255,255,0.7); font-size: 0.85rem; font-weight: 600;">TTFT</th>
5232+ <th onclick="sortTableInline(4, 'number')" style="text-align: right; padding: 0.75rem 0.5rem; color: rgba(255,255,255,0.7); font-size: 0.85rem; font-weight: 600;">Cost/mo</th>
5233+ <th onclick="sortTableInline(5, 'number')" style="text-align: center; padding: 0.75rem 0.5rem; color: rgba(255,255,255,0.7); font-size: 0.85rem; font-weight: 600;">Acc</th>
5234+ <th onclick="sortTableInline(6, 'number')" style="text-align: center; padding: 0.75rem 0.5rem; color: rgba(255,255,255,0.7); font-size: 0.85rem; font-weight: 600;">Score</th>
5235+ <th onclick="sortTableInline(7, 'number')" style="text-align: center; padding: 0.75rem 0.5rem; color: rgba(255,255,255,0.7); font-size: 0.85rem; font-weight: 600;">SLO</th>
5236+ </tr>
5237+ </thead>
5238+ <tbody>
5239+ { '' .join (rows_html )}
5240+ </tbody>
5241+ </table>
5242+ <script>
5243+ let sortDirectionInline = {{}};
5244+
5245+ function sortTableInline(columnIndex, type) {{
5246+ const table = document.getElementById('recsTableInline');
5247+ const tbody = table.querySelector('tbody');
5248+ const rows = Array.from(tbody.querySelectorAll('tr'));
5249+ const headers = table.querySelectorAll('th');
5250+
5251+ // Toggle sort direction
5252+ const key = columnIndex;
5253+ sortDirectionInline[key] = sortDirectionInline[key] === 'asc' ? 'desc' : 'asc';
5254+ const isAsc = sortDirectionInline[key] === 'asc';
5255+
5256+ // Clear all header classes
5257+ headers.forEach(h => {{
5258+ h.classList.remove('sort-asc', 'sort-desc');
5259+ }});
5260+
5261+ // Add class to current header
5262+ headers[columnIndex].classList.add(isAsc ? 'sort-asc' : 'sort-desc');
5263+
5264+ // Sort rows
5265+ rows.sort((a, b) => {{
5266+ let aVal, bVal;
5267+
5268+ if (type === 'number') {{
5269+ const attrs = ['category', 'model', 'gpu', 'ttft', 'cost', 'accuracy', 'balanced', 'slo'];
5270+ const attr = 'data-' + attrs[columnIndex];
5271+ aVal = parseFloat(a.getAttribute(attr)) || 0;
5272+ bVal = parseFloat(b.getAttribute(attr)) || 0;
5273+ }} else {{
5274+ aVal = a.cells[columnIndex].textContent.trim();
5275+ bVal = b.cells[columnIndex].textContent.trim();
5276+ }}
5277+
5278+ if (aVal < bVal) return isAsc ? -1 : 1;
5279+ if (aVal > bVal) return isAsc ? 1 : -1;
5280+ return 0;
5281+ }});
5282+
5283+ // Re-append sorted rows
5284+ rows.forEach(row => tbody.appendChild(row));
5285+ }}
5286+ </script>
5287+ </body>
5288+ </html>
5289+ '''
51635290
5164- st .markdown (table_html , unsafe_allow_html = True )
5291+ components .html (table_html , height = 450 , scrolling = True )
5292+ st .markdown ('<p style="color: rgba(255,255,255,0.6); font-size: 0.85rem; margin-top: 0.5rem;">Click on column headers to sort the table</p>' , unsafe_allow_html = True )
5293+ else :
5294+ st .warning ("No configurations to display" )
51655295
51665296
51675297# =============================================================================
0 commit comments