Spaces:
Sleeping
Sleeping
| import matplotlib.pyplot as plt | |
| import matplotlib | |
| import pandas as pd | |
| import gradio as gr | |
| from gradio_toggle import Toggle | |
| from data import CIResults | |
| from utils import logger | |
| from summary_page import create_summary_page | |
| from model_page import plot_model_stats | |
| from time_series_gradio import ( | |
| create_time_series_summary_gradio, | |
| create_model_time_series_gradio, | |
| get_time_series_summary_dfs, | |
| get_model_time_series_dfs, | |
| ) | |
| # Configure matplotlib to prevent memory warnings and set dark background | |
| matplotlib.rcParams['figure.facecolor'] = '#000000' | |
| matplotlib.rcParams['axes.facecolor'] = '#000000' | |
| matplotlib.rcParams['savefig.facecolor'] = '#000000' | |
| plt.ioff() # Turn off interactive mode to prevent figure accumulation | |
| # Load data once at startup | |
| Ci_results = CIResults() | |
| Ci_results.load_data() | |
| # Preload historical data at startup | |
| if Ci_results.available_dates: | |
| start_date_val = Ci_results.available_dates[-1] # Last date (oldest) | |
| end_date_val = Ci_results.available_dates[0] # First date (newest) | |
| Ci_results.load_historical_data(start_date_val, end_date_val) | |
| logger.info(f"Preloaded historical data: {len(Ci_results.historical_df)} records") | |
| # Start the auto-reload scheduler | |
| Ci_results.schedule_data_reload() | |
| # Function to check if a model has failures | |
| def model_has_failures(model_name): | |
| """Check if a model has any failures (AMD or NVIDIA).""" | |
| if Ci_results.df is None or Ci_results.df.empty: | |
| return False | |
| # Normalize model name to match DataFrame index | |
| model_name_lower = model_name.lower() | |
| # Check if model exists in DataFrame | |
| if model_name_lower not in Ci_results.df.index: | |
| return False | |
| row = Ci_results.df.loc[model_name_lower] | |
| # Check for failures in both AMD and NVIDIA | |
| amd_multi_failures = row.get('failed_multi_no_amd', 0) | |
| amd_single_failures = row.get('failed_single_no_amd', 0) | |
| nvidia_multi_failures = row.get('failed_multi_no_nvidia', 0) | |
| nvidia_single_failures = row.get('failed_single_no_nvidia', 0) | |
| return any([ | |
| amd_multi_failures > 0, | |
| amd_single_failures > 0, | |
| nvidia_multi_failures > 0, | |
| nvidia_single_failures > 0, | |
| ]) | |
| # Function to get current description text | |
| def get_description_text(): | |
| """Get description text with integrated last update time.""" | |
| msg = [ | |
| "Transformer CI Dashboard", | |
| "-", | |
| "AMD runs on MI325", | |
| "NVIDIA runs on A10", | |
| ] | |
| msg = ["**" + x + "**" for x in msg] + [""] | |
| if Ci_results.latest_update_msg: | |
| msg.append(f"*({Ci_results.latest_update_msg})*") | |
| else: | |
| msg.append("*(loading...)*") | |
| return "<br>".join(msg) | |
| # Load CSS from external file | |
| def load_css(): | |
| try: | |
| with open("styles.css", "r") as f: | |
| css_content = f.read() | |
| return css_content | |
| except FileNotFoundError: | |
| logger.warning("styles.css not found, using minimal default styles") | |
| return "body { background: #000; color: #fff; }" | |
| js_func = """ | |
| function refresh() { | |
| const url = new URL(window.location); | |
| if (url.searchParams.get('__theme') !== 'dark') { | |
| url.searchParams.set('__theme', 'dark'); | |
| window.location.href = url.href; | |
| } | |
| } | |
| """ | |
| # Create the Gradio interface with sidebar and dark theme | |
| with gr.Blocks(title="Model Test Results Dashboard", css=load_css(), js=js_func) as demo: | |
| with gr.Row(): | |
| # Sidebar for model selection | |
| with gr.Column(scale=1, elem_classes=["sidebar"]): | |
| gr.Markdown("# π€ TCID", elem_classes=["sidebar-title"]) | |
| # Description with integrated last update time | |
| description_text = get_description_text() | |
| description_display = gr.Markdown(description_text, elem_classes=["sidebar-description"]) | |
| # Summary button (for current view) | |
| summary_button = gr.Button( | |
| "summary\nπ", | |
| variant="primary", | |
| size="lg", | |
| elem_classes=["summary-button"] | |
| ) | |
| history_view_button = Toggle( | |
| label="History view", | |
| value=False, | |
| interactive=True, | |
| elem_classes=["history-view-button"] | |
| ) | |
| # Model selection header (clickable toggle) | |
| model_toggle_button = gr.Button( | |
| f"βΊ Select model ({len(Ci_results.available_models)})", | |
| variant="secondary", | |
| elem_classes=["model-header"] | |
| ) | |
| # Model buttons container (collapsible) - start folded | |
| with gr.Column(elem_classes=["model-list", "model-list-hidden"]) as model_list_container: | |
| # Create individual buttons for each model | |
| model_buttons = [] | |
| model_choices = [model.lower() for model in Ci_results.available_models] if Ci_results.available_models else ["auto", "bert", "clip", "llama"] | |
| print(f"Creating {len(model_choices)} model buttons: {model_choices}") | |
| for model_name in model_choices: | |
| # Check if model has failures to determine styling | |
| has_failures = model_has_failures(model_name) | |
| button_classes = ["model-button"] | |
| if has_failures: | |
| button_classes.append("model-button-failed") | |
| btn = gr.Button( | |
| model_name, | |
| variant="secondary", | |
| size="sm", | |
| elem_classes=button_classes | |
| ) | |
| model_buttons.append(btn) | |
| # CI job links at bottom of sidebar | |
| ci_links_display = gr.Markdown("π **CI Jobs:** *Loading...*", elem_classes=["sidebar-links"]) | |
| # Main content area | |
| with gr.Column(scale=4, elem_classes=["main-content"]): | |
| # Current view components | |
| with gr.Column(visible=True, elem_classes=["current-view"]) as current_view: | |
| # Summary display (default view) | |
| summary_display = gr.Plot( | |
| value=create_summary_page(Ci_results.df, Ci_results.available_models), | |
| label="", | |
| format="png", | |
| elem_classes=["plot-container"], | |
| visible=True | |
| ) | |
| # Detailed view components (hidden by default) | |
| with gr.Column(visible=False, elem_classes=["detail-view"]) as detail_view: | |
| # Create the plot output | |
| plot_output = gr.Plot( | |
| label="", | |
| format="png", | |
| elem_classes=["plot-container"] | |
| ) | |
| # Create two separate failed tests displays in a row layout | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| amd_failed_tests_output = gr.Textbox( | |
| value="", | |
| lines=8, | |
| max_lines=8, | |
| interactive=False, | |
| container=False, | |
| elem_classes=["failed-tests"] | |
| ) | |
| with gr.Column(scale=1): | |
| nvidia_failed_tests_output = gr.Textbox( | |
| value="", | |
| lines=8, | |
| max_lines=8, | |
| interactive=False, | |
| container=False, | |
| elem_classes=["failed-tests"] | |
| ) | |
| # Historical view components (hidden by default) | |
| with gr.Column(visible=False, elem_classes=["historical-view"]) as historical_view: | |
| # Time-series summary displays (multiple Gradio plots) | |
| time_series_failure_rates = gr.LinePlot( | |
| label="", | |
| x="date", | |
| y="failure_rate", | |
| color="platform", | |
| color_map={"AMD": "#FF6B6B", "NVIDIA": "#4ECDC4"}, | |
| title="Overall Failure Rates Over Time", | |
| tooltip=["failure_rate", "date", "change"], | |
| height=300, | |
| x_label_angle=45, | |
| y_title="Failure Rate (%)", | |
| elem_classes=["plot-container"] | |
| ) | |
| time_series_amd_tests = gr.LinePlot( | |
| label="", | |
| x="date", | |
| y="count", | |
| color="test_type", | |
| color_map={"Passed": "#4CAF50", "Failed": "#E53E3E", "Skipped": "#FFA500"}, | |
| title="AMD Test Results Over Time", | |
| tooltip=["count", "date", "change"], | |
| height=300, | |
| x_label_angle=45, | |
| y_title="Number of Tests", | |
| elem_classes=["plot-container"] | |
| ) | |
| time_series_nvidia_tests = gr.LinePlot( | |
| label="", | |
| x="date", | |
| y="count", | |
| color="test_type", | |
| color_map={"Passed": "#4CAF50", "Failed": "#E53E3E", "Skipped": "#FFA500"}, | |
| title="NVIDIA Test Results Over Time", | |
| tooltip=["count", "date", "change"], | |
| height=300, | |
| x_label_angle=45, | |
| y_title="Number of Tests", | |
| elem_classes=["plot-container"] | |
| ) | |
| # Time-series model view (hidden by default) | |
| with gr.Column(visible=False, elem_classes=["time-series-detail-view"]) as time_series_detail_view: | |
| # Time-series plots for specific model (with spacing) | |
| time_series_amd_model_plot = gr.LinePlot( | |
| label="", | |
| x="date", | |
| y="count", | |
| color="test_type", | |
| color_map={"Passed": "#4CAF50", "Failed": "#E53E3E", "Skipped": "#FFA500"}, | |
| title="AMD Results Over Time", | |
| tooltip=["count", "date", "change"], | |
| height=300, | |
| x_label_angle=45, | |
| y_title="Number of Tests", | |
| elem_classes=["plot-container"] | |
| ) | |
| time_series_nvidia_model_plot = gr.LinePlot( | |
| label="", | |
| x="date", | |
| y="count", | |
| color="test_type", | |
| color_map={"Passed": "#4CAF50", "Failed": "#E53E3E", "Skipped": "#FFA500"}, | |
| title="NVIDIA Results Over Time", | |
| tooltip=["count", "date", "change"], | |
| height=300, | |
| x_label_angle=45, | |
| y_title="Number of Tests", | |
| elem_classes=["plot-container"] | |
| ) | |
| # Model toggle functionality | |
| def toggle_model_list(current_visible): | |
| """Toggle the visibility of the model list.""" | |
| new_visible = not current_visible | |
| arrow = "βΌ" if new_visible else "βΊ" | |
| button_text = f"{arrow} Select model ({len(Ci_results.available_models)})" | |
| # Use CSS classes instead of Gradio visibility | |
| css_classes = ["model-list"] | |
| if new_visible: | |
| css_classes.append("model-list-visible") | |
| else: | |
| css_classes.append("model-list-hidden") | |
| return gr.update(value=button_text), gr.update(elem_classes=css_classes), new_visible | |
| # Track model list visibility state | |
| model_list_visible = gr.State(False) | |
| # Track last selected model for mode switches | |
| selected_model_state = gr.State(None) | |
| # Track whether current view is model detail (True) or summary (False) | |
| in_model_view_state = gr.State(False) | |
| model_toggle_button.click( | |
| fn=toggle_model_list, | |
| inputs=[model_list_visible], | |
| outputs=[model_toggle_button, model_list_container, model_list_visible] | |
| ) | |
| # Unified summary handler: respects History toggle | |
| def handle_summary_click(history_mode: bool): | |
| description = get_description_text() | |
| links = get_ci_links() | |
| fr_plot, amd_plot, nvidia_plot = get_historical_summary_plots() | |
| if history_mode: | |
| return ( | |
| description, | |
| links, | |
| gr.update(visible=False), # current_view | |
| gr.update(visible=True), # historical_view | |
| gr.update(visible=False), # summary_display | |
| gr.update(visible=False), # detail_view | |
| fr_plot, | |
| amd_plot, | |
| nvidia_plot, | |
| gr.update(visible=False), # time_series_detail_view | |
| False, # in_model_view_state | |
| ) | |
| else: | |
| fig = create_summary_page(Ci_results.df, Ci_results.available_models) | |
| return ( | |
| description, | |
| links, | |
| gr.update(visible=True), # current_view | |
| gr.update(visible=False), # historical_view | |
| gr.update(value=fig, visible=True), # summary_display | |
| gr.update(visible=False), # detail_view | |
| gr.update(visible=False), # time_series_failure_rates | |
| gr.update(visible=False), # time_series_amd_tests | |
| gr.update(visible=False), # time_series_nvidia_tests | |
| gr.update(visible=False), # time_series_detail_view | |
| False, # in_model_view_state | |
| ) | |
| summary_button.click( | |
| fn=handle_summary_click, | |
| inputs=[history_view_button], | |
| outputs=[ | |
| description_display, | |
| ci_links_display, | |
| current_view, | |
| historical_view, | |
| summary_display, | |
| detail_view, | |
| time_series_failure_rates, | |
| time_series_amd_tests, | |
| time_series_nvidia_tests, | |
| time_series_detail_view, | |
| in_model_view_state, | |
| ], | |
| ) | |
| # Function to get CI job links | |
| def get_ci_links(): | |
| """Get CI job links from the most recent data.""" | |
| try: | |
| # Check if df exists and is not empty | |
| if Ci_results.df is None or Ci_results.df.empty: | |
| return "π **CI Jobs:** *Loading...*" | |
| # Get links from any available model (they should be the same for all models in a run) | |
| amd_multi_link = None | |
| amd_single_link = None | |
| nvidia_multi_link = None | |
| nvidia_single_link = None | |
| for model_name in Ci_results.df.index: | |
| row = Ci_results.df.loc[model_name] | |
| # Extract AMD links | |
| if pd.notna(row.get('job_link_amd')) and (not amd_multi_link or not amd_single_link): | |
| amd_link_raw = row.get('job_link_amd') | |
| if isinstance(amd_link_raw, dict): | |
| if 'multi' in amd_link_raw and not amd_multi_link: | |
| amd_multi_link = amd_link_raw['multi'] | |
| if 'single' in amd_link_raw and not amd_single_link: | |
| amd_single_link = amd_link_raw['single'] | |
| # Extract NVIDIA links | |
| if pd.notna(row.get('job_link_nvidia')) and (not nvidia_multi_link or not nvidia_single_link): | |
| nvidia_link_raw = row.get('job_link_nvidia') | |
| if isinstance(nvidia_link_raw, dict): | |
| if 'multi' in nvidia_link_raw and not nvidia_multi_link: | |
| nvidia_multi_link = nvidia_link_raw['multi'] | |
| if 'single' in nvidia_link_raw and not nvidia_single_link: | |
| nvidia_single_link = nvidia_link_raw['single'] | |
| # Break if we have all links | |
| if amd_multi_link and amd_single_link and nvidia_multi_link and nvidia_single_link: | |
| break | |
| # Add FAQ link at the bottom | |
| links_md = "β [**FAQ**](https://huggingface.co/spaces/transformers-community/transformers-ci-dashboard/blob/main/README.md)\n\n" | |
| links_md += "π **CI Jobs:**\n\n" | |
| # AMD links | |
| if amd_multi_link or amd_single_link: | |
| links_md += "**AMD:**\n" | |
| if amd_multi_link: | |
| links_md += f"β’ [Multi GPU]({amd_multi_link})\n" | |
| if amd_single_link: | |
| links_md += f"β’ [Single GPU]({amd_single_link})\n" | |
| links_md += "\n" | |
| # NVIDIA links | |
| if nvidia_multi_link or nvidia_single_link: | |
| links_md += "**NVIDIA:**\n" | |
| if nvidia_multi_link: | |
| links_md += f"β’ [Multi GPU]({nvidia_multi_link})\n" | |
| if nvidia_single_link: | |
| links_md += f"β’ [Single GPU]({nvidia_single_link})\n" | |
| if not (amd_multi_link or amd_single_link or nvidia_multi_link or nvidia_single_link): | |
| links_md += "*No links available*" | |
| return links_md | |
| except Exception as e: | |
| logger.error(f"getting CI links: {e}") | |
| return "π **CI Jobs:** *Error loading links*\n\nβ **[FAQ](README.md)**" | |
| def get_historical_summary_plots(): | |
| """Get historical summary plots from preloaded data.""" | |
| dfs = get_time_series_summary_dfs(Ci_results.historical_df) | |
| return ( | |
| gr.update(value=dfs['failure_rates_df'], visible=True), | |
| gr.update(value=dfs['amd_tests_df'], visible=True), | |
| gr.update(value=dfs['nvidia_tests_df'], visible=True), | |
| ) | |
| def handle_history_toggle(history_mode, last_selected_model, in_model_view): | |
| if history_mode: | |
| # If currently in model view and valid model, show historical model detail | |
| if in_model_view and last_selected_model: | |
| amd_ts, nvidia_ts = show_time_series_model(last_selected_model) | |
| return ( | |
| gr.update(visible=False), # current_view | |
| gr.update(visible=True), # historical_view | |
| gr.update(visible=False), # summary_display | |
| gr.update(visible=False), # detail_view | |
| gr.update(visible=False), # time_series_failure_rates | |
| gr.update(visible=False), # time_series_amd_tests | |
| gr.update(visible=False), # time_series_nvidia_tests | |
| amd_ts, # time_series_amd_model_plot | |
| nvidia_ts, # time_series_nvidia_model_plot | |
| gr.update(visible=True), # time_series_detail_view | |
| gr.update(), # plot_output | |
| gr.update(), # amd_failed_tests_output | |
| gr.update(), # nvidia_failed_tests_output | |
| True, # in_model_view_state (still in model view) | |
| ) | |
| # Otherwise show historical summary | |
| fr_plot, amd_plot, nvidia_plot = get_historical_summary_plots() | |
| return ( | |
| gr.update(visible=False), # current_view | |
| gr.update(visible=True), # historical_view | |
| gr.update(visible=False), # summary_display | |
| gr.update(visible=False), # detail_view | |
| fr_plot, # time_series_failure_rates (value + keep visibility) | |
| amd_plot, # time_series_amd_tests | |
| nvidia_plot, # time_series_nvidia_tests | |
| gr.update(), # time_series_amd_model_plot | |
| gr.update(), # time_series_nvidia_model_plot | |
| gr.update(visible=False), # time_series_detail_view | |
| gr.update(), # plot_output | |
| gr.update(), # amd_failed_tests_output | |
| gr.update(), # nvidia_failed_tests_output | |
| False, # in_model_view_state | |
| ) | |
| else: | |
| # Switch to current mode: show model if selected; otherwise summary | |
| if last_selected_model and Ci_results.df is not None and not Ci_results.df.empty and last_selected_model in Ci_results.df.index: | |
| fig, amd_txt, nvidia_txt = plot_model_stats(Ci_results.df, last_selected_model) | |
| return ( | |
| gr.update(visible=True), # current_view | |
| gr.update(visible=False), # historical_view | |
| gr.update(visible=False), # summary_display | |
| gr.update(visible=True), # detail_view | |
| gr.update(visible=False), # time_series_failure_rates | |
| gr.update(visible=False), # time_series_amd_tests | |
| gr.update(visible=False), # time_series_nvidia_tests | |
| gr.update(), # time_series_amd_model_plot | |
| gr.update(), # time_series_nvidia_model_plot | |
| gr.update(visible=False), # time_series_detail_view | |
| fig, # plot_output | |
| amd_txt, # amd_failed_tests_output | |
| nvidia_txt, # nvidia_failed_tests_output | |
| True, # in_model_view_state | |
| ) | |
| else: | |
| fig = create_summary_page(Ci_results.df, Ci_results.available_models) | |
| return ( | |
| gr.update(visible=True), # current_view | |
| gr.update(visible=False), # historical_view | |
| gr.update(value=fig, visible=True), # summary_display | |
| gr.update(visible=False), # detail_view | |
| gr.update(visible=False), # time_series_failure_rates | |
| gr.update(visible=False), # time_series_amd_tests | |
| gr.update(visible=False), # time_series_nvidia_tests | |
| gr.update(), # time_series_amd_model_plot | |
| gr.update(), # time_series_nvidia_model_plot | |
| gr.update(visible=False), # time_series_detail_view | |
| gr.update(), # plot_output | |
| gr.update(), # amd_failed_tests_output | |
| gr.update(), # nvidia_failed_tests_output | |
| False, # in_model_view_state | |
| ) | |
| history_view_button.change( | |
| fn=handle_history_toggle, | |
| inputs=[history_view_button, selected_model_state, in_model_view_state], | |
| outputs=[ | |
| current_view, | |
| historical_view, | |
| summary_display, | |
| detail_view, | |
| time_series_failure_rates, | |
| time_series_amd_tests, | |
| time_series_nvidia_tests, | |
| time_series_amd_model_plot, | |
| time_series_nvidia_model_plot, | |
| time_series_detail_view, | |
| plot_output, | |
| amd_failed_tests_output, | |
| nvidia_failed_tests_output, | |
| in_model_view_state, | |
| ], | |
| ) | |
| # Time-series model selection functionality | |
| def show_time_series_model(selected_model): | |
| """Show time-series view for a specific model.""" | |
| dfs = get_model_time_series_dfs(Ci_results.historical_df, selected_model) | |
| return ( | |
| gr.update(value=dfs['amd_df'], visible=True, title=f"{selected_model.upper()} - AMD Results Over Time"), | |
| gr.update(value=dfs['nvidia_df'], visible=True, title=f"{selected_model.upper()} - NVIDIA Results Over Time"), | |
| ) | |
| # Unified model click handler: respects History toggle | |
| def handle_model_click(selected_model: str, history_mode: bool): | |
| if history_mode: | |
| amd_ts, nvidia_ts = show_time_series_model(selected_model) | |
| return ( | |
| gr.update(), # plot_output | |
| gr.update(), # amd_failed_tests_output | |
| gr.update(), # nvidia_failed_tests_output | |
| gr.update(visible=False), # current_view | |
| gr.update(visible=True), # historical_view | |
| gr.update(visible=False), # summary_display | |
| gr.update(visible=False), # detail_view | |
| gr.update(visible=False), # time_series_failure_rates | |
| gr.update(visible=False), # time_series_amd_tests | |
| gr.update(visible=False), # time_series_nvidia_tests | |
| amd_ts, # time_series_amd_model_plot | |
| nvidia_ts, # time_series_nvidia_model_plot | |
| gr.update(visible=True), # time_series_detail_view | |
| selected_model, True) # selected_model_state, in_model_view_state | |
| else: | |
| fig, amd_txt, nvidia_txt = plot_model_stats(Ci_results.df, selected_model) | |
| return ( | |
| fig, | |
| amd_txt, | |
| nvidia_txt, | |
| gr.update(visible=True), # current_view | |
| gr.update(visible=False), # historical_view | |
| gr.update(visible=False), # summary_display | |
| gr.update(visible=True), # detail_view | |
| gr.update(), # time_series_failure_rates | |
| gr.update(), # time_series_amd_tests | |
| gr.update(), # time_series_nvidia_tests | |
| gr.update(), # time_series_amd_model_plot | |
| gr.update(), # time_series_nvidia_model_plot | |
| gr.update(visible=False), # time_series_detail_view | |
| selected_model, True) # selected_model_state, in_model_view_state | |
| for i, btn in enumerate(model_buttons): | |
| model_name = model_choices[i] | |
| btn.click( | |
| fn=lambda history_mode, m=model_name: handle_model_click(m, history_mode), | |
| inputs=[history_view_button], | |
| outputs=[ | |
| plot_output, | |
| amd_failed_tests_output, | |
| nvidia_failed_tests_output, | |
| current_view, | |
| historical_view, | |
| summary_display, | |
| detail_view, | |
| time_series_failure_rates, | |
| time_series_amd_tests, | |
| time_series_nvidia_tests, | |
| time_series_amd_model_plot, | |
| time_series_nvidia_model_plot, | |
| time_series_detail_view, | |
| selected_model_state, | |
| in_model_view_state, | |
| ], | |
| ) | |
| # Auto-update CI links when the interface loads | |
| demo.load( | |
| fn=get_ci_links, | |
| outputs=[ci_links_display] | |
| ) | |
| # Gradio entrypoint | |
| if __name__ == "__main__": | |
| demo.launch() | |