Skip to content
Open
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
10 changes: 10 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 @@ -106,6 +106,16 @@ describe('normalizeArtifactRows', () => {
expect(rows[0].hardware).toBe('mi355x');
});

it('carries kv_transfer_lib so overlays show the KV transfer library', () => {
const rows = normalizeArtifactRows([rawRow({ kv_transfer_lib: 'mooncake' })], '2026-03-01');
expect(rows[0].kv_transfer_lib).toBe('mooncake');
});

it('nulls kv_transfer_lib for artifacts predating the field', () => {
const rows = normalizeArtifactRows([rawRow()], '2026-03-01');
expect(rows[0].kv_transfer_lib).toBeNull();
});

it('resolves model from infmax_model_prefix', () => {
const rows = normalizeArtifactRows(
[rawRow({ infmax_model_prefix: 'gptoss', model: 'openai/gpt-oss-120b' })],
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 @@ -58,6 +58,7 @@ export function normalizeArtifactRows(
// Surface the same per-worker payload the DB path emits so unofficial
// overlays carry the multinode measured-power breakdown too.
workers: params.workers,
kv_transfer_lib: params.kvTransferLib ?? null,
date,
run_url: runUrl,
});
Expand Down
4 changes: 4 additions & 0 deletions packages/app/src/components/inference/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@ export interface AggDataEntry {
// (a prefill, decode, agg, or frontend role). Optional because pre-multinode
// and pre-aggregate_power.py runs don't emit it.
workers?: WorkerPower[];
// KV-cache transfer library for disagg runs ('mooncake', 'nixl', 'mori',
// 'ucx'). Null/undefined = unknown (pre-field history, non-disagg runs) —
// render nothing rather than assume a default.
kv_transfer_lib?: string | null;
disagg: boolean;
num_prefill_gpu: number;
num_decode_gpu: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -366,3 +366,73 @@ describe('generateGPUGraphTooltipContent', () => {
expect(html).toContain('vllm-v0.6.0<br />abc123');
});
});

// ===========================================================================
// KV transfer library line (official + overlay + GPU-graph tooltips)
// ===========================================================================
describe('kv_transfer_lib tooltip line', () => {
function overlayConfig(overrides: Partial<OverlayTooltipConfig> = {}): OverlayTooltipConfig {
return {
...tooltipConfig(),
overlayData: {
label: 'feature-branch',
hardwareConfig: mockHardwareConfig,
data: [],
runUrl: 'https://example.com',
} as any,
...overrides,
};
}

it('shows the mapped display label in the official tooltip', () => {
const html = generateTooltipContent(
tooltipConfig({ data: pt({ kv_transfer_lib: 'mooncake' }) }),
);
expect(html).toContain('KV Transfer');
expect(html).toContain('Mooncake');
});

it('maps known libraries to display casing', () => {
for (const [raw, label] of [
['nixl', 'NIXL'],
['mori', 'MoRI-IO'],
['ucx', 'UCX'],
] as const) {
const html = generateTooltipContent(tooltipConfig({ data: pt({ kv_transfer_lib: raw }) }));
expect(html).toContain(label);
}
});

it('uppercases unmapped values instead of hiding them', () => {
const html = generateTooltipContent(
tooltipConfig({ data: pt({ kv_transfer_lib: 'somefuturelib' }) }),
);
expect(html).toContain('SOMEFUTURELIB');
});

it('renders nothing when the field is absent (unknown history)', () => {
const html = generateTooltipContent(tooltipConfig());
expect(html).not.toContain('KV Transfer');
});

it('shows the line in overlay (unofficial run) tooltips', () => {
const html = generateOverlayTooltipContent(
overlayConfig({ data: pt({ kv_transfer_lib: 'mooncake' }) }),
);
expect(html).toContain('KV Transfer');
expect(html).toContain('Mooncake');
});

it('omits the line in overlay tooltips when absent', () => {
const html = generateOverlayTooltipContent(overlayConfig());
expect(html).not.toContain('KV Transfer');
});

it('shows the line in GPU-graph (date comparison) tooltips', () => {
const html = generateGPUGraphTooltipContent(
tooltipConfig({ data: pt({ kv_transfer_lib: 'nixl' }) }),
);
expect(html).toContain('KV Transfer');
expect(html).toContain('NIXL');
});
});
24 changes: 24 additions & 0 deletions packages/app/src/components/inference/utils/tooltipUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,27 @@ const runLinkHTML = (runUrl?: string) =>
const tooltipLine = (label: string, value: string | number) =>
`<div style="color: var(--muted-foreground); font-size: 11px; margin-bottom: 4px;"><strong>${label}:</strong> ${value}</div>`;

/** Display labels for kv_transfer_lib values; unmapped values are uppercased. */
const KV_TRANSFER_LIB_LABELS: Record<string, string> = {
mooncake: 'Mooncake',
nixl: 'NIXL',
mori: 'MoRI-IO',
ucx: 'UCX',
};

/**
* KV-cache transfer library line for disagg runs. Empty when the field is
* absent (pre-2026 history, non-disagg runs, unresolvable recipes) — unknown
* must render nothing, never a guessed default.
*/
const kvTransferTooltipLine = (d: InferenceData): string =>
d.kv_transfer_lib
? tooltipLine(
'KV Transfer',
KV_TRANSFER_LIB_LABELS[d.kv_transfer_lib] ?? d.kv_transfer_lib.toUpperCase(),
)
: '';

const shortenSha = (image: string) =>
image.replaceAll(/(?<shaPrefix>sha256:[a-f0-9]{7})[a-f0-9]+/giu, '$<shaPrefix>…');

Expand Down Expand Up @@ -180,6 +201,7 @@ export const generateTooltipContent = (config: TooltipConfig): string => {
}
${tooltipLine('Total GPUs', d.tp)}
${generateParallelismHTML(d)}
${kvTransferTooltipLine(d)}
<div style="color: var(--muted-foreground); font-size: 11px; margin-bottom: 4px;">
<strong>Concurrency:</strong> ${d.conc}
</div>
Expand Down Expand Up @@ -236,6 +258,7 @@ export const generateOverlayTooltipContent = (config: OverlayTooltipConfig): str
</div>
${tooltipLine('Total GPUs', d.tp)}
${generateParallelismHTML(d)}
${kvTransferTooltipLine(d)}
<div style="color: var(--muted-foreground); font-size: 11px; margin-bottom: 4px;">
<strong>Concurrency:</strong> ${d.conc}
</div>
Expand Down Expand Up @@ -295,6 +318,7 @@ export const generateGPUGraphTooltipContent = (config: TooltipConfig): string =>
}
${tooltipLine('Total GPUs', d.tp)}
${generateParallelismHTML(d)}
${kvTransferTooltipLine(d)}
<div style="color: var(--muted-foreground); font-size: 11px; margin-bottom: 4px;">
<strong>Concurrency:</strong> ${d.conc}
</div>
Expand Down
7 changes: 7 additions & 0 deletions packages/app/src/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ export interface BenchmarkRow {
* aggregate_power.py.
*/
workers?: WorkerPower[];
/**
* KV-cache transfer library for disaggregated runs ('mooncake', 'nixl',
* 'mori', 'ucx'), emitted by the runner since mid-2026. Null/undefined means
* unknown (pre-field history, non-disagg runs, unresolvable recipes) — the
* UI must render nothing rather than assume a default.
*/
kv_transfer_lib?: string | null;
date: string;
run_url: string | null;
}
Expand Down
20 changes: 20 additions & 0 deletions packages/app/src/lib/benchmark-transform.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -793,3 +793,23 @@ describe('transformBenchmarkRows — dp_attention narrowing', () => {
expect(point.decode_dp_attention).toBe(true);
});
});

