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
8 changes: 8 additions & 0 deletions docs/data-pipeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,14 @@ Summing on an exact `start_ns` emitted one point per component per tick, each ho

Every summed series is therefore evaluated by `sumOntoGrid` on one canonical grid at the blob's native scrape cadence (`canonicalTickNs`, the median gap of the best-sampled series), with each component holding its last sample between its own scrapes and contributing only inside its observed window. The lattice is anchored at `t=0` and shared by every metric, so the pairs that get divided or added downstream (hits/queries, used/total, running+waiting) land on identical `t` values — anchoring each metric at its own first sample instead silently emptied the prefix-cache-hit-rate chart, because `sglang:cached_tokens` starts ~0.18 s off the grid `sglang:prompt_tokens` starts on.

For vLLM builds that expose `prompt_tokens_cached_by_source`, the prompt-token
breakdown combines only `local_compute` from the older logical
`prompt_tokens_by_source` metric with the new physical cached-token buckets.
The logical `local_cache_hit` and `external_kv_transfer` series are replaced,
not added, so HBM, CPU, NVMe, and connector-defined cache tiers do not double
count the same cached tokens. Older vLLM builds retain the logical breakdown,
and SGLang keeps its `cached_tokens{cache_source=...}` mapping.

Mirrored endpoints are collapsed here too, on a **relative** tolerance rather than the gauge path's absolute one — a throughput mean is O(10⁵), so an absolute threshold would never fire and every mirror would be counted twice. This is a correction as well as a de-duplication: the two-API-server vLLM rows were double-counting their queue depth and token totals exactly 2× before v14.

Nothing groups on an exact `start_ns` any more; `aggregateByStart` was removed in v14.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { PathnameContext } from 'next/dist/shared/lib/hooks-client-context.shared-runtime';

import { StackedAreaChart } from '@/components/inference/agentic-point/time-series-chart';

const sourceSeries = {
local_compute: [{ t: 0, value: 300 }],
'cache hit (HBM)': [{ t: 0, value: 400 }],
'cache hit (CPU offload)': [{ t: 0, value: 200 }],
'cache hit (NVMe offload)': [{ t: 0, value: 100 }],
'cache hit (weka)': [{ t: 0, value: 50 }],
};

function mountChart(pathname: string) {
cy.mount(
<PathnameContext.Provider value={pathname}>
<StackedAreaChart sourceSeries={sourceSeries} durationS={1} />
</PathnameContext.Provider>,
);
}

describe('Agentic prompt-token source chart', () => {
it('labels and colors each physical vLLM cache tier', () => {
mountChart('/inference/agentic/421');

cy.contains('Prefill').should('be.visible');
cy.contains('HBM Cache Hit').should('be.visible');
cy.contains('CPU Offload Cache Hit').should('be.visible');
cy.contains('NVMe Offload Cache Hit').should('be.visible');
cy.contains('Cache Hit (weka)').should('be.visible');
cy.get('path[fill="#3b82f6"]').should('exist');
cy.get('path[fill="#22c55e"]').should('exist');
cy.get('path[fill="#a855f7"]').should('exist');
});

it('renders tier labels in Simplified Chinese on the /zh route', () => {
mountChart('/zh/inference/agentic/421');

cy.contains('HBM cache 命中').should('be.visible');
cy.contains('CPU offload cache 命中').should('be.visible');
cy.contains('NVMe offload cache 命中').should('be.visible');
cy.contains('weka cache 命中').should('be.visible');
cy.contains('prefill token 占比').should('be.visible');
cy.contains('% of prefill tokens').should('not.exist');
});
});
6 changes: 3 additions & 3 deletions packages/app/src/app/api/v1/benchmark-siblings/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ export const dynamic = 'force-dynamic';

const getCachedSiblings = cachedQuery(
(id: number): Promise<BenchmarkSiblings | null> => getBenchmarkSiblings(getDb(), id),
// v3 adds DCP/PCP. Roll the blob namespace so cached sibling payloads gain
// the complete parallelism mapping used by chart point labels.
'benchmark-siblings-v3',
// v4 adds the physical KV tier and P90 frontier coordinates. Roll the blob
// namespace so cached sibling payloads can label and mark AgentX points.
'benchmark-siblings-v4',
);

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { RequestChartData } from '@/hooks/api/use-request-chart-data';
import type { MetricSourceDescriptor, QueueDepthPoint } from '@/hooks/api/use-trace-server-metrics';
import { SegmentedToggle, type SegmentedToggleOption } from '@/components/ui/segmented-toggle';
import { track } from '@/lib/analytics';
import { useLocale } from '@/lib/use-locale';

