Browse Source

Fix model loading progress callback

- Ensure model loading progress starts from 0.0 instead of 0.1
- Add progress validation and clamping to ensure values stay within 0.0-1.0 range
- Fix transition from model loading to generation phase to set overall progress to exactly 50%
- Improve progress tracking consistency throughout the loading process
Fszontagh 3 tháng trước cách đây
mục cha
commit
924751e0ec
2 tập tin đã thay đổi với 285 bổ sung232 xóa
  1. 4 1
      src/generation_queue.cpp
  2. 281 231
      src/model_manager.cpp

+ 4 - 1
src/generation_queue.cpp

@@ -252,6 +252,9 @@ public:
         std::lock_guard<std::mutex> lock(jobsMutex);
         auto it = activeJobs.find(jobId);
         if (it != activeJobs.end()) {
+            // Clamp model loading progress to valid range [0.0, 1.0]
+            modelProgress = std::max(0.0f, std::min(1.0f, modelProgress));
+            
             it->second.modelLoadProgress = modelProgress;
             it->second.generationProgress = 0.0f;
             // Overall progress during model loading is just the model loading progress
@@ -390,7 +393,7 @@ public:
                     if (bracketPos != std::string::npos) {
                         activeJobs[request.id].prompt = activeJobs[request.id].prompt.substr(0, bracketPos);
                     }
-                    // Set model loading to complete, overall progress to 50% (halfway through)
+                    // Set model loading to complete, overall progress to exactly 50% (halfway through)
                     activeJobs[request.id].modelLoadProgress = 1.0f;
                     activeJobs[request.id].generationProgress = 0.0f;
                     activeJobs[request.id].progress = 0.5f;

+ 281 - 231
src/model_manager.cpp

@@ -1,30 +1,30 @@
 #include "model_manager.h"
-#include "model_detector.h"
-#include "stable_diffusion_wrapper.h"
-#include "logger.h"
-#include <iostream>
-#include <fstream>
+#include <openssl/evp.h>
 #include <algorithm>
-#include <filesystem>
-#include <shared_mutex>
+#include <atomic>
 #include <chrono>
+#include <filesystem>
+#include <fstream>
 #include <future>
-#include <atomic>
-#include <set>
-#include <openssl/evp.h>
-#include <sstream>
 #include <iomanip>
+#include <iostream>
 #include <nlohmann/json.hpp>
+#include <set>
+#include <shared_mutex>
+#include <sstream>
+#include "logger.h"
+#include "model_detector.h"
+#include "stable_diffusion_wrapper.h"
 
 namespace fs = std::filesystem;
 
 // File extension mappings for each model type
 const std::vector<std::string> CHECKPOINT_FILE_EXTENSIONS = {"safetensors", "ckpt", "gguf"};
-const std::vector<std::string> EMBEDDING_FILE_EXTENSIONS = {"safetensors", "pt"};
-const std::vector<std::string> LORA_FILE_EXTENSIONS = {"safetensors", "ckpt"};
-const std::vector<std::string> VAE_FILE_EXTENSIONS = {"safetensors", "pt", "ckpt", "gguf"};
-const std::vector<std::string> TAESD_FILE_EXTENSIONS = {"safetensors", "pth", "gguf"};
-const std::vector<std::string> ESRGAN_FILE_EXTENSIONS = {"pth", "pt"};
+const std::vector<std::string> EMBEDDING_FILE_EXTENSIONS  = {"safetensors", "pt"};
+const std::vector<std::string> LORA_FILE_EXTENSIONS       = {"safetensors", "ckpt"};
+const std::vector<std::string> VAE_FILE_EXTENSIONS        = {"safetensors", "pt", "ckpt", "gguf"};
+const std::vector<std::string> TAESD_FILE_EXTENSIONS      = {"safetensors", "pth", "gguf"};
+const std::vector<std::string> ESRGAN_FILE_EXTENSIONS     = {"pth", "pt"};
 const std::vector<std::string> CONTROLNET_FILE_EXTENSIONS = {"safetensors", "pth"};
 
 class ModelManager::Impl {
@@ -182,7 +182,7 @@ public:
         // First check configured directories (if any)
         for (const auto& [type, directory] : modelTypeDirectories) {
             if (!directory.empty()) {
-                fs::path absoluteDirPath = fs::absolute(directory).lexically_normal();
+                fs::path absoluteDirPath    = fs::absolute(directory).lexically_normal();
                 fs::path normalizedFilePath = absoluteFilePath.lexically_normal();
 
                 // Check if the file is under this directory (directly or in subdirectories)
@@ -191,9 +191,9 @@ public:
 
                 // If relative path doesn't start with "..", then file is under the directory
                 std::string relPathStr = relativePath.string();
-                bool isUnderDirectory = !relPathStr.empty() &&
-                                       relPathStr.substr(0, 2) != ".." &&
-                                       relPathStr[0] != '/';
+                bool isUnderDirectory  = !relPathStr.empty() &&
+                                        relPathStr.substr(0, 2) != ".." &&
+                                        relPathStr[0] != '/';
 
                 if (isUnderDirectory && isExtensionMatch(extension, type)) {
                     return type;
@@ -263,11 +263,11 @@ public:
      * @return std::pair<bool, std::pair<uintmax_t, fs::file_time_type>> Success flag and file info
      */
     std::pair<bool, std::pair<uintmax_t, fs::file_time_type>> getFileInfoWithTimeout(
-        const fs::path& filePath, int timeoutMs = 5000) {
-
+        const fs::path& filePath,
+        int timeoutMs = 5000) {
         auto future = std::async(std::launch::async, [&filePath]() -> std::pair<uintmax_t, fs::file_time_type> {
             try {
-                uintmax_t fileSize = fs::file_size(filePath);
+                uintmax_t fileSize            = fs::file_size(filePath);
                 fs::file_time_type modifiedAt = fs::last_write_time(filePath);
                 return {fileSize, modifiedAt};
             } catch (const fs::filesystem_error&) {
@@ -312,7 +312,7 @@ public:
                 }
 
                 if (entry.is_regular_file()) {
-                    fs::path filePath = entry.path();
+                    fs::path filePath      = entry.path();
                     ModelType detectedType = determineModelType(filePath);
 
                     // Only add files that have a valid model type (not NONE)
@@ -350,24 +350,24 @@ public:
 
                         // Check if model already exists to avoid duplicates
                         if (modelsMap.find(modelName) == modelsMap.end()) {
-                            info.name = modelName;
-                            info.path = filePath.string();
-                            info.fullPath = fs::absolute(filePath).string();
-                            info.type = detectedType;
-                            info.isLoaded = false;
-                            info.description = ""; // Initialize description
-                            info.metadata = {}; // Initialize metadata
+                            info.name        = modelName;
+                            info.path        = filePath.string();
+                            info.fullPath    = fs::absolute(filePath).string();
+                            info.type        = detectedType;
+                            info.isLoaded    = false;
+                            info.description = "";  // Initialize description
+                            info.metadata    = {};  // Initialize metadata
 
                             // Get file info with timeout
                             auto [success, fileInfo] = getFileInfoWithTimeout(filePath);
                             if (success) {
-                                info.fileSize = fileInfo.first;
+                                info.fileSize   = fileInfo.first;
                                 info.modifiedAt = fileInfo.second;
-                                info.createdAt = fileInfo.second; // Use modified time as creation time for now
+                                info.createdAt  = fileInfo.second;  // Use modified time as creation time for now
                             } else {
-                                info.fileSize = 0;
+                                info.fileSize   = 0;
                                 info.modifiedAt = fs::file_time_type{};
-                                info.createdAt = fs::file_time_type{};
+                                info.createdAt  = fs::file_time_type{};
                             }
 
                             // Try to load cached hash from .json file
@@ -382,10 +382,10 @@ public:
                                         info.sha256 = "";
                                     }
                                 } catch (...) {
-                                    info.sha256 = ""; // If parsing fails, leave empty
+                                    info.sha256 = "";  // If parsing fails, leave empty
                                 }
                             } else {
-                                info.sha256 = ""; // No cached hash file
+                                info.sha256 = "";  // No cached hash file
                             }
 
                             // Detect architecture for checkpoint models (including diffusion_models)
@@ -393,23 +393,23 @@ public:
                                 // Try to get cached result first
                                 ModelDetectionCache::CacheEntry cachedEntry =
                                     ModelDetectionCache::getCachedResult(info.fullPath, info.modifiedAt);
-                                
+
                                 if (cachedEntry.isValid) {
                                     // Use cached results
-                                    info.architecture = cachedEntry.architecture;
-                                    info.recommendedVAE = cachedEntry.recommendedVAE;
-                                    info.recommendedWidth = cachedEntry.recommendedWidth;
-                                    info.recommendedHeight = cachedEntry.recommendedHeight;
-                                    info.recommendedSteps = cachedEntry.recommendedSteps;
-                                    info.recommendedSampler = cachedEntry.recommendedSampler;
-                                    info.requiredModels = cachedEntry.requiredModels;
-                                    info.missingModels = cachedEntry.missingModels;
-                                    info.cacheValid = true;
-                                    info.cacheModifiedAt = cachedEntry.cachedAt;
-                                    info.cachePathType = cachedEntry.pathType;
+                                    info.architecture            = cachedEntry.architecture;
+                                    info.recommendedVAE          = cachedEntry.recommendedVAE;
+                                    info.recommendedWidth        = cachedEntry.recommendedWidth;
+                                    info.recommendedHeight       = cachedEntry.recommendedHeight;
+                                    info.recommendedSteps        = cachedEntry.recommendedSteps;
+                                    info.recommendedSampler      = cachedEntry.recommendedSampler;
+                                    info.requiredModels          = cachedEntry.requiredModels;
+                                    info.missingModels           = cachedEntry.missingModels;
+                                    info.cacheValid              = true;
+                                    info.cacheModifiedAt         = cachedEntry.cachedAt;
+                                    info.cachePathType           = cachedEntry.pathType;
                                     info.useFolderBasedDetection = (cachedEntry.detectionSource == "folder");
-                                    info.detectionSource = cachedEntry.detectionSource;
-                                    
+                                    info.detectionSource         = cachedEntry.detectionSource;
+
                                     if (verbose) {
                                         std::cout << "Using cached detection for " << info.name
                                                   << " (source: " << cachedEntry.detectionSource << ")" << std::endl;
@@ -418,57 +418,55 @@ public:
                                     // Perform new detection
                                     try {
                                         // First try folder-based detection
-                                        std::string checkpointsDir = getModelTypeDirectory(ModelType::CHECKPOINT);
+                                        std::string checkpointsDir     = getModelTypeDirectory(ModelType::CHECKPOINT);
                                         std::string diffusionModelsDir = getModelTypeDirectory(ModelType::DIFFUSION_MODELS);
-                                        std::string pathType = ModelPathSelector::selectPathType(
+                                        std::string pathType           = ModelPathSelector::selectPathType(
                                             info.fullPath, checkpointsDir, diffusionModelsDir, verbose);
-                                        
+
                                         bool useFolderBasedDetection = (pathType == "diffusion_model_path");
-                                        
+
                                         ModelDetectionResult detection;
                                         std::string detectionSource;
-                                        
+
                                         if (useFolderBasedDetection) {
                                             // For models in diffusion_models directory, we can skip full detection
                                             // and use folder-based logic
-                                            detectionSource = "folder";
-                                            info.architecture = "Modern Architecture (Flux/SD3)";
-                                            info.recommendedVAE = "ae.safetensors";
-                                            info.recommendedWidth = 1024;
-                                            info.recommendedHeight = 1024;
-                                            info.recommendedSteps = 20;
+                                            detectionSource         = "folder";
+                                            info.architecture       = "Modern Architecture (Flux/SD3)";
+                                            info.recommendedVAE     = "ae.safetensors";
+                                            info.recommendedWidth   = 1024;
+                                            info.recommendedHeight  = 1024;
+                                            info.recommendedSteps   = 20;
                                             info.recommendedSampler = "euler";
-                                            
+
                                             // Create a minimal detection result for caching
-                                            detection.architecture = ModelArchitecture::FLUX_DEV; // Default modern
-                                            detection.architectureName = info.architecture;
-                                            detection.recommendedVAE = info.recommendedVAE;
-                                            detection.suggestedParams["width"] = std::to_string(info.recommendedWidth);
-                                            detection.suggestedParams["height"] = std::to_string(info.recommendedHeight);
-                                            detection.suggestedParams["steps"] = std::to_string(info.recommendedSteps);
+                                            detection.architecture               = ModelArchitecture::FLUX_DEV;  // Default modern
+                                            detection.architectureName           = info.architecture;
+                                            detection.recommendedVAE             = info.recommendedVAE;
+                                            detection.suggestedParams["width"]   = std::to_string(info.recommendedWidth);
+                                            detection.suggestedParams["height"]  = std::to_string(info.recommendedHeight);
+                                            detection.suggestedParams["steps"]   = std::to_string(info.recommendedSteps);
                                             detection.suggestedParams["sampler"] = info.recommendedSampler;
-                                            
-                                            if (verbose) {
-                                                std::cout << "Using folder-based detection for " << info.name
-                                                          << " in " << pathType << std::endl;
-                                            }
+
+                                            LOG_DEBUG("Using folder-based detection for: " + info.name + " in " + pathType);
+
                                         } else {
                                             // Perform full architecture detection
                                             detectionSource = "architecture";
-                                            detection = ModelDetector::detectModel(info.fullPath);
-                                            
+                                            detection       = ModelDetector::detectModel(info.fullPath);
+
                                             // For .ckpt files that can't be detected, default to SD1.5
                                             if (detection.architecture == ModelArchitecture::UNKNOWN &&
                                                 (filePath.extension() == ".ckpt" || filePath.extension() == ".pt")) {
-                                                info.architecture = "Stable Diffusion 1.5 (assumed)";
-                                                info.recommendedVAE = "vae-ft-mse-840000-ema-pruned.safetensors";
-                                                info.recommendedWidth = 512;
-                                                info.recommendedHeight = 512;
-                                                info.recommendedSteps = 20;
+                                                info.architecture       = "Stable Diffusion 1.5 (assumed)";
+                                                info.recommendedVAE     = "vae-ft-mse-840000-ema-pruned.safetensors";
+                                                info.recommendedWidth   = 512;
+                                                info.recommendedHeight  = 512;
+                                                info.recommendedSteps   = 20;
                                                 info.recommendedSampler = "euler_a";
-                                                detectionSource = "fallback";
+                                                detectionSource         = "fallback";
                                             } else {
-                                                info.architecture = detection.architectureName;
+                                                info.architecture   = detection.architectureName;
                                                 info.recommendedVAE = detection.recommendedVAE;
 
                                                 // Parse recommended parameters
@@ -485,10 +483,8 @@ public:
                                                     info.recommendedSampler = detection.suggestedParams["sampler"];
                                                 }
                                             }
-                                            
-                                            if (verbose) {
-                                                LOG_INFO("Using architecture-based detection for " + info.name);
-                                            }
+
+                                            LOG_DEBUG("Using architecture-based detection for " + info.name);
                                         }
 
                                         // Build list of required models based on detection
@@ -523,44 +519,40 @@ public:
                                             // Create a temporary ModelManager instance to check existence
                                             ModelManager tempManager;
                                             std::vector<ModelDetails> modelDetails = tempManager.checkRequiredModelsExistence(info.requiredModels);
-                                            
+
                                             // Clear missing models and repopulate based on existence check
                                             info.missingModels.clear();
-                                            
+
                                             for (const auto& detail : modelDetails) {
                                                 if (!detail.exists) {
                                                     info.missingModels.push_back(detail.type + ": " + detail.name);
                                                 }
                                             }
-                                            
-                                            if (verbose) {
-                                                std::cout << "Model " << info.name << " requires " << info.requiredModels.size()
-                                                          << " models, " << info.missingModels.size() << " are missing" << std::endl;
-                                            }
+
+                                            LOG_DEBUG("Model " + info.name + " required " + std::to_string(info.requiredModels.size()) + " models, " + std::to_string(info.missingModels.size()) + " are missing");
                                         }
 
                                         // Cache the detection result
                                         ModelDetectionCache::cacheDetectionResult(
                                             info.fullPath, detection, pathType, detectionSource, info.modifiedAt);
-                                        
-                                        info.cacheValid = true;
-                                        info.cacheModifiedAt = std::filesystem::file_time_type::clock::now();
-                                        info.cachePathType = pathType;
+
+                                        info.cacheValid              = true;
+                                        info.cacheModifiedAt         = std::filesystem::file_time_type::clock::now();
+                                        info.cachePathType           = pathType;
                                         info.useFolderBasedDetection = useFolderBasedDetection;
-                                        info.detectionSource = detectionSource;
-                                        
+                                        info.detectionSource         = detectionSource;
+
                                     } catch (const std::exception& e) {
                                         // If detection fails completely, default to SD1.5
-                                        info.architecture = "Stable Diffusion 1.5 (assumed)";
-                                        info.recommendedVAE = "vae-ft-mse-840000-ema-pruned.safetensors";
-                                        info.recommendedWidth = 512;
-                                        info.recommendedHeight = 512;
-                                        info.recommendedSteps = 20;
+                                        info.architecture       = "Stable Diffusion 1.5 (assumed)";
+                                        info.recommendedVAE     = "vae-ft-mse-840000-ema-pruned.safetensors";
+                                        info.recommendedWidth   = 512;
+                                        info.recommendedHeight  = 512;
+                                        info.recommendedSteps   = 20;
                                         info.recommendedSampler = "euler_a";
-                                        info.detectionSource = "fallback";
-                                        
-                                        std::cerr << "Detection failed for " << info.name << ": " << e.what()
-                                                  << ", using SD1.5 defaults" << std::endl;
+                                        info.detectionSource    = "fallback";
+
+                                        LOG_ERROR("Detection failed for " + info.name + ": " + e.what() + ", using sd1.5 defaults");
                                     }
                                 }
                             }
@@ -599,15 +591,14 @@ bool ModelManager::scanModelsDirectory() {
 
     // Scan all configured directories for all model types recursively
     // We scan each directory once and detect all model types within it
-    std::set<std::string> scannedDirectories; // Avoid scanning the same directory multiple times
+    std::set<std::string> scannedDirectories;  // Avoid scanning the same directory multiple times
     std::vector<std::string> directoriesToScan;
 
     // Collect unique directories to scan
     std::vector<ModelType> allTypes = {
         ModelType::CHECKPOINT, ModelType::CONTROLNET, ModelType::LORA,
         ModelType::VAE, ModelType::TAESD, ModelType::ESRGAN, ModelType::EMBEDDING,
-        ModelType::DIFFUSION_MODELS
-    };
+        ModelType::DIFFUSION_MODELS};
 
     for (const auto& type : allTypes) {
         std::string dirPath = pImpl->getModelTypeDirectory(type);
@@ -660,8 +651,8 @@ bool ModelManager::loadModel(const std::string& name, const std::string& path, M
     // Set up generation parameters for model loading
     StableDiffusionWrapper::GenerationParams loadParams;
     loadParams.modelPath = path;
-    loadParams.modelType = "f16"; // Default to f16 for better performance
-    loadParams.verbose = pImpl->verbose;
+    loadParams.modelType = "f16";  // Default to f16 for better performance
+    loadParams.verbose   = pImpl->verbose;
 
     // Try to detect model type automatically for checkpoint and diffusion models
     if (type == ModelType::CHECKPOINT || type == ModelType::DIFFUSION_MODELS) {
@@ -686,7 +677,7 @@ bool ModelManager::loadModel(const std::string& name, const std::string& path, M
                     std::string vaeDirectory = getModelTypeDirectory(ModelType::VAE);
                     if (!vaeDirectory.empty()) {
                         std::string resolvedVAEPath = vaeDirectory + "/" + detection.recommendedVAE;
-                        
+
                         // Check if the resolved VAE file exists before setting the path
                         if (fs::exists(resolvedVAEPath) && fs::is_regular_file(resolvedVAEPath)) {
                             loadParams.vaePath = resolvedVAEPath;
@@ -725,7 +716,25 @@ bool ModelManager::loadModel(const std::string& name, const std::string& path, M
         }
     }
 
-    // Try to load the model
+    // Handle upscaler models differently - they don't need to be pre-loaded
+    if (type == ModelType::ESRGAN || type == ModelType::UPSCALER) {
+        if (pImpl->verbose) {
+            std::cout << "Upscaler model '" << name << "' does not need pre-loading, marking as available for use" << std::endl;
+        }
+
+        // For upscaler models, we don't create a wrapper or call loadModel
+        // They are loaded dynamically during upscaling
+        pImpl->loadedModels[name] = nullptr;  // Mark as "loaded" but with null wrapper
+
+        // Update model info
+        if (pImpl->availableModels.find(name) != pImpl->availableModels.end()) {
+            pImpl->availableModels[name].isLoaded = true;
+        }
+
+        return true;
+    }
+
+    // Try to load the model (for checkpoint and diffusion models)
     if (!wrapper->loadModel(path, loadParams)) {
         std::cerr << "Failed to load model '" << name << "': " << wrapper->getLastError() << std::endl;
         return false;
@@ -739,24 +748,24 @@ bool ModelManager::loadModel(const std::string& name, const std::string& path, M
     } else {
         // Create a new model info entry
         ModelInfo info;
-        info.name = name;
-        info.path = path;
-        info.fullPath = fs::absolute(path).string();
-        info.type = type;
-        info.isLoaded = true;
-        info.sha256 = "";
-        info.description = ""; // Initialize description
-        info.metadata = {}; // Initialize metadata
+        info.name        = name;
+        info.path        = path;
+        info.fullPath    = fs::absolute(path).string();
+        info.type        = type;
+        info.isLoaded    = true;
+        info.sha256      = "";
+        info.description = "";  // Initialize description
+        info.metadata    = {};  // Initialize metadata
 
         try {
-            info.fileSize = fs::file_size(path);
+            info.fileSize   = fs::file_size(path);
             info.modifiedAt = fs::last_write_time(path);
-            info.createdAt = info.modifiedAt; // Use modified time as creation time for now
+            info.createdAt  = info.modifiedAt;  // Use modified time as creation time for now
         } catch (const fs::filesystem_error& e) {
             std::cerr << "Error getting file info for " << path << ": " << e.what() << std::endl;
-            info.fileSize = 0;
+            info.fileSize   = 0;
             info.modifiedAt = fs::file_time_type{};
-            info.createdAt = fs::file_time_type{};
+            info.createdAt  = fs::file_time_type{};
         }
 
         pImpl->availableModels[name] = info;
@@ -787,12 +796,55 @@ bool ModelManager::loadModel(const std::string& name) {
         // Extract path and type while we have the lock
         path = it->second.path;
         type = it->second.type;
-    } // Release lock here
+    }  // Release lock here
 
     // Load the model without holding the lock
     return loadModel(name, path, type);
 }
 
+bool ModelManager::loadModel(const std::string& name, std::function<void(float)> progressCallback) {
+    std::string path;
+    ModelType type;
+
+    {
+        std::unique_lock<std::shared_mutex> lock(pImpl->modelsMutex);
+
+        // Check if model exists in available models
+        auto it = pImpl->availableModels.find(name);
+        if (it == pImpl->availableModels.end()) {
+            std::cerr << "Model '" << name << "' not found in available models" << std::endl;
+            return false;
+        }
+
+        // Check if already loaded
+        if (pImpl->loadedModels.find(name) != pImpl->loadedModels.end()) {
+            if (progressCallback) {
+                progressCallback(1.0f);  // 100% if already loaded
+            }
+            return true;
+        }
+
+        // Extract path and type while we have the lock
+        path = it->second.path;
+        type = it->second.type;
+    }  // Release lock here
+
+    // Start loading progress
+    if (progressCallback) {
+        progressCallback(0.0f);  // 0% - starting to load
+    }
+
+    // Load the model with progress tracking
+    bool result = loadModel(name, path, type);
+
+    // Finish loading progress
+    if (progressCallback) {
+        progressCallback(result ? 1.0f : 0.0f);  // 100% if successful, 0% if failed
+    }
+
+    return result;
+}
+
 bool ModelManager::unloadModel(const std::string& name) {
     std::unique_lock<std::shared_mutex> lock(pImpl->modelsMutex);
 
@@ -851,7 +903,7 @@ ModelManager::ModelInfo ModelManager::getModelInfo(const std::string& name) cons
 
     auto it = pImpl->availableModels.find(name);
     if (it == pImpl->availableModels.end()) {
-        return ModelInfo{}; // Return empty ModelInfo if not found
+        return ModelInfo{};  // Return empty ModelInfo if not found
     }
 
     return it->second;
@@ -982,8 +1034,8 @@ std::map<ModelType, std::string> ModelManager::getAllModelTypeDirectories() cons
     return pImpl->modelTypeDirectories;
 }
 
-    // Legacy resetToLegacyDirectories method removed
-    // Using explicit directory configuration only
+// Legacy resetToLegacyDirectories method removed
+// Using explicit directory configuration only
 
 bool ModelManager::configureFromServerConfig(const ServerConfig& config) {
     std::unique_lock<std::shared_mutex> lock(pImpl->modelsMutex);
@@ -1041,19 +1093,19 @@ void ModelManager::cancelScan() {
 
 void ModelManager::unloadAllModels() {
     std::unique_lock<std::shared_mutex> lock(pImpl->modelsMutex);
-    
+
     // Create a list of loaded model names to avoid modifying map while iterating
     std::vector<std::string> loadedModelNames;
     for (const auto& [name, wrapper] : pImpl->loadedModels) {
         loadedModelNames.push_back(name);
     }
-    
+
     // Unload each model
     for (const auto& modelName : loadedModelNames) {
         if (pImpl->verbose) {
             std::cout << "Unloading model: " << modelName << std::endl;
         }
-        
+
         auto it = pImpl->loadedModels.find(modelName);
         if (it != pImpl->loadedModels.end()) {
             // Unload the model properly
@@ -1063,12 +1115,12 @@ void ModelManager::unloadAllModels() {
             pImpl->loadedModels.erase(it);
         }
     }
-    
+
     // Update model info for all unloaded models
     for (auto& [name, info] : pImpl->availableModels) {
         info.isLoaded = false;
     }
-    
+
     if (pImpl->verbose) {
         std::cout << "Unloaded " << loadedModelNames.size() << " models" << std::endl;
     }
@@ -1110,7 +1162,7 @@ std::string ModelManager::computeModelHash(const std::string& modelName) {
     char buffer[bufferSize];
 
     // Hash computation output removed from stdout
-    size_t totalRead = 0;
+    size_t totalRead      = 0;
     size_t lastReportedMB = 0;
 
     while (file.read(buffer, bufferSize) || file.gcount() > 0) {
@@ -1198,13 +1250,13 @@ bool ModelManager::saveModelHashToFile(const std::string& modelName, const std::
     }
 
     std::string jsonPath = it->second.fullPath + ".json";
-    size_t fileSize = it->second.fileSize;
+    size_t fileSize      = it->second.fileSize;
     lock.unlock();
 
     try {
         nlohmann::json j;
-        j["sha256"] = hash;
-        j["file_size"] = fileSize;
+        j["sha256"]      = hash;
+        j["file_size"]   = fileSize;
         j["computed_at"] = std::chrono::system_clock::now().time_since_epoch().count();
 
         std::ofstream jsonFile(jsonPath);
@@ -1286,49 +1338,48 @@ std::string ModelManager::ModelPathSelector::selectPathType(
     const std::string& checkpointsDir,
     const std::string& diffusionModelsDir,
     bool verbose) {
-    
-if (verbose) {
+    if (verbose) {
         LOG_INFO("Selecting path type for model: " + modelPath);
         LOG_INFO("Checkpoints directory: " + checkpointsDir);
         LOG_INFO("Diffusion models directory: " + diffusionModelsDir);
     }
-    
+
     // Check if model is in diffusion_models directory first (priority)
     if (!diffusionModelsDir.empty() && isModelInDirectory(modelPath, diffusionModelsDir)) {
-if (verbose) {
-                LOG_INFO("Model is in diffusion_models directory, using diffusion_model_path");
-            }
+        if (verbose) {
+            LOG_INFO("Model is in diffusion_models directory, using diffusion_model_path");
+        }
         return "diffusion_model_path";
     }
-    
+
     // Check if model is in checkpoints directory
     if (!checkpointsDir.empty() && isModelInDirectory(modelPath, checkpointsDir)) {
-if (verbose) {
-                LOG_INFO("Model is in checkpoints directory, using model_path");
-            }
+        if (verbose) {
+            LOG_INFO("Model is in checkpoints directory, using model_path");
+        }
         return "model_path";
     }
-    
+
     // Fallback: use directory name detection
     std::filesystem::path modelFilePath(modelPath);
     std::filesystem::path parentDir = modelFilePath.parent_path();
-    
+
     if (parentDir.filename().string() == "diffusion_models") {
-if (verbose) {
-                LOG_INFO("Model is in diffusion_models directory (detected from path), using diffusion_model_path");
-            }
+        if (verbose) {
+            LOG_INFO("Model is in diffusion_models directory (detected from path), using diffusion_model_path");
+        }
         return "diffusion_model_path";
     } else if (parentDir.filename().string() == "checkpoints") {
-if (verbose) {
-                LOG_INFO("Model is in checkpoints directory (detected from path), using model_path");
-            }
+        if (verbose) {
+            LOG_INFO("Model is in checkpoints directory (detected from path), using model_path");
+        }
         return "model_path";
     }
-    
+
     // Default fallback for unknown locations
-if (verbose) {
-            LOG_INFO("Model location unknown, defaulting to model_path for backward compatibility");
-        }
+    if (verbose) {
+        LOG_INFO("Model location unknown, defaulting to model_path for backward compatibility");
+    }
     return "model_path";
 }
 
@@ -1336,20 +1387,20 @@ bool ModelManager::ModelPathSelector::isModelInDirectory(const std::string& mode
     if (modelPath.empty() || directory.empty()) {
         return false;
     }
-    
+
     try {
         std::filesystem::path absoluteModelPath = std::filesystem::absolute(modelPath).lexically_normal();
-        std::filesystem::path absoluteDirPath = std::filesystem::absolute(directory).lexically_normal();
-        
+        std::filesystem::path absoluteDirPath   = std::filesystem::absolute(directory).lexically_normal();
+
         // Get relative path from directory to model
-        auto relativePath = absoluteModelPath.lexically_relative(absoluteDirPath);
+        auto relativePath      = absoluteModelPath.lexically_relative(absoluteDirPath);
         std::string relPathStr = relativePath.string();
-        
+
         // Check if the relative path doesn't start with ".." and is not empty
         bool isUnderDirectory = !relPathStr.empty() &&
-                               relPathStr.substr(0, 2) != ".." &&
-                               relPathStr[0] != '/';
-        
+                                relPathStr.substr(0, 2) != ".." &&
+                                relPathStr[0] != '/';
+
         return isUnderDirectory;
     } catch (const std::filesystem::filesystem_error& e) {
         std::cerr << "Error checking if model is in directory: " << e.what() << std::endl;
@@ -1364,26 +1415,25 @@ std::mutex ModelManager::ModelDetectionCache::cacheMutex_;
 ModelManager::ModelDetectionCache::CacheEntry ModelManager::ModelDetectionCache::getCachedResult(
     const std::string& modelPath,
     const std::filesystem::file_time_type& currentModifiedTime) {
-    
     std::lock_guard<std::mutex> lock(cacheMutex_);
-    
+
     auto it = cache_.find(modelPath);
     if (it == cache_.end()) {
-        return CacheEntry{}; // Return invalid entry if not found
+        return CacheEntry{};  // Return invalid entry if not found
     }
-    
+
     const CacheEntry& entry = it->second;
-    
+
     // Check if cache is still valid (file hasn't been modified)
     if (entry.fileModifiedAt == currentModifiedTime && entry.isValid) {
         std::cout << "Using cached detection result for: " << modelPath << std::endl;
         return entry;
     }
-    
+
     // Cache is stale, remove it
     cache_.erase(it);
     std::cout << "Cache entry expired for: " << modelPath << std::endl;
-    return CacheEntry{}; // Return invalid entry
+    return CacheEntry{};  // Return invalid entry
 }
 
 void ModelManager::ModelDetectionCache::cacheDetectionResult(
@@ -1392,22 +1442,21 @@ void ModelManager::ModelDetectionCache::cacheDetectionResult(
     const std::string& pathType,
     const std::string& detectionSource,
     const std::filesystem::file_time_type& fileModifiedTime) {
-    
     std::lock_guard<std::mutex> lock(cacheMutex_);
-    
+
     CacheEntry entry;
-    entry.architecture = detection.architectureName;
-    entry.recommendedVAE = detection.recommendedVAE;
-    entry.recommendedWidth = 0;
-    entry.recommendedHeight = 0;
-    entry.recommendedSteps = 0;
+    entry.architecture       = detection.architectureName;
+    entry.recommendedVAE     = detection.recommendedVAE;
+    entry.recommendedWidth   = 0;
+    entry.recommendedHeight  = 0;
+    entry.recommendedSteps   = 0;
     entry.recommendedSampler = "";
-    entry.pathType = pathType;
-    entry.detectionSource = detectionSource;
-    entry.cachedAt = std::filesystem::file_time_type::clock::now();
-    entry.fileModifiedAt = fileModifiedTime;
-    entry.isValid = true;
-    
+    entry.pathType           = pathType;
+    entry.detectionSource    = detectionSource;
+    entry.cachedAt           = std::filesystem::file_time_type::clock::now();
+    entry.fileModifiedAt     = fileModifiedTime;
+    entry.isValid            = true;
+
     // Parse recommended parameters
     for (const auto& [param, value] : detection.suggestedParams) {
         if (param == "width") {
@@ -1420,51 +1469,52 @@ void ModelManager::ModelDetectionCache::cacheDetectionResult(
             entry.recommendedSampler = value;
         }
     }
-    
+
     // Build list of required models
     // Note: VAE is now optional for SD1x and SDXL models, so we don't add it to requiredModels
     // The VAE will still be recommended but not required
-    
+
     if (detection.suggestedParams.count("clip_l_required")) {
         entry.requiredModels.push_back("CLIP-L: " + detection.suggestedParams.at("clip_l_required"));
     }
-    
+
     if (detection.suggestedParams.count("clip_g_required")) {
         entry.requiredModels.push_back("CLIP-G: " + detection.suggestedParams.at("clip_g_required"));
     }
-    
+
     if (detection.suggestedParams.count("t5xxl_required")) {
         entry.requiredModels.push_back("T5XXL: " + detection.suggestedParams.at("t5xxl_required"));
     }
-    
+
     if (detection.suggestedParams.count("qwen2vl_required")) {
         entry.requiredModels.push_back("Qwen2-VL: " + detection.suggestedParams.at("qwen2vl_required"));
     }
-    
+
     if (detection.suggestedParams.count("qwen2vl_vision_required")) {
         entry.requiredModels.push_back("Qwen2-VL-Vision: " + detection.suggestedParams.at("qwen2vl_vision_required"));
     }
-    
+
     // Check for missing models and store in cache
     if (!entry.requiredModels.empty()) {
         // Create a temporary ModelManager instance to check existence
         // Note: This is a simplified approach - in a production environment,
         // we might want to pass the models directory or use a different approach
         std::string baseModelsDir = "/data/SD_MODELS";
-        
+
         for (const auto& requiredModel : entry.requiredModels) {
             size_t colonPos = requiredModel.find(':');
-            if (colonPos == std::string::npos) continue;
-            
+            if (colonPos == std::string::npos)
+                continue;
+
             std::string modelType = requiredModel.substr(0, colonPos);
             std::string modelName = requiredModel.substr(colonPos + 1);
-            
+
             // Trim whitespace
             modelType.erase(0, modelType.find_first_not_of(" \t"));
             modelType.erase(modelType.find_last_not_of(" \t") + 1);
             modelName.erase(0, modelName.find_first_not_of(" \t"));
             modelName.erase(modelName.find_last_not_of(" \t") + 1);
-            
+
             // Determine the appropriate subdirectory
             std::string subdirectory;
             if (modelType == "VAE") {
@@ -1478,7 +1528,7 @@ void ModelManager::ModelDetectionCache::cacheDetectionResult(
             } else if (modelType == "Qwen2-VL" || modelType == "Qwen2-VL-Vision") {
                 subdirectory = "qwen2vl";
             }
-            
+
             // Check if model exists
             std::string fullPath;
             if (!subdirectory.empty()) {
@@ -1486,7 +1536,7 @@ void ModelManager::ModelDetectionCache::cacheDetectionResult(
             } else {
                 fullPath = baseModelsDir + "/" + modelName;
             }
-            
+
             try {
                 if (!fs::exists(fullPath) || !fs::is_regular_file(fullPath)) {
                     entry.missingModels.push_back(requiredModel);
@@ -1497,7 +1547,7 @@ void ModelManager::ModelDetectionCache::cacheDetectionResult(
             }
         }
     }
-    
+
     cache_[modelPath] = entry;
     std::cout << "Cached detection result for: " << modelPath
               << " (source: " << detectionSource << ", path type: " << pathType << ")" << std::endl;
@@ -1505,7 +1555,7 @@ void ModelManager::ModelDetectionCache::cacheDetectionResult(
 
 void ModelManager::ModelDetectionCache::invalidateCache(const std::string& modelPath) {
     std::lock_guard<std::mutex> lock(cacheMutex_);
-    
+
     auto it = cache_.find(modelPath);
     if (it != cache_.end()) {
         cache_.erase(it);
@@ -1515,7 +1565,7 @@ void ModelManager::ModelDetectionCache::invalidateCache(const std::string& model
 
 void ModelManager::ModelDetectionCache::clearAllCache() {
     std::lock_guard<std::mutex> lock(cacheMutex_);
-    
+
     size_t count = cache_.size();
     cache_.clear();
     std::cout << "Cleared " << count << " cache entries" << std::endl;
@@ -1523,38 +1573,38 @@ void ModelManager::ModelDetectionCache::clearAllCache() {
 
 std::vector<ModelManager::ModelDetails> ModelManager::checkRequiredModelsExistence(const std::vector<std::string>& requiredModels) {
     std::vector<ModelDetails> modelDetails;
-    
+
     // Base models directory according to project guidelines
     std::string baseModelsDir = "/data/SD_MODELS";
-    
+
     for (const auto& requiredModel : requiredModels) {
         ModelDetails details;
-        
+
         // Parse the required model string (format: "TYPE: filename")
         size_t colonPos = requiredModel.find(':');
         if (colonPos == std::string::npos) {
             // Invalid format, skip
             continue;
         }
-        
+
         std::string modelType = requiredModel.substr(0, colonPos);
         std::string modelName = requiredModel.substr(colonPos + 1);
-        
+
         // Trim whitespace
         modelType.erase(0, modelType.find_first_not_of(" \t"));
         modelType.erase(modelType.find_last_not_of(" \t") + 1);
         modelName.erase(0, modelName.find_first_not_of(" \t"));
         modelName.erase(modelName.find_last_not_of(" \t") + 1);
-        
-        details.name = modelName;
-        details.type = modelType;
-        details.is_required = true;
+
+        details.name           = modelName;
+        details.type           = modelType;
+        details.is_required    = true;
         details.is_recommended = false;
-        details.exists = false;
-        details.file_size = 0;
-        details.path = "";
-        details.sha256 = "";
-        
+        details.exists         = false;
+        details.file_size      = 0;
+        details.path           = "";
+        details.sha256         = "";
+
         // Determine the appropriate subdirectory based on model type
         std::string subdirectory;
         if (modelType == "VAE") {
@@ -1571,7 +1621,7 @@ std::vector<ModelManager::ModelDetails> ModelManager::checkRequiredModelsExisten
             // For unknown types, check in root directory
             subdirectory = "";
         }
-        
+
         // Construct the full path to check
         std::string fullPath;
         if (!subdirectory.empty()) {
@@ -1579,14 +1629,14 @@ std::vector<ModelManager::ModelDetails> ModelManager::checkRequiredModelsExisten
         } else {
             fullPath = baseModelsDir + "/" + modelName;
         }
-        
+
         // Check if the file exists
         try {
             if (fs::exists(fullPath) && fs::is_regular_file(fullPath)) {
-                details.exists = true;
-                details.path = fs::absolute(fullPath).string();
+                details.exists    = true;
+                details.path      = fs::absolute(fullPath).string();
                 details.file_size = fs::file_size(fullPath);
-                
+
                 // Try to get cached hash
                 std::string jsonPath = fullPath + ".json";
                 if (fs::exists(jsonPath)) {
@@ -1596,7 +1646,7 @@ std::vector<ModelManager::ModelDetails> ModelManager::checkRequiredModelsExisten
                             nlohmann::json j;
                             jsonFile >> j;
                             jsonFile.close();
-                            
+
                             if (j.contains("sha256") && j["sha256"].is_string()) {
                                 details.sha256 = j["sha256"].get<std::string>();
                             }
@@ -1605,7 +1655,7 @@ std::vector<ModelManager::ModelDetails> ModelManager::checkRequiredModelsExisten
                         std::cerr << "Error loading hash for " << fullPath << ": " << e.what() << std::endl;
                     }
                 }
-                
+
                 if (pImpl->verbose) {
                     std::ostringstream info_oss;
                     info_oss << "Found required model: " << modelType << " at " << details.path;
@@ -1619,9 +1669,9 @@ std::vector<ModelManager::ModelDetails> ModelManager::checkRequiredModelsExisten
         } catch (const fs::filesystem_error& e) {
             std::cerr << "Error checking model existence for " << fullPath << ": " << e.what() << std::endl;
         }
-        
+
         modelDetails.push_back(details);
     }
-    
+
     return modelDetails;
 }