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("
") gr.HTML(""" """) if __name__ == "__main__": demo.launch(share=True)