Guard large unaligned Qwen expert gathers

This commit is contained in:
Georg Bauer committed 2026-10-04 15:18:26 +02:00
1 parent 57b8ac28a7
commit 990abb4755
22 files changed
+951 -3

No files matched your search

@@ -32,6 +32,8 @@ struct Manifest {
#[serde(default)]
verify_gate: bool,
#[serde(default)]
sorted_gather_guard: bool,
#[serde(default)]
images: Vec<crate::engine::qwen_vision::Identity>,
tokens: Vec<i32>,
lazy_kv: bool,
@@ -119,6 +121,7 @@ impl LiveSession {
let offset = file.stream_position().map_err(|e| e.to_string())?;
let manifest = serde_json::to_vec(&Manifest {
verify_gate: true,
sorted_gather_guard: true,
identity,
tag,
images: self.current_images.clone(),
@@ -193,6 +196,14 @@ impl LiveSession {
"Qwen checkpoint uses obsolete MTP attention-gate math; rebuild the cache".into(),
);
}
// Old files do not record prefill chunk sizes or producer hardware.
// At ten routes/token this length can contain an affected chunk;
// shorter sessions cannot. Reject before allocating/restoring state.
if !manifest.sorted_gather_guard && manifest.tokens.len() >= 3277 {
return Err(
"Qwen checkpoint predates the sorted-gather row guard; rebuild the cache".into(),
);
}
if !manifest.payload["mtp_history_snapshot"].is_null() != identity.mtp {
return Err("Qwen checkpoint MTP payload differs".into());
}
@@ -480,6 +491,40 @@ fn qwen_checkpoint_roundtrip_and_failed_restore_preserve_live_state() {
.unwrap()
);
assert_eq!(fingerprint(&live), ar_original);
let bytes = fs::read(&path).unwrap();
let offset = u64::from_le_bytes(bytes[8..16].try_into().unwrap()) as usize;
let mut manifest: serde_json::Value = serde_json::from_slice(&bytes[offset..]).unwrap();
manifest
.as_object_mut()
.unwrap()
.remove("sorted_gather_guard");
let write_manifest = |manifest: &serde_json::Value| {
let mut old = bytes[..offset].to_vec();
old.extend(serde_json::to_vec(manifest).unwrap());
fs::write(&broken, old).unwrap();
};
write_manifest(&manifest);
assert!(
live.load_checkpoint(&broken, ar_identity, &streams, stream, |_| {}, || false)
.unwrap()
);
assert_eq!(fingerprint(&live), ar_original);
manifest["tokens"] = serde_json::json!(vec![1; 3277]);
manifest["identity"]["context"] = 4096.into();
write_manifest(&manifest);
let large_identity = Identity {
context: 4096,
..ar_identity
};
assert!(
live.load_checkpoint(&broken, large_identity, &streams, stream, |_| {}, || false)
.unwrap_err()
.contains("sorted-gather row guard")
);
assert_eq!(fingerprint(&live), ar_original);
println!(
"Qwen sorted-gather checkpoint policy: old short state exact, possibly affected long state rejected before restore"
);
println!(
"Qwen checkpoint: exact roundtrip, cancellation/corruption controls, old MTP rejected and old AR restored"
);