Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 28 additions & 4 deletions docs/data-pipeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,40 @@ Every INSERT uses `ON CONFLICT DO UPDATE` or `DO NOTHING`. This means:
- **Partial failures recover**: If ingest crashes mid-batch, re-running picks up where it left off.
- **No cleanup needed**: No "delete old data first" step that could leave the DB empty on failure.

The unique constraints match natural keys (e.g., `(workflow_run_id, config_id, isl, osl, conc)` for benchmarks), not surrogate keys.
The unique constraints match natural keys (for benchmarks, workflow/config/scenario,
concurrency, offload mode, and the nullable recipe fingerprint), not surrogate keys.

### Append-Only Curve Extensions

Normal workflow runs are complete line snapshots: the latest run for a line replaces
the prior run as a unit, so partial re-sweeps cannot silently stitch points from
different recipes. A changelog containing only `append-only: true` entries marks the
one narrow exception. The latest-curve queries then walk backward through consecutive
append-only runs and include the nearest full snapshot, selecting the newest producer
for each recipe and concurrency. The producer stamps every new point with a deterministic
`recipe_fingerprint`, so variants that share the app's normalized topology and concurrency
remain separate. Historical rows have a null fingerprint and retain their legacy identity.

The chain continues only while the image is identical. Each returned benchmark row
keeps its original workflow-run ID and run URL, so extending a curve does not erase
point provenance; `curve_date` and `curve_workflow_run_id` carry the separate logical
snapshot identity used by charts and history. InferenceX CI separately verifies that
the generated matrix is strictly additive: existing recipes and points are immutable,
while newly added concurrency points or complete recipe variants may run as deltas.
All appended points must retain the target curve's image, and benchmark-affecting code
changes must be isolated to the new points.

### Audited Point Purges

`packages/db/src/etl/run-overrides.ts` is the durable audit record for exceptional
data removal. Use `PURGED_BENCHMARK_POINTS` when a valid workflow run contains a
specific invalid result, such as a server hang. Each record names `githubRunId`,
`runAttempt`, `configId`, `benchmarkType`, `isl`, `osl`, `conc`, and `offloadMode`,
with a dated reason comment. The full identity is required because one run can
contain multiple serving configurations at the same sequence lengths and concurrency.
`runAttempt`, `configId`, `benchmarkType`, `isl`, `osl`, `conc`, `offloadMode`, and
the optional nullable `recipeFingerprint`, with a dated reason comment. A fingerprint
targets exactly that recipe; omitting it or setting it to null targets only legacy rows
whose stored fingerprint is null. The full identity is required because one run can
contain multiple serving configurations and recipes at the same sequence lengths and
concurrency.

`bun run db:apply-overrides` previews every matching row and requires confirmation
before deleting it in a transaction. It also removes unreferenced server logs and
Expand Down
15 changes: 15 additions & 0 deletions packages/app/src/app/api/unofficial-run/route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,21 @@ describe('normalizeArtifactRows', () => {
expect(m.mean_e2el).toBe(1.5);
});

it('preserves recipe identity for unofficial overlays', () => {
const rows = normalizeArtifactRows(
[
rawRow({
recipe_fingerprint: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
}),
],
'2026-03-01',
);

expect(rows[0].recipe_fingerprint).toBe(
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
);
});

