diff --git a/packages/app/src/app/api/v1/benchmarks/route.test.ts b/packages/app/src/app/api/v1/benchmarks/route.test.ts index 6970358..4b7e574 100644 --- a/packages/app/src/app/api/v1/benchmarks/route.test.ts +++ b/packages/app/src/app/api/v1/benchmarks/route.test.ts @@ -52,7 +52,7 @@ describe('GET /api/v1/benchmarks', () => { expect(res.status).toBe(200); const body = await res.json(); expect(body).toEqual(mockRows); - expect(mockGetLatestBenchmarks).toHaveBeenCalledWith('mock-sql', 'dsr1', undefined); + expect(mockGetLatestBenchmarks).toHaveBeenCalledWith('mock-sql', 'dsr1', undefined, undefined); }); it('passes date param to query when provided', async () => { @@ -60,7 +60,22 @@ describe('GET /api/v1/benchmarks', () => { const res = await GET(req('/api/v1/benchmarks?model=DeepSeek-R1-0528&date=2026-03-01')); expect(res.status).toBe(200); - expect(mockGetLatestBenchmarks).toHaveBeenCalledWith('mock-sql', 'dsr1', '2026-03-01'); + expect(mockGetLatestBenchmarks).toHaveBeenCalledWith( + 'mock-sql', + 'dsr1', + '2026-03-01', + undefined, + ); + }); + + it('passes exact=true when query param set', async () => { + mockGetLatestBenchmarks.mockResolvedValueOnce([]); + + const res = await GET( + req('/api/v1/benchmarks?model=DeepSeek-R1-0528&date=2026-03-01&exact=true'), + ); + expect(res.status).toBe(200); + expect(mockGetLatestBenchmarks).toHaveBeenCalledWith('mock-sql', 'dsr1', '2026-03-01', true); }); it('returns 500 when query throws', async () => { diff --git a/packages/app/src/app/api/v1/benchmarks/route.ts b/packages/app/src/app/api/v1/benchmarks/route.ts index 50e5569..fb65640 100644 --- a/packages/app/src/app/api/v1/benchmarks/route.ts +++ b/packages/app/src/app/api/v1/benchmarks/route.ts @@ -9,9 +9,9 @@ import { cachedJson, cachedQuery } from '@/lib/api-cache'; export const dynamic = 'force-dynamic'; const getCachedBenchmarks = cachedQuery( - async (dbModelKey: string, date?: string) => { + async (dbModelKey: string, date?: string, exact?: boolean) => { const sql = getDb(); - return getLatestBenchmarks(sql, dbModelKey, date); + return getLatestBenchmarks(sql, dbModelKey, date, exact); }, 'benchmarks', { blobOnly: true }, @@ -21,13 +21,14 @@ export async function GET(request: NextRequest) { const params = request.nextUrl.searchParams; const model = params.get('model') ?? ''; const date = params.get('date') ?? undefined; + const exact = params.get('exact') === 'true'; const dbModelKey = DISPLAY_MODEL_TO_DB[model]; if (!dbModelKey) { return NextResponse.json({ error: 'Unknown model' }, { status: 400 }); } try { - const rows = await getCachedBenchmarks(dbModelKey, date); + const rows = await getCachedBenchmarks(dbModelKey, date, exact || undefined); return cachedJson(rows); } catch (error) { console.error('Error fetching benchmarks:', error); diff --git a/packages/app/src/components/inference/hooks/useChartData.ts b/packages/app/src/components/inference/hooks/useChartData.ts index 3c8854f..16d7717 100644 --- a/packages/app/src/components/inference/hooks/useChartData.ts +++ b/packages/app/src/components/inference/hooks/useChartData.ts @@ -100,7 +100,9 @@ export function useChartData( }, [selectedGPUs, selectedDates, selectedDateRange, selectedRunDate]); const comparisonQueries = useQueries({ - queries: comparisonDates.map((date) => benchmarkQueryOptions(selectedModel, date, enabled)), + queries: comparisonDates.map((date) => + benchmarkQueryOptions(selectedModel, date, enabled, true), + ), }); const comparisonLoading = comparisonQueries.some((q) => q.isLoading); diff --git a/packages/app/src/components/inference/ui/ChartControls.tsx b/packages/app/src/components/inference/ui/ChartControls.tsx index b027627..6726020 100644 --- a/packages/app/src/components/inference/ui/ChartControls.tsx +++ b/packages/app/src/components/inference/ui/ChartControls.tsx @@ -11,6 +11,7 @@ import { } from '@/components/ui/chart-selectors'; import { DateRangePicker } from '@/components/ui/date-range-picker'; import { LabelWithTooltip } from '@/components/ui/label-with-tooltip'; +import { MultiDatePicker } from '@/components/ui/multi-date-picker'; import { MultiSelect } from '@/components/ui/multi-select'; import { Select, @@ -68,9 +69,14 @@ const GROUPED_Y_AXIS_OPTIONS = (() => { interface ChartControlsProps { /** Hide GPU Config selector and related date pickers (used by Historical Trends tab) */ hideGpuComparison?: boolean; + /** Intermediate dates within the comparison range that have changelog entries */ + intermediateDates?: string[]; } -export default function ChartControls({ hideGpuComparison = false }: ChartControlsProps) { +export default function ChartControls({ + hideGpuComparison = false, + intermediateDates = [], +}: ChartControlsProps) { const { selectedModel, setSelectedModel, @@ -86,6 +92,8 @@ export default function ChartControls({ hideGpuComparison = false }: ChartContro availableGPUs, selectedDateRange, setSelectedDateRange, + selectedDates, + setSelectedDates, dateRangeAvailableDates, isCheckingAvailableDates, availablePrecisions, @@ -327,6 +335,32 @@ export default function ChartControls({ hideGpuComparison = false }: ChartContro /> )} + + {!hideGpuComparison && + selectedGPUs.length > 0 && + selectedDateRange.startDate && + selectedDateRange.endDate && + intermediateDates.length > 0 && ( +
No config changelog data matching the selected GPUs and precisions for this date range. Changelog tracking began Dec 30, 2025.
) : ( - sortedChangelogs.map((item) => ( + filteredChangelogs.map((item) => (