import { CHART_SIZES, ChartEmpty, ChartSkeleton } from './chart-shared';
import { ExpandableChart } from './expandable-chart';
Expand Down Expand Up @@ -386,9 +387,12 @@ export function ThroughputCard({
}

export function PromptTokenSourceCard({ sliced }: { sliced: SlicedServerSeries }) {
const locale = useLocale();
return (
<ExpandableChart
title="Cumulative prompt token source breakdown"
title={
locale === 'zh' ? '累计 prompt token 来源构成' : 'Cumulative prompt token source breakdown'
}
render={(expanded) => {
const size = expanded ? CHART_SIZES.expanded : CHART_SIZES.inline;
if (!sliced) return <ChartSkeleton />;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { describe, expect, it } from 'vitest';

import type { BenchmarkSibling } from '@/hooks/api/use-benchmark-siblings';

import { chipLabel } from './sibling-nav';
import { chipLabel, dualParetoSiblingIds } from './sibling-nav';

function sibling(overrides: Partial<BenchmarkSibling> = {}): BenchmarkSibling {
return {
id: 437312,
conc: 2,
offload_mode: 'off',
kv_offloading: 'none',
decode_tp: 0,
decode_ep: 0,
decode_pp: 1,
Expand All @@ -28,6 +29,8 @@ function sibling(overrides: Partial<BenchmarkSibling> = {}): BenchmarkSibling {
disagg: false,
is_multinode: true,
tput_per_gpu: 348.31,
p90_intvty: 40,
p90_ttft: 2,
total_requests: 169,
is_current: true,
has_trace: true,
Expand Down Expand Up @@ -74,4 +77,45 @@ describe('chipLabel', () => {
),
).toBe('1xTP8PP2/DCP2/PCP4+1xTP8/DCP8 • c=2');
});

it('names each physical offload tier without the legacy off=ON suffix', () => {
expect(chipLabel(sibling({ kv_offloading: 'dram', offload_mode: 'on' }))).toBe(
'TP8PP2 • c=2 • DRAM',
);
expect(chipLabel(sibling({ kv_offloading: 'nvme', offload_mode: 'on' }))).toBe(
'TP8PP2 • c=2 • NVMe',
);
expect(chipLabel(sibling({ kv_offloading: 'dram+nvme', offload_mode: 'on' }))).toBe(
'TP8PP2 • c=2 • DRAM+NVMe',
);
});

it('falls back to a readable legacy label when an enabled point has no tier metadata', () => {
expect(chipLabel(sibling({ kv_offloading: null, offload_mode: 'on' }))).toBe(
'TP8PP2 • c=2 • Offload',
);
});
});

describe('dualParetoSiblingIds', () => {
it('intersects the P90 interactivity and TTFT frontiers using throughput per GPU', () => {
const rows = [
sibling({ id: 1, p90_intvty: 100, p90_ttft: 1, tput_per_gpu: 100 }),
sibling({ id: 2, p90_intvty: 80, p90_ttft: 2, tput_per_gpu: 200 }),
sibling({ id: 3, p90_intvty: 50, p90_ttft: 3, tput_per_gpu: 50 }),
sibling({ id: 4, p90_intvty: 120, p90_ttft: 4, tput_per_gpu: 150 }),
];

expect([...dualParetoSiblingIds(rows)]).toEqual([2]);
});

it('excludes points missing any frontier coordinate', () => {
const rows = [
sibling({ id: 1, p90_intvty: 100, p90_ttft: 1, tput_per_gpu: 200 }),
sibling({ id: 2, p90_intvty: null, p90_ttft: 0.5, tput_per_gpu: 100 }),
sibling({ id: 3, p90_intvty: 200, p90_ttft: null, tput_per_gpu: 100 }),
];

expect([...dualParetoSiblingIds(rows)]).toEqual([1]);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
meaningfulParallelismSize,
parallelismLabel,
} from '@/components/inference/utils/parallelism-label';
import { offloadTypeLabel } from '@/components/inference/utils/runtime-metadata-labels';
import {
Select,
SelectContent,
Expand All @@ -17,6 +18,7 @@ import {
SelectValue,
} from '@/components/ui/select';
import { track } from '@/lib/analytics';
import { isFrontierEligible, paretoFrontUpperLeft, paretoFrontUpperRight } from '@/lib/chart-utils';
import { isZhPathname, ZH_PREFIX } from '@/lib/i18n';

const HW_LABELS: Record<string, string> = {
Expand Down Expand Up @@ -61,7 +63,15 @@ function frameworkLabel(fw: string) {
return fw;
}

/** Short label for a sibling chip: parallelism + concurrency. */
function offloadTierLabel(s: BenchmarkSibling): string | null {
const descriptor = s.kv_offloading?.trim();
if (descriptor) {
return descriptor.toLowerCase() === 'none' ? null : offloadTypeLabel(descriptor);
}
return s.offload_mode?.toLowerCase() === 'on' ? 'Offload' : null;
}

/** Short label for a sibling chip: parallelism + concurrency + physical KV tier. */
export function chipLabel(s: BenchmarkSibling): string {
const usePrefill =
!s.disagg &&
Expand Down Expand Up @@ -117,8 +127,42 @@ export function chipLabel(s: BenchmarkSibling): string {
decodeDpAttention: s.decode_dp_attention,
decodeNumWorkers: s.decode_num_workers,
});
const offload = s.offload_mode === 'on' ? ' • off=ON' : '';
return `${parallel} • c=${s.conc}${offload}`;
const offload = offloadTierLabel(s);
return `${parallel} • c=${s.conc}${offload ? ` • ${offload}` : ''}`;
}

interface SiblingParetoPoint {
id: number;
x: number;
y: number;
}

const siblingParetoPoints = (
siblings: BenchmarkSibling[],
xMetric: 'p90_intvty' | 'p90_ttft',
): SiblingParetoPoint[] =>
siblings
.map((sibling) => ({
id: sibling.id,
x: sibling[xMetric] ?? Number.NaN,
y: sibling.tput_per_gpu ?? Number.NaN,
}))
.filter((point) => isFrontierEligible(point) && Number.isFinite(point.y));

/**
* Point IDs that lie on both P90 frontiers used by AgentX: interactivity vs.
* throughput (upper-left in the reversed chart) and TTFT vs. throughput
* (upper-right). The shared chart helpers keep tie behavior identical to the
* plotted Pareto lines.
*/
export function dualParetoSiblingIds(siblings: BenchmarkSibling[]): Set<number> {
const interactivity = new Set(
paretoFrontUpperLeft(siblingParetoPoints(siblings, 'p90_intvty')).map((point) => point.id),
);
const ttft = new Set(
paretoFrontUpperRight(siblingParetoPoints(siblings, 'p90_ttft')).map((point) => point.id),
);
return new Set([...interactivity].filter((id) => ttft.has(id)));
}

type SortMode = 'default' | 'conc' | 'parallelism' | 'tput' | 'requests';
Expand Down Expand Up @@ -182,9 +226,8 @@ const isSortMode = (v: string | null): v is SortMode =>
export function SiblingNav({ sku, siblings }: { sku: BenchmarkSku; siblings: BenchmarkSibling[] }) {
const router = useRouter();
const pathname = usePathname();
const agenticBase = isZhPathname(pathname)
? `${ZH_PREFIX}/inference/agentic`
: '/inference/agentic';
const isZh = isZhPathname(pathname);
const agenticBase = isZh ? `${ZH_PREFIX}/inference/agentic` : '/inference/agentic';
// Persist the sort in the URL so clicking a point (which remounts this
// component on the new route) keeps the chosen order instead of resetting.
// Read it once from the URL on mount — this component only renders after the
Expand All @@ -197,6 +240,7 @@ export function SiblingNav({ sku, siblings }: { sku: BenchmarkSku; siblings: Ben
});

const sorted = useMemo(() => sortSiblings(siblings, sortMode), [siblings, sortMode]);
const dualParetoIds = useMemo(() => dualParetoSiblingIds(siblings), [siblings]);

// prev/next follow the displayed (sorted) order so navigation matches the row.
const currentIdx = sorted.findIndex((s) => s.is_current);
Expand Down Expand Up @@ -272,6 +316,17 @@ export function SiblingNav({ sku, siblings }: { sku: BenchmarkSku; siblings: Ben
<div className="flex items-center gap-1 flex-wrap">
{sorted.map((s) => {
const active = s.is_current;
const isDualPareto = dualParetoIds.has(s.id);
const title = [
isDualPareto
? isZh
? '同时位于 P90 交互性与 TTFT Pareto 前沿'
: 'On both P90 interactivity and TTFT Pareto frontiers'
: null,
s.has_trace ? null : isZh ? '无已存储的追踪数据' : 'No stored trace data',
]
.filter(Boolean)
.join(' · ');
return (
<button
key={s.id}
Expand All @@ -286,8 +341,18 @@ export function SiblingNav({ sku, siblings }: { sku: BenchmarkSku; siblings: Ben
active
? 'border-primary bg-primary text-primary-foreground font-medium'
: 'border-border/40 text-foreground hover:bg-accent'
} ${
isDualPareto ? 'ring-2 ring-primary/70 ring-offset-1 ring-offset-background' : ''
} ${s.has_trace ? '' : 'opacity-60'}`}
title={s.has_trace ? undefined : 'No stored trace data'}
title={title || undefined}
aria-label={`${chipLabel(s)}${
isDualPareto
? isZh
? ',同时位于两个 P90 Pareto 前沿'
: ', on both P90 Pareto frontiers'
: ''
}`}
data-dual-pareto={isDualPareto || undefined}
>
{chipLabel(s)}
</button>
Expand All @@ -309,6 +374,15 @@ export function SiblingNav({ sku, siblings }: { sku: BenchmarkSku; siblings: Ben
next <ChevronRight className="size-3.5" />
</button>
</div>
{dualParetoIds.size > 0 && (
<div className="mt-3 flex items-center gap-2 text-xs text-muted-foreground">
<span
className="inline-block h-3 w-5 rounded-sm border border-border ring-2 ring-primary/70 ring-offset-1 ring-offset-background"
aria-hidden="true"
/>
<span>{isZh ? 'P90 交互性 + TTFT Pareto' : 'P90 interactivity + TTFT Pareto'}</span>
</div>
)}
</div>
);
}
Loading