Rework of model page
Browse files- model_page.py +48 -62
- styles.css +2 -1
- utils.py +1 -1
model_page.py
CHANGED
|
@@ -38,60 +38,6 @@ MODEL_TITLE_Y = 1
|
|
| 38 |
MAX_FAILURE_ITEMS = 10
|
| 39 |
|
| 40 |
|
| 41 |
-
def _process_failure_category(failures_obj: dict, category: str, info_lines: list) -> None:
|
| 42 |
-
"""Process a single failure category (multi or single) and add to info_lines."""
|
| 43 |
-
if category in failures_obj and failures_obj[category]:
|
| 44 |
-
info_lines.append(generate_underlined_line(f"{category.title()} GPU failure details:"))
|
| 45 |
-
if isinstance(failures_obj[category], list):
|
| 46 |
-
# Handle list of failures (could be strings or dicts)
|
| 47 |
-
for i, failure in enumerate(failures_obj[category][:MAX_FAILURE_ITEMS]):
|
| 48 |
-
if isinstance(failure, dict):
|
| 49 |
-
# Extract meaningful info from dict (e.g., test name, line, etc.)
|
| 50 |
-
failure_str = failure.get('line', failure.get('test',
|
| 51 |
-
failure.get('name', str(failure))))
|
| 52 |
-
info_lines.append(f" {i+1}. {failure_str}")
|
| 53 |
-
else:
|
| 54 |
-
info_lines.append(f" {i+1}. {str(failure)}")
|
| 55 |
-
if len(failures_obj[category]) > MAX_FAILURE_ITEMS:
|
| 56 |
-
remaining = len(failures_obj[category]) - MAX_FAILURE_ITEMS
|
| 57 |
-
info_lines.append(f"... and {remaining} more")
|
| 58 |
-
else:
|
| 59 |
-
info_lines.append(str(failures_obj[category]))
|
| 60 |
-
info_lines.append("")
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
def extract_failure_info(failures_obj, device: str, multi_count: int, single_count: int) -> str:
|
| 64 |
-
"""Extract failure information from failures object."""
|
| 65 |
-
if (not failures_obj or pd.isna(failures_obj)) and multi_count == 0 and single_count == 0:
|
| 66 |
-
return f"No failures on {device}"
|
| 67 |
-
|
| 68 |
-
info_lines = []
|
| 69 |
-
|
| 70 |
-
# Add counts summary
|
| 71 |
-
if multi_count > 0 or single_count > 0:
|
| 72 |
-
info_lines.append(generate_underlined_line(f"Failure Summary for {device}:"))
|
| 73 |
-
if multi_count > 0:
|
| 74 |
-
info_lines.append(f"Multi GPU failures: {multi_count}")
|
| 75 |
-
if single_count > 0:
|
| 76 |
-
info_lines.append(f"Single GPU failures: {single_count}")
|
| 77 |
-
info_lines.append("")
|
| 78 |
-
|
| 79 |
-
# Try to extract detailed failure information
|
| 80 |
-
try:
|
| 81 |
-
if isinstance(failures_obj, dict):
|
| 82 |
-
_process_failure_category(failures_obj, 'multi', info_lines)
|
| 83 |
-
_process_failure_category(failures_obj, 'single', info_lines)
|
| 84 |
-
|
| 85 |
-
return "\n".join(info_lines) if info_lines else f"No detailed failure info for {device}"
|
| 86 |
-
|
| 87 |
-
except Exception as e:
|
| 88 |
-
if multi_count > 0 or single_count > 0:
|
| 89 |
-
error_msg = (f"Failures detected on {device} (Multi: {multi_count}, Single: {single_count})\n"
|
| 90 |
-
f"Details unavailable: {str(e)}")
|
| 91 |
-
return error_msg
|
| 92 |
-
return f"Error processing failure info for {device}: {str(e)}"
|
| 93 |
-
|
| 94 |
-
|
| 95 |
def _create_pie_chart(ax: plt.Axes, device_label: str, filtered_stats: dict) -> None:
|
| 96 |
"""Create a pie chart for device statistics."""
|
| 97 |
if not filtered_stats:
|
|
@@ -111,7 +57,7 @@ def _create_pie_chart(ax: plt.Axes, device_label: str, filtered_stats: dict) ->
|
|
| 111 |
filtered_stats.values(),
|
| 112 |
labels=[label.lower() for label in filtered_stats.keys()], # Lowercase for minimal look
|
| 113 |
colors=chart_colors,
|
| 114 |
-
autopct=lambda pct: f'{int(pct
|
| 115 |
startangle=PIE_START_ANGLE,
|
| 116 |
explode=None, # No separation
|
| 117 |
shadow=False,
|
|
@@ -146,22 +92,23 @@ def plot_model_stats(df: pd.DataFrame, model_name: str) -> tuple[plt.Figure, str
|
|
| 146 |
# Create empty stats for both devices
|
| 147 |
amd_filtered = {}
|
| 148 |
nvidia_filtered = {}
|
| 149 |
-
failed_multi_amd = failed_single_amd = failed_multi_nvidia = failed_single_nvidia = 0
|
| 150 |
failures_amd = failures_nvidia = {}
|
| 151 |
else:
|
| 152 |
row = df.loc[model_name]
|
| 153 |
|
| 154 |
# Extract and process model data
|
| 155 |
-
amd_stats, nvidia_stats
|
| 156 |
-
extract_model_data(row)
|
| 157 |
|
| 158 |
# Filter out categories with 0 values for cleaner visualization
|
| 159 |
amd_filtered = {k: v for k, v in amd_stats.items() if v > 0}
|
| 160 |
nvidia_filtered = {k: v for k, v in nvidia_stats.items() if v > 0}
|
| 161 |
|
| 162 |
# Generate failure info directly from dataframe
|
| 163 |
-
failures_amd = row.get('failures_amd', {})
|
| 164 |
-
failures_nvidia = row.get('failures_nvidia', {})
|
|
|
|
|
|
|
|
|
|
| 165 |
|
| 166 |
# Always create figure with two subplots side by side with padding
|
| 167 |
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(FIGURE_WIDTH_DUAL, FIGURE_HEIGHT_DUAL), facecolor=BLACK)
|
|
@@ -186,7 +133,46 @@ def plot_model_stats(df: pd.DataFrame, model_name: str) -> tuple[plt.Figure, str
|
|
| 186 |
plt.tight_layout()
|
| 187 |
plt.subplots_adjust(top=SUBPLOT_TOP, wspace=SUBPLOT_WSPACE)
|
| 188 |
|
| 189 |
-
amd_failed_info =
|
| 190 |
-
nvidia_failed_info =
|
| 191 |
|
| 192 |
return fig, amd_failed_info, nvidia_failed_info
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
MAX_FAILURE_ITEMS = 10
|
| 39 |
|
| 40 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
def _create_pie_chart(ax: plt.Axes, device_label: str, filtered_stats: dict) -> None:
|
| 42 |
"""Create a pie chart for device statistics."""
|
| 43 |
if not filtered_stats:
|
|
|
|
| 57 |
filtered_stats.values(),
|
| 58 |
labels=[label.lower() for label in filtered_stats.keys()], # Lowercase for minimal look
|
| 59 |
colors=chart_colors,
|
| 60 |
+
autopct=lambda pct: f'{int(pct * sum(filtered_stats.values()) / 100)}',
|
| 61 |
startangle=PIE_START_ANGLE,
|
| 62 |
explode=None, # No separation
|
| 63 |
shadow=False,
|
|
|
|
| 92 |
# Create empty stats for both devices
|
| 93 |
amd_filtered = {}
|
| 94 |
nvidia_filtered = {}
|
|
|
|
| 95 |
failures_amd = failures_nvidia = {}
|
| 96 |
else:
|
| 97 |
row = df.loc[model_name]
|
| 98 |
|
| 99 |
# Extract and process model data
|
| 100 |
+
amd_stats, nvidia_stats = extract_model_data(row)[:2]
|
|
|
|
| 101 |
|
| 102 |
# Filter out categories with 0 values for cleaner visualization
|
| 103 |
amd_filtered = {k: v for k, v in amd_stats.items() if v > 0}
|
| 104 |
nvidia_filtered = {k: v for k, v in nvidia_stats.items() if v > 0}
|
| 105 |
|
| 106 |
# Generate failure info directly from dataframe
|
| 107 |
+
failures_amd = dict(row.get('failures_amd', {}))
|
| 108 |
+
failures_nvidia = dict(row.get('failures_nvidia', {}))
|
| 109 |
+
|
| 110 |
+
# failure_xxx = {"single": [test, ...], "multi": [...]}
|
| 111 |
+
# test = {"line": test_name. "trace": error_msg}
|
| 112 |
|
| 113 |
# Always create figure with two subplots side by side with padding
|
| 114 |
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(FIGURE_WIDTH_DUAL, FIGURE_HEIGHT_DUAL), facecolor=BLACK)
|
|
|
|
| 133 |
plt.tight_layout()
|
| 134 |
plt.subplots_adjust(top=SUBPLOT_TOP, wspace=SUBPLOT_WSPACE)
|
| 135 |
|
| 136 |
+
amd_failed_info = prepare_textbox_content(failures_amd, 'AMD', bool(amd_filtered))
|
| 137 |
+
nvidia_failed_info = prepare_textbox_content(failures_nvidia, 'NVIDIA', bool(nvidia_filtered))
|
| 138 |
|
| 139 |
return fig, amd_failed_info, nvidia_failed_info
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
def prepare_textbox_content(failures: dict[str, list], device: str, data_available: bool) -> str:
|
| 143 |
+
"""Extract failure information from failures object."""
|
| 144 |
+
# Catch the case where there is no data
|
| 145 |
+
if not data_available:
|
| 146 |
+
return generate_underlined_line(f"No data for {device}")
|
| 147 |
+
# Catch the case where there are no failures
|
| 148 |
+
if not failures:
|
| 149 |
+
return generate_underlined_line(f"No failures for {device}")
|
| 150 |
+
|
| 151 |
+
# Summary of failures
|
| 152 |
+
single_failures = failures.get("single", [])
|
| 153 |
+
multi_failures = failures.get("multi", [])
|
| 154 |
+
info_lines = [
|
| 155 |
+
generate_underlined_line(f"Failure summary for {device}:"),
|
| 156 |
+
f"Single GPU failures: {len(single_failures)}",
|
| 157 |
+
f"Multi GPU failures: {len(multi_failures)}",
|
| 158 |
+
""
|
| 159 |
+
]
|
| 160 |
+
|
| 161 |
+
# Add single-gpu failures
|
| 162 |
+
if single_failures:
|
| 163 |
+
info_lines.append(generate_underlined_line("Single GPU failures:"))
|
| 164 |
+
for test in single_failures:
|
| 165 |
+
name = test.get("line", "::*could not find name*")
|
| 166 |
+
name = name.split("::")[-1]
|
| 167 |
+
info_lines.append(name)
|
| 168 |
+
info_lines.append("\n")
|
| 169 |
+
|
| 170 |
+
# Add multi-gpu failures
|
| 171 |
+
if multi_failures:
|
| 172 |
+
info_lines.append(generate_underlined_line("Multi GPU failures:"))
|
| 173 |
+
for test in multi_failures:
|
| 174 |
+
name = test.get("line", "::*could not find name*")
|
| 175 |
+
name = name.split("::")[-1]
|
| 176 |
+
info_lines.append(name)
|
| 177 |
+
|
| 178 |
+
return "\n".join(info_lines)
|
styles.css
CHANGED
|
@@ -539,8 +539,9 @@ h1, h2, h3, p, .markdown {
|
|
| 539 |
resize: none !important;
|
| 540 |
scrollbar-width: thin !important;
|
| 541 |
scrollbar-color: #333333 #000000 !important;
|
| 542 |
-
scroll-behavior: auto;
|
| 543 |
transition: opacity 0.5s ease-in-out !important;
|
|
|
|
| 544 |
}
|
| 545 |
|
| 546 |
/* WebKit scrollbar styling for failed tests */
|
|
|
|
| 539 |
resize: none !important;
|
| 540 |
scrollbar-width: thin !important;
|
| 541 |
scrollbar-color: #333333 #000000 !important;
|
| 542 |
+
scroll-behavior: auto !important;
|
| 543 |
transition: opacity 0.5s ease-in-out !important;
|
| 544 |
+
scroll-padding-top: 0 !important;
|
| 545 |
}
|
| 546 |
|
| 547 |
/* WebKit scrollbar styling for failed tests */
|
utils.py
CHANGED
|
@@ -48,4 +48,4 @@ logger = setup_logger()
|
|
| 48 |
|
| 49 |
|
| 50 |
def generate_underlined_line(text: str) -> str:
|
| 51 |
-
return text + "\n" + "─" * len(text)
|
|
|
|
| 48 |
|
| 49 |
|
| 50 |
def generate_underlined_line(text: str) -> str:
|
| 51 |
+
return text + "\n" + "─" * len(text)
|