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; }