diff --git a/dashboard.py b/dashboard.py
index 479532a..15f55aa 100644
--- a/dashboard.py
+++ b/dashboard.py
@@ -3778,7 +3778,7 @@ def render_dataset_representation_section(selected_profile, use_expander=True):
@st.fragment
-def render_performance_plots_section(filtered_df, use_expander=True):
+def render_performance_plots_section(filtered_df, per_turn_df=None, use_expander=True):
"""📊 Performance Plots Section - Complete functionality from original."""
if use_expander:
if "performance_plots_expanded" not in st.session_state:
@@ -3838,12 +3838,62 @@ def render_performance_plots_section(filtered_df, use_expander=True):
_sort_cols.append("DP")
filtered_df_sorted = filtered_df.sort_values(_sort_cols).copy()
+ # Build per-turn plot df with run_identifier (same suffixes as filtered_df)
+ per_turn_plot_df = pd.DataFrame()
+ if per_turn_df is not None and not per_turn_df.empty:
+ per_turn_plot_df = per_turn_df.copy()
+ per_turn_plot_df["run_identifier"] = (
+ per_turn_plot_df["accelerator"].fillna("?")
+ + " | "
+ + per_turn_plot_df["model"].fillna("?")
+ + " | "
+ + per_turn_plot_df["version"].fillna("?")
+ + " | TP="
+ + per_turn_plot_df["TP"].apply(
+ lambda x: str(int(x)) if pd.notna(x) else "N/A"
+ )
+ )
+ if _has_dp_data and "DP" in per_turn_plot_df.columns:
+ per_turn_plot_df["run_identifier"] += per_turn_plot_df["DP"].apply(
+ lambda x: f" | DP={int(x)}" if pd.notna(x) else ""
+ )
+ if (
+ "spec_decoding" in per_turn_plot_df.columns
+ and per_turn_plot_df["spec_decoding"].any()
+ ):
+ per_turn_plot_df["run_identifier"] += per_turn_plot_df[
+ "spec_decoding"
+ ].apply(lambda x: f" | SD={x}" if x else "")
+ if (
+ "prefix_caching" in per_turn_plot_df.columns
+ and per_turn_plot_df["prefix_caching"].any()
+ ):
+ per_turn_plot_df["run_identifier"] += per_turn_plot_df[
+ "prefix_caching"
+ ].apply(lambda x: f" | PC={x}" if x else "")
+ if (
+ "turns" in per_turn_plot_df.columns
+ and (per_turn_plot_df["turns"] > 1).any()
+ ):
+ per_turn_plot_df["run_identifier"] += per_turn_plot_df.apply(
+ lambda r: (
+ f" | {r['turns']}T"
+ + (f"/{r['prefix_tokens']}pt" if r.get("prefix_tokens") else "")
+ + (f"/{r['prefix_count']}pc" if r.get("prefix_count") else "")
+ if r["turns"] > 1
+ else ""
+ ),
+ axis=1,
+ )
+
col1, col2, col3 = st.columns(3)
with col1:
x_axis_options = {
"Concurrency": "intended concurrency",
"Throughput (Output Tok/s)": "output_tok/sec",
}
+ if not per_turn_plot_df.empty:
+ x_axis_options["Turn (Multi-turn)"] = "turn_index"
x_axis_label = st.selectbox(
"Select X-Axis",
options=list(x_axis_options.keys()),
@@ -3905,6 +3955,47 @@ def render_performance_plots_section(filtered_df, use_expander=True):
filtered_df_sorted = filtered_df_sorted[
filtered_df_sorted["intended concurrency"] <= max_conc
]
+ elif x_axis == "turn_index" and not per_turn_plot_df.empty:
+ concurrency_values = sorted(
+ int(x)
+ for x in per_turn_plot_df["intended concurrency"]
+ .dropna()
+ .unique()
+ .tolist()
+ )
+ if concurrency_values:
+ _turn_conc_key = "perf_plots_turn_concurrency"
+ if _turn_conc_key not in st.session_state:
+ st.session_state[_turn_conc_key] = [max(concurrency_values)]
+ else:
+ # Filter stored list to only still-valid values; reset to max if all stale
+ _valid = [
+ c
+ for c in (st.session_state[_turn_conc_key] or [])
+ if c in concurrency_values
+ ]
+ st.session_state[_turn_conc_key] = _valid or [
+ max(concurrency_values)
+ ]
+ selected_concs = st.multiselect(
+ "Concurrency",
+ options=concurrency_values,
+ key=_turn_conc_key,
+ on_change=keep_expander_open,
+ args=("performance_plots_expanded",),
+ )
+ st.caption(
+ "💡 Select multiple concurrency levels to compare turn curves side by side."
+ )
+ if selected_concs:
+ per_turn_plot_df = per_turn_plot_df[
+ per_turn_plot_df["intended concurrency"].isin(
+ selected_concs
+ )
+ ]
+
+ # Evaluate once so all branches below stay consistent
+ _is_turn_view = x_axis == "turn_index" and not per_turn_plot_df.empty
# Add units to y-axis label for certain metrics
y_axis_display_label = y_axis_label
@@ -3915,27 +4006,26 @@ def render_performance_plots_section(filtered_df, use_expander=True):
elif y_axis == "request_latency_median" or y_axis == "request_latency_max":
y_axis_display_label = f"{y_axis_label} (s)"
- # Build ISL/OSL subtitle from unique values in the filtered data
+ # Build ISL/OSL subtitle from the active data source
+ _subtitle_src = per_turn_plot_df if _is_turn_view else filtered_df_sorted
_isl_osl_subtitle = ""
if (
- "prompt toks" in filtered_df_sorted.columns
- and "output toks" in filtered_df_sorted.columns
+ "prompt toks" in _subtitle_src.columns
+ and "output toks" in _subtitle_src.columns
):
isl_osl_pairs = (
- filtered_df_sorted[["prompt toks", "output toks"]]
- .dropna()
- .drop_duplicates()
+ _subtitle_src[["prompt toks", "output toks"]].dropna().drop_duplicates()
)
if not isl_osl_pairs.empty:
pair_labels = []
for _, r in isl_osl_pairs.iterrows():
isl, osl = int(r["prompt toks"]), int(r["output toks"])
if isl == 0 and osl == 0:
- if "dataset" in filtered_df_sorted.columns:
+ if "dataset" in _subtitle_src.columns:
ds_names = (
- filtered_df_sorted.loc[
- (filtered_df_sorted["prompt toks"] == 0)
- & (filtered_df_sorted["output toks"] == 0),
+ _subtitle_src.loc[
+ (_subtitle_src["prompt toks"] == 0)
+ & (_subtitle_src["output toks"] == 0),
"dataset",
]
.dropna()
@@ -3948,39 +4038,108 @@ def render_performance_plots_section(filtered_df, use_expander=True):
if pair_labels:
_isl_osl_subtitle = f"
ISL/OSL: {', '.join(sorted(set(pair_labels)))}"
- fig = px.line(
- filtered_df_sorted.sort_values(by=x_axis),
- x=x_axis,
- y=y_axis,
- color="run_identifier",
- markers=True,
- title=f"{x_axis_label} vs. {y_axis_label}{_isl_osl_subtitle}",
- labels={
- x_axis: x_axis_label,
- y_axis: y_axis_display_label,
- "run_identifier": "Run",
- },
- template="plotly_white_light",
- category_orders={
- "run_identifier": filtered_df_sorted["run_identifier"].unique().tolist()
- },
- )
- _legend_parts = "Accelerator | Model | Version | TP"
- if _has_dp_data:
- _legend_parts += " | DP"
- if (filtered_df_sorted["turns"] > 1).any():
- _legend_parts += " | Turns/PrefixTokens/PrefixCount"
- fig.update_layout(
- legend_title_text=f"Run Details ({_legend_parts})",
- legend={"font": {"size": 14}},
- )
- st.plotly_chart(fig, use_container_width=True, theme=None)
+ if _is_turn_view:
+ per_turn_plot_df["_plot_label"] = (
+ per_turn_plot_df["run_identifier"]
+ + " | conc="
+ + per_turn_plot_df["intended concurrency"].apply(
+ lambda x: str(int(x)) if pd.notna(x) else "?"
+ )
+ )
+ _pt_valid = (
+ per_turn_plot_df.dropna(subset=[y_axis]).copy()
+ if y_axis in per_turn_plot_df.columns
+ else pd.DataFrame()
+ )
+ if _pt_valid.empty:
+ st.info(f"No per-turn data available for '{y_axis_label}'.")
+ fig = None
+ else:
+ fig = px.line(
+ _pt_valid.sort_values("turn_index"),
+ x="turn_index",
+ y=y_axis,
+ color="_plot_label",
+ markers=True,
+ title=f"Turn vs. {y_axis_label}{_isl_osl_subtitle}",
+ labels={
+ "turn_index": "Turn",
+ y_axis: y_axis_display_label,
+ "_plot_label": "Run | Concurrency",
+ },
+ template="plotly_white_light",
+ )
+ fig.update_xaxes(
+ tickmode="array",
+ tickvals=sorted(_pt_valid["turn_index"].dropna().unique()),
+ )
+ fig.update_layout(
+ legend_title_text="Run Details (Accelerator | Model | Version | TP | Concurrency)",
+ legend={"font": {"size": 14}},
+ )
+ elif x_axis == "turn_index":
+ # Turn x-axis selected but per_turn_plot_df is empty — don't fall through
+ # to the aggregate sort which would sort by NaN turn_index values.
+ st.info("No per-turn data available for the current filter selection.")
+ fig = None
+ else:
+ fig = px.line(
+ filtered_df_sorted.sort_values(by=x_axis),
+ x=x_axis,
+ y=y_axis,
+ color="run_identifier",
+ markers=True,
+ title=f"{x_axis_label} vs. {y_axis_label}{_isl_osl_subtitle}",
+ labels={
+ x_axis: x_axis_label,
+ y_axis: y_axis_display_label,
+ "run_identifier": "Run",
+ },
+ template="plotly_white_light",
+ category_orders={
+ "run_identifier": filtered_df_sorted["run_identifier"]
+ .unique()
+ .tolist()
+ },
+ )
+ _legend_parts = "Accelerator | Model | Version | TP"
+ if _has_dp_data:
+ _legend_parts += " | DP"
+ if (filtered_df_sorted["turns"] > 1).any():
+ _legend_parts += " | Turns/PrefixTokens/PrefixCount"
+ fig.update_layout(
+ legend_title_text=f"Run Details ({_legend_parts})",
+ legend={"font": {"size": 14}},
+ )
+ if fig is not None:
+ st.plotly_chart(fig, use_container_width=True, theme=None)
# Right-align the legend caption
caption_col1, caption_col2 = st.columns([3, 1])
with caption_col2:
st.caption("📜 **Tip**: Scroll within the legend box to see all runs")
+ # Push all perf-plots URL params directly — fragment reruns don't trigger
+ # the parent encode_filters_to_url / from_dict, so widgets changed inside
+ # the fragment would otherwise leave the URL stale.
+ # Suppress only Streamlit API errors (e.g. no session context outside a run);
+ # let TypeError/AttributeError from bad values surface for debugging.
+ try:
+ st.query_params["pp_x"] = x_axis_label
+ st.query_params["pp_y"] = y_axis_label
+ if x_axis == "intended concurrency":
+ conc_val = st.session_state.get("perf_plots_max_concurrency")
+ if conc_val is not None:
+ st.query_params["pp_conc"] = str(int(conc_val))
+ elif x_axis == "turn_index":
+ turn_conc = st.session_state.get("perf_plots_turn_concurrency")
+ if turn_conc:
+ st.query_params["pp_turn_conc"] = ",".join(
+ str(int(c)) for c in turn_conc
+ )
+ except Exception: # noqa: BLE001
+ pass # Outside Streamlit session context — no-op
+
def load_pareto_data(csv_file_path, preloaded_df=None):
"""Load benchmark results from CSV file or S3 for Pareto analysis.
@@ -10339,7 +10498,7 @@ def render_view_logs_section(filtered_df, use_expander=True):
@st.fragment
-def render_filtered_data_section(filtered_df, use_expander=True):
+def render_filtered_data_section(filtered_df, per_turn_df=None, use_expander=True):
"""📄 Filtered Data Display Section - View only, no download functionality."""
if use_expander:
ctx = st.expander("📄 Filtered Data from the above filters", expanded=False)
@@ -10348,12 +10507,42 @@ def render_filtered_data_section(filtered_df, use_expander=True):
with ctx:
if not use_expander:
st.subheader("📄 Filtered Data")
+
+ _has_per_turn = per_turn_df is not None and not per_turn_df.empty
+ if _has_per_turn:
+ _show_per_turn = st.toggle(
+ "🔄 Show per-turn rows",
+ value=False,
+ key="filtered_data_show_per_turn",
+ help="Switch between aggregate rows (one per concurrency level) and per-turn breakdown rows",
+ )
+ else:
+ _show_per_turn = False
+
st.info(
"💡 **Tips**: Hover over column headers to see detailed descriptions of each field. "
"Select a row to view its server log."
+ + (
+ " Per-turn rows (turn_index 0, 1, 2…) are shown below each aggregate row."
+ if _show_per_turn and _has_per_turn
+ else ""
+ )
)
- display_filtered_df = filtered_df.copy()
- display_filtered_df.reset_index(drop=True, inplace=True)
+ if _show_per_turn and _has_per_turn:
+ display_filtered_df = (
+ pd.concat([filtered_df, per_turn_df], ignore_index=True)
+ .sort_values(
+ ["model", "intended concurrency", "turn_index"],
+ key=lambda s: pd.to_numeric(s, errors="coerce").fillna(
+ -1 if s.name == "turn_index" else s.rank(method="dense")
+ ),
+ )
+ .reset_index(drop=True)
+ )
+ else:
+ display_filtered_df = filtered_df.copy().reset_index(drop=True)
+ if "turn_index" in display_filtered_df.columns:
+ display_filtered_df = display_filtered_df.drop(columns=["turn_index"])
display_filtered_df.insert(0, "Row #", range(1, len(display_filtered_df) + 1))
# Add Run Date column from guidellm_start_time_ms (epoch milliseconds)
@@ -11124,6 +11313,7 @@ def main():
"pp_x": "perf_plots_x_axis",
"pp_y": "perf_plots_y_axis",
"pp_conc": "perf_plots_max_concurrency",
+ "pp_turn_conc": "perf_plots_turn_concurrency",
},
"pareto": {
"par_model": "pareto_model_select",
@@ -11163,7 +11353,7 @@ def main():
}
def encode_filters_to_url(accelerators, models, versions, profile, tp_sizes):
- """Encode main filter state to URL parameters."""
+ """Encode main filter state and active section widget state to URL parameters."""
url_params = {}
if accelerators:
@@ -11177,6 +11367,21 @@ def encode_filters_to_url(accelerators, models, versions, profile, tp_sizes):
if tp_sizes:
url_params["tp_sizes"] = ",".join(map(str, tp_sizes))
+ # Also push active section's widget state so the bare URL bar is shareable
+ active_slug = SECTION_TO_SLUG.get(
+ st.session_state.get("active_section", ""), ""
+ )
+ if active_slug:
+ url_params["section"] = active_slug
+ for url_key, ss_key in SECTION_FILTER_KEYS.get(active_slug, {}).items():
+ val = st.session_state.get(ss_key)
+ if val is not None:
+ url_params[url_key] = (
+ ",".join(map(str, val))
+ if isinstance(val, list)
+ else str(val)
+ )
+
st.query_params.update(url_params)
def build_share_url():
@@ -11356,8 +11561,10 @@ def decode_filters_from_url():
"trends_tp_multi",
"energy_accelerator_filter",
"energy_model_filter",
+ "perf_plots_turn_concurrency",
}
NUMERIC_LIST_SESSION_KEYS = {"trends_tp_multi"}
+ INT_LIST_SESSION_KEYS = {"perf_plots_turn_concurrency"}
INT_SESSION_KEYS = {
"perf_plots_max_concurrency",
"model_comparison_concurrency",
@@ -11383,6 +11590,14 @@ def decode_filters_from_url():
):
converted.append(float(v))
parts = converted
+ elif ss_key in INT_LIST_SESSION_KEYS:
+ converted = []
+ for v in parts:
+ with contextlib.suppress(
+ ValueError, OverflowError
+ ):
+ converted.append(int(v))
+ parts = converted
url_section_filters[ss_key] = parts
elif ss_key in INT_SESSION_KEYS:
with contextlib.suppress(ValueError):
@@ -11441,33 +11656,95 @@ def decode_filters_from_url():
df["prefix_caching"] = ""
df["prefix_caching"] = df["prefix_caching"].fillna("").astype(str).replace("no", "")
+ # Shared normalizer for numeric-string columns (prefix_tokens, prefix_count)
+ # used by both the per_turn_df and df preprocessing paths below.
+ def _str_norm(v):
+ return str(int(float(v))) if v != "" and str(v) not in ("", "nan") else ""
+
+ if "turn_index" in df.columns:
+ _ti = df["turn_index"].astype(str).str.strip()
+ per_turn_df = df[df["turn_index"].notna() & (_ti != "") & (_ti != "nan")].copy()
+ if not per_turn_df.empty:
+ per_turn_df["turn_index"] = pd.to_numeric(
+ per_turn_df["turn_index"], errors="coerce"
+ )
+ per_turn_df = per_turn_df[per_turn_df["turn_index"].notna()].copy()
+ per_turn_df["turn_index"] = per_turn_df["turn_index"].astype(int)
+ if (
+ "errored_requests" in per_turn_df.columns
+ and "successful_requests" in per_turn_df.columns
+ ):
+ per_turn_df["error_rate"] = (
+ per_turn_df["errored_requests"]
+ / (
+ per_turn_df["successful_requests"]
+ + per_turn_df["errored_requests"]
+ )
+ * 100
+ ).fillna(0)
+ else:
+ per_turn_df["error_rate"] = np.nan
+ if "output_tok/sec" in per_turn_df.columns and "TP" in per_turn_df.columns:
+ per_turn_df["efficiency_ratio"] = (
+ per_turn_df["output_tok/sec"] / per_turn_df["TP"]
+ ).replace([np.inf, -np.inf], np.nan)
+ else:
+ per_turn_df["efficiency_ratio"] = np.nan
+ per_turn_df["ttft_p95_s"] = (
+ per_turn_df["ttft_p95"] / 1000
+ if "ttft_p95" in per_turn_df.columns
+ else np.nan
+ )
+ per_turn_df["ttft_median_s"] = (
+ per_turn_df["ttft_median"] / 1000
+ if "ttft_median" in per_turn_df.columns
+ else np.nan
+ )
+ # Mirror the same normalizations applied to df below so _apply_filters
+ # types match the sidebar multiselect values (which are sourced from df).
+ per_turn_df["turns"] = (
+ per_turn_df.get("turns", pd.Series(1, index=per_turn_df.index))
+ .fillna(1)
+ .astype(int)
+ )
+ per_turn_df["prefix_tokens"] = (
+ per_turn_df.get("prefix_tokens", pd.Series("", index=per_turn_df.index))
+ .fillna("")
+ .apply(_str_norm)
+ )
+ per_turn_df["prefix_count"] = (
+ per_turn_df.get("prefix_count", pd.Series("", index=per_turn_df.index))
+ .fillna("")
+ .apply(_str_norm)
+ )
+ per_turn_df["spec_decoding"] = (
+ per_turn_df.get("spec_decoding", pd.Series("", index=per_turn_df.index))
+ .fillna("")
+ .astype(str)
+ )
+ per_turn_df["prefix_caching"] = (
+ per_turn_df.get(
+ "prefix_caching", pd.Series("", index=per_turn_df.index)
+ )
+ .fillna("")
+ .astype(str)
+ .replace("no", "")
+ )
+ df = df[df["turn_index"].isna() | (_ti == "") | (_ti == "nan")].copy()
+ else:
+ per_turn_df = pd.DataFrame()
+
if "turns" not in df.columns:
df["turns"] = 1
df["turns"] = df["turns"].fillna(1).astype(int)
if "prefix_tokens" not in df.columns:
df["prefix_tokens"] = ""
- df["prefix_tokens"] = (
- df["prefix_tokens"]
- .fillna("")
- .apply(
- lambda v: (
- str(int(float(v))) if v != "" and str(v) not in ("", "nan") else ""
- )
- )
- )
+ df["prefix_tokens"] = df["prefix_tokens"].fillna("").apply(_str_norm)
if "prefix_count" not in df.columns:
df["prefix_count"] = ""
- df["prefix_count"] = (
- df["prefix_count"]
- .fillna("")
- .apply(
- lambda v: (
- str(int(float(v))) if v != "" and str(v) not in ("", "nan") else ""
- )
- )
- )
+ df["prefix_count"] = df["prefix_count"].fillna("").apply(_str_norm)
if "request_type" not in df.columns:
df["request_type"] = ""
@@ -12178,13 +12455,13 @@ def decode_filters_from_url():
temp_df = temp_df[
temp_df["multiturn_isl_osl"] == selected_multiturn_isl_osl
]
- if selected_mt_turns is not None:
+ if selected_mt_turns:
temp_df = temp_df[temp_df["turns"].isin(selected_mt_turns)]
- if selected_mt_prefix_tokens is not None:
+ if selected_mt_prefix_tokens:
temp_df = temp_df[
temp_df["prefix_tokens"].isin(selected_mt_prefix_tokens)
]
- if selected_mt_prefix_count is not None:
+ if selected_mt_prefix_count:
temp_df = temp_df[
temp_df["prefix_count"].isin(selected_mt_prefix_count)
]
@@ -12354,13 +12631,13 @@ def decode_filters_from_url():
temp_df = temp_df[
temp_df["multiturn_isl_osl"] == selected_multiturn_isl_osl
]
- if selected_mt_turns is not None:
+ if selected_mt_turns:
temp_df = temp_df[temp_df["turns"].isin(selected_mt_turns)]
- if selected_mt_prefix_tokens is not None:
+ if selected_mt_prefix_tokens:
temp_df = temp_df[
temp_df["prefix_tokens"].isin(selected_mt_prefix_tokens)
]
- if selected_mt_prefix_count is not None:
+ if selected_mt_prefix_count:
temp_df = temp_df[
temp_df["prefix_count"].isin(selected_mt_prefix_count)
]
@@ -12544,72 +12821,77 @@ def decode_filters_from_url():
else:
st.caption("No DP data available")
- dp_mask = (
- (df["DP"].isin(selected_dp) | df["DP"].isna())
- if st.session_state.get("show_advanced_filters", False)
- and _has_dp
- and selected_dp
- else True
- )
+ def _apply_filters(d):
+ """Apply all sidebar filters to any DataFrame with the same schema."""
+ _tp = d["TP"].isin(selected_tp) | d["TP"].isna()
+ _dp = (
+ (d["DP"].isin(selected_dp) | d["DP"].isna())
+ if st.session_state.get("show_advanced_filters", False)
+ and _has_dp
+ and selected_dp
+ else True
+ )
+ _custom = (
+ (d["custom_isl_osl"] == selected_custom_isl_osl)
+ if selected_profile == "Custom ISL/OSL" and selected_custom_isl_osl
+ else True
+ )
+ _dataset = (
+ (d["dataset"] == selected_dataset_filter)
+ if selected_dataset_filter is not None
+ else True
+ )
+ _sd = (
+ d["spec_decoding"].isin(selected_spec_decoding_filter)
+ if selected_spec_decoding_filter
+ else True
+ )
+ _pc = (
+ d["prefix_caching"].isin(selected_prefix_caching_filter)
+ if selected_prefix_caching_filter
+ else True
+ )
+ _mt_isl_osl = (
+ (d["multiturn_isl_osl"] == selected_multiturn_isl_osl)
+ if selected_profile == "Multi-turn" and selected_multiturn_isl_osl
+ else True
+ )
+ _mt_turns = (
+ d["turns"].isin(selected_mt_turns)
+ if selected_mt_turns is not None
+ else True
+ )
+ _mt_pt = (
+ d["prefix_tokens"].isin(selected_mt_prefix_tokens)
+ if selected_mt_prefix_tokens is not None
+ else True
+ )
+ _mt_pc = (
+ d["prefix_count"].isin(selected_mt_prefix_count)
+ if selected_mt_prefix_count is not None
+ else True
+ )
+ return d[
+ d["accelerator"].isin(selected_accelerators)
+ & d["model"].isin(selected_models)
+ & d["version"].isin(selected_versions)
+ & (d["profile"].isin(selected_profiles) if selected_profiles else True)
+ & _tp
+ & _custom
+ & _dataset
+ & _sd
+ & _pc
+ & _mt_isl_osl
+ & _mt_turns
+ & _mt_pt
+ & _mt_pc
+ & _dp
+ ].copy()
- custom_mask = (
- (df["custom_isl_osl"] == selected_custom_isl_osl)
- if selected_profile == "Custom ISL/OSL" and selected_custom_isl_osl
- else True
- )
- dataset_mask = (
- (df["dataset"] == selected_dataset_filter)
- if selected_dataset_filter is not None
- else True
- )
- spec_decoding_mask = (
- df["spec_decoding"].isin(selected_spec_decoding_filter)
- if selected_spec_decoding_filter
- else True
- )
- prefix_caching_mask = (
- df["prefix_caching"].isin(selected_prefix_caching_filter)
- if selected_prefix_caching_filter
- else True
+ filtered_df = _apply_filters(df)
+ filtered_per_turn_df = (
+ _apply_filters(per_turn_df) if not per_turn_df.empty else pd.DataFrame()
)
- # Multi-turn masks
- multiturn_isl_osl_mask = (
- (df["multiturn_isl_osl"] == selected_multiturn_isl_osl)
- if selected_profile == "Multi-turn" and selected_multiturn_isl_osl
- else True
- )
- mt_turns_mask = (
- df["turns"].isin(selected_mt_turns)
- if selected_mt_turns is not None
- else True
- )
- mt_prefix_tokens_mask = (
- df["prefix_tokens"].isin(selected_mt_prefix_tokens)
- if selected_mt_prefix_tokens is not None
- else True
- )
- mt_prefix_count_mask = (
- df["prefix_count"].isin(selected_mt_prefix_count)
- if selected_mt_prefix_count is not None
- else True
- )
- tp_mask = df["TP"].isin(selected_tp) | df["TP"].isna()
- filtered_df = df[
- df["accelerator"].isin(selected_accelerators)
- & df["model"].isin(selected_models)
- & df["version"].isin(selected_versions)
- & (df["profile"].isin(selected_profiles) if selected_profiles else True)
- & tp_mask
- & custom_mask
- & dataset_mask
- & spec_decoding_mask
- & prefix_caching_mask
- & multiturn_isl_osl_mask
- & mt_turns_mask
- & mt_prefix_tokens_mask
- & mt_prefix_count_mask
- & dp_mask
- ].copy()
# Detect if filters have changed and close expanders
current_filter_state = {
@@ -12750,7 +13032,9 @@ def _render_selected_section(sel):
elif sel == "🔍 Competitive Analysis":
render_competitive_analysis_section(df)
elif sel == "📊 Performance Plots":
- render_performance_plots_section(filtered_df, use_expander=False)
+ render_performance_plots_section(
+ filtered_df, filtered_per_turn_df, use_expander=False
+ )
elif sel == "📈 Dataset Representation":
render_dataset_representation_section(
selected_profile, use_expander=False
@@ -12780,7 +13064,9 @@ def _render_selected_section(sel):
elif sel == "📋 View Logs":
render_view_logs_section(filtered_df, use_expander=False)
elif sel == "📄 Filtered Data":
- render_filtered_data_section(filtered_df, use_expander=False)
+ render_filtered_data_section(
+ filtered_df, filtered_per_turn_df, use_expander=False
+ )
_render_selected_section(current_section)
@@ -12823,13 +13109,13 @@ def _fmt(v):
str(int(v)) if isinstance(v, float) and v == int(v) else str(v)
)
- if selected_mt_turns is not None:
+ if selected_mt_turns:
desired_params["mt_turns"] = ",".join(map(_fmt, selected_mt_turns))
- if selected_mt_prefix_tokens is not None:
+ if selected_mt_prefix_tokens:
desired_params["mt_prefix_tokens"] = ",".join(
map(_fmt, selected_mt_prefix_tokens)
)
- if selected_mt_prefix_count is not None:
+ if selected_mt_prefix_count:
desired_params["mt_prefix_count"] = ",".join(
map(_fmt, selected_mt_prefix_count)
)