describe('kv_transfer_lib passthrough', () => {
it('carries kv_transfer_lib from the row to the chart point', () => {
const rows = [makeRow({ kv_transfer_lib: 'mooncake' })];
const { chartData } = transformBenchmarkRows(rows);
expect(chartData.flat()[0].kv_transfer_lib).toBe('mooncake');
});

it('normalizes null to undefined (unknown)', () => {
const rows = [makeRow({ kv_transfer_lib: null })];
const { chartData } = transformBenchmarkRows(rows);
expect(chartData.flat()[0].kv_transfer_lib).toBeUndefined();
});

it('is undefined when the row predates the field', () => {
const rows = [makeRow()];
const { chartData } = transformBenchmarkRows(rows);
expect(chartData.flat()[0].kv_transfer_lib).toBeUndefined();
});
});
1 change: 1 addition & 0 deletions packages/app/src/lib/benchmark-transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export function rowToAggDataEntry(row: BenchmarkRow): AggDataEntry {
// scalar `metrics` dict (see api.ts). Narrow defensively so a malformed
// payload can't poison downstream consumers.
workers: Array.isArray(row.workers) ? row.workers : undefined,
kv_transfer_lib: row.kv_transfer_lib ?? undefined,
disagg: row.disagg,
num_prefill_gpu: row.num_prefill_gpu,
num_decode_gpu: row.num_decode_gpu,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
-- ============================================================
-- BENCHMARK_RESULTS.KV_TRANSFER_LIB — KV-cache transfer library
-- ============================================================
--
-- Disaggregated runs move KV cache from prefill to decode workers through a
-- transfer library (mooncake, nixl, mori, ucx). The benchmarking repo now
-- derives it at result-processing time (InferenceX utils/kv_transfer_lib.py)
-- and emits an optional `kv_transfer_lib` string on each result row.
--
-- Stored on benchmark_results, NOT on configs: the library is result-level
-- metadata, deliberately excluded from the config natural key so config
-- identity — and therefore historical trend-line continuity — is unchanged.
-- NULL means unknown: every row ingested before the runner emitted the field,
-- non-disagg runs (no KV transfer), and runs whose recipe could not be
-- resolved. Consumers must render nothing for NULL rather than assume a
-- default.

alter table benchmark_results add column kv_transfer_lib text;

alter table benchmark_results
add constraint benchmark_results_kv_transfer_lib_lowercase
check (kv_transfer_lib is null or kv_transfer_lib = lower(kv_transfer_lib));

-- latest_benchmarks materializes `select br.*` at creation time, so it must be
-- rebuilt to expose the new column. Definition is identical to migration 007.

drop materialized view if exists latest_benchmarks;

create materialized view latest_benchmarks as
with winners as (
select distinct on (br.config_id, br.benchmark_type, br.isl, br.osl)
br.config_id, br.benchmark_type, br.isl, br.osl,
br.workflow_run_id as winning_run_id
from benchmark_results br
join latest_workflow_runs wr on wr.id = br.workflow_run_id
where br.error is null
order by br.config_id, br.benchmark_type, br.isl, br.osl,
br.date desc, wr.run_started_at desc nulls last, br.workflow_run_id desc
)
select br.*
from benchmark_results br
join winners w
on w.config_id = br.config_id
and w.benchmark_type = br.benchmark_type
and w.isl is not distinct from br.isl
and w.osl is not distinct from br.osl
and w.winning_run_id = br.workflow_run_id
where br.error is null;

create unique index latest_benchmarks_pk
on latest_benchmarks (config_id, conc, isl, osl, benchmark_type);
create index latest_benchmarks_model_idx on latest_benchmarks (config_id);
10 changes: 7 additions & 3 deletions packages/db/src/etl/benchmark-ingest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,13 @@ export async function bulkIngestBenchmarkRows(
const workersJsons = deduped.map((r) =>
r.workers === undefined ? null : JSON.stringify(r.workers),
);
// kv_transfer_lib is optional — SQL NULL for rows that didn't emit it.
const kvTransferLibs = deduped.map((r) => r.kvTransferLib ?? null);

const result = await sql<{ inserted: boolean; id: number }[]>`
insert into benchmark_results (
workflow_run_id, config_id, benchmark_type, date,
isl, osl, conc, image, metrics, workers
isl, osl, conc, image, metrics, workers, kv_transfer_lib
)
select
${workflowRunId},
Expand All @@ -62,12 +64,14 @@ export async function bulkIngestBenchmarkRows(
unnest(${sql.array(concs)}::int[]),
unnest(${sql.array(images)}),
unnest(${sql.array(metricsJsons)}::jsonb[]),
unnest(${sql.array(workersJsons)}::jsonb[])
unnest(${sql.array(workersJsons)}::jsonb[]),
unnest(${sql.array(kvTransferLibs)}::text[])
on conflict (workflow_run_id, config_id, benchmark_type, isl, osl, conc)
do update set
metrics = excluded.metrics,
image = excluded.image,
workers = excluded.workers
workers = excluded.workers,
kv_transfer_lib = excluded.kv_transfer_lib
returning (xmax = 0) as inserted, id
`;

Expand Down
37 changes: 37 additions & 0 deletions packages/db/src/etl/benchmark-mapper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -570,3 +570,40 @@ describe('extractWorkers', () => {
expect(extractWorkers([null, 'bad', 0, undefined])).toBeUndefined();
});
});

