From 2387552f9fb684fdf5b0e377ed44df2e883490c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ng=C3=B4=20Quang=20H=C3=B2a?= Date: Tue, 26 May 2026 16:20:23 +0700 Subject: [PATCH] feat(charts): split 1x and 2x deployments by topology + show latest on /eval MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two fixes after the gemma4 ingest exposed gaps: 1. /inference series-key (Option A): getHardwareKey now appends a "_{totalGpus}x" suffix when the deployment uses more than one GPU. 1× H100 and 2× H100 stop collapsing into a single "h100 (vllm, mtp)" legend entry. Single-GPU runs stay un-suffixed for parity with upstream legends. Disagg sums prefill + decode pools. Same suffix added to buildAvailabilityHwKey via an optional `totalGpus` parameter — callers that know the count (InferenceContext from benchmark rows) can pass it; legacy callers (availability rows lacking GPU counts) pass undefined and the suffix is skipped. 2. /evaluation no longer filters by selectedRunDate. The user reported the "Batch size" filter chip not appearing on /evaluation: cause was selectedRunDate being sticky at 2026-05-25 (set by an earlier InferenceContext mount) while the mnbt rows are dated 2026-05-26. The API correctly returned 12 of 19 rows, and categorizeTechniques never saw a batch-size variant. RecipeComparison is a "current state of recipes" view, not historical, so always use the no-date "latest across all dates" path. 194/194 unit tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../evaluation/RecipeComparison.tsx | 22 +++++++------------ packages/app/src/lib/chart-utils.ts | 16 ++++++++++++++ 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/packages/app/src/components/evaluation/RecipeComparison.tsx b/packages/app/src/components/evaluation/RecipeComparison.tsx index dd961a9d3..6d0f13a1d 100644 --- a/packages/app/src/components/evaluation/RecipeComparison.tsx +++ b/packages/app/src/components/evaluation/RecipeComparison.tsx @@ -143,14 +143,15 @@ const COLUMNS: DataTableColumn[] = [ ]; export default function RecipeComparison() { - const { selectedModel, selectedRunDate } = useGlobalFilters(); + const { selectedModel } = useGlobalFilters(); const [category, setCategory] = useState('all'); - const { - data: benchmarks, - isLoading: bmkLoading, - error: bmkError, - } = useBenchmarks(selectedModel, selectedRunDate || undefined); + // Always use the no-date "latest state across all dates" path. The recipe + // table is meant to surface current recipe variants, not historical data — + // a stale `selectedRunDate` inherited from /inference would hide rows + // ingested on more recent dates (e.g. the user reported missing the + // batch-size filter chip because mnbt rows landed after the sticky date). + const { data: benchmarks, isLoading: bmkLoading, error: bmkError } = useBenchmarks(selectedModel); const { data: evals, isLoading: evalLoading, error: evalError } = useEvaluations(); const allRows = useMemo(() => { @@ -231,14 +232,7 @@ export default function RecipeComparison() { {!loading && rows.length === 0 && (
- No data for {selectedModel || '(no model)'} - {selectedRunDate ? ( - <> - {' '} - on {selectedRunDate} - - ) : null} - . Pick a different model or date. + No data for {selectedModel || '(no model)'}. Pick a different model.
)} diff --git a/packages/app/src/lib/chart-utils.ts b/packages/app/src/lib/chart-utils.ts index 554bb7a5b..8f761da56 100644 --- a/packages/app/src/lib/chart-utils.ts +++ b/packages/app/src/lib/chart-utils.ts @@ -174,6 +174,16 @@ export const getHardwareKey = (entry: AggDataEntry): string => { } else if (entry['spec_decoding'] && entry['spec_decoding'] !== 'none') { normalizedHwName = `${normalizedHwName}_${entry['spec_decoding']}`; } + // Topology suffix when there's more than one GPU in the deployment, so that + // 1× and 2× (or larger) deployments don't collapse into a single chart + // series. Suppressed for single-GPU runs to keep parity with upstream + // legends. Disagg = prefill + decode pools; non-disagg = single pool. + const totalGpus = entry.disagg + ? (entry.num_prefill_gpu ?? 0) + (entry.num_decode_gpu ?? 0) + : (entry.num_prefill_gpu ?? entry.num_decode_gpu ?? 0); + if (totalGpus > 1) { + normalizedHwName = `${normalizedHwName}_${totalGpus}x`; + } return normalizedHwName; }; @@ -224,6 +234,7 @@ export function buildAvailabilityHwKey( framework?: string, specMethod?: string, disagg?: boolean, + totalGpus?: number, ): string { let hwKey = hardware.split('-')[0]; const fw = framework ? resolveFrameworkAlias(framework) : undefined; @@ -241,6 +252,11 @@ export function buildAvailabilityHwKey( } if (specMethod === 'mtp') hwKey = `${hwKey}_mtp`; else if (specMethod && specMethod !== 'none') hwKey = `${hwKey}_${specMethod}`; + // Optional topology suffix — keeps 1× and 2× deployments as separate + // legend entries. Mirror of the suffix in getHardwareKey above. Callers + // that don't know the topology (e.g. legacy availability rows lacking GPU + // counts) pass undefined and the suffix is skipped. + if (totalGpus !== undefined && totalGpus > 1) hwKey = `${hwKey}_${totalGpus}x`; return hwKey; }