[Docs] Playground: migrate CP knob to canonical prefill-CP flags, align gating with runtime semantics (#31411)
This commit is contained in:
@@ -147,6 +147,38 @@ schemas (full reference in the `_playground.jsx` header):
|
||||
- Constraints are AND across keys, OR within each key's array.
|
||||
- Bare `disabled: true` / `disable: true` is a static always-disabled form
|
||||
(used for "Coming soon" chips).
|
||||
- `disable` may also be an ARRAY of `{when: constraint, reason}` items (OR
|
||||
across items, first match wins and supplies its own tooltip) — for
|
||||
conditions that need OR across keys or per-condition reasons, e.g. the
|
||||
GLM-5.2 CP knob (grayed on non-Hopper hw OR multi-node, different reasons).
|
||||
- Constraint keys are the 5 cell dims (`hw`/`variant`/`quant`/`strategy`/
|
||||
`nodes`) plus cross-axis live facts: `dpAttnOn` (effective DP-Attention on),
|
||||
`cpOn` (effective prefill-CP on), `cpStrategy` (effective CP layout),
|
||||
`cpSizeTarget` (the only enable-able CP size — see below), `effTp`
|
||||
(effective TP degree — override else derived), and `pdMode` (live
|
||||
PD-Disagg role).
|
||||
- On the `attention` axis, knob-level `hide`/`disable` (on the knob object,
|
||||
not a value entry) hides/grays the whole select; apply() skips
|
||||
knobs/values that are disabled under the live facts, so stale picks never
|
||||
emit a blocked combination. Interleave prefill-CP + DP-Attention is
|
||||
deliberately NOT grayed (combined support is planned upstream even though
|
||||
current releases assert `dp_size == 1` for interleave) — the engine shows
|
||||
a warning hint below the command box instead.
|
||||
- The CP knob emits `--attn-cp-size N --enable-prefill-cp --cp-strategy S`,
|
||||
where S is: an optional `{ id: "cpStrategy", values: [null, "interleave",
|
||||
"zigzag"] }` knob's pick > the strategy already baked in the base cell
|
||||
(legacy mode flags map in: in-seq-split → zigzag, round-robin-split →
|
||||
interleave) > "interleave". Declare the cpStrategy knob only on models
|
||||
whose runtime accepts both layouts (DeepSeek-V4 rejects zigzag; DSA
|
||||
zigzag forces deepep + ep=tp + batch_size=1 — label it experimental).
|
||||
- CP sizes auto-gate in the engine to the runtime derivation
|
||||
`attn_cp_size = tp/dp` (both in the grayed options and in apply), so
|
||||
configs list plain size values — no per-value `effTp` constraints needed.
|
||||
A model whose runtime honors arbitrary `--attn-cp-size` opts out with
|
||||
`freeSize: true` on the cp knob.
|
||||
- Only expose a CP knob on models with model-side CP integration in SGLang
|
||||
(DeepSeek-family / Qwen-MoE / Mellum); on others the emitted flags do
|
||||
nothing or crash (that's why Hy3 and MiniMax-M3 have no CP knob).
|
||||
|
||||
## 2.4 Create the MDX page
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ The Playground lets you turn on additional knobs on top of whichever Deploy cell
|
||||
|
||||
The knobs come in two flavors:
|
||||
|
||||
- **Built-in SGLang features** — parallelism overrides (TP / CP / DP-Attention), MoE backend + EP, reasoning / tool-call parsers, speculative-decoding presets, prefill/decode disaggregation, and HiCache tiers.
|
||||
- **Built-in SGLang features** — parallelism overrides (TP / DP-Attention), MoE backend + EP, reasoning / tool-call parsers, speculative-decoding presets, prefill/decode disaggregation, and HiCache tiers.
|
||||
- **Hy3 specific** — `--tool-call-parser auto` / `--reasoning-parser auto` (auto-detect Hy3's suffix-aware `hunyuan` parsers from the chat template; resolve the real special tokens from the tokenizer vocab at runtime).
|
||||
|
||||
Lines highlighted **green** are added by your overrides; lines with **red strikethrough** were in the verified base but stripped by an override. When no override differs from the base cell, the playground inherits the base's **Verified** badge; any actual change flips it to **Not Verified** until the new configuration is run end-to-end and submitted back.
|
||||
|
||||
@@ -110,6 +110,9 @@ export const Playground = ({ config }) => {
|
||||
// mapping base-cell dims to allowed-value arrays; matches when every key
|
||||
// matches (AND across keys, OR within a key). Empty/malformed never match.
|
||||
// `disabled: true` / `disable: true` are static always-disabled forms.
|
||||
// `disable` may also be an ARRAY of `{when: constraint, reason?}` items
|
||||
// (OR across items, first match wins and supplies its own reason) for
|
||||
// conditions that need OR across keys or per-condition tooltips.
|
||||
const matchConstraint = (base, constraint) => {
|
||||
if (!constraint || typeof constraint !== "object") return false;
|
||||
const entries = Object.entries(constraint);
|
||||
@@ -130,8 +133,20 @@ export const Playground = ({ config }) => {
|
||||
}
|
||||
const hidden = entry.hide ? matchConstraint(base, entry.hide) : false;
|
||||
let disabled = entry.disabled === true || entry.disable === true;
|
||||
let disableReason = entry.disableReason || "";
|
||||
if (!disabled && entry.disable && typeof entry.disable === "object") {
|
||||
disabled = matchConstraint(base, entry.disable);
|
||||
if (Array.isArray(entry.disable)) {
|
||||
for (const item of entry.disable) {
|
||||
const cond = (item && item.when) || item;
|
||||
if (matchConstraint(base, cond)) {
|
||||
disabled = true;
|
||||
if (item && item.reason) disableReason = item.reason;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
disabled = matchConstraint(base, entry.disable);
|
||||
}
|
||||
}
|
||||
return {
|
||||
...entry,
|
||||
@@ -139,7 +154,7 @@ export const Playground = ({ config }) => {
|
||||
label: entry.label,
|
||||
hidden,
|
||||
disabled,
|
||||
disableReason: entry.disableReason || "",
|
||||
disableReason,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -239,6 +254,37 @@ export const Playground = ({ config }) => {
|
||||
ANCHOR_NEAR_DPATTN, ANCHOR_NEAR_MOE,
|
||||
};
|
||||
|
||||
// -------- Prefill-CP flag family (shared by the attention axis) --------
|
||||
// Every flag head that toggles/parameterizes prefill context parallelism:
|
||||
// the canonical pair plus all per-family legacy spellings.
|
||||
const CP_ENABLE_HEADS = [
|
||||
"--enable-prefill-cp",
|
||||
"--enable-nsa-prefill-context-parallel",
|
||||
"--enable-dsa-prefill-context-parallel",
|
||||
"--enable-prefill-context-parallel",
|
||||
];
|
||||
const CP_MODE_HEADS = [
|
||||
"--nsa-prefill-cp-mode", "--dsa-prefill-cp-mode", "--prefill-cp-mode",
|
||||
];
|
||||
const CP_OWNED_HEADS = [
|
||||
...CP_ENABLE_HEADS, ...CP_MODE_HEADS, "--cp-strategy", "--attn-cp-size",
|
||||
];
|
||||
// Legacy mode spellings → new-style --cp-strategy values.
|
||||
const CP_MODE_TO_STRATEGY = {
|
||||
"in-seq-split": "zigzag",
|
||||
"round-robin-split": "interleave",
|
||||
};
|
||||
const cpEnabledIn = (flags) =>
|
||||
CP_ENABLE_HEADS.some((head) => hasFlag(flags, head));
|
||||
// Strategy a cell's flags carry: --cp-strategy first, else a mapped legacy
|
||||
// mode flag, else null (no strategy baked).
|
||||
const bakedCpStrategy = (flags) =>
|
||||
findFlagArg(flags, "--cp-strategy")
|
||||
|| CP_MODE_TO_STRATEGY[findFlagArg(flags, "--nsa-prefill-cp-mode")]
|
||||
|| CP_MODE_TO_STRATEGY[findFlagArg(flags, "--dsa-prefill-cp-mode")]
|
||||
|| CP_MODE_TO_STRATEGY[findFlagArg(flags, "--prefill-cp-mode")]
|
||||
|| null;
|
||||
|
||||
// ==========================================================================
|
||||
// 5. AXIS_HANDLERS — the built-in playground axis registry
|
||||
// ==========================================================================
|
||||
@@ -255,12 +301,17 @@ export const Playground = ({ config }) => {
|
||||
// ---- Axis: Attention Parallelism ----------------------------------------
|
||||
// TP / CP / DP-Attention sub-knobs; `null` = inherit. DP-Attention is
|
||||
// combined: a numeric value emits `--dp N --enable-dp-attention`, `false`
|
||||
// strips both.
|
||||
// strips both. An optional `cpStrategy` knob (values from --cp-strategy:
|
||||
// "zigzag" / "interleave") picks the CP layout; without it the strategy
|
||||
// baked in the base is preserved, defaulting to "interleave" (the legacy
|
||||
// knob's round-robin-split).
|
||||
attention: {
|
||||
initState: () => ({ tp: null, cp: null, dpAttn: null }),
|
||||
initState: () => ({ tp: null, cp: null, cpStrategy: null, dpAttn: null }),
|
||||
|
||||
// DP-Attention: `--dp N --enable-dp-attention` → N; neither → false;
|
||||
// bare `--enable-dp-attention` → 1. CP only resolves on/off (→ 2).
|
||||
// bare `--enable-dp-attention` → 1. CP: any enable spelling →
|
||||
// `--attn-cp-size N` (bare enable → 2, the legacy convention), plus the
|
||||
// baked strategy (legacy mode flags mapped to zigzag/interleave).
|
||||
deriveFromBase: (cell, fc, h) => {
|
||||
const flags = (cell && cell.flags) || [];
|
||||
const dpVal = h.parseIntFlag(flags, "--dp");
|
||||
@@ -269,9 +320,11 @@ export const Playground = ({ config }) => {
|
||||
if (dpVal !== null) dpAttn = dpVal;
|
||||
else if (hasDpAttn) dpAttn = 1;
|
||||
else dpAttn = false;
|
||||
const cpSize = h.parseIntFlag(flags, "--attn-cp-size");
|
||||
return {
|
||||
tp: h.parseIntFlag(flags, "--tp"),
|
||||
cp: h.hasFlag(flags, "--enable-nsa-prefill-context-parallel") ? 2 : null,
|
||||
cp: cpEnabledIn(flags) ? (cpSize !== null ? cpSize : 2) : null,
|
||||
cpStrategy: bakedCpStrategy(flags),
|
||||
dpAttn,
|
||||
};
|
||||
},
|
||||
@@ -289,12 +342,84 @@ export const Playground = ({ config }) => {
|
||||
return changed ? next : value;
|
||||
},
|
||||
|
||||
apply: ({ flags, env, value, h }) => {
|
||||
if (value.tp !== null) {
|
||||
apply: ({ flags, env, value, fc, sel, h }) => {
|
||||
// Live facts for constraint checks (same keys the render-side
|
||||
// constraintBase exposes), recomputed after each mutation.
|
||||
const knobEntry = (id) => (fc.knobs || []).find((k) => k.id === id) || {};
|
||||
const factsNow = () => ({
|
||||
...(sel || {}),
|
||||
dpAttnOn: h.hasFlag(flags, "--enable-dp-attention"),
|
||||
cpOn: cpEnabledIn(flags),
|
||||
cpStrategy: bakedCpStrategy(flags) || "interleave",
|
||||
effTp: h.parseIntFlag(flags, "--tp"),
|
||||
});
|
||||
// The runtime derives the prefill-CP size as attn_cp_size = tp/dp
|
||||
// (a mismatched --attn-cp-size is overridden), so with DP-Attention
|
||||
// off only CP == TP is real. With DP-Attention on the sizes are NOT
|
||||
// gated: CP + DP-Attention is an allowed experiment (warning hint
|
||||
// below the command box). Mirrors the render-side auto-gating; a
|
||||
// config opts out entirely with `freeSize: true` on the cp knob.
|
||||
const cpSizeTargetNow = () => {
|
||||
if (knobEntry("cp").freeSize) return null;
|
||||
const dpIntent = (value.dpAttn !== null && value.dpAttn !== undefined)
|
||||
? value.dpAttn
|
||||
: (h.hasFlag(flags, "--enable-dp-attention")
|
||||
? (h.parseIntFlag(flags, "--dp") ?? 1) : false);
|
||||
if (typeof dpIntent === "number" && dpIntent > 1) return null;
|
||||
return h.parseIntFlag(flags, "--tp");
|
||||
};
|
||||
// Skip a knob whose entry or picked value is hidden/disabled under
|
||||
// the live facts — mirrors the grayed controls, so stale state never
|
||||
// emits a blocked combination.
|
||||
const blocked = (id, v) => {
|
||||
const facts = factsNow();
|
||||
const kc = h.evaluateChip(knobEntry(id), facts);
|
||||
if (kc.hidden || kc.disabled) return true;
|
||||
if (id === "cp" && typeof v === "number" && v > 1) {
|
||||
const target = cpSizeTargetNow();
|
||||
if (target !== null && v !== target) return true;
|
||||
}
|
||||
const e = h.findEntry(knobEntry(id).values || [], v);
|
||||
return !!(e !== null && e !== undefined
|
||||
&& h.evaluateChip(e, facts).disabled);
|
||||
};
|
||||
// NOTE: interleave prefill-CP + DP-Attention currently fails the
|
||||
// runtime's dp_size == 1 assert, but combined support is planned
|
||||
// upstream — the combination is allowed here (with a warning hint
|
||||
// below the command box) rather than banned.
|
||||
|
||||
if (value.tp !== null && !blocked("tp", value.tp)) {
|
||||
flags = h.stripFlagsByFirstToken(flags, ["--tp"]);
|
||||
flags = h.insertAfter(flags, h.ANCHOR_NEAR_MODEL_PATH, [`--tp ${value.tp}`]);
|
||||
}
|
||||
if (value.dpAttn !== null && value.dpAttn !== undefined) {
|
||||
// CP override: an explicit size pick, or a strategy-only pick on a
|
||||
// base that already carries CP. Strategy precedence: explicit knob >
|
||||
// baked-in-base > "interleave" (the legacy knob's round-robin-split).
|
||||
const cpStrategyOverride =
|
||||
(value.cpStrategy && !blocked("cpStrategy", value.cpStrategy))
|
||||
? value.cpStrategy : null;
|
||||
const cpPick = (value.cp !== null)
|
||||
? value.cp
|
||||
: ((cpStrategyOverride && cpEnabledIn(flags))
|
||||
? (h.parseIntFlag(flags, "--attn-cp-size") ?? 2)
|
||||
: null);
|
||||
const cpStrategyPick =
|
||||
cpStrategyOverride || bakedCpStrategy(flags) || "interleave";
|
||||
if (cpPick !== null && !blocked("cp", cpPick)) {
|
||||
// Own the whole CP flag family (canonical + every legacy spelling)
|
||||
// so an override fully replaces (or removes) whatever the base
|
||||
// recipe baked in.
|
||||
flags = h.stripFlagsByFirstToken(flags, CP_OWNED_HEADS);
|
||||
if (cpPick > 1) {
|
||||
flags = h.insertAfter(flags, h.ANCHOR_NEAR_DPATTN, [
|
||||
`--attn-cp-size ${cpPick}`,
|
||||
"--enable-prefill-cp",
|
||||
`--cp-strategy ${cpStrategyPick}`,
|
||||
]);
|
||||
}
|
||||
}
|
||||
if (value.dpAttn !== null && value.dpAttn !== undefined
|
||||
&& !blocked("dpAttn", value.dpAttn)) {
|
||||
flags = h.stripFlagsByFirstToken(flags, ["--dp", "--enable-dp-attention"]);
|
||||
if (typeof value.dpAttn === "number" && value.dpAttn > 0) {
|
||||
flags = h.insertAfter(flags, h.ANCHOR_NEAR_TP, [
|
||||
@@ -303,24 +428,17 @@ export const Playground = ({ config }) => {
|
||||
]);
|
||||
}
|
||||
}
|
||||
if (value.cp !== null) {
|
||||
flags = h.stripFlagsByFirstToken(flags, [
|
||||
"--enable-nsa-prefill-context-parallel", "--nsa-prefill-cp-mode",
|
||||
]);
|
||||
if (value.cp > 1) {
|
||||
flags = h.insertAfter(flags, h.ANCHOR_NEAR_DPATTN, [
|
||||
"--enable-nsa-prefill-context-parallel",
|
||||
"--nsa-prefill-cp-mode round-robin-split",
|
||||
]);
|
||||
}
|
||||
}
|
||||
return { flags, env };
|
||||
},
|
||||
|
||||
render: ({ axisId, value, setValue, fc, base, s, renderSelect, derived }) => {
|
||||
render: ({ axisId, value, setValue, fc, base, s, h, renderSelect, derived }) => {
|
||||
const knobs = fc.knobs || [];
|
||||
if (!knobs.length) return null;
|
||||
const setKnob = (k, v) => setValue({ ...value, [k]: v });
|
||||
// Interleave prefill-CP + DP-Attention is deliberately NOT grayed:
|
||||
// current releases assert dp_size == 1 for interleave, but combined
|
||||
// support is planned upstream — a warning hint below the command box
|
||||
// covers it instead.
|
||||
const labelFor = (knob) => (c) => {
|
||||
if (c.label !== undefined) return c.label;
|
||||
if (knob.id === "dpAttn") {
|
||||
@@ -341,18 +459,44 @@ export const Playground = ({ config }) => {
|
||||
const d = derived ? derived[knob.id] : null;
|
||||
return (d !== null && d !== undefined) ? [null] : [];
|
||||
};
|
||||
// Auto-gate CP sizes to the runtime derivation attn_cp_size = tp/dp
|
||||
// (mirrors apply's cpSizeTargetNow; `freeSize: true` opts out).
|
||||
const entriesFor = (knob) => {
|
||||
const vals = knob.values || [null];
|
||||
if (knob.id !== "cp" || knob.freeSize) return vals;
|
||||
const target = base.cpSizeTarget;
|
||||
if (target === null || target === undefined) return vals;
|
||||
return vals.map((entry) => {
|
||||
const v = (entry === null || typeof entry !== "object")
|
||||
? entry : (entry.id !== undefined ? entry.id : entry.value);
|
||||
if (typeof v !== "number" || v <= 1 || v === target) return entry;
|
||||
const wrapped = (entry === null || typeof entry !== "object")
|
||||
? { value: entry } : { ...entry };
|
||||
return {
|
||||
...wrapped,
|
||||
disabled: true,
|
||||
disableReason: `SGLang derives the prefill-CP size as attn_cp_size = TP / DP-Attention (= ${target} here), so only that size can be enabled.`,
|
||||
};
|
||||
});
|
||||
};
|
||||
return (
|
||||
<div key={axisId} style={s.card}>
|
||||
<div style={s.compactRow}>
|
||||
<span style={s.axisTitle}>Attention</span>
|
||||
{knobs.map((knob) => (
|
||||
<span key={knob.id} style={s.field}>
|
||||
<span style={s.fieldLabel}>{knob.label || knob.id.toUpperCase()}</span>
|
||||
{renderSelect(knobDisplay(knob), knob.values || [null],
|
||||
(nv) => setKnob(knob.id, nv), base, labelFor(knob),
|
||||
{ hideValues: hideNullFor(knob) })}
|
||||
</span>
|
||||
))}
|
||||
{knobs.map((knob) => {
|
||||
const kc = h.evaluateChip(knob, base);
|
||||
if (kc.hidden) return null;
|
||||
return (
|
||||
<span key={knob.id} style={s.field}>
|
||||
<span style={s.fieldLabel}>{knob.label || knob.id.toUpperCase()}</span>
|
||||
{renderSelect(knobDisplay(knob), entriesFor(knob),
|
||||
(nv) => setKnob(knob.id, nv), base, labelFor(knob),
|
||||
{ hideValues: hideNullFor(knob),
|
||||
disabled: kc.disabled,
|
||||
disabledReason: kc.disableReason })}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -1638,16 +1782,63 @@ export const Playground = ({ config }) => {
|
||||
// (render path only; revertHidden keeps the clean 5-dim base).
|
||||
// dpAttnOn — effective DP-Attention resolves to "on" (positive degree
|
||||
// or true), explicit override else derived-from-base.
|
||||
// cpOn — effective prefill-CP resolves to "on" (degree > 1),
|
||||
// explicit override else derived-from-base.
|
||||
// cpStrategy — effective CP layout ("zigzag" / "interleave"; explicit
|
||||
// override else baked-in-base else "interleave").
|
||||
// cpSizeTarget — the only enable-able CP size (runtime derives
|
||||
// attn_cp_size = tp/dp); null when TP is unknown or the cp
|
||||
// knob opts out via `freeSize: true`.
|
||||
// effTp — effective TP degree (override else derived).
|
||||
// pdMode — live PD-Disagg role; gates the decode-only HiSparse card.
|
||||
const attnDelta = deltas.attention || {};
|
||||
const attnDerived = derivedMap.attention || {};
|
||||
const attnKnobs = ((pgFeatures.attention || {}).knobs) || [];
|
||||
const effTp = (attnDelta.tp !== null && attnDelta.tp !== undefined)
|
||||
? attnDelta.tp
|
||||
: (attnDerived.tp !== undefined ? attnDerived.tp : null);
|
||||
// A picked value whose entry is disabled under the live facts is skipped
|
||||
// by apply() and must not count as "on" (stale-state corner: e.g. CP=8
|
||||
// picked, then TP switched to 4). Only explicit picks can go stale;
|
||||
// derived-from-base values are always real.
|
||||
const staleExplicit = (knobId, picked) => {
|
||||
const knob = attnKnobs.find((k) => k.id === knobId);
|
||||
const e = knob ? findEntry(knob.values || [], picked) : null;
|
||||
return !!(e !== null && e !== undefined
|
||||
&& evaluateChip(e, { ...base, effTp }).disabled);
|
||||
};
|
||||
const effDpAttn = (attnDelta.dpAttn !== null && attnDelta.dpAttn !== undefined)
|
||||
? attnDelta.dpAttn
|
||||
? (staleExplicit("dpAttn", attnDelta.dpAttn) ? null : attnDelta.dpAttn)
|
||||
: (attnDerived.dpAttn !== undefined ? attnDerived.dpAttn : null);
|
||||
const dpAttnOn = (effDpAttn === true)
|
||||
|| (typeof effDpAttn === "number" && effDpAttn > 0);
|
||||
// Runtime derivation attn_cp_size = tp/dp: with DP-Attention off, the only
|
||||
// enable-able CP size is TP. With DP-Attention on, sizes are NOT gated —
|
||||
// CP + DP-Attention is an allowed experiment covered by a warning hint
|
||||
// (null also when TP is unknown or the cp knob opts out via `freeSize`).
|
||||
const dpDegEff = (typeof effDpAttn === "number" && effDpAttn > 0)
|
||||
? effDpAttn : 1;
|
||||
const cpKnobFreeSize = !!(attnKnobs.find((k) => k.id === "cp") || {}).freeSize;
|
||||
const cpSizeTarget =
|
||||
(!cpKnobFreeSize && dpDegEff === 1
|
||||
&& typeof effTp === "number" && effTp > 0)
|
||||
? effTp : null;
|
||||
const cpSizeStale = (v) => typeof v === "number" && v > 1
|
||||
&& cpSizeTarget !== null && v !== cpSizeTarget;
|
||||
const effCp = (attnDelta.cp !== null && attnDelta.cp !== undefined)
|
||||
? ((staleExplicit("cp", attnDelta.cp) || cpSizeStale(attnDelta.cp))
|
||||
? null : attnDelta.cp)
|
||||
: (attnDerived.cp !== undefined ? attnDerived.cp : null);
|
||||
const cpOn = typeof effCp === "number" && effCp > 1;
|
||||
const cpStrategy = ((attnDelta.cpStrategy
|
||||
&& !staleExplicit("cpStrategy", attnDelta.cpStrategy))
|
||||
? attnDelta.cpStrategy
|
||||
: (attnDerived.cpStrategy !== undefined ? attnDerived.cpStrategy : null))
|
||||
|| "interleave";
|
||||
const pdMode = (deltas.pdDisagg && deltas.pdDisagg.mode) || "off";
|
||||
const constraintBase = { ...base, dpAttnOn, pdMode };
|
||||
const constraintBase = {
|
||||
...base, dpAttnOn, cpOn, cpStrategy, cpSizeTarget, effTp, pdMode,
|
||||
};
|
||||
|
||||
let baseCommand = "";
|
||||
let playgroundCommand = "";
|
||||
@@ -1678,6 +1869,14 @@ export const Playground = ({ config }) => {
|
||||
pgFlagsLatest.some((f) => f.split(/[\s=]/)[0] === "--speculative-algorithm") &&
|
||||
!pgFlagsLatest.some((f) => f.split(/[\s=]/)[0] === "--max-running-requests");
|
||||
|
||||
// Interleave prefill-CP + DP-Attention hint on the EFFECTIVE command:
|
||||
// deliberately allowed (combined support is planned upstream), but current
|
||||
// releases assert dp_size == 1 for the interleave layout at startup.
|
||||
const pgCpDpHint =
|
||||
cpEnabledIn(pgFlagsLatest)
|
||||
&& (bakedCpStrategy(pgFlagsLatest) || "interleave") === "interleave"
|
||||
&& pgFlagsLatest.some((f) => f.split(/[\s=]/)[0] === "--enable-dp-attention");
|
||||
|
||||
// Submission snippets: proposed cell + existing cell at the same match.
|
||||
const proposedCellSnippet = baseCell
|
||||
? serializeCell(base, pgEnvLatest, pgFlagsLatest) : "";
|
||||
@@ -1766,6 +1965,8 @@ export const Playground = ({ config }) => {
|
||||
// value to dodge form-value serialization; `onPick` gets the original
|
||||
// value. `labelFor` is an optional label resolver; `opts.hideValues`
|
||||
// suppresses values (e.g. the inherit sentinel when a base default exists).
|
||||
// `opts.disabled` grays the whole select (knob-level gating), with
|
||||
// `opts.disabledReason` as the hover tooltip.
|
||||
const renderSelect = (current, entries, onPick, base, labelFor, opts = {}) => {
|
||||
const hideSet = new Set(opts.hideValues || []);
|
||||
const items = [];
|
||||
@@ -1783,7 +1984,9 @@ export const Playground = ({ config }) => {
|
||||
if (idx === -1) idx = 0;
|
||||
return (
|
||||
<select
|
||||
style={s.select}
|
||||
style={{ ...s.select, ...(opts.disabled ? s.chipDisabled : {}) }}
|
||||
disabled={!!opts.disabled}
|
||||
title={opts.disabled ? (opts.disabledReason || "Not available") : ""}
|
||||
value={idx}
|
||||
onChange={(e) => {
|
||||
const next = items[parseInt(e.target.value, 10)];
|
||||
@@ -1931,6 +2134,11 @@ export const Playground = ({ config }) => {
|
||||
⚠️ Speculative decoding (MTP) is on — SGLang resets <code>--max-running-requests</code> to <strong>48</strong> when it isn't set. Add <code>--max-running-requests <N></code> sized for your target concurrency.
|
||||
</div>
|
||||
)}
|
||||
{pgCpDpHint && (
|
||||
<div style={s.mtpWarn}>
|
||||
⚠️ Interleave prefill-CP together with DP-Attention: current SGLang releases assert <code>dp_size == 1</code> for the interleave layout, so this command fails at startup. Combined CP + DP-Attention support is planned upstream — keep one of the two off until it lands.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -114,10 +114,13 @@ sgl-eval run mmmu_pro \\
|
||||
playgroundFeatures: {
|
||||
|
||||
// ----- Attention Parallelism -----
|
||||
// No CP knob: prefill Context Parallel needs model-side integration in
|
||||
// SGLang (DeepSeek-family / Qwen-MoE / Mellum have it) and
|
||||
// MiniMaxM3SparseForCausalLM has none — the engine's CP knob would emit
|
||||
// --enable-prefill-cp flags that don't work on this model.
|
||||
attention: {
|
||||
knobs: [
|
||||
{ id: "tp", label: "TP", values: [null, 1, 2, 4, 8] },
|
||||
{ id: "cp", label: "CP", values: [null, 1, 2, 4] },
|
||||
{ id: "dpAttn", label: "DP-Attention",
|
||||
values: [null, false, 1, 2, 4, 8],
|
||||
labels: { "auto": "Auto", "false": "Off" } },
|
||||
|
||||
@@ -173,6 +173,12 @@ sgl-eval run aime25 \\
|
||||
|
||||
// ----- Card 1: "Attention Parallelism" -----
|
||||
// DP-Attention is a combined knob: value is the DP degree AND toggles `--enable-dp-attention`.
|
||||
// CP sizes auto-gate in the engine to the runtime derivation
|
||||
// attn_cp_size = tp/dp (a user-passed --attn-cp-size is overridden).
|
||||
// CP is single-machine only (tp_size <= 8). Interleave CP + DP-Attention
|
||||
// currently fails the runtime's dp_size == 1 assert but is allowed here
|
||||
// with a warning (combined support is planned upstream). No `cpStrategy`
|
||||
// knob: DeepSeek-V4 supports only interleave (the runtime rejects zigzag).
|
||||
attention: {
|
||||
knobs: [
|
||||
{ id: "tp", label: "TP", values: [
|
||||
@@ -184,7 +190,12 @@ sgl-eval run aime25 \\
|
||||
{ value: 16, disable: { nodes: ["single"] },
|
||||
disableReason: "TP=16 requires 16 ranks — switch the Deploy panel's Nodes to Multi-Nodes first." },
|
||||
]},
|
||||
{ id: "cp", label: "CP", values: [null, 1, 2, 4] },
|
||||
{ id: "cp", label: "CP",
|
||||
values: [null, { value: 1, label: "Off" }, 2, 4, 8],
|
||||
disable: [
|
||||
{ when: { nodes: ["multi-2"] },
|
||||
reason: "Prefill Context Parallel is single-machine only (SGLang asserts tp_size <= 8; cross-machine CP has precision issues)." },
|
||||
] },
|
||||
{ id: "dpAttn", label: "DP-Attention",
|
||||
values: [
|
||||
null,
|
||||
|
||||
@@ -120,8 +120,9 @@ sgl-eval run gsm8k \\
|
||||
|
||||
// M.1 is global-attention (no SWA); expose TP + DP-Attention here. No CP: the default
|
||||
// trtllm_mha backend has no CP-aware KV-store (crashes), and the engine's built-in attention CP
|
||||
// knob emits NSA flags (--enable-nsa-prefill-context-parallel) that apply to DeepSeek-family
|
||||
// models, not M.1. (CP works only via the fa3 backend, which is Hopper SM90 — left out here.)
|
||||
// knob emits prefill-CP flags (--enable-prefill-cp / --cp-strategy / --attn-cp-size) that apply
|
||||
// to DeepSeek-family models, not M.1. (CP works only via the fa3 backend, which is Hopper
|
||||
// SM90 — left out here.)
|
||||
// DP-Attention: VERIFIED functionally correct on 8×B200 BF16 (GSM8K 0.94, identical to the TP
|
||||
// baseline) but ~15–28% slower on this GQA model (8 KV heads). Playground experiment only —
|
||||
// deliberately NOT in the shipped Balanced recipe.
|
||||
|
||||
@@ -109,6 +109,10 @@ sgl-eval run aime26 \\
|
||||
playgroundFeatures: {
|
||||
|
||||
// ----- Card 1: "Attention Parallelism" -----
|
||||
// No CP knob: prefill Context Parallel needs model-side integration in
|
||||
// SGLang (DeepSeek-family / Qwen-MoE / Mellum have it) and HYV3ForCausalLM
|
||||
// has none — the engine's CP knob would emit --enable-prefill-cp flags
|
||||
// that don't work on this model.
|
||||
attention: {
|
||||
knobs: [
|
||||
{ id: "tp", label: "TP", values: [
|
||||
@@ -120,7 +124,6 @@ sgl-eval run aime26 \\
|
||||
{ value: 16, disable: { nodes: ["single"] },
|
||||
disableReason: "TP=16 requires 16 ranks — switch the Deploy panel's Nodes to Multi-Nodes first." },
|
||||
]},
|
||||
{ id: "cp", label: "CP", values: [null, 1, 2, 4] },
|
||||
{ id: "dpAttn", label: "DP-Attention",
|
||||
values: [
|
||||
null,
|
||||
|
||||
@@ -107,14 +107,39 @@ sgl-eval run aime25 \\
|
||||
|
||||
// ----- Card 1: "Attention Parallelism" -----
|
||||
// DSA prefill Context Parallelism (CP) splits the long-prefill attention across
|
||||
// `cp` ranks — verified on Hopper (H200). On Blackwell the DSA-CP FP8 rope kernel
|
||||
// is not yet adapted, so keep CP off there for now.
|
||||
// `cp` ranks — runs on Hopper (H200) and Blackwell (B200/GB300/B300).
|
||||
// CP sizes auto-gate in the engine to the runtime derivation
|
||||
// attn_cp_size = tp/dp (a user-passed --attn-cp-size is overridden).
|
||||
// CP is single-machine only (tp_size <= 8). Interleave CP + DP-Attention
|
||||
// currently fails the runtime's dp_size == 1 assert but is allowed here
|
||||
// with a warning (combined support is planned upstream).
|
||||
// Strategy knob: interleave (ex round-robin-split) is the layout verified
|
||||
// here and the default; zigzag (ex in-seq-split) is exposed as an
|
||||
// experiment — the runtime auto-configures deepep + ep=tp for it and
|
||||
// restricts it to batch_size=1 (long-context single-request runs).
|
||||
attention: {
|
||||
knobs: [
|
||||
{ id: "tp", label: "TP", values: [null, 4, 8] },
|
||||
{ id: "cp", label: "CP (DSA prefill)", values: [null, 1, 2, 4, 8],
|
||||
disable: { hw: ["b200", "gb300", "b300", "mi355x", "mi325x", "mi300x"] },
|
||||
disableReason: "DSA prefill Context Parallel is verified on Hopper (H200); the Blackwell sm100 DSA-CP FP8 rope kernel is not yet adapted, and the ROCm DSA-CP path is not yet validated on AMD (MI300X/MI325X/MI355X)." },
|
||||
{ id: "cp", label: "CP (DSA prefill)",
|
||||
values: [null, { value: 1, label: "Off" }, 4, 8],
|
||||
disable: [
|
||||
{ when: { hw: ["mi355x", "mi325x", "mi300x"] },
|
||||
reason: "The ROCm DSA-CP path is not yet validated on AMD (MI300X/MI325X/MI355X) — keep CP off there for now." },
|
||||
{ when: { nodes: ["multi-2"] },
|
||||
reason: "Prefill Context Parallel is single-machine only (SGLang asserts tp_size <= 8; cross-machine CP has precision issues)." },
|
||||
] },
|
||||
{ id: "cpStrategy", label: "CP Strategy",
|
||||
values: [
|
||||
null,
|
||||
"interleave",
|
||||
{ value: "zigzag", label: "zigzag (experimental)" },
|
||||
],
|
||||
disable: [
|
||||
{ when: { hw: ["mi355x", "mi325x", "mi300x"] },
|
||||
reason: "The ROCm DSA-CP path is not yet validated on AMD (MI300X/MI325X/MI355X) — keep CP off there for now." },
|
||||
{ when: { nodes: ["multi-2"] },
|
||||
reason: "Prefill Context Parallel is single-machine only (SGLang asserts tp_size <= 8; cross-machine CP has precision issues)." },
|
||||
] },
|
||||
{ id: "dpAttn", label: "DP-Attention",
|
||||
values: [null, false, 4, 8],
|
||||
labels: { "auto": "Auto", "false": "Off" } },
|
||||
|
||||
Reference in New Issue
Block a user