Support DeepSeek V4 Pro execution
This commit is contained in:
@@ -372,8 +372,8 @@ pub(crate) struct CompactionOutput {
|
|||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
impl Generator {
|
impl Generator {
|
||||||
pub(crate) fn open(settings: &EngineSettings, metrics: Arc<Metrics>) -> Result<Self, String> {
|
pub(crate) fn open(settings: &EngineSettings, metrics: Arc<Metrics>) -> Result<Self, String> {
|
||||||
if settings.model != ModelChoice::DeepSeekV4Flash {
|
if settings.model == ModelChoice::Glm52 {
|
||||||
return Err("local generation currently supports DeepSeek V4 Flash only".into());
|
return Err("GLM 5.2 generation is not initialized by the DeepSeek executor".into());
|
||||||
}
|
}
|
||||||
if settings.speculative.dspark || settings.ssd.enabled || settings.steering.file.is_some() {
|
if settings.speculative.dspark || settings.ssd.enabled || settings.steering.file.is_some() {
|
||||||
return Err(
|
return Err(
|
||||||
|
|||||||
@@ -150,10 +150,10 @@ struct IndexerWeights {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Layer {
|
impl Layer {
|
||||||
fn bind(model: &Gguf, index: u32) -> Result<Self, String> {
|
fn bind(model: &Gguf, shape: super::Shape, index: u32) -> Result<Self, String> {
|
||||||
let required = |suffix: &str| Weight::bind(model, &format!("blk.{index}.{suffix}"));
|
let required = |suffix: &str| Weight::bind(model, &format!("blk.{index}.{suffix}"));
|
||||||
let optional = |suffix: &str| Weight::optional(model, &format!("blk.{index}.{suffix}"));
|
let optional = |suffix: &str| Weight::optional(model, &format!("blk.{index}.{suffix}"));
|
||||||
let attn_compressor = (index >= 2)
|
let attn_compressor = (compression_ratio(shape, index) != 0)
|
||||||
.then(|| {
|
.then(|| {
|
||||||
Ok::<_, String>(CompressorWeights {
|
Ok::<_, String>(CompressorWeights {
|
||||||
ape: required("attn_compressor_ape.weight")?,
|
ape: required("attn_compressor_ape.weight")?,
|
||||||
@@ -163,7 +163,7 @@ impl Layer {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
let indexer = (compression_ratio(index) == 4)
|
let indexer = (compression_ratio(shape, index) == 4)
|
||||||
.then(|| {
|
.then(|| {
|
||||||
Ok::<_, String>(IndexerWeights {
|
Ok::<_, String>(IndexerWeights {
|
||||||
q: required("indexer.attn_q_b.weight")?,
|
q: required("indexer.attn_q_b.weight")?,
|
||||||
@@ -215,13 +215,13 @@ impl Layer {
|
|||||||
("shared down", layer.shared_down),
|
("shared down", layer.shared_down),
|
||||||
] {
|
] {
|
||||||
if weight.kind != Q8_0 {
|
if weight.kind != Q8_0 {
|
||||||
return Err(format!("Flash Metal path requires Q8_0 {name} weights"));
|
return Err(format!("DeepSeek Metal path requires Q8_0 {name} weights"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(indexer) = layer.indexer
|
if let Some(indexer) = layer.indexer
|
||||||
&& (!matches!(indexer.q.kind, F16 | Q8_0) || indexer.proj.kind != F16)
|
&& (!matches!(indexer.q.kind, F16 | Q8_0) || indexer.proj.kind != F16)
|
||||||
{
|
{
|
||||||
return Err("Flash Metal path requires F16/Q8 indexer weights".into());
|
return Err("DeepSeek Metal path requires F16/Q8 indexer weights".into());
|
||||||
}
|
}
|
||||||
Ok(layer)
|
Ok(layer)
|
||||||
}
|
}
|
||||||
@@ -240,15 +240,15 @@ struct Weights {
|
|||||||
impl Weights {
|
impl Weights {
|
||||||
fn bind(model: &Model) -> Result<Self, String> {
|
fn bind(model: &Model) -> Result<Self, String> {
|
||||||
if model.shape.family != ModelFamily::DeepSeek {
|
if model.shape.family != ModelFamily::DeepSeek {
|
||||||
return Err("the Rust Metal executor currently supports DeepSeek Flash only".into());
|
return Err("the DeepSeek Metal executor received a different model family".into());
|
||||||
}
|
}
|
||||||
let main = &model.main;
|
let main = &model.main;
|
||||||
let output = Weight::bind(main, "output.weight")?;
|
let output = Weight::bind(main, "output.weight")?;
|
||||||
if output.kind != Q8_0 {
|
if output.kind != Q8_0 {
|
||||||
return Err("Flash Metal path requires a Q8_0 output weight".into());
|
return Err("DeepSeek Metal path requires a Q8_0 output weight".into());
|
||||||
}
|
}
|
||||||
let layers = (0..model.shape.layers)
|
let layers = (0..model.shape.layers)
|
||||||
.map(|index| Layer::bind(main, index))
|
.map(|index| Layer::bind(main, model.shape, index))
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
token_embedding: Weight::bind(main, "token_embd.weight")?,
|
token_embedding: Weight::bind(main, "token_embd.weight")?,
|
||||||
@@ -486,7 +486,7 @@ struct CompressionState {
|
|||||||
impl LayerState {
|
impl LayerState {
|
||||||
fn allocate(model: &Model, index: u32, context: u32, raw_cap: u32) -> Result<Self, String> {
|
fn allocate(model: &Model, index: u32, context: u32, raw_cap: u32) -> Result<Self, String> {
|
||||||
let shape = model.shape;
|
let shape = model.shape;
|
||||||
let ratio = compression_ratio(index);
|
let ratio = compression_ratio(shape, index);
|
||||||
let compression = if ratio == 0 {
|
let compression = if ratio == 0 {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
@@ -1284,8 +1284,8 @@ fn encode_batch_layer(
|
|||||||
let hc_dim = shape.hc * shape.embd;
|
let hc_dim = shape.hc * shape.embd;
|
||||||
let mix_hc = 2 * shape.hc + shape.hc * shape.hc;
|
let mix_hc = 2 * shape.hc + shape.hc * shape.hc;
|
||||||
let q_dim = shape.heads * shape.head_dim;
|
let q_dim = shape.heads * shape.head_dim;
|
||||||
let compressed = layer >= 2;
|
let ratio = compression_ratio(shape, layer);
|
||||||
let ratio = compression_ratio(layer);
|
let compressed = ratio != 0;
|
||||||
let freq_base = if compressed {
|
let freq_base = if compressed {
|
||||||
shape.compress_rope_base
|
shape.compress_rope_base
|
||||||
} else {
|
} else {
|
||||||
@@ -2515,7 +2515,7 @@ fn encode_layer(
|
|||||||
n_comp,
|
n_comp,
|
||||||
shape.indexer_top_k as u32,
|
shape.indexer_top_k as u32,
|
||||||
shape.sliding_window as u32,
|
shape.sliding_window as u32,
|
||||||
compression_ratio(layer),
|
compression_ratio(shape, layer),
|
||||||
shape.heads as u32,
|
shape.heads as u32,
|
||||||
shape.head_dim as u32,
|
shape.head_dim as u32,
|
||||||
)
|
)
|
||||||
@@ -2965,13 +2965,19 @@ fn update_compressor_stage(
|
|||||||
Ok((pos + 1).is_multiple_of(state.ratio))
|
Ok((pos + 1).is_multiple_of(state.ratio))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compression_ratio(layer: u32) -> u32 {
|
fn compression_ratio(shape: super::Shape, layer: u32) -> u32 {
|
||||||
if layer < 2 {
|
match shape.model {
|
||||||
0
|
crate::model::ModelChoice::DeepSeekV4Flash if layer < 2 => 0,
|
||||||
} else if layer.is_multiple_of(2) {
|
crate::model::ModelChoice::DeepSeekV4Pro if layer < 2 => 128,
|
||||||
4
|
crate::model::ModelChoice::DeepSeekV4Flash | crate::model::ModelChoice::DeepSeekV4Pro
|
||||||
} else {
|
if layer.is_multiple_of(2) =>
|
||||||
128
|
{
|
||||||
|
4
|
||||||
|
}
|
||||||
|
crate::model::ModelChoice::DeepSeekV4Flash | crate::model::ModelChoice::DeepSeekV4Pro => {
|
||||||
|
128
|
||||||
|
}
|
||||||
|
crate::model::ModelChoice::Glm52 => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3250,7 +3256,16 @@ fn check(result: i32, operation: &str) -> Result<(), String> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{raw_batch_span, raw_decode_span};
|
use super::{compression_ratio, raw_batch_span, raw_decode_span};
|
||||||
|
use crate::engine::{FLASH, PRO};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn compression_schedule_tracks_the_deepseek_model_shape() {
|
||||||
|
assert_eq!(compression_ratio(FLASH, 0), 0);
|
||||||
|
assert_eq!(compression_ratio(FLASH, 2), 4);
|
||||||
|
assert_eq!(compression_ratio(PRO, 0), 128);
|
||||||
|
assert_eq!(compression_ratio(PRO, 1), 128);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn decode_raw_span_tracks_the_logical_sliding_window() {
|
fn decode_raw_span_tracks_the_logical_sliding_window() {
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ impl Executor {
|
|||||||
let mut indexer_rows = Vec::with_capacity(shape.layers as usize);
|
let mut indexer_rows = Vec::with_capacity(shape.layers as usize);
|
||||||
for layer in 0..shape.layers {
|
for layer in 0..shape.layers {
|
||||||
let rows = read_u32(file)?;
|
let rows = read_u32(file)?;
|
||||||
let ratio = compression_ratio(layer);
|
let ratio = compression_ratio(shape, layer);
|
||||||
if rows != token_count.checked_div(ratio).unwrap_or(0) {
|
if rows != token_count.checked_div(ratio).unwrap_or(0) {
|
||||||
return Err("KV checkpoint compressed row count is invalid".into());
|
return Err("KV checkpoint compressed row count is invalid".into());
|
||||||
}
|
}
|
||||||
@@ -233,7 +233,7 @@ impl Executor {
|
|||||||
}
|
}
|
||||||
for layer in 0..shape.layers {
|
for layer in 0..shape.layers {
|
||||||
let rows = read_u32(file)?;
|
let rows = read_u32(file)?;
|
||||||
let expected = if compression_ratio(layer) == 4 {
|
let expected = if compression_ratio(shape, layer) == 4 {
|
||||||
token_count / 4
|
token_count / 4
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
|
|||||||
@@ -544,7 +544,7 @@ fn compatible_completion(
|
|||||||
fn installed_endpoint_models(models_path: &std::path::Path) -> Vec<ModelChoice> {
|
fn installed_endpoint_models(models_path: &std::path::Path) -> Vec<ModelChoice> {
|
||||||
model::installed_models(models_path)
|
model::installed_models(models_path)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|model| *model == ModelChoice::DeepSeekV4Flash)
|
.filter(|model| *model != ModelChoice::Glm52)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user