Spaces:
Running
Running
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)
|