it('surfaces pipeline-parallelism fields in metrics (auto-capture)', () => {
// pp has no configs-table column: the frontend reads it from the metrics
// JSONB (rowToAggDataEntry), so the overlay route must keep passing it
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/app/api/unofficial-run/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export function normalizeArtifactRows(
osl: params.osl,
conc: params.conc,
image: params.image,
recipe_fingerprint: params.recipeFingerprint,
metrics: params.metrics,
// Surface the same per-worker payload the DB path emits so unofficial
// overlays carry the multinode measured-power breakdown too.
Expand Down
5 changes: 3 additions & 2 deletions packages/app/src/app/api/v1/benchmarks/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ const getCachedBenchmarks = cachedQuery(
{ blobOnly: true },
);

// Exactly one run's results (GPU comparison of individual same-day runs). Cached
// under a distinct key prefix so it never collides with the latest/as-of query.
// One logical run snapshot (GPU comparison of individual same-day runs). For an
// append-only run this includes its same-image predecessor chain. Cached under a
// distinct key prefix so it never collides with the latest/as-of query.
const getCachedBenchmarksForRun = cachedQuery(
(dbModelKeys: string[], runId: string) => getBenchmarksForRun(getDb(), dbModelKeys, runId),
'benchmarks-run-agentic-run-metadata',
Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/components/GlobalFilterContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ interface RunInfo {
description: string;
pr_link: string | null;
head_ref: string;
append_only?: boolean;
}[];
};
}
Expand Down Expand Up @@ -134,6 +135,7 @@ function buildRunInfo(data: WorkflowInfoResponse): Record<string, RunInfo> {
description: c.description,
pr_link: c.pr_link,
head_ref: c.head_ref,
append_only: c.append_only,
})),
},
}),
Expand Down
102 changes: 94 additions & 8 deletions packages/app/src/components/inference/hooks/useChartData.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ interface DedupeInput {
date: string;
workflow_run_id?: number;
run_started_at?: string | null;
curve_date?: string;
curve_workflow_run_id?: number;
curve_run_started_at?: string | null;
}

