@@ -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>