mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 17:38:10 +00:00
load flux models from R2
This commit is contained in:
1 parent
c27055761e
commit
aa25ae8c87
3 files changed
+97
-88
No files matched your search
@@ -1,3 +1,4 @@
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
use crate::ml::image::dynamic_image_to_tensor::dynamic_image_to_tensor;
|
||||
@@ -11,7 +12,7 @@ use candle_core::{DType, IndexOp, Tensor, D, Device, Module};
|
||||
use candle_transformers::models::stable_diffusion::vae::{AutoEncoderKL, DiagonalGaussianDistribution};
|
||||
use candle_transformers::models::stable_diffusion::lcm::LCMScheduler;
|
||||
use image::DynamicImage;
|
||||
use log::info;
|
||||
use log::{debug, info};
|
||||
use rand::Rng;
|
||||
use tauri::{AppHandle, Emitter};
|
||||
use candle_transformers::models::{clip, t5, flux};
|
||||
@@ -38,10 +39,41 @@ pub struct FluxPipelineArgs<'a> {
|
||||
pub seed: Option<u64>,
|
||||
pub use_quantized_model: Option<bool>,
|
||||
pub model_config: &'a ModelConfig,
|
||||
pub flux_dev_or_schnell_path: &'a Path,
|
||||
pub flux_schnell_quantized_gguf: &'a Path,
|
||||
pub flux_autoencoder_path: &'a Path,
|
||||
pub google_t5_model_path: &'a Path,
|
||||
pub google_t5_config_path: &'a Path,
|
||||
pub lmz_t5_tokenizer_path: &'a Path,
|
||||
pub openai_clip_model_path: &'a Path,
|
||||
pub openai_clip_tokenizer_json_path: &'a Path,
|
||||
}
|
||||
|
||||
pub fn flux_pipeline(args: FluxPipelineArgs<'_>) -> Result<RgbImage> {
|
||||
let FluxPipelineArgs { prompt, model_cache, prompt_cache, app, image, flux_model, active_blocks, use_cuda_stream, prefetch_next_batch, use_full_gpu, img2img_strength, seed, use_quantized_model, model_config } = args;
|
||||
let FluxPipelineArgs {
|
||||
prompt,
|
||||
model_cache,
|
||||
prompt_cache,
|
||||
app,
|
||||
image,
|
||||
flux_model,
|
||||
active_blocks,
|
||||
use_cuda_stream,
|
||||
prefetch_next_batch,
|
||||
use_full_gpu,
|
||||
img2img_strength,
|
||||
seed,
|
||||
use_quantized_model,
|
||||
model_config ,
|
||||
flux_dev_or_schnell_path,
|
||||
flux_schnell_quantized_gguf,
|
||||
flux_autoencoder_path,
|
||||
google_t5_model_path,
|
||||
google_t5_config_path,
|
||||
lmz_t5_tokenizer_path,
|
||||
openai_clip_model_path,
|
||||
openai_clip_tokenizer_json_path,
|
||||
} = args;
|
||||
|
||||
if use_quantized_model.unwrap_or(false) {
|
||||
#[cfg(feature = "cuda")]
|
||||
@@ -55,14 +87,15 @@ pub fn flux_pipeline(args: FluxPipelineArgs<'_>) -> Result<RgbImage> {
|
||||
let device = model_config.device.clone();
|
||||
let dtype = device.bf16_default_to_f32();
|
||||
|
||||
println!("Starting image generation with FLUX model:");
|
||||
println!(" Model: {:?}", flux_model);
|
||||
println!(" Prompt: {}", prompt);
|
||||
println!(" Dimensions: {}x{}", width, height);
|
||||
println!(" Device: {:?}", model_config.device);
|
||||
info!("Starting image generation with FLUX model:");
|
||||
info!(" Model: {:?}", flux_model);
|
||||
info!(" Prompt: {}", prompt);
|
||||
info!(" Dimensions: {}x{}", width, height);
|
||||
info!(" Device: {:?}", model_config.device);
|
||||
|
||||
let seed_value = seed.unwrap_or_else(|| rand::thread_rng().gen());
|
||||
device.set_seed(seed_value)?;
|
||||
|
||||
info!("Using seed: {}", seed_value);
|
||||
|
||||
// Setup FLUX model configuration
|
||||
@@ -71,48 +104,30 @@ pub fn flux_pipeline(args: FluxPipelineArgs<'_>) -> Result<RgbImage> {
|
||||
FluxModel::Schnell => flux::model::Config::schnell(),
|
||||
};
|
||||
|
||||
let bf_repo = match flux_model {
|
||||
FluxModel::Dev => model_config.hf_api.repo(hf_hub::Repo::model("black-forest-labs/FLUX.1-dev".to_string())),
|
||||
FluxModel::Schnell => model_config.hf_api.repo(hf_hub::Repo::model("black-forest-labs/FLUX.1-schnell".to_string())),
|
||||
};
|
||||
// Get T5 embeddings
|
||||
println!("Generating T5 embeddings for prompt...");
|
||||
let t5_emb = {
|
||||
let repo = model_config.hf_api.repo(hf_hub::Repo::with_revision("google/t5-v1_1-xxl".to_string(), hf_hub::RepoType::Model, "refs/pr/2".to_string()));
|
||||
println!("Downloading model (1)");
|
||||
let model_file = repo.get("model.safetensors")?;
|
||||
println!("model downloaded (1)");
|
||||
let vb = unsafe { VarBuilder::from_mmaped_safetensors(&[model_file], dtype , &device)? };
|
||||
println!("Downloading model (2)");
|
||||
let config_filename = repo.get("config.json")?;
|
||||
println!("model downloaded (2)");
|
||||
let config = std::fs::read_to_string(config_filename)?;
|
||||
info!("Generating T5 embeddings for prompt...");
|
||||
let vb = unsafe { VarBuilder::from_mmaped_safetensors(&[google_t5_model_path], dtype , &device)? };
|
||||
let config = std::fs::read_to_string(google_t5_config_path)?;
|
||||
let config: t5::Config = serde_json::from_str(&config)?;
|
||||
let mut model = t5::T5EncoderModel::load(vb, &config)?;
|
||||
println!("Downloading model (3)");
|
||||
let tokenizer_filename = model_config.hf_api.model("lmz/mt5-tokenizers".to_string()).get("t5-v1_1-xxl.tokenizer.json")?;
|
||||
println!("model downloaded (3)");
|
||||
let tokenizer = Tokenizer::from_file(tokenizer_filename).map_err(E::msg)?;
|
||||
|
||||
println!("encoding");
|
||||
let mut model = t5::T5EncoderModel::load(vb, &config)?;
|
||||
let tokenizer = Tokenizer::from_file(lmz_t5_tokenizer_path).map_err(E::msg)?;
|
||||
|
||||
let mut tokens = tokenizer.encode(prompt.as_str(), true).map_err(E::msg)?.get_ids().to_vec();
|
||||
tokens.resize(256, 0);
|
||||
let input_token_ids = Tensor::new(&tokens[..], &device)?.unsqueeze(0)?;
|
||||
|
||||
println!("forward");
|
||||
model.forward(&input_token_ids)?
|
||||
};
|
||||
println!("T5 embeddings generated");
|
||||
|
||||
info!("T5 embeddings generated");
|
||||
|
||||
// Get CLIP embeddings
|
||||
println!("Generating CLIP embeddings for prompt...");
|
||||
let clip_emb = {
|
||||
let repo = model_config.hf_api.repo(hf_hub::Repo::model(
|
||||
"openai/clip-vit-large-patch14".to_string(),
|
||||
));
|
||||
let model_file = repo.get("model.safetensors")?;
|
||||
info!("Generating CLIP embeddings for prompt...");
|
||||
let vb =
|
||||
unsafe { VarBuilder::from_mmaped_safetensors(&[model_file], dtype, &device)? };
|
||||
unsafe { VarBuilder::from_mmaped_safetensors(&[openai_clip_model_path], dtype, &device)? };
|
||||
// https://huggingface.co/openai/clip-vit-large-patch14/blob/main/config.json
|
||||
let config = clip::text_model::ClipTextConfig {
|
||||
vocab_size: 49408,
|
||||
@@ -127,37 +142,34 @@ pub fn flux_pipeline(args: FluxPipelineArgs<'_>) -> Result<RgbImage> {
|
||||
};
|
||||
let model =
|
||||
clip::text_model::ClipTextTransformer::new(vb.pp("text_model"), &config)?;
|
||||
let tokenizer_filename = repo.get("tokenizer.json")?;
|
||||
let tokenizer = Tokenizer::from_file(tokenizer_filename).map_err(E::msg)?;
|
||||
let tokenizer = Tokenizer::from_file(openai_clip_tokenizer_json_path).map_err(E::msg)?;
|
||||
let tokens = tokenizer
|
||||
.encode(prompt.as_str(), true)
|
||||
.map_err(E::msg)?
|
||||
.get_ids()
|
||||
.to_vec();
|
||||
let input_token_ids = Tensor::new(&tokens[..], &device)?.unsqueeze(0)?;
|
||||
|
||||
|
||||
model.forward(&input_token_ids)?
|
||||
};
|
||||
println!("CLIP embeddings generated");
|
||||
info!("CLIP embeddings generated");
|
||||
|
||||
// Process input image to tensor
|
||||
println!("Converting input image to tensor...");
|
||||
info!("Converting input image to tensor...");
|
||||
let img_tensor = dynamic_image_to_tensor(image, &device, dtype)?;
|
||||
println!("Image tensor shape: {:?}", img_tensor.shape());
|
||||
info!("Image tensor shape: {:?}", img_tensor.shape());
|
||||
|
||||
// Create initial noise - this is important to match the expected format
|
||||
println!("Generating initial noise for FLUX...");
|
||||
info!("Generating initial noise for FLUX...");
|
||||
let initial_tensor = flux::sampling::get_noise(1, height, width, &device)?.to_dtype(dtype)?;
|
||||
println!("Initial tensor shape: {:?}", initial_tensor.shape());
|
||||
info!("Initial tensor shape: {:?}", initial_tensor.shape());
|
||||
|
||||
// Blend initial noise with input image if img2img_strength is specified
|
||||
let initial_tensor = if let Some(strength) = img2img_strength {
|
||||
if strength > 0.0 && strength < 1.0 {
|
||||
println!("Using img2img with strength: {}", strength);
|
||||
info!("Using img2img with strength: {}", strength);
|
||||
// Reshape the input tensor to match the noise tensor dimensions
|
||||
let adjusted_img = if img_tensor.shape() != initial_tensor.shape() {
|
||||
println!("Reshaping input image to match noise dimensions...");
|
||||
info!("Reshaping input image to match noise dimensions...");
|
||||
// You may need more complex reshaping logic here depending on your use case
|
||||
let (_, c, h, w) = initial_tensor.dims4()?;
|
||||
img_tensor.upsample_nearest2d(h, w)?
|
||||
@@ -177,10 +189,10 @@ pub fn flux_pipeline(args: FluxPipelineArgs<'_>) -> Result<RgbImage> {
|
||||
} else {
|
||||
initial_tensor
|
||||
};
|
||||
println!("Final initial tensor shape: {:?}", initial_tensor.shape());
|
||||
info!("Final initial tensor shape: {:?}", initial_tensor.shape());
|
||||
|
||||
// Create FLUX state
|
||||
println!("Creating FLUX state...");
|
||||
info!("Creating FLUX state...");
|
||||
let state = if quantized {
|
||||
flux::sampling::State::new(
|
||||
&t5_emb.to_dtype(DType::F32)?,
|
||||
@@ -190,7 +202,7 @@ pub fn flux_pipeline(args: FluxPipelineArgs<'_>) -> Result<RgbImage> {
|
||||
} else {
|
||||
flux::sampling::State::new(&t5_emb, &clip_emb, &initial_tensor)?
|
||||
};
|
||||
println!("{state:?}");
|
||||
info!("{state:?}");
|
||||
|
||||
// Set appropriate timesteps based on model
|
||||
let timesteps = match flux_model {
|
||||
@@ -198,98 +210,88 @@ pub fn flux_pipeline(args: FluxPipelineArgs<'_>) -> Result<RgbImage> {
|
||||
FluxModel::Schnell => flux::sampling::get_schedule(4, None),
|
||||
};
|
||||
|
||||
println!("Starting FLUX denoising with timesteps: {:?}", timesteps);
|
||||
info!("Starting FLUX denoising with timesteps: {:?}", timesteps);
|
||||
|
||||
// Actual model loading and inference
|
||||
let img_latent = if quantized {
|
||||
// Quantized model path
|
||||
println!("Loading quantized FLUX model...");
|
||||
info!("Loading quantized FLUX model...");
|
||||
let model_file = match flux_model {
|
||||
FluxModel::Schnell => model_config.hf_api.repo(hf_hub::Repo::model("lmz/candle-flux".to_string())).get("flux1-schnell.gguf")?,
|
||||
FluxModel::Schnell => flux_schnell_quantized_gguf,
|
||||
FluxModel::Dev => return Err(anyhow!("Quantized model not available for FLUX.1-dev")),
|
||||
};
|
||||
|
||||
println!("Loading quantized model from {}", model_file.to_str().unwrap());
|
||||
info!("Loading quantized model from {}", model_file.to_str().unwrap());
|
||||
|
||||
let vb = candle_transformers::quantized_var_builder::VarBuilder::from_gguf(model_file, &device)?;
|
||||
let mut model = flux::quantized_model::Flux::new(&flux_config, vb)?;
|
||||
println!("Quantized model loaded on {:?}", device);
|
||||
|
||||
info!("Quantized model loaded on {:?}", device);
|
||||
|
||||
// Run denoising
|
||||
println!("Running denoising...");
|
||||
info!("Running denoising...");
|
||||
let img_latent = flux::sampling::denoise(&mut model, &state.img, &state.img_ids, &state.txt, &state.txt_ids, &state.vec, ×teps, 4.)?.to_dtype(dtype)?;
|
||||
println!("Denoising completed");
|
||||
info!("Denoising completed");
|
||||
img_latent
|
||||
} else {
|
||||
// Full precision model
|
||||
println!("Loading FLUX model...");
|
||||
let model_file = match flux_model {
|
||||
FluxModel::Schnell => bf_repo.get("flux1-schnell.safetensors")?,
|
||||
FluxModel::Dev => bf_repo.get("flux1-dev.safetensors")?,
|
||||
};
|
||||
|
||||
info!("Loading FLUX model...");
|
||||
|
||||
let mut model = if use_full_gpu.unwrap_or(false) {
|
||||
// Load entire model on GPU
|
||||
println!("Loading entire model directly to GPU...");
|
||||
let gpu_vb = unsafe { VarBuilder::from_mmaped_safetensors(&[model_file], dtype, &device)? };
|
||||
info!("Loading entire model directly to GPU...");
|
||||
let gpu_vb = unsafe { VarBuilder::from_mmaped_safetensors(&[flux_dev_or_schnell_path], dtype, &device)? };
|
||||
let mut model = flux::model::Flux::new(&flux_config, gpu_vb)?;
|
||||
model.set_use_device_management(false);
|
||||
model
|
||||
} else {
|
||||
// Use memory-efficient approach with blocks on CPU
|
||||
println!("Using memory-efficient approach with blocks on CPU...");
|
||||
info!("Using memory-efficient approach with blocks on CPU...");
|
||||
let cpu_device = candle_core::Device::Cpu;
|
||||
let cpu_vb = unsafe { VarBuilder::from_mmaped_safetensors(&[model_file], dtype, &cpu_device)? };
|
||||
let cpu_vb = unsafe { VarBuilder::from_mmaped_safetensors(&[flux_dev_or_schnell_path], dtype, &cpu_device)? };
|
||||
let mut model = flux::model::Flux::new_with_gpu_core(&flux_config, cpu_vb, &device)?;
|
||||
|
||||
// Set active blocks
|
||||
let active_blocks_count = active_blocks.unwrap_or(1);
|
||||
model.set_max_active_blocks(active_blocks_count);
|
||||
println!("Using {} active blocks on GPU", active_blocks_count);
|
||||
info!("Using {} active blocks on GPU", active_blocks_count);
|
||||
|
||||
// Enable prefetching if requested
|
||||
if prefetch_next_batch.unwrap_or(false) {
|
||||
model.set_prefetch_next_batch(true);
|
||||
println!("Prefetching enabled - will overlap transfers with computation");
|
||||
info!("Prefetching enabled - will overlap transfers with computation");
|
||||
}
|
||||
|
||||
model
|
||||
};
|
||||
|
||||
|
||||
// Run denoising
|
||||
flux::sampling::denoise(&mut model, &state.img, &state.img_ids, &state.txt, &state.txt_ids, &state.vec, ×teps, 4.)?
|
||||
};
|
||||
|
||||
println!("FLUX denoising completed, unpacking latent image...");
|
||||
info!("FLUX denoising completed, unpacking latent image...");
|
||||
let img_unpacked = flux::sampling::unpack(&img_latent, height, width)?;
|
||||
|
||||
// Load autoencoder for final decoding
|
||||
println!("Loading autoencoder for final decoding...");
|
||||
info!("Loading autoencoder for final decoding...");
|
||||
|
||||
let ae_config = match flux_model {
|
||||
FluxModel::Dev => flux::autoencoder::Config::dev(),
|
||||
FluxModel::Schnell => flux::autoencoder::Config::schnell(),
|
||||
};
|
||||
|
||||
let model_file = bf_repo.get("ae.safetensors")?;
|
||||
|
||||
|
||||
let vb = unsafe { VarBuilder::from_mmaped_safetensors(&[model_file], dtype, &device)? };
|
||||
let vb = unsafe { VarBuilder::from_mmaped_safetensors(&[flux_autoencoder_path], dtype, &device)? };
|
||||
let ae_model = flux::autoencoder::AutoEncoder::new(&ae_config, vb)?;
|
||||
|
||||
|
||||
println!("Decoding final image...");
|
||||
info!("Decoding final image...");
|
||||
let img = ae_model.decode(&img_unpacked)?;
|
||||
|
||||
// Convert tensor to image
|
||||
println!("Converting tensor to image...");
|
||||
info!("Converting tensor to image...");
|
||||
let img = ((img.clamp(-1f32, 1f32)? + 1.0)? * 127.5)?.to_dtype(candle_core::DType::U8)?;
|
||||
|
||||
// Convert to RGB image
|
||||
let image = tensor_to_image_buffer(&img.i(0)?)?;
|
||||
|
||||
println!("FLUX pipeline completed successfully");
|
||||
info!("FLUX pipeline completed successfully");
|
||||
Ok(image)
|
||||
}
|
||||
@@ -1,3 +1 @@
|
||||
pub mod flux_pipeline;
|
||||
|
||||
pub use flux_pipeline::{flux_pipeline, FluxPipelineArgs, FluxModel};
|
||||
pub mod flux_pipeline;
|
||||
@@ -1,22 +1,20 @@
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
use ml_models::ml::flux::flux_pipeline::{flux_pipeline, FluxPipelineArgs, FluxModel};
|
||||
use ml_models::ml::flux::flux_pipeline::{flux_pipeline, FluxModel, FluxPipelineArgs};
|
||||
|
||||
use crate::state::app_config::AppConfig;
|
||||
use crate::state::app_dir::AppDataRoot;
|
||||
use crate::state::app_dir::{AppDataRoot, AppWeightsDir};
|
||||
use crate::stubs::model_cache::ModelCache;
|
||||
use crate::stubs::prompt_cache::PromptCache;
|
||||
use crate::utils::image::encode_dynamic_image_base64_png::encode_dynamic_image_base64_png;
|
||||
use image::imageops::FilterType;
|
||||
use image::{DynamicImage, ImageReader, RgbImage};
|
||||
use log::{error, info};
|
||||
use ml_weights_registry::weights_registry::weights::{CLIP_JSON, LYKON_DEAMSHAPER_7_VAE, SDXL_TURBO_CLIP_TEXT_ENCODER, SIMIANLUO_LCM_DREAMSHAPER_V7_UNET};
|
||||
|
||||
use ml_weights_registry::weights_registry::flux_weights::{FLUX_DEV, FLUX_SCHNELL, FLUX_SCHNELL_AUTOENCODER, GOOGLE_T5_V1_1_XXL_CONFIG, GOOGLE_T5_V1_1_XXL_MODEL, LMZ_CANDLE_FLUX_SCHNELL_QUANTIZED_GGUF, LMZ_T5_TOKENIZER_JSON, OPENAI_CLIP_VIT_P14_MODEL, OPENAI_CLIP_VIT_P14_TOKENIZER_JSON};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::io::Cursor;
|
||||
use tauri::{AppHandle, State};
|
||||
|
||||
const RANDOM_SEED: u32 = 42;
|
||||
|
||||
const STRENGTH: f64 = 100.0;
|
||||
const PNG_BYTES : &[u8] = include_bytes!("../../binary_includes/1024.png");
|
||||
|
||||
@@ -36,6 +34,7 @@ pub async fn text_to_image(
|
||||
model_cache: State<'_, ModelCache>,
|
||||
prompt_cache: State<'_, PromptCache>,
|
||||
app: AppHandle,
|
||||
app_data_root: State<'_, AppDataRoot>,
|
||||
) -> Result<String, String> {
|
||||
info!("text_to_image endpoint called.");
|
||||
|
||||
@@ -50,6 +49,7 @@ pub async fn text_to_image(
|
||||
&model_config,
|
||||
&model_cache,
|
||||
prompt_cache,
|
||||
app_data_root.weights_dir(),
|
||||
app,
|
||||
).await;
|
||||
|
||||
@@ -78,6 +78,7 @@ async fn text_to_image_impl(
|
||||
config: &AppConfig,
|
||||
model_cache: &ModelCache,
|
||||
prompt_cache: State<'_, PromptCache>,
|
||||
weights_dir: &AppWeightsDir,
|
||||
app: AppHandle,
|
||||
) -> Result<RgbImage, String> {
|
||||
|
||||
@@ -96,6 +97,14 @@ async fn text_to_image_impl(
|
||||
seed: None,
|
||||
use_quantized_model: Some(true),
|
||||
model_config: &config.model_config,
|
||||
flux_dev_or_schnell_path: &weights_dir.weight_path(&FLUX_SCHNELL),
|
||||
flux_schnell_quantized_gguf: &weights_dir.weight_path(&LMZ_CANDLE_FLUX_SCHNELL_QUANTIZED_GGUF),
|
||||
flux_autoencoder_path: &weights_dir.weight_path(&FLUX_SCHNELL_AUTOENCODER),
|
||||
google_t5_model_path: &weights_dir.weight_path(&GOOGLE_T5_V1_1_XXL_MODEL),
|
||||
google_t5_config_path: &weights_dir.weight_path(&GOOGLE_T5_V1_1_XXL_CONFIG),
|
||||
lmz_t5_tokenizer_path: &weights_dir.weight_path(&LMZ_T5_TOKENIZER_JSON),
|
||||
openai_clip_model_path: &weights_dir.weight_path(&OPENAI_CLIP_VIT_P14_MODEL),
|
||||
openai_clip_tokenizer_json_path: &weights_dir.weight_path(&OPENAI_CLIP_VIT_P14_TOKENIZER_JSON),
|
||||
};
|
||||
|
||||
let image = flux_pipeline(args)
|
||||
|
||||
Reference in new issue
Block a user