const drow = (over: Partial<DedupeInput> = {}): DedupeInput => ({
Expand Down Expand Up @@ -93,18 +96,43 @@ describe('dedupeRowsToLatestPerConfig', () => {

it('dedupes mixed agentic spec methods as one curve', () => {
const rows = [
drow({ id: 1, benchmark_type: 'agentic_traces', spec_method: 'none', date: '2026-06-01' }),
drow({ id: 2, benchmark_type: 'agentic_traces', spec_method: 'mtp', date: '2026-06-03' }),
drow({ id: 3, benchmark_type: 'agentic_traces', spec_method: 'eagle', date: '2026-06-03' }),
drow({
id: 1,
benchmark_type: 'agentic_traces',
spec_method: 'none',
date: '2026-06-01',
}),
drow({
id: 2,
benchmark_type: 'agentic_traces',
spec_method: 'mtp',
date: '2026-06-03',
}),
drow({
id: 3,
benchmark_type: 'agentic_traces',
spec_method: 'eagle',
date: '2026-06-03',
}),
];

expect(dedupeRowsToLatestPerConfig(rows).map((r) => r.id)).toEqual([2, 3]);
});

it('continues deduping fixed-sequence spec methods independently', () => {
const rows = [
drow({ id: 1, benchmark_type: 'single_turn', spec_method: 'none', date: '2026-06-01' }),
drow({ id: 2, benchmark_type: 'single_turn', spec_method: 'mtp', date: '2026-06-03' }),
drow({
id: 1,
benchmark_type: 'single_turn',
spec_method: 'none',
date: '2026-06-01',
}),
drow({
id: 2,
benchmark_type: 'single_turn',
spec_method: 'mtp',
date: '2026-06-03',
}),
];

expect(dedupeRowsToLatestPerConfig(rows).map((r) => r.id)).toEqual([1, 2]);
Expand Down Expand Up @@ -169,6 +197,52 @@ describe('dedupeRowsToLatestPerConfig', () => {
'eagle',
]);
});

it('keeps cross-day points carried into one append-only snapshot', () => {
const rows = [
drow({
id: 1,
date: '2026-06-01',
workflow_run_id: 10,
curve_date: '2026-06-03',
curve_workflow_run_id: 12,
curve_run_started_at: '2026-06-03T12:00:00Z',
}),
drow({
id: 2,
date: '2026-06-03',
workflow_run_id: 12,
curve_date: '2026-06-03',
curve_workflow_run_id: 12,
curve_run_started_at: '2026-06-03T12:00:00Z',
}),
];

expect(dedupeRowsToLatestPerConfig(rows).map((r) => r.id)).toEqual([1, 2]);
});

it('keeps same-day agentic points carried from an earlier producer run', () => {
const rows = [
drow({
id: 1,
benchmark_type: 'agentic_traces',
workflow_run_id: 20,
run_started_at: '2026-06-03T10:00:00Z',
curve_workflow_run_id: 21,
curve_run_started_at: '2026-06-03T12:00:00Z',
}),
drow({
id: 2,
benchmark_type: 'agentic_traces',
workflow_run_id: 21,
run_started_at: '2026-06-03T12:00:00Z',
curve_workflow_run_id: 21,
curve_run_started_at: '2026-06-03T12:00:00Z',
}),
];

expect(dedupeRowsToLatestPerConfig(rows).map((r) => r.id)).toEqual([1, 2]);
});
});

describe('dedupeAgenticHistoryRuns', () => {
Expand Down Expand Up @@ -320,9 +394,19 @@ describe('filterByGPU', () => {
describe('filterOverviewHistoryRows', () => {
it('keeps only the serving envelope encoded by the Overview history link', () => {
const rows = [
drow({ id: 1, hardware: 'mi355x', framework: 'sglang', precision: 'fp8' }),
drow({
id: 1,
hardware: 'mi355x',
framework: 'sglang',
precision: 'fp8',
}),
drow({ id: 2, hardware: 'mi355x', framework: 'vllm', precision: 'fp4' }),
drow({ id: 3, hardware: 'mi355x', framework: 'sglang', precision: 'fp4' }),
drow({
id: 3,
hardware: 'mi355x',
framework: 'sglang',
precision: 'fp4',
}),
];
const key = JSON.stringify(['qwen3.5', 'mi355x', 'vllm', 'none', 'fp4', false, false, 'off']);

Expand Down Expand Up @@ -468,7 +552,9 @@ describe('applyScopeFilters', () => {
// the user's legend selection with whatever set it is handed and never
// re-widens, so a universe that shrinks when a Measured Energy axis is
// picked deletes the telemetry-less configs for good.
const withTelemetry = scopePoint('b200_sglang', { measuredAvgPower: { y: 900, roof: false } });
const withTelemetry = scopePoint('b200_sglang', {
measuredAvgPower: { y: 900, roof: false },
});
const withoutTelemetry = scopePoint('h200_vllm');
const points = [withTelemetry, withoutTelemetry];

Expand Down
29 changes: 23 additions & 6 deletions packages/app/src/components/inference/hooks/useChartData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
} from '@/lib/constants';
import { mergeRunScopedRows, transformBenchmarkRows } from '@/lib/benchmark-transform';
import {
benchmarkCurveDate,
dedupeAgenticHistoryRuns,
dedupeRowsToLatestPerConfig as dedupeLatestBenchmarkSeries,
} from '@/lib/benchmark-run-selection';
Expand Down Expand Up @@ -341,9 +342,11 @@ export function useChartData(
// an offload=on sweep can't hide a differently-dated offload=off series.
const deduped = dedupeRowsToLatestPerConfig(seqFiltered);

const mainRows = deduped.map((r) =>
selectedRunDate ? { ...r, date: selectedRunDate, actualDate: r.date } : r,
);
const mainRows = deduped.map((r) => ({
...r,
date: selectedRunDate ?? benchmarkCurveDate(r),
actualDate: r.date,
}));
if (comparisonDates.length === 0) return mainRows;
const extraRows = comparisonQueries.flatMap((q, i) => {
const filtered = filterOverviewHistoryRows(
Expand All @@ -352,7 +355,11 @@ export function useChartData(
);
const selected =
selectedSequence === Sequence.AgenticTraces ? dedupeAgenticHistoryRuns(filtered) : filtered;
return selected.map((r) => ({ ...r, date: comparisonDates[i], actualDate: r.date }));
return selected.map((r) => ({
...r,
date: comparisonDates[i],
actualDate: r.date,
}));
});
return [...mainRows, ...extraRows];
}, [
Expand All @@ -368,7 +375,10 @@ export function useChartData(
// Transform filtered rows into chart data
const { chartData, hardwareConfig: rawHardwareConfig } = useMemo(() => {
if (rows.length === 0)
return { chartData: [] as InferenceData[][], hardwareConfig: {} as HardwareConfig };
return {
chartData: [] as InferenceData[][],
hardwareConfig: {} as HardwareConfig,
};
return transformBenchmarkRows(rows, selectedPercentile);
}, [rows, selectedPercentile]);

Expand Down Expand Up @@ -611,5 +621,12 @@ export function useChartData(
[chartData, selectedGPUs, quickFilters, compareGpuPair],
);

return { graphs, selectionPoints, loading, error, hardwareConfig, availableQuickFilters };
return {
graphs,
selectionPoints,
loading,
error,
hardwareConfig,
availableQuickFilters,
};
}
Loading
Loading