From 7299c92aba7f8738b76adacbade369533fd6e854 Mon Sep 17 00:00:00 2001 From: newfla Date: Fri, 20 Feb 2026 13:31:33 +0100 Subject: [PATCH 1/2] fix: dangling lora_t path pointer --- src/api.rs | 75 ++++++++++++++++++------------------------------------ 1 file changed, 25 insertions(+), 50 deletions(-) diff --git a/src/api.rs b/src/api.rs index 9dfc439..2a0889d 100644 --- a/src/api.rs +++ b/src/api.rs @@ -107,13 +107,7 @@ pub enum ClipSkip { } type EmbeddingsStorage = (PathBuf, Vec<(CLibString, CLibPath)>, Vec); - -#[derive(Default, Debug, Clone)] -struct LoraStorage { - lora_model_dir: CLibPath, - data: Vec<(CLibPath, String, f32)>, - loras_t: Vec, -} +type LoraStorage = Vec<(CLibPath, LoraSpec)>; /// Specify the instructions for a Lora model #[derive(Default, Debug, Clone)] @@ -573,7 +567,7 @@ impl ModelConfigBuilder { )) } - fn filter_valid_extensions(&self, path: &Path) -> impl Iterator { + fn filter_valid_extensions(path: &Path) -> impl Iterator { WalkDir::new(path) .into_iter() .filter_map(|entry| entry.ok()) @@ -586,26 +580,17 @@ impl ModelConfigBuilder { .unwrap_or(false) }) } - fn build_single_lora_storage( spec: &LoraSpec, - is_high_noise: bool, valid_loras: &HashMap, - ) -> ((CLibPath, String, f32), sd_lora_t) { + ) -> (CLibPath, LoraSpec) { let path = valid_loras.get(&spec.file_name).unwrap().as_path(); let c_path = CLibPath::from(path); - let lora = sd_lora_t { - is_high_noise, - multiplier: spec.multiplier, - path: c_path.as_ptr(), - }; - let data = (c_path, spec.file_name.clone(), spec.multiplier); - (data, lora) + (c_path, spec.clone()) } pub fn embeddings(&mut self, embeddings_dir: &Path) -> &mut Self { - let data: Vec<(CLibString, CLibPath)> = self - .filter_valid_extensions(embeddings_dir) + let data: Vec<(CLibString, CLibPath)> = Self::filter_valid_extensions(embeddings_dir) .map(|entry| { let file_stem = entry .path() @@ -628,8 +613,7 @@ impl ModelConfigBuilder { } pub fn lora_models(&mut self, lora_model_dir: &Path, specs: Vec) -> &mut Self { - let valid_loras: HashMap = self - .filter_valid_extensions(lora_model_dir) + let valid_loras: HashMap = Self::filter_valid_extensions(lora_model_dir) .map(|entry| { let path = entry.path(); ( @@ -645,24 +629,17 @@ impl ModelConfigBuilder { let standard = specs .iter() .filter(|s| valid_lora_names.contains(&&s.file_name) && !s.is_high_noise) - .map(|s| Self::build_single_lora_storage(s, false, &valid_loras)); + .map(|s| Self::build_single_lora_storage(s, &valid_loras)); let high_noise = specs .iter() .filter(|s| valid_lora_names.contains(&&s.file_name) && s.is_high_noise) - .map(|s| Self::build_single_lora_storage(s, true, &valid_loras)); + .map(|s| Self::build_single_lora_storage(s, &valid_loras)); - let mut data = Vec::new(); - let mut loras_t = Vec::new(); - for lora in standard.chain(high_noise) { - data.push(lora.0); - loras_t.push(lora.1); - } + self.lora_models_internal(standard.chain(high_noise).collect()) + } - self.lora_models = Some(LoraStorage { - lora_model_dir: lora_model_dir.into(), - data, - loras_t, - }); + fn lora_models_internal(&mut self, lora_storage: LoraStorage) -> &mut Self { + self.lora_models = Some(lora_storage); self } @@ -829,19 +806,7 @@ impl From for ModelConfigBuilder { .circular_y(value.circular_y) .use_qwen_image_zero_cond_true(value.use_qwen_image_zero_cond_true); - let lora_model_dir = Into::::into(&value.lora_models.lora_model_dir); - let lora_specs = value - .lora_models - .data - .iter() - .map(|(_, name, multiplier)| LoraSpec { - file_name: name.clone(), - is_high_noise: false, - multiplier: *multiplier, - }) - .collect(); - - builder.lora_models(&lora_model_dir, lora_specs); + builder.lora_models_internal(value.lora_models.clone()); if let Some(model) = &value.upscale_model { builder.upscale_model(model.clone()); @@ -1431,6 +1396,16 @@ fn gen_img_maybe_progress( sd_set_progress_callback(Some(progress_callback), sender_ptr); } + let loras: Vec = model_config + .lora_models + .iter() + .map(|(c_path, spec)| sd_lora_t { + is_high_noise: spec.is_high_noise, + multiplier: spec.multiplier, + path: c_path.as_ptr(), + }) + .collect(); + let sd_img_gen_params = sd_img_gen_params_t { prompt: prompt.as_ptr(), negative_prompt: config.negative_prompt.as_ptr(), @@ -1452,8 +1427,8 @@ fn gen_img_maybe_progress( vae_tiling_params, auto_resize_ref_image: config.disable_auto_resize_ref_image, cache: config.cache, - loras: model_config.lora_models.loras_t.as_ptr(), - lora_count: model_config.lora_models.loras_t.len() as u32, + loras: loras.as_ptr(), + lora_count: loras.len() as u32, }; let params_str = CString::from_raw(sd_img_gen_params_to_str(&sd_img_gen_params)) From 5dd7fddcf07e13c464a162fe0edab0114f1c84b3 Mon Sep 17 00:00:00 2001 From: newfla Date: Fri, 20 Feb 2026 13:32:15 +0100 Subject: [PATCH 2/2] feat: add a fresh collection of flux lora modifiers --- src/modifier.rs | 204 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 189 insertions(+), 15 deletions(-) diff --git a/src/modifier.rs b/src/modifier.rs index 83cff67..f92dfe8 100644 --- a/src/modifier.rs +++ b/src/modifier.rs @@ -104,6 +104,109 @@ pub fn lcm_lora_sdxl_base_1_0(mut builder: ConfigsBuilder) -> Result +pub fn lora_pixel_art_sdxl_base_1_0( + mut builder: ConfigsBuilder, +) -> Result { + let lora_path = download_file_hf_hub("nerijs/pixel-art-xl", "pixel-art-xl.safetensors")?; + + builder.1.lora_models( + lora_path.parent().unwrap(), + vec![LoraSpec { + file_name: "pixel-art-xl".to_string(), + is_high_noise: false, + multiplier: 1.2, + }], + ); + Ok(builder) +} + +/// Apply +pub fn lora_pastelcomic_2_flux(mut builder: ConfigsBuilder) -> Result { + let lora_path = download_file_hf_hub("nerijs/pastelcomic-flux", "pastelcomic_v2.safetensors")?; + + builder.1.lora_models( + lora_path.parent().unwrap(), + vec![LoraSpec { + file_name: "pastelcomic_v2".to_string(), + is_high_noise: false, + multiplier: 1.2, + }], + ); + Ok(builder) +} + +/// Apply +pub fn lora_ghibli_flux(mut builder: ConfigsBuilder) -> Result { + let lora_path = download_file_hf_hub( + "strangerzonehf/Ghibli-Flux-Cartoon-LoRA", + "Ghibili-Cartoon-Art.safetensors", + )?; + + builder.1.lora_models( + lora_path.parent().unwrap(), + vec![LoraSpec { + file_name: "Ghibili-Cartoon-Art".to_string(), + is_high_noise: false, + multiplier: 1.0, + }], + ); + Ok(builder) +} + +/// Apply +pub fn lora_midjourney_mix_2_flux(mut builder: ConfigsBuilder) -> Result { + let lora_path = download_file_hf_hub( + "strangerzonehf/Flux-Midjourney-Mix2-LoRA", + "mjV6.safetensors", + )?; + + builder.1.lora_models( + lora_path.parent().unwrap(), + vec![LoraSpec { + file_name: "mjV6".to_string(), + is_high_noise: false, + multiplier: 1.0, + }], + ); + Ok(builder) +} + +/// Apply +pub fn lora_retro_pixel_flux(mut builder: ConfigsBuilder) -> Result { + let lora_path = download_file_hf_hub( + "prithivMLmods/Retro-Pixel-Flux-LoRA", + "Retro-Pixel.safetensors", + )?; + + builder.1.lora_models( + lora_path.parent().unwrap(), + vec![LoraSpec { + file_name: "Retro-Pixel".to_string(), + is_high_noise: false, + multiplier: 1.0, + }], + ); + Ok(builder) +} + +/// Apply +pub fn lora_canopus_pixar_3d_flux(mut builder: ConfigsBuilder) -> Result { + let lora_path = download_file_hf_hub( + "prithivMLmods/Canopus-Pixar-3D-Flux-LoRA", + "Canopus-Pixar-3D-FluxDev-LoRA.safetensors", + )?; + + builder.1.lora_models( + lora_path.parent().unwrap(), + vec![LoraSpec { + file_name: "Canopus-Pixar-3D-FluxDev-LoRA".to_string(), + is_high_noise: false, + multiplier: 1.0, + }], + ); + Ok(builder) +} /// Apply fp8_e4m3fn t5xxl text encoder to reduce memory usage pub fn t5xxl_fp8_flux_1(mut builder: ConfigsBuilder) -> Result { let t5xxl_path = download_file_hf_hub( @@ -216,7 +319,7 @@ pub fn enable_flash_attention(mut builder: ConfigsBuilder) -> Result to [crate::preset::Preset::SegmindVega] -pub fn segmind_vega_rt_lcm_lora(mut builder: ConfigsBuilder) -> Result { +pub fn lcm_lora_segmind_vega_rt(mut builder: ConfigsBuilder) -> Result { let lora_path = download_file_hf_hub("segmind/Segmind-VegaRT", "pytorch_lora_weights.safetensors")?; builder.1.lora_models( @@ -238,8 +341,10 @@ mod tests { use crate::{ api::gen_img, modifier::{ - enable_flash_attention, lcm_lora_ssd_1b, offload_params_to_cpu, preview_proj, - preview_tae, preview_vae, segmind_vega_rt_lcm_lora, vae_tiling, + enable_flash_attention, lcm_lora_segmind_vega_rt, lcm_lora_ssd_1b, + lora_canopus_pixar_3d_flux, lora_ghibli_flux, lora_midjourney_mix_2_flux, + lora_pastelcomic_2_flux, lora_pixel_art_sdxl_base_1_0, lora_retro_pixel_flux, + offload_params_to_cpu, preview_proj, preview_tae, preview_vae, vae_tiling, }, preset::{ConfigsBuilder, Flux1Weight, Preset, PresetBuilder}, util::set_hf_token, @@ -251,13 +356,13 @@ mod tests { static PROMPT: &str = "a lovely dinosaur made by crochet"; - fn run(preset: Preset, m: F) + fn run(preset: Preset, prompt: &str, m: F) where F: FnOnce(ConfigsBuilder) -> Result + 'static, { let (mut config, mut model_config) = PresetBuilder::default() .preset(preset) - .prompt(PROMPT) + .prompt(prompt) .with_modifier(m) .build() .unwrap(); @@ -267,37 +372,37 @@ mod tests { #[ignore] #[test] fn test_taesd() { - run(Preset::StableDiffusion1_5, taesd); + run(Preset::StableDiffusion1_5, PROMPT, taesd); } #[ignore] #[test] fn test_taesd_xl() { - run(Preset::SDXLTurbo1_0, taesd_xl); + run(Preset::SDXLTurbo1_0, PROMPT, taesd_xl); } #[ignore] #[test] fn test_hybrid_taesd() { - run(Preset::StableDiffusion1_5, hybrid_taesd); + run(Preset::StableDiffusion1_5, PROMPT, hybrid_taesd); } #[ignore] #[test] fn test_hybrid_taesd_xl() { - run(Preset::SDXLTurbo1_0, hybrid_taesd_xl); + run(Preset::SDXLTurbo1_0, PROMPT, hybrid_taesd_xl); } #[ignore] #[test] fn test_lcm_lora_sd_1_5() { - run(Preset::StableDiffusion1_5, lcm_lora_sd_1_5); + run(Preset::StableDiffusion1_5, PROMPT, lcm_lora_sd_1_5); } #[ignore] #[test] fn test_lcm_lora_sdxl_base_1_0() { - run(Preset::SDXLBase1_0, lcm_lora_sdxl_base_1_0); + run(Preset::SDXLBase1_0, PROMPT, lcm_lora_sdxl_base_1_0); } #[ignore] @@ -306,6 +411,7 @@ mod tests { set_hf_token(include_str!("../token.txt")); run( Preset::Flux1Schnell(Flux1Weight::Q2_K), + PROMPT, offload_params_to_cpu, ); } @@ -315,6 +421,7 @@ mod tests { fn test_lcm_lora_ssd_1b() { run( Preset::SSD1B(crate::preset::SSD1BWeight::F8_E4M3), + PROMPT, lcm_lora_ssd_1b, ); } @@ -324,6 +431,7 @@ mod tests { fn test_vae_tiling() { run( Preset::SSD1B(crate::preset::SSD1BWeight::F8_E4M3), + PROMPT, vae_tiling, ); } @@ -331,19 +439,19 @@ mod tests { #[ignore] #[test] fn test_preview_proj() { - run(Preset::SDXLTurbo1_0, preview_proj); + run(Preset::SDXLTurbo1_0, PROMPT, preview_proj); } #[ignore] #[test] fn test_preview_tae() { - run(Preset::SDXLTurbo1_0, preview_tae); + run(Preset::SDXLTurbo1_0, PROMPT, preview_tae); } #[ignore] #[test] fn test_preview_vae() { - run(Preset::SDXLTurbo1_0, preview_vae); + run(Preset::SDXLTurbo1_0, PROMPT, preview_vae); } #[ignore] @@ -352,6 +460,7 @@ mod tests { set_hf_token(include_str!("../token.txt")); run( Preset::Flux1Mini(crate::preset::Flux1MiniWeight::Q2_K), + PROMPT, enable_flash_attention, ); } @@ -359,6 +468,71 @@ mod tests { #[ignore] #[test] fn test_segmind_vega_rt_lcm_lora() { - run(Preset::SegmindVega, segmind_vega_rt_lcm_lora); + run(Preset::SegmindVega, PROMPT, lcm_lora_segmind_vega_rt); + } + + #[ignore] + #[test] + fn test_lora_pixel_art_xl() { + run( + Preset::SDXLBase1_0, + "pixel, a cute corgi", + lora_pixel_art_sdxl_base_1_0, + ); + } + + #[ignore] + #[test] + fn test_lora_pastelcomic_2_flux() { + set_hf_token(include_str!("../token.txt")); + run( + Preset::Flux1Schnell(Flux1Weight::Q2_K), + PROMPT, + lora_pastelcomic_2_flux, + ); + } + + #[ignore] + #[test] + fn test_lora_ghibli_flux() { + set_hf_token(include_str!("../token.txt")); + run( + Preset::Flux1Schnell(Flux1Weight::Q2_K), + "Ghibli Art – A wise old fisherman sits on a wooden dock, gazing out at the vast, blue ocean. He wears a worn-out straw hat and a navy-blue coat, and he holds a fishing rod in his hands. A black cat with bright green eyes sits beside him, watching the waves. In the distance, a lighthouse stands tall against the horizon, with seagulls soaring in the sky. The water glistens under the golden sunset.", + lora_ghibli_flux, + ); + } + + #[ignore] + #[test] + fn test_lora_midjourney_mix_2_flux() { + set_hf_token(include_str!("../token.txt")); + run( + Preset::Flux1Schnell(Flux1Weight::Q2_K), + "MJ v6, delicious dipped chocolate pastry japo gallery, white background, in the style of dark brown, close-up intensity, duckcore, rounded, high resolution --ar 2:3 --v 5", + lora_midjourney_mix_2_flux, + ); + } + + #[ignore] + #[test] + fn test_lora_retro_pixel_flux() { + set_hf_token(include_str!("../token.txt")); + run( + Preset::Flux1Schnell(Flux1Weight::Q2_K), + "Retro Pixel, pixel art of a Hamburger in the style of an old video game, hero, pixelated 8bit, final boss ", + lora_retro_pixel_flux, + ); + } + + #[ignore] + #[test] + fn test_lora_canopus_pixar_3d_flux() { + set_hf_token(include_str!("../token.txt")); + run( + Preset::Flux1Schnell(Flux1Weight::Q2_K), + "A young man with light brown wavy hair and light brown eyes sitting in an armchair and looking directly at the camera, pixar style, disney pixar, office background, ultra detailed, 1 man", + lora_canopus_pixar_3d_flux, + ); } }