import gradio as gr from src.helper import * # Custom CSS to replicate the Google-style card design from the image custom_head_html = """ """ new_header_html = """



Sahara logo


""" 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 = '' # 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("
") # 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("

Main Leaderboard


") gr.HTML(df_to_html(main_overall_tab)) # Task Clusters leaderboards with gr.Tab("Task-Cluster Leaderboard", id="clusters"): gr.HTML("

Task-Cluster Leaderboard


") CLUSTERS_NAME=[cname for cname, cdf in cluster_tabs.items()] cname = CLUSTERS_NAME[0] initial_cluster_title_html = f"

Task-Cluster name: {cname}

" # 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 "No cluser found") def update_cluster_table(cluster_name): df = get_claster_table(cluster_name) cluster_title_html = f"

Task-Cluster name: {cluster_name}

" return cluster_title_html, df_to_html(df) if df is not None else "No data found" 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("

Task-Specific Leaderboard (per langauge)


") 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"

Task name: {tname}
Task identifier: {tid}

" # 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"

Task name: {tname}
Task identifier: {tid}

" # new_title = f"

{task_name_with_id} Leaderboard


" # 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("

Language-Specific Leaderboard (per task)


") lang_name=LANG_NAME_LIST[0] initial_lang_title_html = f"

Language name: {lang_name}
Language ISO-3: {next(iter(LANGNAME2ISOS.get(lang_name)))}

" # 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 "No languages found") def update_lang_table(lang_name): df = get_lang_table(lang_name) new_title = f"

Language name: {lang_name}
Language ISO-3: {next(iter(LANGNAME2ISOS.get(lang_name)))}

" 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("

Model-Specific Leaderboard (per task)


") initial_model_name = MODEL_NAME_LIST[0] initial_model_title_html = f"

Model name: {initial_model_name}

" # 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"

Model name: {model_name}

" 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("

Compare Two Models


") 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 green indicates that **Model 1** performed better on that task. * A negative value in red 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("

Select two models and click Compare to see the results.

") # --- 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), "

Please select two models to compare.

" 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("
") gr.HTML("

Citation

If you use the Sahara benchmark for your scientific publication, or if you find the resources in this website useful, please cite our ACL2025 paper .") gr.HTML("
") if __name__ == "__main__": demo.launch(share=True)