Skip to content
9 changes: 8 additions & 1 deletion crates/core/src/perfmodel/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,10 @@ pub const ENGINE_CONFIG_SCHEMA_VERSION: u32 = 1;
// - 20 (DeepSeek-V4.1 FPM): FpmForwardOp gained original_fmha_quant_mode
// for selector diagnostics. This appends a positional field after the schema-19
// release; serde defaults support legacy JSON, not legacy bincode.
pub const ENGINE_SPEC_SCHEMA_VERSION: u32 = 20;
// - 21 (AIC-1781): EngineConfig and MoeOp gained exact `moe_kernel_source`
// identity. Renumbered from the branch's concurrent v20 claim after the
// DeepSeek-V4.1 FPM layout landed first.
pub const ENGINE_SPEC_SCHEMA_VERSION: u32 = 21;

/// Static engine identity and setup information carried by an
/// [`crate::perfmodel::engine::spec::EngineSpec`].
Expand Down Expand Up @@ -152,6 +155,10 @@ pub struct EngineConfig {
/// Use the backend-verified bounded DeepSeek-V4.1 decoder execution profile.
#[serde(default)]
pub decoder_replay: bool,
/// Exact collected MoE compute kernel-source lane. Unlike
/// `moe_backend`, this selects one measured MoE table lane.
#[serde(default)]
pub moe_kernel_source: Option<String>,

// KV
pub kv_block_size: Option<u32>,
Expand Down
1 change: 1 addition & 0 deletions crates/core/src/perfmodel/engine/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2323,6 +2323,7 @@ mod tests {
forward_model: None,
fpm_parquet_path: None,
decoder_replay: false,
moe_kernel_source: None,
kv_block_size: None,
parallel: ParallelMapping {
tp_size: 8,
Expand Down
28 changes: 28 additions & 0 deletions crates/core/src/perfmodel/engine/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ mod tests {
workload_distribution: "power_law_1.2".into(),
is_gated: true,
moe_backend: None,
moe_kernel_source: Some("sglang_flashinfer_trtllm_moe".into()),
enable_eplb: false,
is_context: false,
}
Expand Down Expand Up @@ -827,6 +828,7 @@ mod tests {
forward_model: None,
fpm_parquet_path: None,
decoder_replay: false,
moe_kernel_source: None,
kv_block_size: Some(64),
parallel: ParallelMapping {
tp_size: 8,
Expand Down Expand Up @@ -986,6 +988,32 @@ mod tests {
assert_eq!(spec, decoded);
}

#[test]
fn moe_and_fpm_fields_round_trip_and_reject_concurrent_schema20() {
// These independent positional fields were both introduced as schema20
// on separate branches. The merged wire layout needs its own version.
let spec = EngineSpec::new(
sample_engine_config(),
vec![OpSpec::FpmForward(fpm_forward())],
vec![OpSpec::Moe(moe())],
);
assert_eq!(spec.schema_version, 21);
let mut bytes = spec.to_bincode().unwrap();
assert_eq!(EngineSpec::from_bincode(&bytes).unwrap(), spec);

bytes[..4].copy_from_slice(&20u32.to_le_bytes());
// Reject the stale version before even trying to decode its payload.
bytes.truncate(4);
assert!(matches!(
EngineSpec::from_bincode(&bytes),
Err(AicError::UnsupportedSchemaVersion {
kind: "EngineSpec",
got: 20,
expected: ENGINE_SPEC_SCHEMA_VERSION,
})
));
}

/// A version skew combined with an op-layout change must surface as a clear
/// [`AicError::UnsupportedSchemaVersion`], NOT a generic bincode I/O error.
///
Expand Down
73 changes: 72 additions & 1 deletion crates/core/src/perfmodel/fpm/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ pub struct ForwardPassPerfModelConfig {
pub estimator_config: EstimatorConfig,
#[serde(default)]
pub attention_backend: Option<String>,
/// Exact collected MoE compute kernel-source lane. This is separate from
/// `moe_backend`, which describes topology/runtime behavior.
#[serde(default)]
pub moe_kernel_source: Option<String>,
#[serde(default)]
pub moe_backend: Option<String>,
#[serde(default)]
Expand Down Expand Up @@ -189,6 +193,7 @@ impl ForwardPassPerfModelConfig {
moe_backend: None,
enable_eplb: false,
wideep_num_slots: None,
moe_kernel_source: None,
enable_shared_layer: None,
strict_provenance: false,
}
Expand Down Expand Up @@ -231,6 +236,13 @@ impl ForwardPassPerfModelConfig {
{
return Err(invalid_config("backend_version cannot be empty"));
}
if self
.moe_kernel_source
.as_ref()
.is_some_and(|value| value.trim().is_empty())
{
return Err(invalid_config("moe_kernel_source cannot be empty"));
}
if self.tp == 0 || self.pp == 0 || self.attention_dp == 0 {
return Err(invalid_config("tp, pp, and attention_dp must be positive"));
}
Expand Down Expand Up @@ -268,14 +280,15 @@ impl ForwardPassPerfModelConfig {
}
if self.estimation_mode == EstimationMode::FpmInterpolation
&& (self.enable_eplb
|| self.moe_kernel_source.is_some()
|| self.wideep_num_slots.is_some()
|| self
.moe_backend
.as_deref()
.is_some_and(|value| value != "default"))
{
return Err(invalid_config(
"fpm_interpolation does not support EPLB, slots or moe_backend overrides",
"fpm_interpolation does not support EPLB, slots, moe_backend or moe_kernel_source overrides",
));
}
if self.nextn > 5 {
Expand Down Expand Up @@ -416,6 +429,64 @@ mod tests {
cfg.validate().unwrap();
}

#[test]
fn moe_kernel_source_round_trips_without_pinning_the_default() {
let default = config(serde_json::json!({}));
assert_eq!(default.moe_kernel_source, None);

let pinned = config(serde_json::json!({
"moe_kernel_source": "sglang_flashinfer_trtllm_moe"
}));
let round_trip: ForwardPassPerfModelConfig =
serde_json::from_str(&serde_json::to_string(&pinned).unwrap()).unwrap();
assert_eq!(round_trip, pinned);
}

#[test]
fn interpolation_rejects_exact_moe_sources_before_fallback() {
for fallback in ["deny", "allow"] {
for fpm_selector in [None, Some("fp8")] {
let cfg = config(serde_json::json!({
"estimation_mode": "fpm_interpolation",
"fallback_policy": fallback,
"moe_kernel_source": "source_that_does_not_exist",
"fpm_fmha_quant_mode": fpm_selector
}));
let error = crate::ForwardPassPerfModel::best_available(cfg).unwrap_err();
assert!(error.to_string().contains("moe_kernel_source overrides"));
}
}
config(serde_json::json!({"estimation_mode": "fpm_interpolation"}))
.validate()
.unwrap();
config(serde_json::json!({"moe_kernel_source": "sglang_flashinfer_trtllm_moe"}))
.validate()
.unwrap();
}

#[test]
fn blank_moe_kernel_source_is_rejected_before_estimator_selection() {
for source in ["", " \t"] {
let error = config(serde_json::json!({"moe_kernel_source": source}))
.validate()
.unwrap_err();
assert_eq!(
error.to_string(),
"invalid engine config: invalid forward pass perf model config: moe_kernel_source cannot be empty"
);
}

for estimation_mode in [EstimationMode::Auto, EstimationMode::FpmRegression] {
let mut cfg = config(serde_json::json!({"moe_kernel_source": " \t"}));
cfg.estimation_mode = estimation_mode;
let error = crate::ForwardPassPerfModel::best_available(cfg).unwrap_err();
assert_eq!(
error.to_string(),
"invalid engine config: invalid forward pass perf model config: moe_kernel_source cannot be empty"
);
}
}

#[test]
fn prompt_lookup_is_distinct_from_mtp_and_validated_before_selection() {
let mut cfg = config(serde_json::json!({
Expand Down
18 changes: 17 additions & 1 deletion crates/core/src/perfmodel/fpm/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -705,14 +705,15 @@ fn build_native_candidate(
) -> Result<(Engine, PathBuf), AicError> {
if config.estimation_mode == EstimationMode::FpmInterpolation
&& (config.enable_eplb
|| config.moe_kernel_source.is_some()
|| config.wideep_num_slots.is_some()
|| config
.moe_backend
.as_deref()
.is_some_and(|value| value != "default"))
{
return Err(AicError::UnsupportedModel(
"FPM interpolation does not support EPLB, slots or moe_backend overrides".into(),
"FPM interpolation does not support EPLB, slots, moe_backend or moe_kernel_source overrides".into(),
));
}
if config.estimation_mode == EstimationMode::FpmInterpolation && config.nextn != 0 {
Expand Down Expand Up @@ -1115,6 +1116,21 @@ fn can_fallback_to_regression(err: &AicError) -> bool {
mod fallback_errors {
use super::*;

#[cfg(feature = "python")]
#[test]
fn interpolation_candidate_does_not_discard_exact_moe_source() {
let mut config = ForwardPassPerfModelConfig::new(
"model",
"system",
crate::BackendKind::Sglang,
crate::ForwardPassWorkerType::Decode,
);
config.moe_kernel_source = Some("source_that_does_not_exist".into());
config.estimation_mode = EstimationMode::FpmInterpolation;
let error = build_native_candidate(&config).err().unwrap();
assert!(error.to_string().contains("moe_kernel_source overrides"));
}

#[test]
fn corruption_is_not_a_coverage_gap() {
let yaml = serde_yaml::from_str::<serde_yaml::Value>("broken: [").unwrap_err();
Expand Down
1 change: 1 addition & 0 deletions crates/core/src/perfmodel/fpm/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ fn fixture_engine_config() -> EngineConfig {
forward_model: None,
fpm_parquet_path: None,
decoder_replay: false,
moe_kernel_source: None,
kv_block_size: None,
parallel: ParallelMapping {
tp_size: 8,
Expand Down
1 change: 1 addition & 0 deletions crates/core/src/perfmodel/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@ mod tests {
forward_model: None,
fpm_parquet_path: None,
decoder_replay: false,
moe_kernel_source: None,
kv_block_size: None,
parallel: ParallelMapping {
tp_size: 1,
Expand Down
6 changes: 3 additions & 3 deletions crates/core/src/perfmodel/operators/attention.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ pub struct ContextAttentionOp {
/// lanes, density-ranked donor tiers, `"default"`, and the table's own
/// leftover lanes — and it is REPLAYED VERBATIM here: no re-deriving, no
/// extending, no sorting. Appended at the struct TAIL because bincode
/// payloads are positional (current ENGINE_SPEC_SCHEMA_VERSION 20).
/// payloads are positional (current ENGINE_SPEC_SCHEMA_VERSION 21).
#[serde(default = "default_lane_order")]
pub lane_order: Vec<String>,
/// Whether the fused prefill kernel applies rotary position embeddings.
Expand All @@ -197,7 +197,7 @@ pub struct ContextAttentionOp {

/// Lane precedence for ops built without an explicit order (Rust-side
/// constructors and hand-written JSON fixtures predating the `lane_order`
/// field — introduced at schema v8, current ENGINE_SPEC_SCHEMA_VERSION 20).
/// field — introduced at schema v8, current ENGINE_SPEC_SCHEMA_VERSION 21).
/// Mirrors the Python fallback in `_attention_lane_order` for an
/// unresolvable database: the always-valid `("default",)`.
pub(crate) fn default_lane_order() -> Vec<String> {
Expand Down Expand Up @@ -385,7 +385,7 @@ pub struct GenerationAttentionOp {
pub kv_cache_dtype: KvCacheQuantMode,
/// Kernel-source lane precedence; see
/// [`ContextAttentionOp::lane_order`] (appended at the struct TAIL —
/// bincode payloads are positional, current ENGINE_SPEC_SCHEMA_VERSION 20).
/// bincode payloads are positional, current ENGINE_SPEC_SCHEMA_VERSION 21).
#[serde(default = "default_lane_order")]
pub lane_order: Vec<String>,
/// Per-head RMSNorm on Q and K before decode attention. Appended at the
Expand Down
1 change: 1 addition & 0 deletions crates/core/src/perfmodel/operators/fpm_sol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,7 @@ mod tests {
workload_distribution: "uniform".into(),
is_gated: true,
moe_backend: None,
moe_kernel_source: None,
enable_eplb: false,
is_context: true,
};
Expand Down
Loading
Loading