Spaces:
Running
Running
| import gradio as gr | |
| from src.helper import * | |
| # Custom CSS to replicate the Google-style card design from the image | |
| custom_head_html = """ | |
| <link rel="stylesheet" href="https://africa.dlnlp.ai/sahara/font-awesome/css/font-awesome.min.css"> | |
| <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> | |
| <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.0/jquery.min.js"></script> | |
| <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> | |
| <link rel="stylesheet" type="text/css" href="./public/css/style.min.css"> | |
| <script defer src="./public/js/script.js"></script> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=Rubik:wght@400;600&display=swap" rel="stylesheet"> | |
| """ | |
| new_header_html = """ | |
| <center> | |
| <br><br><br> | |
| <img src="https://africa.dlnlp.ai/sahara/img/sahara_web_main.jpg" alt="Sahara logo" width="60%"> | |
| </p> | |
| </center> | |
| <br style="height:1px;"> | |
| """ | |
| google_style_css = """ | |
| /* Add this rule to your google_style_css string */ | |
| /* Citation Block */ | |
| .citation-block { | |
| position: relative; | |
| background-color: #FDF6E3 !important; /* Light cream background */ | |
| border-radius: 8px; | |
| padding: 25px; | |
| } | |
| .citation-block pre { | |
| background-color: transparent; | |
| border: none; | |
| padding: 0; | |
| /* font-size: 14px; */ | |
| white-space: pre-wrap; | |
| word-break: break-all; | |
| } | |
| .citation-block .btn-copy { | |
| position: absolute; | |
| top: 15px; | |
| right: 15px; | |
| background-color: #D97706 !important; | |
| border-color: #c56a05 !important; | |
| color: white !important; | |
| } | |
| .citation-block .btn-copy:hover, .citation-block .btn-copy:focus { | |
| background-color: #c56a05 !important; | |
| color: white !important; | |
| } | |
| .fillable.svelte-15jxnnn.svelte-15jxnnn:not(.fill_width) { | |
| /* min-width: 400px !important; */ | |
| max-width: 1580px !important; | |
| } | |
| .flat-navy-button { | |
| background-color: #117b75 !important; /* Navy Blue */ | |
| color: #fff !important; | |
| font-weight: bold !important; | |
| border-radius: 5px !important; /* Slightly rounded corners */ | |
| border: none !important; | |
| box-shadow: none !important; | |
| } | |
| .flat-navy-button:hover { | |
| background-color: #117b75 !important; /* Lighter navy for hover */ | |
| color: #e8850e !important; | |
| } | |
| div[class*="gradio-container"] { | |
| background:#FFFBF5 !important; | |
| color:#000 !important; | |
| } | |
| div.svelte-1nguped { | |
| background: white !important; | |
| } | |
| /* Main Content Area */ | |
| .content-section { | |
| padding: 60px 0; | |
| } | |
| .content-card { | |
| background-color: #fff !important; | |
| border-radius: 12px; | |
| box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -2px rgba(0,0,0,0.05); | |
| padding: 40px; | |
| margin-bottom: 40px; | |
| } | |
| .btn-cite { | |
| color: #7d3561; | |
| font-size: 16px; | |
| margin: 0 3px; /* Add spacing between multiple icons */ | |
| } | |
| .content-card h4 { | |
| font-family: "Rubik", sans-serif; | |
| color: #7d3561 !important; | |
| } | |
| .content-card h2 { | |
| font-family: "Rubik", sans-serif; | |
| font-size: 30px; | |
| font-weight: 600; | |
| line-height: 1.25; | |
| letter-spacing: -1px; | |
| color: #2f3b7d !important; | |
| text-transform:none; | |
| /* font-size: 30px; | |
| font-weight: bold; | |
| color: #D97706; /* Brand Orange */ | |
| margin-top: 0; | |
| margin-bottom: 20px; */ | |
| } | |
| .content-card h3 { | |
| font-size: 20px; | |
| color: #2f3b7d !important; | |
| } | |
| .content-card h3 .title { | |
| color: #7d3561 !important; | |
| } | |
| .content-card p { | |
| /* font-size: 18px; */ | |
| /* line-height: 1.7; */ | |
| } | |
| div.svelte-wv8on1{ | |
| # border: 2px solid #074e4a !important; | |
| border-top: 0 !important; | |
| /* background-color: #fff2eb !important; */ | |
| padding: 10px !important; | |
| } | |
| .padding.svelte-phx28p { | |
| padding:0 !important; | |
| } | |
| .tab-wrapper.svelte-1tcem6n.svelte-1tcem6n { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| position: relative; | |
| height: 0 !important; | |
| padding-bottom: 0 !important; | |
| } | |
| .selected.svelte-1tcem6n.svelte-1tcem6n { | |
| background-color: #7d3561 !important; | |
| color: #fff !important; | |
| } | |
| .tabs.svelte-1tcem6n.svelte-1tcem6n { | |
| /* border: 1px solid #dca02a !important; */ | |
| border-top: 0 !important; | |
| /* background-color: #dca02a !important; */ | |
| } | |
| button.svelte-1tcem6n.svelte-1tcem6n { | |
| color: #7d3561 !important; | |
| /* border: 1px solid #dca02a !important; */ | |
| font-weight: bold; | |
| /* font-size: 16px; */ | |
| padding: 8px 5px; | |
| background-color: #fff !important; | |
| } | |
| button.svelte-1tcem6n.svelte-1tcem6n:hover { | |
| background-color: #de8fc2 !important; | |
| } | |
| .tab-container.svelte-1tcem6n.svelte-1tcem6n:after { | |
| content: ""; | |
| position: absolute; | |
| bottom: 0; | |
| left: 0; | |
| right: 0; | |
| height: 2px; | |
| background-color: #7d3561 !important; | |
| } | |
| div[class*="gradio-container"] .prose table, | |
| div[class*="gradio-container"] .prose tr, | |
| div[class*="gradio-container"] .prose td, | |
| div[class*="gradio-container"] .prose th { | |
| border: 0 !important; | |
| border-top: 2px solid #dca02a; | |
| border-bottom: 2px solid #dca02a; | |
| } | |
| div[class*="gradio-container"] .prose table { | |
| color:#000 !important; | |
| border-top: 2px solid #dca02a !important; | |
| border-bottom: 2px solid #dca02a !important; | |
| margin-bottom:20px; | |
| margin-left: auto; | |
| margin-right: auto; | |
| width: 100%; | |
| border-collapse: collapse; | |
| table-layout: fixed; | |
| } | |
| div[class*="gradio-container"] .prose thead tr { | |
| border-bottom: 2px solid #dca02a !important; | |
| } | |
| div[class*="gradio-container"] .prose th .rotate_div { | |
| transform: rotate(-45deg) !important; | |
| color: #7d3561 !important; | |
| padding-top: 10px !important; | |
| padding-bottom: 5px !important; | |
| font-weight: None !important; | |
| font-size: 12px !important; | |
| /*height: 30px !important; Give the cell enough height for the rotated text */ | |
| } | |
| div[class*="gradio-container"] .prose th { | |
| /* transform: rotate(-90deg) !important; */ | |
| color: #7d3561 !important; | |
| font-weight: bold; | |
| /* font-size: 20px; */ | |
| padding: 8px 5px; | |
| vertical-align: middle; | |
| border: 0 !important; | |
| } | |
| div[class*="gradio-container"] .prose td { | |
| /* font-size: 18px; */ | |
| padding: 8px 5px; | |
| border: 0 !important; | |
| vertical-align: middle; | |
| color:#000 !important; | |
| } | |
| div[class*="gradio-container"] .prose th:first-child, | |
| div[class*="gradio-container"] .prose td:first-child { | |
| transform: rotate(0deg) !important; | |
| min-width: 400px !important; | |
| /* max-width: 400px !important; */ | |
| width:400px !important; | |
| text-align: left !important; | |
| } | |
| div[class*="gradio-container"] .prose th:not(:first-child), | |
| div[class*="gradio-container"] .prose td:not(:first-child) { | |
| /* min-width: 90px; | |
| max-width: 140px; | |
| width:auto !important; */ | |
| text-align: center; | |
| } | |
| /* --- CUSTOM RULE FOR THE SECOND CHILD --- */ | |
| #model_specific_table .prose td:nth-child(2) { | |
| text-align: left; /* Example: A custom alignment */ | |
| } | |
| /* --- Styles for the Model Comparison Table --- */ | |
| /* Rule for the Second Column (Task Name) */ | |
| #models_comparasion_table .prose th:nth-child(2), | |
| #models_comparasion_table .prose td:nth-child(2) { | |
| width: 200px !important; /* Give it enough width */ | |
| text-align: left !important; | |
| white-space: nowrap; /* Prevent text from wrapping */ | |
| } | |
| /* Rule for the First Column (Cluster) */ | |
| #models_comparasion_table .prose th:first-child, | |
| #models_comparasion_table .prose td:first-child { | |
| width: 130px !important; | |
| text-align: left !important; | |
| } | |
| /* Rule for other columns (Task ID, Metric, Scores, etc.) */ | |
| #models_comparasion_table .prose th:not(:nth-child(1)):not(:nth-child(2)), | |
| #models_comparasion_table .prose td:not(:nth-child(1)):not(:nth-child(2)) { | |
| width: 95px !important; /* Set a consistent width for other columns */ | |
| text-align: center; | |
| } | |
| div[class*="gradio-container"] .md :not(pre)>code | |
| { | |
| background: #fcbb99 !important; | |
| border: 1px solid #763412 !important; | |
| } | |
| div[class*="gradio-container"] .md * | |
| { | |
| color: #000 !important; | |
| } | |
| """ | |
| introduction_text = """ | |
| """ | |
| favicon_head = '<link rel="icon" type="image/jpeg" href="https://africa.dlnlp.ai/sahara/img/sahara_logo.jpg">' | |
| # with gr.Blocks(title="Sahara Leaderboard", css=custom_css) as demo: | |
| # with gr.Blocks(title="Sahara Leaderboard") as demo: | |
| with gr.Blocks(theme=gr.themes.Default(), title="Sahara Benchmark Leaderboards", css=google_style_css, head=favicon_head) as demo: | |
| # Use elem_classes to apply our custom CSS to this group | |
| gr.HTML(new_header_html) | |
| # === UPDATED BUTTONS START === | |
| with gr.Row(): | |
| gr.Button("Official Website", link="https://africa.dlnlp.ai/sahara", elem_classes=['flat-navy-button']) | |
| gr.Button("ACL 2025 Paper", link="https://aclanthology.org/2025.acl-long.1572", elem_classes=['flat-navy-button']) | |
| gr.Button("Tasks", link="https://africa.dlnlp.ai/sahara/tasks", elem_classes=['flat-navy-button']) | |
| # gr.Button("Submission Instructions", link="https://africa.dlnlp.ai/sahara/instructions", elem_classes=['flat-navy-button']) | |
| # gr.Button("New Submission", link="https://africa.dlnlp.ai/sahara/submit", elem_classes=['flat-navy-button']) | |
| gr.Button("Test Dataset", link="https://huggingface.co/datasets/UBC-NLP/sahara_benchmark", elem_classes=['flat-navy-button']) | |
| gr.Button("GitHub Repo", link="https://github.com/UBC-NLP/sahara", elem_classes=['flat-navy-button']) | |
| gr.Button("Submission Guidelines", link="https://sahara-benchmark.readthedocs.io/en/latest/instructions.html", elem_classes=['flat-navy-button']) | |
| # These buttons will now show an alert message | |
| # submission_btn = gr.Button("Submission Instructions", elem_classes=['flat-navy-button']) | |
| # new_submission_btn = gr.Button("New Submission", elem_classes=['flat-navy-button']) | |
| # github_btn = gr.Button("GitHub Repo", elem_classes=['flat-navy-button']) | |
| # # Function to display the alert | |
| # def show_coming_soon_alert(): | |
| # gr.Info("Stay tuned! It will be ready soon.") | |
| # # Link the click event of each button to the alert function | |
| # submission_btn.click(fn=show_coming_soon_alert) | |
| # new_submission_btn.click(fn=show_coming_soon_alert) | |
| # github_btn.click(fn=show_coming_soon_alert) | |
| # === UPDATED BUTTONS END === | |
| with gr.Group(elem_classes="content-card"): | |
| gr.Markdown("<br>") | |
| # Hidden component to trigger JavaScript on load | |
| # url_trigger = gr.Textbox(visible=False) | |
| # State to hold URL parameters | |
| # url_params_state = gr.State({}) | |
| with gr.Tabs() as tabs: | |
| # Main leaderboard | |
| with gr.Tab("Main Leaderboard", id="main"): | |
| gr.HTML("<br><br><center><h2>Main Leaderboard</h2></center><br>") | |
| gr.HTML(df_to_html(main_overall_tab)) | |
| # Task Clusters leaderboards | |
| with gr.Tab("Task-Cluster Leaderboard", id="clusters"): | |
| gr.HTML("<br><br><center><h2>Task-Cluster Leaderboard</h2></center><br>") | |
| CLUSTERS_NAME=[cname for cname, cdf in cluster_tabs.items()] | |
| cname = CLUSTERS_NAME[0] | |
| initial_cluster_title_html = f"<h3><span class='title'>Task-Cluster name:</span> {cname}</span></h3>" | |
| # 2. Create a variable for the title component so it can be updated. | |
| cluster_title_component = gr.HTML(initial_cluster_title_html) | |
| clusters_dropdown = gr.Dropdown( | |
| choices=CLUSTERS_NAME, | |
| label="Select Task-CLuster", | |
| interactive=True, | |
| elem_id="cluster_dropdown", | |
| value=CLUSTERS_NAME[0] # Set default value | |
| ) | |
| def get_claster_table(cluster_name): | |
| for cname, cdf in cluster_tabs.items(): | |
| if cname== cluster_name: | |
| return cdf | |
| return None | |
| cluster_table_component = gr.HTML(df_to_html(get_claster_table(CLUSTERS_NAME[0])) if CLUSTERS_NAME else "<b>No cluser found</b>") | |
| def update_cluster_table(cluster_name): | |
| df = get_claster_table(cluster_name) | |
| cluster_title_html = f"<h3><span class='title'>Task-Cluster name:</span> {cluster_name}</span></h3>" | |
| return cluster_title_html, df_to_html(df) if df is not None else "<b>No data found</b>" | |
| clusters_dropdown.change(update_cluster_table, clusters_dropdown, outputs=[cluster_title_component, cluster_table_component]) | |
| # Languages Leaderboards | |
| # Task-Specific Leaderboards | |
| with gr.Tab("Task-Specific Leaderboard", id="tasks"): | |
| # --- MODIFIED --- | |
| # 1. Define the initial title based on the first task in the list. | |
| gr.HTML("<br><br><center><h2>Task-Specific Leaderboard (per langauge)</h2></center><br>") | |
| initial_task_name = TASK_NAME_LIST[0] | |
| tname=initial_task_name.split(' (')[0] | |
| tid=initial_task_name.split(' (')[-1].split(')')[0] | |
| initial_title_html = f"<h3><span class='title'>Task name:</span> {tname}<br> <span class='title'>Task identifier:</span> {tid}</h3>" | |
| # 2. Create a variable for the title component so it can be updated. | |
| task_title_component = gr.HTML(initial_title_html) | |
| # Dropdown for selecting the task (remains the same) | |
| task_dropdown = gr.Dropdown(choices=TASK_NAME_LIST, label="Select Task", interactive=True, value=initial_task_name) | |
| # --- MODIFIED --- | |
| # 3. Modify the update function to return TWO values: the new title and the new table. | |
| def update_task_table(task_name_with_id): | |
| # Create the new dynamic title HTML | |
| tname=task_name_with_id.split(' (')[0] | |
| tid=task_name_with_id.split(' (')[-1].split(')')[0] | |
| new_title = f"<h3><span class='title'>Task name:</span> {tname}<br> <span class='title'>Task identifier:</span> {tid}</h3>" | |
| # new_title = f"<br><br><center><h2>{task_name_with_id} Leaderboard</h2></center><br>" | |
| # Parse the task key to get the data | |
| task_key = task_name_with_id.split('(')[-1].strip(')') | |
| df = get_task_leaderboard(task_key) | |
| # Return both the new title and the HTML for the table | |
| return new_title, df_to_html(df) | |
| # Initial table display (remains the same) | |
| initial_task_key = initial_task_name.split('(')[-1].strip(')') | |
| task_table_component = gr.HTML(df_to_html(get_task_leaderboard(initial_task_key))) | |
| # --- MODIFIED --- | |
| # 4. Update the .change() event to send outputs to BOTH the title and table components. | |
| task_dropdown.change( | |
| fn=update_task_table, | |
| inputs=task_dropdown, | |
| outputs=[task_title_component, task_table_component] | |
| ) | |
| with gr.Tab("Language-Specific Leaderboard", id="langs"): | |
| gr.HTML("<br><br><center><h2>Language-Specific Leaderboard (per task)</h2></center><br>") | |
| lang_name=LANG_NAME_LIST[0] | |
| initial_lang_title_html = f"<h3><span class='title'>Language name:</span> {lang_name} <br> <span class='title'>Language ISO-3:</span> {next(iter(LANGNAME2ISOS.get(lang_name)))} </h3>" | |
| # 2. Create a variable for the title component so it can be updated. | |
| lang_title_component = gr.HTML(initial_lang_title_html) | |
| lang_dropdown = gr.Dropdown(choices=LANG_NAME_LIST, label="Select Language", interactive=True) | |
| lang_table_component = gr.HTML(df_to_html(get_lang_table(LANG_NAME_LIST[0])) if LANG_NAME_LIST else "<b>No languages found</b>") | |
| def update_lang_table(lang_name): | |
| df = get_lang_table(lang_name) | |
| new_title = f"<h3><span class='title'>Language name:</span> {lang_name} <br> <span class='title'>Language ISO-3:</span> {next(iter(LANGNAME2ISOS.get(lang_name)))}</h3>" | |
| return new_title, df_to_html(df) | |
| lang_dropdown.change(update_lang_table, lang_dropdown, outputs=[lang_title_component, lang_table_component]) | |
| # --- NEW TAB FOR MODEL-SPECIFIC LEADERBOARD --- | |
| with gr.Tab("Model-Specific Leaderboard", id="models", elem_id="model_specific_table"): | |
| gr.HTML("<br><br><center><h2>Model-Specific Leaderboard (per task)</h2></center><br>") | |
| initial_model_name = MODEL_NAME_LIST[0] | |
| initial_model_title_html = f"<h3><span class='title'>Model name:</span> {initial_model_name}</h3>" | |
| # Component to display the dynamic title | |
| model_title_component = gr.HTML(initial_model_title_html) | |
| # Dropdown for selecting the model | |
| model_dropdown = gr.Dropdown( | |
| choices=MODEL_NAME_LIST, | |
| label="Select Model", | |
| interactive=True, | |
| value=initial_model_name | |
| ) | |
| # Component to display the model's performance table | |
| model_table_component = gr.HTML(df_to_html(get_model_table(initial_model_name))) | |
| # Function to update the title and table based on dropdown selection | |
| def update_model_table(model_name): | |
| df = get_model_table(model_name) | |
| new_title = f"<h3><span class='title'>Model name:</span> {model_name}</h3>" | |
| return new_title, df_to_html(df) | |
| # Link the dropdown's change event to the update function | |
| model_dropdown.change( | |
| fn=update_model_table, | |
| inputs=model_dropdown, | |
| outputs=[model_title_component, model_table_component] | |
| ) | |
| # --- NEW TAB TO COMPARE MODELS --- | |
| with gr.Tab("Compare Models", id="compare"): | |
| gr.HTML("<br><br><center><h2>Compare Two Models</h2></center><br>") | |
| with gr.Row(): | |
| model_1_dd = gr.Dropdown(MODEL_NAME_LIST, label="Select Model 1", interactive=True) | |
| model_2_dd = gr.Dropdown(MODEL_NAME_LIST, label="Select Model 2", interactive=True) | |
| compare_btn = gr.Button("Compare") | |
| # Note for the 'Difference' column | |
| explanation_note = """ | |
| **Note on the 'Difference' Column:** | |
| * This value is calculated as: `(Score of Model 1) - (Score of Model 2)`. | |
| * A positive value in <span style='color:green; font-weight:bold;'>green</span> indicates that **Model 1** performed better on that task. | |
| * A negative value in <span style='color:red; font-weight:bold;'>red</span> indicates that **Model 2** performed better on that task. | |
| """ | |
| # --- MODIFIED: Make the note invisible by default --- | |
| explanation_note_md = gr.Markdown(explanation_note, visible=False) | |
| # Create a container with a unique ID for the comparison table | |
| with gr.Column(elem_id="models_comparasion_table"): | |
| comparison_output = gr.HTML("<p style='text-align:center;'>Select two models and click Compare to see the results.</p>") | |
| # --- MODIFIED: The function now returns TWO values (visibility and html) --- | |
| def update_comparison_table(m1, m2): | |
| if not m1 or not m2: | |
| gr.Info("Please select both models before clicking Compare.") | |
| # Return an update to hide the note and the placeholder text | |
| return gr.update(visible=False), "<p style='text-align:center;'>Please select two models to compare.</p>" | |
| df = compare_models(m1, m2) | |
| # Return an update to SHOW the note and the results table | |
| return gr.update(visible=True), df_to_html(df) | |
| # --- MODIFIED: Update the outputs list to target both components --- | |
| compare_btn.click( | |
| fn=update_comparison_table, | |
| inputs=[model_1_dd, model_2_dd], | |
| outputs=[explanation_note_md, comparison_output] | |
| ) | |
| with gr.Group(elem_classes="content-card"): | |
| gr.Markdown("<br>") | |
| gr.HTML("<h2>Citation</h2>If you use the Sahara benchmark for your scientific publication, or if you find the resources in this website useful, please cite our <a href='https://africa.dlnlp.ai/sahara/'>ACL2025 paper </a>.") | |
| gr.HTML("<center><img src='https://africa.dlnlp.ai/sahara//img/sahara_web_sponsers.jpg' width='25%'> </center>") | |
| if __name__ == "__main__": | |
| demo.launch(share=True) |