describe('kv_transfer_lib', () => {
it('captures kv_transfer_lib as a config sibling, not a metric', () => {
const tracker = createSkipTracker();
const result = mapBenchmarkRow(makeV2Row({ kv_transfer_lib: 'mooncake' }), tracker);

expect(result!.kvTransferLib).toBe('mooncake');
expect(result!.metrics).not.toHaveProperty('kv_transfer_lib');
});

it('normalizes to lowercase and trims', () => {
const tracker = createSkipTracker();
const result = mapBenchmarkRow(makeV2Row({ kv_transfer_lib: ' NIXL ' }), tracker);

expect(result!.kvTransferLib).toBe('nixl');
});

it('is undefined when absent', () => {
const tracker = createSkipTracker();
const result = mapBenchmarkRow(makeV2Row(), tracker);

expect(result!.kvTransferLib).toBeUndefined();
});

it('is undefined for empty or non-string values', () => {
const tracker = createSkipTracker();
expect(
mapBenchmarkRow(makeV2Row({ kv_transfer_lib: '' }), tracker)!.kvTransferLib,
).toBeUndefined();
expect(
mapBenchmarkRow(makeV2Row({ kv_transfer_lib: 42 }), tracker)!.kvTransferLib,
).toBeUndefined();
expect(
mapBenchmarkRow(makeV2Row({ kv_transfer_lib: null }), tracker)!.kvTransferLib,
).toBeUndefined();
});
});
18 changes: 18 additions & 0 deletions packages/db/src/etl/benchmark-mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ const NON_METRIC_KEYS = new Set([
'decode_num_workers',
'num_prefill_gpu',
'num_decode_gpu',
// KV-cache transfer library (string, e.g. 'mooncake'). Surfaced as a
// sibling of the metrics JSONB by mapBenchmarkRow, like `workers`.
'kv_transfer_lib',
// per-worker measured-power array (not a numeric scalar). Surfaced as a
// sibling of the metrics JSONB by mapBenchmarkRow so the metrics column
// stays Record<string, number> for the index signature on BenchmarkRow.
Expand Down Expand Up @@ -105,6 +108,13 @@ export interface BenchmarkParams {
* predating the multinode patch.
*/
workers?: WorkerPower[];
/**
* KV-cache transfer library used by a disaggregated run ('mooncake',
* 'nixl', 'mori', 'ucx'), derived by the runner's process_result.py.
* Undefined for non-disagg runs, runs predating the field, and runs whose
* recipe could not be resolved — consumers must treat that as unknown.
*/
kvTransferLib?: string;
}

/**
Expand Down Expand Up @@ -222,6 +232,13 @@ export function mapBenchmarkRow(
// narrowing — anything other than a non-empty array of objects is dropped.
const workers = extractWorkers(row.workers);

// KV transfer library: non-empty string, normalized to lowercase.
// Anything else (absent, empty, non-string) is treated as unknown.
const kvTransferLib =
typeof row.kv_transfer_lib === 'string' && row.kv_transfer_lib.trim() !== ''
? row.kv_transfer_lib.trim().toLowerCase()
: undefined;

return {
config: {
hardware: gpuKey,
Expand All @@ -248,6 +265,7 @@ export function mapBenchmarkRow(
image,
metrics,
workers,
kvTransferLib,
};
}

Expand Down
5 changes: 5 additions & 0 deletions packages/db/src/json-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ interface RawBenchmarkResult {
metrics: Record<string, number>;
/** Added in migration 006; older dumps omit this field — surfaced as undefined. */
workers?: BenchmarkWorkerRow[] | null;
/** Added in migration 008; older dumps omit this field — surfaced as null. */
kv_transfer_lib?: string | null;
error: string | null;
server_log_id: number | null;
}
Expand Down Expand Up @@ -307,6 +309,9 @@ function toBenchmarkRow(
// simply lack the field — defensively narrow to an array or undefined so
// downstream consumers can rely on the property being well-typed.
workers: Array.isArray(br.workers) ? br.workers : undefined,
// kv_transfer_lib: optional column added in migration 008. Older dumps
// lack the field — normalize to null (unknown).
kv_transfer_lib: typeof br.kv_transfer_lib === 'string' ? br.kv_transfer_lib : null,
date: toDateString(br.date),
run_url: buildRunUrl(wr),
};
Expand Down
Loading
Loading