diff --git a/src/app.rs b/src/app.rs index 225b92d..33c7519 100644 --- a/src/app.rs +++ b/src/app.rs @@ -2566,6 +2566,7 @@ mod tests { Some(Message::FocusPrevious) )); assert!(ModelChoice::DeepSeekV4Flash.supports_dspark()); + assert!(!ModelChoice::DeepSeekV4Flash0731.supports_dspark()); assert!(!ModelChoice::DeepSeekV4Pro.supports_dspark()); assert!(!ModelChoice::Glm52.supports_dspark()); } diff --git a/src/engine.rs b/src/engine.rs index af6dcfc..1a0a69c 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -150,6 +150,11 @@ const FLASH: Shape = Shape { original_context: 65_536, }; +const FLASH_0731: Shape = Shape { + model: ModelChoice::DeepSeekV4Flash0731, + ..FLASH +}; + const PRO: Shape = Shape { model: ModelChoice::DeepSeekV4Pro, layers: 61, diff --git a/src/engine/metal.rs b/src/engine/metal.rs index 652f6f9..511bd1b 100644 --- a/src/engine/metal.rs +++ b/src/engine/metal.rs @@ -1985,7 +1985,7 @@ impl SsdPlan { let mut by_layer = vec![Vec::<(i32, u32)>::new(); model.shape.layers as usize]; let mut loaded = 0_u32; let hotlist = match model.shape.model { - ModelChoice::DeepSeekV4Flash => hotlist::FLASH, + ModelChoice::DeepSeekV4Flash | ModelChoice::DeepSeekV4Flash0731 => hotlist::FLASH, ModelChoice::DeepSeekV4Pro => hotlist::PRO, ModelChoice::Glm52 => unreachable!("GLM uses its dedicated executor"), }; @@ -3437,12 +3437,14 @@ impl DeepSeekExecutor { if self.session.position == 0 && self.ssd.is_some() { unsafe { ds4_gpu_stream_expert_cache_reset_route_hotness() }; } - let streaming_decode_cap = if self.model.shape.model == ModelChoice::DeepSeekV4Flash - && self.weights.layers.first().is_some_and(|layer| { - layer.expert_gate.kind == Q4_K - && layer.expert_up.kind == Q4_K - && layer.expert_down.kind == Q4_K - }) { + let streaming_decode_cap = if matches!( + self.model.shape.model, + ModelChoice::DeepSeekV4Flash | ModelChoice::DeepSeekV4Flash0731 + ) && self.weights.layers.first().is_some_and(|layer| { + layer.expert_gate.kind == Q4_K + && layer.expert_up.kind == Q4_K + && layer.expert_down.kind == Q4_K + }) { 64 } else { 18 @@ -6469,16 +6471,23 @@ fn update_compressor_stage( fn compression_ratio(shape: super::Shape, layer: u32) -> u32 { match shape.model { - crate::model::ModelChoice::DeepSeekV4Flash if layer < 2 => 0, + crate::model::ModelChoice::DeepSeekV4Flash + | crate::model::ModelChoice::DeepSeekV4Flash0731 + if layer < 2 => + { + 0 + } crate::model::ModelChoice::DeepSeekV4Pro if layer < 2 => 128, - crate::model::ModelChoice::DeepSeekV4Flash | crate::model::ModelChoice::DeepSeekV4Pro + crate::model::ModelChoice::DeepSeekV4Flash + | crate::model::ModelChoice::DeepSeekV4Flash0731 + | crate::model::ModelChoice::DeepSeekV4Pro if layer.is_multiple_of(2) => { 4 } - crate::model::ModelChoice::DeepSeekV4Flash | crate::model::ModelChoice::DeepSeekV4Pro => { - 128 - } + crate::model::ModelChoice::DeepSeekV4Flash + | crate::model::ModelChoice::DeepSeekV4Flash0731 + | crate::model::ModelChoice::DeepSeekV4Pro => 128, crate::model::ModelChoice::Glm52 => 0, } } diff --git a/src/engine/validation.rs b/src/engine/validation.rs index 28527b2..1ba830c 100644 --- a/src/engine/validation.rs +++ b/src/engine/validation.rs @@ -223,10 +223,11 @@ pub(super) fn validate_main(model: &Gguf, expected: ModelChoice) -> Result GLM, - ModelFamily::DeepSeek => match model.u32("deepseek4.block_count")? { - 43 => FLASH, - 61 => PRO, - layers => return Err(format!("unsupported DeepSeek layer count: {layers}")), + ModelFamily::DeepSeek => match (model.u32("deepseek4.block_count")?, expected) { + (43, ModelChoice::DeepSeekV4Flash0731) => FLASH_0731, + (43, _) => FLASH, + (61, _) => PRO, + (layers, _) => return Err(format!("unsupported DeepSeek layer count: {layers}")), }, }; if shape.model != expected { @@ -993,10 +994,18 @@ fn float_eq(actual: f32, expected: f32) -> bool { fn compression_ratio(shape: &Shape, layer: u32) -> u32 { match shape.model { - ModelChoice::DeepSeekV4Flash if layer < 2 => 0, + ModelChoice::DeepSeekV4Flash | ModelChoice::DeepSeekV4Flash0731 if layer < 2 => 0, ModelChoice::DeepSeekV4Pro if layer < 2 => 128, - ModelChoice::DeepSeekV4Flash | ModelChoice::DeepSeekV4Pro if layer.is_multiple_of(2) => 4, - ModelChoice::DeepSeekV4Flash | ModelChoice::DeepSeekV4Pro => 128, + ModelChoice::DeepSeekV4Flash + | ModelChoice::DeepSeekV4Flash0731 + | ModelChoice::DeepSeekV4Pro + if layer.is_multiple_of(2) => + { + 4 + } + ModelChoice::DeepSeekV4Flash + | ModelChoice::DeepSeekV4Flash0731 + | ModelChoice::DeepSeekV4Pro => 128, ModelChoice::Glm52 => 0, } } diff --git a/src/metrics.rs b/src/metrics.rs index dfc3324..32fa7b8 100644 --- a/src/metrics.rs +++ b/src/metrics.rs @@ -847,6 +847,7 @@ fn model_code(model: ModelChoice) -> u8 { ModelChoice::DeepSeekV4Flash => 1, ModelChoice::DeepSeekV4Pro => 2, ModelChoice::Glm52 => 3, + ModelChoice::DeepSeekV4Flash0731 => 4, } } @@ -855,6 +856,7 @@ fn model_name(value: u8) -> &'static str { 1 => "DeepSeek V4 Flash", 2 => "DeepSeek V4 Pro", 3 => "GLM 5.2", + 4 => "DeepSeek V4 Flash 0731", _ => "No model loaded", } } diff --git a/src/model.rs b/src/model.rs index 5704ada..e648935 100644 --- a/src/model.rs +++ b/src/model.rs @@ -9,20 +9,23 @@ use std::fmt; use std::fs; use std::path::{Path, PathBuf}; -pub(crate) const MODEL_CHOICES: [ModelChoice; 3] = [ +pub(crate) const MODEL_CHOICES: [ModelChoice; 4] = [ ModelChoice::DeepSeekV4Flash, + ModelChoice::DeepSeekV4Flash0731, ModelChoice::DeepSeekV4Pro, ModelChoice::Glm52, ]; -pub(crate) const MANAGED_ARTIFACTS: [ManagedArtifactId; 5] = [ +pub(crate) const MANAGED_ARTIFACTS: [ManagedArtifactId; 6] = [ ManagedArtifactId::DeepSeekV4Flash, ManagedArtifactId::DeepSeekV4FlashMtp, ManagedArtifactId::DeepSeekV4FlashDspark, + ManagedArtifactId::DeepSeekV4Flash0731, ManagedArtifactId::DeepSeekV4Pro, ManagedArtifactId::Glm52, ]; const DEEPSEEK_REPOSITORY: &str = "antirez/deepseek-v4-gguf"; +const DEEPSEEK_FLASH_0731_REPOSITORY: &str = "Rednalreden/DeepSeek-V4-Flash-0731-dwarfstar-q2-gguf"; const GLM_REPOSITORY: &str = "antirez/glm-5.2-gguf"; const FLASH: Artifact = Artifact { @@ -49,6 +52,14 @@ const FLASH_MTP: Artifact = Artifact { sha256: "afd481ee689dce9037f70f39085fcdae5a5b096d521cdad43b19fa52bf8f4083", support: Some(true), }; +const FLASH_0731: Artifact = Artifact { + label: "DeepSeek V4 Flash 0731 model", + file_name: "DeepSeek-V4-Flash-0731-IQ2XXS-w2Q2K-AProjQ8-SExpQ8-OutQ8-imatrix.gguf", + repository: DEEPSEEK_FLASH_0731_REPOSITORY, + size: 86_720_111_520, + sha256: "0b39f9c337d6b49c77db2190556b8563abf3c5fbb98be3b58cf8d3a1db191e5f", + support: Some(false), +}; const PRO: Artifact = Artifact { label: "DeepSeek V4 Pro model", file_name: "DeepSeek-V4-Pro-IQ2XXS-w2Q2K-AProjQ8-SExpQ8-OutQ8-Instruct-imatrix.gguf", @@ -71,6 +82,8 @@ pub(crate) enum ModelChoice { #[default] #[serde(rename = "deepseek-v4-flash")] DeepSeekV4Flash, + #[serde(rename = "deepseek-v4-flash-0731")] + DeepSeekV4Flash0731, #[serde(rename = "deepseek-v4-pro")] DeepSeekV4Pro, #[serde(rename = "glm-5.2")] @@ -81,6 +94,7 @@ impl ModelChoice { pub(crate) fn id(self) -> &'static str { match self { Self::DeepSeekV4Flash => "deepseek-v4-flash", + Self::DeepSeekV4Flash0731 => "deepseek-v4-flash-0731", Self::DeepSeekV4Pro => "deepseek-v4-pro", Self::Glm52 => "glm-5.2", } @@ -97,6 +111,7 @@ impl ModelChoice { fn main_artifact(self) -> &'static Artifact { match self { Self::DeepSeekV4Flash => &FLASH, + Self::DeepSeekV4Flash0731 => &FLASH_0731, Self::DeepSeekV4Pro => &PRO, Self::Glm52 => &GLM, } @@ -147,6 +162,7 @@ pub(crate) enum ManagedArtifactId { DeepSeekV4Flash, DeepSeekV4FlashMtp, DeepSeekV4FlashDspark, + DeepSeekV4Flash0731, DeepSeekV4Pro, Glm52, } @@ -157,6 +173,7 @@ impl ManagedArtifactId { Self::DeepSeekV4Flash | Self::DeepSeekV4FlashMtp | Self::DeepSeekV4FlashDspark => { ModelChoice::DeepSeekV4Flash } + Self::DeepSeekV4Flash0731 => ModelChoice::DeepSeekV4Flash0731, Self::DeepSeekV4Pro => ModelChoice::DeepSeekV4Pro, Self::Glm52 => ModelChoice::Glm52, } @@ -167,6 +184,7 @@ impl ManagedArtifactId { Self::DeepSeekV4Flash => &FLASH, Self::DeepSeekV4FlashMtp => &FLASH_MTP, Self::DeepSeekV4FlashDspark => &FLASH_DSPARK, + Self::DeepSeekV4Flash0731 => &FLASH_0731, Self::DeepSeekV4Pro => &PRO, Self::Glm52 => &GLM, } @@ -261,6 +279,7 @@ impl fmt::Display for ModelChoice { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str(match self { Self::DeepSeekV4Flash => "DeepSeek V4 Flash", + Self::DeepSeekV4Flash0731 => "DeepSeek V4 Flash 0731", Self::DeepSeekV4Pro => "DeepSeek V4 Pro", Self::Glm52 => "GLM 5.2", }) diff --git a/src/model/transfer.rs b/src/model/transfer.rs index 25e25f7..23655dc 100644 --- a/src/model/transfer.rs +++ b/src/model/transfer.rs @@ -325,17 +325,31 @@ mod tests { #[test] fn catalog_and_checksum_verification_are_explicit() { assert_eq!(ModelChoice::from_id("glm-5.2"), Some(ModelChoice::Glm52)); + assert_eq!( + ModelChoice::from_id("deepseek-v4-flash-0731"), + Some(ModelChoice::DeepSeekV4Flash0731) + ); assert!(ModelChoice::from_id("unknown").is_none()); assert_eq!( ModelChoice::DeepSeekV4Flash.main_artifact().size, 86_720_111_488 ); assert_eq!(ModelChoice::Glm52.main_artifact().size, 211_075_856_448); + assert_eq!( + ModelChoice::DeepSeekV4Flash0731.main_artifact().size, + 86_720_111_520 + ); assert_eq!( ModelChoice::DeepSeekV4Flash.artifacts(true, true).count(), 3 ); assert_eq!(ModelChoice::Glm52.artifacts(true, true).count(), 1); + assert_eq!( + ModelChoice::DeepSeekV4Flash0731 + .artifacts(true, true) + .count(), + 1 + ); let id = SystemTime::now() .duration_since(UNIX_EPOCH) @@ -351,6 +365,11 @@ mod tests { engine.mtp.as_deref().and_then(Path::file_name), Some(std::ffi::OsStr::new(FLASH_DSPARK.file_name)) ); + assert!( + engine_artifacts(ModelChoice::DeepSeekV4Flash0731, true, true, &models_path) + .mtp + .is_none() + ); let empty = Artifact { label: "empty", file_name: "empty",