Add native Qwen MTP speculation
This commit is contained in:
@@ -2492,15 +2492,15 @@ mtp.safetensors mtp.layers.0.mlp.shared_expert.up_proj.weight U32 640x320 4 64 a
|
|||||||
mtp.safetensors mtp.layers.0.mlp.shared_expert_gate.biases BF16 1x40 4 64 affine 140045196 140045276
|
mtp.safetensors mtp.layers.0.mlp.shared_expert_gate.biases BF16 1x40 4 64 affine 140045196 140045276
|
||||||
mtp.safetensors mtp.layers.0.mlp.shared_expert_gate.scales BF16 1x40 4 64 affine 140045276 140045356
|
mtp.safetensors mtp.layers.0.mlp.shared_expert_gate.scales BF16 1x40 4 64 affine 140045276 140045356
|
||||||
mtp.safetensors mtp.layers.0.mlp.shared_expert_gate.weight U32 1x320 4 64 affine 620915756 620917036
|
mtp.safetensors mtp.layers.0.mlp.shared_expert_gate.weight U32 1x320 4 64 affine 620915756 620917036
|
||||||
mtp.safetensors mtp.layers.0.mlp.switch_mlp.down_proj.biases BF16 512x2560x20 2 64 affine 140966956 193395756
|
mtp.safetensors mtp.layers.0.mlp.switch_mlp.down_proj.biases BF16 512x2560x20 4 32 affine 140966956 193395756
|
||||||
mtp.safetensors mtp.layers.0.mlp.switch_mlp.down_proj.scales BF16 512x2560x20 2 64 affine 641468716 693897516
|
mtp.safetensors mtp.layers.0.mlp.switch_mlp.down_proj.scales BF16 512x2560x20 4 32 affine 641468716 693897516
|
||||||
mtp.safetensors mtp.layers.0.mlp.switch_mlp.down_proj.weight U32 512x2560x80 2 64 affine 1253145132 1672575532
|
mtp.safetensors mtp.layers.0.mlp.switch_mlp.down_proj.weight U32 512x2560x80 4 32 affine 1253145132 1672575532
|
||||||
mtp.safetensors mtp.layers.0.mlp.switch_mlp.gate_proj.biases BF16 512x640x80 2 64 affine 693897772 746326572
|
mtp.safetensors mtp.layers.0.mlp.switch_mlp.gate_proj.biases BF16 512x640x80 4 32 affine 693897772 746326572
|
||||||
mtp.safetensors mtp.layers.0.mlp.switch_mlp.gate_proj.scales BF16 512x640x80 2 64 affine 20277900 72706700
|
mtp.safetensors mtp.layers.0.mlp.switch_mlp.gate_proj.scales BF16 512x640x80 4 32 affine 20277900 72706700
|
||||||
mtp.safetensors mtp.layers.0.mlp.switch_mlp.gate_proj.weight U32 512x640x320 2 64 affine 760314412 1179744812
|
mtp.safetensors mtp.layers.0.mlp.switch_mlp.gate_proj.weight U32 512x640x320 4 32 affine 760314412 1179744812
|
||||||
mtp.safetensors mtp.layers.0.mlp.switch_mlp.up_proj.biases BF16 512x640x80 2 64 affine 87616396 140045196
|
mtp.safetensors mtp.layers.0.mlp.switch_mlp.up_proj.biases BF16 512x640x80 4 32 affine 87616396 140045196
|
||||||
mtp.safetensors mtp.layers.0.mlp.switch_mlp.up_proj.scales BF16 512x640x80 2 64 affine 1192852012 1245280812
|
mtp.safetensors mtp.layers.0.mlp.switch_mlp.up_proj.scales BF16 512x640x80 4 32 affine 1192852012 1245280812
|
||||||
mtp.safetensors mtp.layers.0.mlp.switch_mlp.up_proj.weight U32 512x640x320 2 64 affine 194808876 614239276
|
mtp.safetensors mtp.layers.0.mlp.switch_mlp.up_proj.weight U32 512x640x320 4 32 affine 194808876 614239276
|
||||||
mtp.safetensors mtp.layers.0.mlp_hyper_connection.block_inject_weight.weight BF16 4x10240 74345100 74427020
|
mtp.safetensors mtp.layers.0.mlp_hyper_connection.block_inject_weight.weight BF16 4x10240 74345100 74427020
|
||||||
mtp.safetensors mtp.layers.0.mlp_hyper_connection.hc_norm.weight BF16 10240 752921132 752941612
|
mtp.safetensors mtp.layers.0.mlp_hyper_connection.hc_norm.weight BF16 10240 752921132 752941612
|
||||||
mtp.safetensors mtp.layers.0.mlp_hyper_connection.input_mix_weight_down.weight BF16 320x10240 81062540 87616140
|
mtp.safetensors mtp.layers.0.mlp_hyper_connection.input_mix_weight_down.weight BF16 320x10240 81062540 87616140
|
||||||
|
|||||||
|
@@ -11,7 +11,7 @@
|
|||||||
"converter": "qwen38-artifacts-v1-identity"
|
"converter": "qwen38-artifacts-v1-identity"
|
||||||
},
|
},
|
||||||
"tensor_inventory": "qwen38-flash-next-bare-speed-tensors.tsv",
|
"tensor_inventory": "qwen38-flash-next-bare-speed-tensors.tsv",
|
||||||
"tensor_inventory_sha256": "369cdc7e53c09eaa5eff5cdcc49f0d0eab0ddc175f34965ba2ed1be2673d5858",
|
"tensor_inventory_sha256": "b5731e6febcf865d276a0e7b144da02375f2d7e3129594f271ee9c6c351f4c8f",
|
||||||
"config": {
|
"config": {
|
||||||
"/architectures/0": "Qwen4ExpForConditionalGeneration",
|
"/architectures/0": "Qwen4ExpForConditionalGeneration",
|
||||||
"/model_type": "qwen4_exp",
|
"/model_type": "qwen4_exp",
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
{"class":"qsa","file":"model-00016-of-00017.safetensors","tensor":"language_model.model.layers.11.self_attn.indexer.index_qk_proj.weight","row":0,"values":64,"bits":8,"group_size":64,"sha256":"c803ea77148621a5d6dfa4060a52c75f57185234a0bec4771671f69336bb8346"},
|
{"class":"qsa","file":"model-00016-of-00017.safetensors","tensor":"language_model.model.layers.11.self_attn.indexer.index_qk_proj.weight","row":0,"values":64,"bits":8,"group_size":64,"sha256":"c803ea77148621a5d6dfa4060a52c75f57185234a0bec4771671f69336bb8346"},
|
||||||
{"class":"gdn","file":"model-00016-of-00017.safetensors","tensor":"language_model.model.layers.0.linear_attn.A_log","row":0,"values":48,"sha256":"88c53a2a04bda1d96ee1ade6fa7dfa9c49d3245b1cc1e83673f02b7a36e07a85"},
|
{"class":"gdn","file":"model-00016-of-00017.safetensors","tensor":"language_model.model.layers.0.linear_attn.A_log","row":0,"values":48,"sha256":"88c53a2a04bda1d96ee1ade6fa7dfa9c49d3245b1cc1e83673f02b7a36e07a85"},
|
||||||
{"class":"ple","file":"ngram-table.safetensors","tensor":"ngram.weight","row":0,"values":64,"bits":4,"group_size":32,"sha256":"2243dd9766046bb80d98e3baf5e59ba246958cfe4f340ebce8c1fff57a2810d9"},
|
{"class":"ple","file":"ngram-table.safetensors","tensor":"ngram.weight","row":0,"values":64,"bits":4,"group_size":32,"sha256":"2243dd9766046bb80d98e3baf5e59ba246958cfe4f340ebce8c1fff57a2810d9"},
|
||||||
{"class":"mtp","file":"mtp.safetensors","tensor":"mtp.layers.0.mlp.switch_mlp.gate_proj.weight","row":0,"values":64,"bits":2,"group_size":64,"sha256":"6293cce5e1b25a4f35cb8af46e6e13b8378ec4ca84ec7f68859066dda48049cc"}
|
{"class":"mtp","file":"mtp.safetensors","tensor":"mtp.layers.0.mlp.switch_mlp.gate_proj.weight","row":0,"values":64,"bits":4,"group_size":32,"sha256":"393d82675ae7e25275243fd4907d3894146ac1c5662b601aae998c489d10af5c"}
|
||||||
],
|
],
|
||||||
"files": [
|
"files": [
|
||||||
{"path":"model-00001-of-00017.safetensors","role":"core","size":4666167150,"sha256":"a27232c9434b9d8961f198cf36f44e228b3f23a5f8d874ba4ca8af32a1b23ffc"},
|
{"path":"model-00001-of-00017.safetensors","role":"core","size":4666167150,"sha256":"a27232c9434b9d8961f198cf36f44e228b3f23a5f8d874ba4ca8af32a1b23ffc"},
|
||||||
|
|||||||
@@ -865,16 +865,22 @@ impl App {
|
|||||||
self.preference_error = None;
|
self.preference_error = None;
|
||||||
}
|
}
|
||||||
Message::PreferenceGlmMtpChanged(value) => {
|
Message::PreferenceGlmMtpChanged(value) => {
|
||||||
self.preference_draft.glm_mtp =
|
self.preference_draft.glm_mtp = self
|
||||||
self.preference_draft.acceleration_model.supports_glm_mtp() && value;
|
.preference_draft
|
||||||
|
.acceleration_model
|
||||||
|
.supports_integrated_mtp()
|
||||||
|
&& value;
|
||||||
if !self.preference_draft.glm_mtp {
|
if !self.preference_draft.glm_mtp {
|
||||||
self.preference_draft.glm_mtp_timing = false;
|
self.preference_draft.glm_mtp_timing = false;
|
||||||
}
|
}
|
||||||
self.preference_error = None;
|
self.preference_error = None;
|
||||||
}
|
}
|
||||||
Message::PreferenceGlmMtpTimingChanged(value) => {
|
Message::PreferenceGlmMtpTimingChanged(value) => {
|
||||||
self.preference_draft.glm_mtp_timing =
|
self.preference_draft.glm_mtp_timing = self
|
||||||
self.preference_draft.acceleration_model.supports_glm_mtp() && value;
|
.preference_draft
|
||||||
|
.acceleration_model
|
||||||
|
.supports_integrated_mtp()
|
||||||
|
&& value;
|
||||||
if self.preference_draft.glm_mtp_timing {
|
if self.preference_draft.glm_mtp_timing {
|
||||||
self.preference_draft.glm_mtp = true;
|
self.preference_draft.glm_mtp = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,12 +17,12 @@ impl App {
|
|||||||
let glm_mtp_toggle: Option<fn(bool) -> Message> = self
|
let glm_mtp_toggle: Option<fn(bool) -> Message> = self
|
||||||
.preference_draft
|
.preference_draft
|
||||||
.acceleration_model
|
.acceleration_model
|
||||||
.supports_glm_mtp()
|
.supports_integrated_mtp()
|
||||||
.then_some(Message::PreferenceGlmMtpChanged);
|
.then_some(Message::PreferenceGlmMtpChanged);
|
||||||
let glm_mtp_timing_toggle: Option<fn(bool) -> Message> = self
|
let glm_mtp_timing_toggle: Option<fn(bool) -> Message> = self
|
||||||
.preference_draft
|
.preference_draft
|
||||||
.acceleration_model
|
.acceleration_model
|
||||||
.supports_glm_mtp()
|
.supports_integrated_mtp()
|
||||||
.then_some(Message::PreferenceGlmMtpTimingChanged);
|
.then_some(Message::PreferenceGlmMtpTimingChanged);
|
||||||
let keep_vision_loaded_toggle: Option<fn(bool) -> Message> =
|
let keep_vision_loaded_toggle: Option<fn(bool) -> Message> =
|
||||||
(self.preference_draft.acceleration_model == ModelChoice::Glm53Flash)
|
(self.preference_draft.acceleration_model == ModelChoice::Glm53Flash)
|
||||||
@@ -608,13 +608,13 @@ impl App {
|
|||||||
text("SPECULATIVE DECODING").size(11).color(muted_text()),
|
text("SPECULATIVE DECODING").size(11).color(muted_text()),
|
||||||
hint(
|
hint(
|
||||||
toggle(self.preference_draft.glm_mtp)
|
toggle(self.preference_draft.glm_mtp)
|
||||||
.label("Enable integrated GLM MTP")
|
.label("Enable integrated MTP")
|
||||||
.on_toggle_maybe(glm_mtp_toggle),
|
.on_toggle_maybe(glm_mtp_toggle),
|
||||||
"Uses the prediction head built into GLM for speculative decoding, so no separate draft model is loaded.",
|
"Uses the selected model's managed prediction head for speculative decoding. Qwen loads its pinned MTP sidecar; GLM uses its embedded head.",
|
||||||
),
|
),
|
||||||
hint(
|
hint(
|
||||||
toggle(self.preference_draft.glm_mtp_timing)
|
toggle(self.preference_draft.glm_mtp_timing)
|
||||||
.label("Log GLM MTP timing counters")
|
.label("Log MTP timing counters")
|
||||||
.on_toggle_maybe(glm_mtp_timing_toggle),
|
.on_toggle_maybe(glm_mtp_timing_toggle),
|
||||||
"Records per-stage timings of the speculative path to the log, to show where the acceleration actually goes. A diagnostic aid that costs a little throughput.",
|
"Records per-stage timings of the speculative path to the log, to show where the acceleration actually goes. A diagnostic aid that costs a little throughput.",
|
||||||
),
|
),
|
||||||
@@ -644,8 +644,8 @@ impl App {
|
|||||||
),
|
),
|
||||||
text(if self.preference_draft.acceleration_model.supports_dspark() {
|
text(if self.preference_draft.acceleration_model.supports_dspark() {
|
||||||
"DeepSeek V4 Flash 0731 uses its managed DSpark support artifact."
|
"DeepSeek V4 Flash 0731 uses its managed DSpark support artifact."
|
||||||
} else if self.preference_draft.acceleration_model.supports_glm_mtp() {
|
} else if self.preference_draft.acceleration_model.supports_integrated_mtp() {
|
||||||
"GLM MTP is integrated; DSpark is unavailable for this model."
|
"Integrated MTP is available; DSpark is unavailable for this model."
|
||||||
} else {
|
} else {
|
||||||
"No speculative-decoding support is available for this model."
|
"No speculative-decoding support is available for this model."
|
||||||
})
|
})
|
||||||
@@ -656,7 +656,7 @@ impl App {
|
|||||||
|engine| {
|
|engine| {
|
||||||
let settings = engine.speculative;
|
let settings = engine.speculative;
|
||||||
format!(
|
format!(
|
||||||
"Engine: GLM MTP {} • timing {} • DSpark {} • confidence {}{} • target-only {} • exact sampling {}",
|
"Engine: integrated MTP {} • timing {} • DSpark {} • confidence {}{} • target-only {} • exact sampling {}",
|
||||||
if settings.glm_mtp { "on" } else { "off" },
|
if settings.glm_mtp { "on" } else { "off" },
|
||||||
if settings.glm_mtp_timing { "on" } else { "off" },
|
if settings.glm_mtp_timing { "on" } else { "off" },
|
||||||
if settings.dspark { "on" } else { "off" },
|
if settings.dspark { "on" } else { "off" },
|
||||||
|
|||||||
@@ -369,6 +369,7 @@ impl App {
|
|||||||
match stats.speculative_mode {
|
match stats.speculative_mode {
|
||||||
2 => "DSpark",
|
2 => "DSpark",
|
||||||
3 => "GLM MTP",
|
3 => "GLM MTP",
|
||||||
|
4 => "Qwen MTP",
|
||||||
_ => "Off",
|
_ => "Off",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -376,11 +377,27 @@ impl App {
|
|||||||
metric_row("Drafted", format_count(stats.drafted_tokens)),
|
metric_row("Drafted", format_count(stats.drafted_tokens)),
|
||||||
metric_row("Accepted", format_count(stats.accepted_draft_tokens)),
|
metric_row("Accepted", format_count(stats.accepted_draft_tokens)),
|
||||||
metric_row("Acceptance", format!("{:.1}%", acceptance * 100.0)),
|
metric_row("Acceptance", format!("{:.1}%", acceptance * 100.0)),
|
||||||
|
metric_row(
|
||||||
|
"Mean accepted depth",
|
||||||
|
if stats.speculative_cycles == 0 {
|
||||||
|
"0.00".into()
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
"{:.2}",
|
||||||
|
stats.accepted_depth_total as f64 / stats.speculative_cycles as f64
|
||||||
|
)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
metric_row(
|
||||||
|
"Maximum accepted depth",
|
||||||
|
format_count(stats.accepted_depth_max)
|
||||||
|
),
|
||||||
metric_row(
|
metric_row(
|
||||||
"Target verifier passes",
|
"Target verifier passes",
|
||||||
format_count(stats.verifier_passes)
|
format_count(stats.verifier_passes)
|
||||||
),
|
),
|
||||||
metric_row("Verifier wall time", format_milliseconds(stats.verifier_ms)),
|
metric_row("Verifier wall time", format_milliseconds(stats.verifier_ms)),
|
||||||
|
metric_row("Repair wall time", format_milliseconds(stats.repair_ms)),
|
||||||
metric_row(
|
metric_row(
|
||||||
"Effective target-pass speedup",
|
"Effective target-pass speedup",
|
||||||
format!("{effective_speedup:.2}×")
|
format!("{effective_speedup:.2}×")
|
||||||
|
|||||||
108
src/engine.rs
108
src/engine.rs
@@ -329,9 +329,13 @@ impl LoadedModel {
|
|||||||
)?;
|
)?;
|
||||||
let context = u32::try_from(settings.context_tokens)
|
let context = u32::try_from(settings.context_tokens)
|
||||||
.map_err(|_| "Qwen context must be a positive whole number")?;
|
.map_err(|_| "Qwen context must be a positive whole number")?;
|
||||||
qwen::QwenModel::open(&settings.artifacts.model, context)
|
qwen::QwenModel::open_configured(
|
||||||
.map(Box::new)
|
&settings.artifacts.model,
|
||||||
.map(Self::Qwen)
|
context,
|
||||||
|
settings.speculative.glm_mtp,
|
||||||
|
)
|
||||||
|
.map(Box::new)
|
||||||
|
.map(Self::Qwen)
|
||||||
} else {
|
} else {
|
||||||
Model::open(settings).map(Box::new).map(Self::Gguf)
|
Model::open(settings).map(Box::new).map(Self::Gguf)
|
||||||
}
|
}
|
||||||
@@ -815,8 +819,11 @@ impl Generator {
|
|||||||
stats.speculative_cycles,
|
stats.speculative_cycles,
|
||||||
stats.drafted_tokens,
|
stats.drafted_tokens,
|
||||||
stats.accepted_draft_tokens,
|
stats.accepted_draft_tokens,
|
||||||
|
stats.accepted_depth_total,
|
||||||
|
stats.accepted_depth_max,
|
||||||
stats.verifier_passes,
|
stats.verifier_passes,
|
||||||
stats.verifier_ms,
|
stats.verifier_ms,
|
||||||
|
stats.repair_ms,
|
||||||
);
|
);
|
||||||
self.metrics.ssd_stats(SsdStats {
|
self.metrics.ssd_stats(SsdStats {
|
||||||
enabled: stats.ssd_enabled,
|
enabled: stats.ssd_enabled,
|
||||||
@@ -1988,7 +1995,7 @@ fn sample_probabilities(
|
|||||||
.sum();
|
.sum();
|
||||||
let mut choice = rng.unit() * total;
|
let mut choice = rng.unit() * total;
|
||||||
for (token, probability) in probabilities {
|
for (token, probability) in probabilities {
|
||||||
if Some(*token) == excluded {
|
if Some(*token) == excluded || !probability.is_finite() || *probability <= 0.0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
choice -= probability;
|
choice -= probability;
|
||||||
@@ -1999,7 +2006,9 @@ fn sample_probabilities(
|
|||||||
probabilities
|
probabilities
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.find(|(token, _)| Some(*token) != excluded)
|
.find(|(token, probability)| {
|
||||||
|
Some(*token) != excluded && probability.is_finite() && *probability > 0.0
|
||||||
|
})
|
||||||
.map_or(0, |(token, _)| *token as i32)
|
.map_or(0, |(token, _)| *token as i32)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2028,6 +2037,67 @@ fn exact_delta_sample(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "macos", test))]
|
||||||
|
fn sample_from_logits(
|
||||||
|
logits: &[f32],
|
||||||
|
temperature: f32,
|
||||||
|
top_p: f32,
|
||||||
|
min_p: f32,
|
||||||
|
top_k: i32,
|
||||||
|
rng: &mut Rng,
|
||||||
|
) -> i32 {
|
||||||
|
sample_probabilities(
|
||||||
|
&sampling_probabilities(logits, temperature, top_p, min_p, top_k),
|
||||||
|
rng,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "macos", test))]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn exact_speculative_sample(
|
||||||
|
target_logits: &[f32],
|
||||||
|
draft_logits: &[f32],
|
||||||
|
draft: i32,
|
||||||
|
temperature: f32,
|
||||||
|
top_p: f32,
|
||||||
|
min_p: f32,
|
||||||
|
top_k: i32,
|
||||||
|
rng: &mut Rng,
|
||||||
|
) -> (i32, bool) {
|
||||||
|
let mut target = sampling_probabilities(target_logits, temperature, top_p, min_p, top_k);
|
||||||
|
let mut proposal = sampling_probabilities(draft_logits, temperature, top_p, min_p, top_k);
|
||||||
|
target.sort_unstable_by_key(|(token, _)| *token);
|
||||||
|
proposal.sort_unstable_by_key(|(token, _)| *token);
|
||||||
|
let probability = |values: &[(usize, f32)], token: usize| {
|
||||||
|
values
|
||||||
|
.binary_search_by_key(&token, |(candidate, _)| *candidate)
|
||||||
|
.ok()
|
||||||
|
.map_or(0.0, |index| values[index].1)
|
||||||
|
};
|
||||||
|
let target_probability = probability(&target, draft as usize);
|
||||||
|
let draft_probability = probability(&proposal, draft as usize);
|
||||||
|
if draft_probability > 0.0 && rng.unit() <= (target_probability / draft_probability).min(1.0) {
|
||||||
|
return (draft, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
let target_best = target
|
||||||
|
.iter()
|
||||||
|
.max_by(|left, right| left.1.total_cmp(&right.1))
|
||||||
|
.map_or(draft, |(token, _)| *token as i32);
|
||||||
|
let mut residual = Vec::with_capacity(target.len());
|
||||||
|
for (token, target_probability) in target {
|
||||||
|
let remaining = target_probability - probability(&proposal, token);
|
||||||
|
if remaining > 0.0 {
|
||||||
|
residual.push((token, remaining));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if residual.is_empty() {
|
||||||
|
return (target_best, false);
|
||||||
|
}
|
||||||
|
(sample_probabilities(&residual, rng, None), false)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(any(target_os = "macos", test))]
|
#[cfg(any(target_os = "macos", test))]
|
||||||
struct Rng(u64);
|
struct Rng(u64);
|
||||||
|
|
||||||
@@ -2113,6 +2183,34 @@ mod sampling_tests {
|
|||||||
assert_eq!((replacement, was_draft), (1, false));
|
assert_eq!((replacement, was_draft), (1, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn probability_ratio_speculation_preserves_the_target_distribution() {
|
||||||
|
let target = [0.75_f32.ln(), 0.25_f32.ln()];
|
||||||
|
let proposal = [0.25_f32.ln(), 0.75_f32.ln()];
|
||||||
|
let mut rng = Rng::new(0x51a7_1c5e);
|
||||||
|
let mut counts = [0_u32; 2];
|
||||||
|
for _ in 0..40_000 {
|
||||||
|
let draft = sample_from_logits(&proposal, 1.0, 1.0, 0.0, 0, &mut rng);
|
||||||
|
let (token, _) =
|
||||||
|
exact_speculative_sample(&target, &proposal, draft, 1.0, 1.0, 0.0, 0, &mut rng);
|
||||||
|
counts[token as usize] += 1;
|
||||||
|
}
|
||||||
|
let observed = counts[0] as f32 / counts.iter().sum::<u32>() as f32;
|
||||||
|
assert!((observed - 0.75).abs() < 0.015, "observed {observed}");
|
||||||
|
|
||||||
|
let run = |seed| {
|
||||||
|
let mut rng = Rng::new(seed);
|
||||||
|
(0..32)
|
||||||
|
.map(|_| {
|
||||||
|
let draft = sample_from_logits(&proposal, 1.0, 1.0, 0.0, 0, &mut rng);
|
||||||
|
exact_speculative_sample(&target, &proposal, draft, 1.0, 1.0, 0.0, 0, &mut rng)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
};
|
||||||
|
assert_eq!(run(77), run(77));
|
||||||
|
assert!(run(77).iter().any(|(_, accepted)| !accepted));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sampling_probabilities_match_ds4_filter_order() {
|
fn sampling_probabilities_match_ds4_filter_order() {
|
||||||
let probabilities = sampling_probabilities(&[0.0, 2.0, 1.0], 1.0, 0.8, 0.2, 0);
|
let probabilities = sampling_probabilities(&[0.0, 2.0, 1.0], 1.0, 0.8, 0.2, 0);
|
||||||
|
|||||||
@@ -14,7 +14,10 @@ use qwen::QwenExecutor;
|
|||||||
|
|
||||||
use super::gguf::{BF16, F16, F32, Gguf, IQ2_XXS, MXFP4, Q2_K, Q4_K, Q8_0, Tensor as GgufTensor};
|
use super::gguf::{BF16, F16, F32, Gguf, IQ2_XXS, MXFP4, Q2_K, Q4_K, Q8_0, Tensor as GgufTensor};
|
||||||
use super::validation::{DsparkConfig, SupportKind, dspark_config};
|
use super::validation::{DsparkConfig, SupportKind, dspark_config};
|
||||||
use super::{LoadedModel, Model, ModelFamily, ModelRef, Rng, exact_delta_sample};
|
use super::{
|
||||||
|
LoadedModel, Model, ModelFamily, ModelRef, Rng, exact_delta_sample, exact_speculative_sample,
|
||||||
|
sample_from_logits,
|
||||||
|
};
|
||||||
use crate::model::ModelChoice;
|
use crate::model::ModelChoice;
|
||||||
use crate::settings::{
|
use crate::settings::{
|
||||||
EngineSpeculativeSettings, EngineSsdSettings, EngineSteeringSettings, ReasoningMode,
|
EngineSpeculativeSettings, EngineSsdSettings, EngineSteeringSettings, ReasoningMode,
|
||||||
@@ -2949,8 +2952,11 @@ pub(super) struct ExecutionStats {
|
|||||||
pub(super) speculative_cycles: u64,
|
pub(super) speculative_cycles: u64,
|
||||||
pub(super) drafted_tokens: u64,
|
pub(super) drafted_tokens: u64,
|
||||||
pub(super) accepted_draft_tokens: u64,
|
pub(super) accepted_draft_tokens: u64,
|
||||||
|
pub(super) accepted_depth_total: u64,
|
||||||
|
pub(super) accepted_depth_max: u64,
|
||||||
pub(super) verifier_passes: u64,
|
pub(super) verifier_passes: u64,
|
||||||
pub(super) verifier_ms: u64,
|
pub(super) verifier_ms: u64,
|
||||||
|
pub(super) repair_ms: u64,
|
||||||
pub(super) ssd_enabled: bool,
|
pub(super) ssd_enabled: bool,
|
||||||
pub(super) ssd_resident_bytes: u64,
|
pub(super) ssd_resident_bytes: u64,
|
||||||
pub(super) ssd_cache_bytes: u64,
|
pub(super) ssd_cache_bytes: u64,
|
||||||
@@ -4460,9 +4466,11 @@ impl Executor {
|
|||||||
.map(Self::Glm)
|
.map(Self::Glm)
|
||||||
}
|
}
|
||||||
LoadedModel::Gguf(_) => unreachable!("Qwen never uses a GGUF model"),
|
LoadedModel::Gguf(_) => unreachable!("Qwen never uses a GGUF model"),
|
||||||
LoadedModel::Qwen(model) => QwenExecutor::open(*model, context)
|
LoadedModel::Qwen(model) => {
|
||||||
.map(Box::new)
|
{ QwenExecutor::open_configured(*model, context, speculative) }
|
||||||
.map(Self::Qwen),
|
.map(Box::new)
|
||||||
|
.map(Self::Qwen)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4495,10 +4503,17 @@ impl Executor {
|
|||||||
executor.eval(token)?;
|
executor.eval(token)?;
|
||||||
Ok(vec![token])
|
Ok(vec![token])
|
||||||
}
|
}
|
||||||
Self::Qwen(executor) => {
|
Self::Qwen(executor) => executor.eval_speculative_sampled(
|
||||||
executor.eval(token)?;
|
token,
|
||||||
Ok(vec![token])
|
max_tokens,
|
||||||
}
|
reasoning,
|
||||||
|
temperature,
|
||||||
|
top_p,
|
||||||
|
min_p,
|
||||||
|
top_k,
|
||||||
|
rng,
|
||||||
|
cancelled,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4548,9 +4563,7 @@ impl Executor {
|
|||||||
executor.eval_speculative_greedy(token, max_tokens, cancelled)
|
executor.eval_speculative_greedy(token, max_tokens, cancelled)
|
||||||
}
|
}
|
||||||
Self::Qwen(executor) => {
|
Self::Qwen(executor) => {
|
||||||
let _ = (max_tokens, reasoning, cancelled);
|
executor.eval_speculative_greedy(token, max_tokens, reasoning, cancelled)
|
||||||
executor.eval(token)?;
|
|
||||||
Ok(vec![token])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -30,6 +30,8 @@ const MTP_TOKEN_RESERVE: u64 = 3;
|
|||||||
const GDN_STATE_BYTES: u64 = 113_246_208;
|
const GDN_STATE_BYTES: u64 = 113_246_208;
|
||||||
const GDN_CONV_BYTES: u64 = 2_211_840;
|
const GDN_CONV_BYTES: u64 = 2_211_840;
|
||||||
const PLE_CONV_BYTES: u64 = 184_320;
|
const PLE_CONV_BYTES: u64 = 184_320;
|
||||||
|
const MTP_CAPTURE_HIDDEN_BYTES: u64 = 10_240 * 4;
|
||||||
|
const MTP_CAPTURE_LOGITS_BYTES: u64 = 248_320 * 4;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct Manifest {
|
struct Manifest {
|
||||||
@@ -125,10 +127,22 @@ pub(super) struct QwenModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl QwenModel {
|
impl QwenModel {
|
||||||
|
#[cfg(test)]
|
||||||
pub(super) fn open(root: &Path, context: u32) -> Result<Self, String> {
|
pub(super) fn open(root: &Path, context: u32) -> Result<Self, String> {
|
||||||
let loaded = load(root, context, false)?;
|
Self::open_configured(root, context, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn open_configured(
|
||||||
|
root: &Path,
|
||||||
|
context: u32,
|
||||||
|
enable_mtp: bool,
|
||||||
|
) -> Result<Self, String> {
|
||||||
|
let loaded = load(root, context, enable_mtp)?;
|
||||||
let mut bindings = loaded.bindings.core;
|
let mut bindings = loaded.bindings.core;
|
||||||
bindings.extend(loaded.bindings.ple);
|
bindings.extend(loaded.bindings.ple);
|
||||||
|
if enable_mtp {
|
||||||
|
bindings.extend(loaded.bindings.mtp);
|
||||||
|
}
|
||||||
let mut paths = bindings
|
let mut paths = bindings
|
||||||
.iter()
|
.iter()
|
||||||
.map(|binding| binding.file.clone())
|
.map(|binding| binding.file.clone())
|
||||||
@@ -176,7 +190,7 @@ impl QwenModel {
|
|||||||
pub(super) fn tensor(&self, name: &str) -> Result<&QwenTensor, String> {
|
pub(super) fn tensor(&self, name: &str) -> Result<&QwenTensor, String> {
|
||||||
self.tensors
|
self.tensors
|
||||||
.get(name)
|
.get(name)
|
||||||
.ok_or_else(|| format!("Qwen core tensor is missing: {name}"))
|
.ok_or_else(|| format!("Qwen tensor is missing: {name}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn map(&self, index: usize) -> (&[u8], &Path) {
|
pub(super) fn map(&self, index: usize) -> (&[u8], &Path) {
|
||||||
@@ -424,10 +438,13 @@ fn validate_precision(name: &str, tensor: &ExpectedTensor) -> Result<(), String>
|
|||||||
(Some(bits @ (2 | 4 | 8)), Some(group @ (32 | 64)), Some("affine"))
|
(Some(bits @ (2 | 4 | 8)), Some(group @ (32 | 64)), Some("affine"))
|
||||||
if tensor.dtype == "U32" || name.ends_with(".scales") || name.ends_with(".biases") =>
|
if tensor.dtype == "U32" || name.ends_with(".scales") || name.ends_with(".biases") =>
|
||||||
{
|
{
|
||||||
if group == 32 && !name.starts_with("ngram.") {
|
if group == 32
|
||||||
|
&& !name.starts_with("ngram.")
|
||||||
|
&& !name.starts_with("mtp.layers.0.mlp.switch_mlp.")
|
||||||
|
{
|
||||||
return Err(format!("{name} unexpectedly uses 32-value groups"));
|
return Err(format!("{name} unexpectedly uses 32-value groups"));
|
||||||
}
|
}
|
||||||
if bits == 2 && !name.starts_with("mtp.") {
|
if bits == 2 {
|
||||||
return Err(format!("{name} unexpectedly uses 2-bit weights"));
|
return Err(format!("{name} unexpectedly uses 2-bit weights"));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -666,7 +683,30 @@ pub(super) fn memory_plan(
|
|||||||
.and_then(|bytes| bytes.checked_add(QSA_FIXED_SCRATCH_BYTES))
|
.and_then(|bytes| bytes.checked_add(QSA_FIXED_SCRATCH_BYTES))
|
||||||
.and_then(|bytes| bytes.checked_add(topk_scratch))
|
.and_then(|bytes| bytes.checked_add(topk_scratch))
|
||||||
.ok_or_else(|| "Qwen prefill transient size overflows".to_owned())?;
|
.ok_or_else(|| "Qwen prefill transient size overflows".to_owned())?;
|
||||||
let admitted_mtp = if enable_mtp { MTP_BYTES } else { 0 };
|
let admitted_mtp = if enable_mtp {
|
||||||
|
let capture_rows = MTP_TOKEN_RESERVE + 1;
|
||||||
|
let recurrent_capture = (GDN_STATE_BYTES
|
||||||
|
+ GDN_CONV_BYTES
|
||||||
|
+ PLE_CONV_BYTES
|
||||||
|
+ MTP_CAPTURE_HIDDEN_BYTES
|
||||||
|
+ MTP_CAPTURE_LOGITS_BYTES)
|
||||||
|
.checked_mul(capture_rows)
|
||||||
|
.ok_or_else(|| "Qwen MTP verifier capture size overflows".to_owned())?;
|
||||||
|
let mtp_attention = (KV_BYTES_PER_TOKEN + QSA_RAW_BYTES_PER_TOKEN)
|
||||||
|
.checked_mul(token_capacity)
|
||||||
|
.and_then(|bytes| {
|
||||||
|
QSA_POOLED_BYTES_PER_BLOCK
|
||||||
|
.checked_mul(block_capacity)
|
||||||
|
.and_then(|pooled| bytes.checked_add(pooled))
|
||||||
|
})
|
||||||
|
.ok_or_else(|| "Qwen MTP attention size overflows".to_owned())?;
|
||||||
|
MTP_BYTES
|
||||||
|
.checked_add(recurrent_capture)
|
||||||
|
.and_then(|bytes| bytes.checked_add(mtp_attention))
|
||||||
|
.ok_or_else(|| "Qwen MTP admission size overflows".to_owned())?
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
let admission = CORE_BYTES
|
let admission = CORE_BYTES
|
||||||
.checked_add(admitted_mtp)
|
.checked_add(admitted_mtp)
|
||||||
.and_then(|bytes| bytes.checked_add(kv_and_recurrent))
|
.and_then(|bytes| bytes.checked_add(kv_and_recurrent))
|
||||||
@@ -706,7 +746,7 @@ mod tests {
|
|||||||
assert_eq!(plan.optional_mtp, MTP_BYTES);
|
assert_eq!(plan.optional_mtp, MTP_BYTES);
|
||||||
assert_eq!(plan.kv_and_recurrent, 7_564_812_288);
|
assert_eq!(plan.kv_and_recurrent, 7_564_812_288);
|
||||||
assert_eq!(plan.prefill_transient, 28_056_068);
|
assert_eq!(plan.prefill_transient, 28_056_068);
|
||||||
assert_eq!(plan.admission, 81_008_126_487);
|
assert_eq!(plan.admission, 88_924_002_839);
|
||||||
let without_mtp = memory_plan(262_144, false, 512).unwrap();
|
let without_mtp = memory_plan(262_144, false, 512).unwrap();
|
||||||
assert_eq!(without_mtp.optional_mtp, MTP_BYTES);
|
assert_eq!(without_mtp.optional_mtp, MTP_BYTES);
|
||||||
assert_eq!(without_mtp.admission, 79_335_550_955);
|
assert_eq!(without_mtp.admission, 79_335_550_955);
|
||||||
|
|||||||
@@ -113,8 +113,11 @@ pub(crate) struct MetricsSnapshot {
|
|||||||
pub(crate) speculative_cycles: u64,
|
pub(crate) speculative_cycles: u64,
|
||||||
pub(crate) drafted_tokens: u64,
|
pub(crate) drafted_tokens: u64,
|
||||||
pub(crate) accepted_draft_tokens: u64,
|
pub(crate) accepted_draft_tokens: u64,
|
||||||
|
pub(crate) accepted_depth_total: u64,
|
||||||
|
pub(crate) accepted_depth_max: u64,
|
||||||
pub(crate) verifier_passes: u64,
|
pub(crate) verifier_passes: u64,
|
||||||
pub(crate) verifier_ms: u64,
|
pub(crate) verifier_ms: u64,
|
||||||
|
pub(crate) repair_ms: u64,
|
||||||
pub(crate) ssd_enabled: bool,
|
pub(crate) ssd_enabled: bool,
|
||||||
pub(crate) ssd_resident_bytes: u64,
|
pub(crate) ssd_resident_bytes: u64,
|
||||||
pub(crate) ssd_cache_bytes: u64,
|
pub(crate) ssd_cache_bytes: u64,
|
||||||
@@ -206,8 +209,11 @@ pub(crate) struct Metrics {
|
|||||||
speculative_cycles: AtomicU64,
|
speculative_cycles: AtomicU64,
|
||||||
drafted_tokens: AtomicU64,
|
drafted_tokens: AtomicU64,
|
||||||
accepted_draft_tokens: AtomicU64,
|
accepted_draft_tokens: AtomicU64,
|
||||||
|
accepted_depth_total: AtomicU64,
|
||||||
|
accepted_depth_max: AtomicU64,
|
||||||
verifier_passes: AtomicU64,
|
verifier_passes: AtomicU64,
|
||||||
verifier_ms: AtomicU64,
|
verifier_ms: AtomicU64,
|
||||||
|
repair_ms: AtomicU64,
|
||||||
ssd_enabled: AtomicBool,
|
ssd_enabled: AtomicBool,
|
||||||
ssd_resident_bytes: AtomicU64,
|
ssd_resident_bytes: AtomicU64,
|
||||||
ssd_cache_bytes: AtomicU64,
|
ssd_cache_bytes: AtomicU64,
|
||||||
@@ -304,8 +310,11 @@ impl Metrics {
|
|||||||
speculative_cycles: AtomicU64::new(0),
|
speculative_cycles: AtomicU64::new(0),
|
||||||
drafted_tokens: AtomicU64::new(0),
|
drafted_tokens: AtomicU64::new(0),
|
||||||
accepted_draft_tokens: AtomicU64::new(0),
|
accepted_draft_tokens: AtomicU64::new(0),
|
||||||
|
accepted_depth_total: AtomicU64::new(0),
|
||||||
|
accepted_depth_max: AtomicU64::new(0),
|
||||||
verifier_passes: AtomicU64::new(0),
|
verifier_passes: AtomicU64::new(0),
|
||||||
verifier_ms: AtomicU64::new(0),
|
verifier_ms: AtomicU64::new(0),
|
||||||
|
repair_ms: AtomicU64::new(0),
|
||||||
ssd_enabled: AtomicBool::new(false),
|
ssd_enabled: AtomicBool::new(false),
|
||||||
ssd_resident_bytes: AtomicU64::new(0),
|
ssd_resident_bytes: AtomicU64::new(0),
|
||||||
ssd_cache_bytes: AtomicU64::new(0),
|
ssd_cache_bytes: AtomicU64::new(0),
|
||||||
@@ -472,23 +481,32 @@ impl Metrics {
|
|||||||
self.source.store(WorkSource::None as u8, Ordering::Relaxed);
|
self.source.store(WorkSource::None as u8, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub(crate) fn speculative_stats(
|
pub(crate) fn speculative_stats(
|
||||||
&self,
|
&self,
|
||||||
mode: u8,
|
mode: u8,
|
||||||
cycles: u64,
|
cycles: u64,
|
||||||
drafted: u64,
|
drafted: u64,
|
||||||
accepted: u64,
|
accepted: u64,
|
||||||
|
accepted_depth_total: u64,
|
||||||
|
accepted_depth_max: u64,
|
||||||
verifier_passes: u64,
|
verifier_passes: u64,
|
||||||
verifier_ms: u64,
|
verifier_ms: u64,
|
||||||
|
repair_ms: u64,
|
||||||
) {
|
) {
|
||||||
self.speculative_mode.store(mode, Ordering::Relaxed);
|
self.speculative_mode.store(mode, Ordering::Relaxed);
|
||||||
self.speculative_cycles.store(cycles, Ordering::Relaxed);
|
self.speculative_cycles.store(cycles, Ordering::Relaxed);
|
||||||
self.drafted_tokens.store(drafted, Ordering::Relaxed);
|
self.drafted_tokens.store(drafted, Ordering::Relaxed);
|
||||||
self.accepted_draft_tokens
|
self.accepted_draft_tokens
|
||||||
.store(accepted, Ordering::Relaxed);
|
.store(accepted, Ordering::Relaxed);
|
||||||
|
self.accepted_depth_total
|
||||||
|
.store(accepted_depth_total, Ordering::Relaxed);
|
||||||
|
self.accepted_depth_max
|
||||||
|
.store(accepted_depth_max, Ordering::Relaxed);
|
||||||
self.verifier_passes
|
self.verifier_passes
|
||||||
.store(verifier_passes, Ordering::Relaxed);
|
.store(verifier_passes, Ordering::Relaxed);
|
||||||
self.verifier_ms.store(verifier_ms, Ordering::Relaxed);
|
self.verifier_ms.store(verifier_ms, Ordering::Relaxed);
|
||||||
|
self.repair_ms.store(repair_ms, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn ssd_stats(&self, stats: SsdStats) {
|
pub(crate) fn ssd_stats(&self, stats: SsdStats) {
|
||||||
@@ -540,7 +558,7 @@ impl Metrics {
|
|||||||
self.decode_tps.store(0, Ordering::Relaxed);
|
self.decode_tps.store(0, Ordering::Relaxed);
|
||||||
self.prefill_tps.store(0, Ordering::Relaxed);
|
self.prefill_tps.store(0, Ordering::Relaxed);
|
||||||
self.prefill_sample.store(0, Ordering::Relaxed);
|
self.prefill_sample.store(0, Ordering::Relaxed);
|
||||||
self.speculative_stats(0, 0, 0, 0, 0, 0);
|
self.speculative_stats(0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||||
self.ssd_stats(SsdStats::default());
|
self.ssd_stats(SsdStats::default());
|
||||||
self.model_unloads.fetch_add(1, Ordering::Relaxed);
|
self.model_unloads.fetch_add(1, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
@@ -695,8 +713,11 @@ impl Metrics {
|
|||||||
speculative_cycles: self.speculative_cycles.load(Ordering::Relaxed),
|
speculative_cycles: self.speculative_cycles.load(Ordering::Relaxed),
|
||||||
drafted_tokens: self.drafted_tokens.load(Ordering::Relaxed),
|
drafted_tokens: self.drafted_tokens.load(Ordering::Relaxed),
|
||||||
accepted_draft_tokens: self.accepted_draft_tokens.load(Ordering::Relaxed),
|
accepted_draft_tokens: self.accepted_draft_tokens.load(Ordering::Relaxed),
|
||||||
|
accepted_depth_total: self.accepted_depth_total.load(Ordering::Relaxed),
|
||||||
|
accepted_depth_max: self.accepted_depth_max.load(Ordering::Relaxed),
|
||||||
verifier_passes: self.verifier_passes.load(Ordering::Relaxed),
|
verifier_passes: self.verifier_passes.load(Ordering::Relaxed),
|
||||||
verifier_ms: self.verifier_ms.load(Ordering::Relaxed),
|
verifier_ms: self.verifier_ms.load(Ordering::Relaxed),
|
||||||
|
repair_ms: self.repair_ms.load(Ordering::Relaxed),
|
||||||
ssd_enabled: self.ssd_enabled.load(Ordering::Relaxed),
|
ssd_enabled: self.ssd_enabled.load(Ordering::Relaxed),
|
||||||
ssd_resident_bytes: self.ssd_resident_bytes.load(Ordering::Relaxed),
|
ssd_resident_bytes: self.ssd_resident_bytes.load(Ordering::Relaxed),
|
||||||
ssd_cache_bytes: self.ssd_cache_bytes.load(Ordering::Relaxed),
|
ssd_cache_bytes: self.ssd_cache_bytes.load(Ordering::Relaxed),
|
||||||
|
|||||||
@@ -128,8 +128,8 @@ impl ModelChoice {
|
|||||||
self == Self::Qwen38FlashNext
|
self == Self::Qwen38FlashNext
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn supports_glm_mtp(self) -> bool {
|
pub(crate) fn supports_integrated_mtp(self) -> bool {
|
||||||
self.is_glm()
|
self.is_glm() || self.is_qwen38()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn main_artifact_size(self) -> u64 {
|
pub(crate) fn main_artifact_size(self) -> u64 {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
|||||||
pub(super) const LABEL: &str = "Qwen3.8 Flash Next Bare Speed artifact set";
|
pub(super) const LABEL: &str = "Qwen3.8 Flash Next Bare Speed artifact set";
|
||||||
pub(super) const REPOSITORY: &str = "Youssofal/Qwen3.8-Flash-Next-MTPLX-Bare-Speed";
|
pub(super) const REPOSITORY: &str = "Youssofal/Qwen3.8-Flash-Next-MTPLX-Bare-Speed";
|
||||||
pub(super) const REVISION: &str = "74559cdf34fbfc0b593de72d17e93f37fd4f9ea7";
|
pub(super) const REVISION: &str = "74559cdf34fbfc0b593de72d17e93f37fd4f9ea7";
|
||||||
const MANIFEST_SHA256: &str = "eeec490fd3d0c0b1c9093be0a7fe7b9be4b49389ed5276de7530c2d3c11290ca";
|
const MANIFEST_SHA256: &str = "6f1172de47fa30b9602e13fc7ad14e578a813bb5b1fab8f0ef320041aac6c19a";
|
||||||
const MANIFEST_BYTES: &[u8] =
|
const MANIFEST_BYTES: &[u8] =
|
||||||
include_bytes!("../../assets/models/qwen38-flash-next-bare-speed.json");
|
include_bytes!("../../assets/models/qwen38-flash-next-bare-speed.json");
|
||||||
|
|
||||||
|
|||||||
@@ -45,10 +45,10 @@ pub(crate) struct SpeculativePreferences {
|
|||||||
impl SpeculativePreferences {
|
impl SpeculativePreferences {
|
||||||
pub(crate) fn validate(&self, model: ModelChoice) -> Result<(), String> {
|
pub(crate) fn validate(&self, model: ModelChoice) -> Result<(), String> {
|
||||||
if self.glm_mtp_timing && !self.glm_mtp {
|
if self.glm_mtp_timing && !self.glm_mtp {
|
||||||
return Err("GLM MTP timing requires GLM MTP.".into());
|
return Err("MTP timing requires integrated MTP.".into());
|
||||||
}
|
}
|
||||||
if !model.supports_glm_mtp() && (self.glm_mtp || self.glm_mtp_timing) {
|
if !model.supports_integrated_mtp() && (self.glm_mtp || self.glm_mtp_timing) {
|
||||||
return Err("GLM MTP is available only for GLM models.".into());
|
return Err("Integrated MTP is unavailable for the selected model.".into());
|
||||||
}
|
}
|
||||||
if self.keep_vision_loaded && model != ModelChoice::Glm53Flash {
|
if self.keep_vision_loaded && model != ModelChoice::Glm53Flash {
|
||||||
return Err("Persistent vision weights are available only for GLM 5.3 Flash.".into());
|
return Err("Persistent vision weights are available only for GLM 5.3 Flash.".into());
|
||||||
@@ -83,7 +83,7 @@ impl SpeculativePreferences {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||||
pub(crate) struct EngineSpeculativeSettings {
|
pub(crate) struct EngineSpeculativeSettings {
|
||||||
pub(crate) glm_mtp: bool,
|
pub(crate) glm_mtp: bool,
|
||||||
pub(crate) glm_mtp_timing: bool,
|
pub(crate) glm_mtp_timing: bool,
|
||||||
|
|||||||
@@ -657,7 +657,7 @@ fn precision_assignments(
|
|||||||
reject_unassigned_quantized(&core, &assignments)?;
|
reject_unassigned_quantized(&core, &assignments)?;
|
||||||
|
|
||||||
let mtp = tensor_headers(manifest, source, Some("mtp"), complete)?;
|
let mtp = tensor_headers(manifest, source, Some("mtp"), complete)?;
|
||||||
add_inferred_sidecar(&mtp, 64, &mut assignments)?;
|
add_inferred_sidecar(&mtp, &mut assignments)?;
|
||||||
let ple = tensor_headers(manifest, source, Some("ple"), complete)?;
|
let ple = tensor_headers(manifest, source, Some("ple"), complete)?;
|
||||||
let ple_group = config
|
let ple_group = config
|
||||||
.pointer("/mtplx_recipe/ngram/group_size")
|
.pointer("/mtplx_recipe/ngram/group_size")
|
||||||
@@ -711,7 +711,6 @@ fn validate_unquantized(base: &str, tensors: &BTreeMap<String, Tensor>) -> Resul
|
|||||||
|
|
||||||
fn add_inferred_sidecar(
|
fn add_inferred_sidecar(
|
||||||
tensors: &BTreeMap<String, Tensor>,
|
tensors: &BTreeMap<String, Tensor>,
|
||||||
group_size: u64,
|
|
||||||
assignments: &mut BTreeMap<String, Quantization>,
|
assignments: &mut BTreeMap<String, Quantization>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
for (name, tensor) in tensors {
|
for (name, tensor) in tensors {
|
||||||
@@ -732,6 +731,11 @@ fn add_inferred_sidecar(
|
|||||||
.shape
|
.shape
|
||||||
.last()
|
.last()
|
||||||
.ok_or_else(|| format!("{base}.scales has no dimensions"))?;
|
.ok_or_else(|| format!("{base}.scales has no dimensions"))?;
|
||||||
|
let group_size = if base.contains(".switch_mlp.") {
|
||||||
|
32
|
||||||
|
} else {
|
||||||
|
64
|
||||||
|
};
|
||||||
let pack = groups
|
let pack = groups
|
||||||
.checked_mul(group_size)
|
.checked_mul(group_size)
|
||||||
.and_then(|columns| columns.checked_div(packed))
|
.and_then(|columns| columns.checked_div(packed))
|
||||||
|
|||||||
Reference in New Issue
Block a user