|
|
@@ -388,72 +388,151 @@ public:
|
|
|
|
|
|
// Detect architecture for checkpoint models (including diffusion_models)
|
|
|
if (detectedType == ModelType::CHECKPOINT || detectedType == ModelType::DIFFUSION_MODELS) {
|
|
|
- try {
|
|
|
- ModelDetectionResult 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.recommendedSampler = "euler_a";
|
|
|
- } else {
|
|
|
- info.architecture = detection.architectureName;
|
|
|
- info.recommendedVAE = detection.recommendedVAE;
|
|
|
-
|
|
|
- // Parse recommended parameters
|
|
|
- if (detection.suggestedParams.count("width")) {
|
|
|
- info.recommendedWidth = std::stoi(detection.suggestedParams["width"]);
|
|
|
- }
|
|
|
- if (detection.suggestedParams.count("height")) {
|
|
|
- info.recommendedHeight = std::stoi(detection.suggestedParams["height"]);
|
|
|
- }
|
|
|
- if (detection.suggestedParams.count("steps")) {
|
|
|
- info.recommendedSteps = std::stoi(detection.suggestedParams["steps"]);
|
|
|
+ // 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.cacheValid = true;
|
|
|
+ info.cacheModifiedAt = cachedEntry.cachedAt;
|
|
|
+ info.cachePathType = cachedEntry.pathType;
|
|
|
+ info.useFolderBasedDetection = (cachedEntry.detectionSource == "folder");
|
|
|
+ info.detectionSource = cachedEntry.detectionSource;
|
|
|
+
|
|
|
+ std::cout << "Using cached detection for " << info.name
|
|
|
+ << " (source: " << cachedEntry.detectionSource << ")" << std::endl;
|
|
|
+ } else {
|
|
|
+ // Perform new detection
|
|
|
+ try {
|
|
|
+ // First try folder-based detection
|
|
|
+ std::string checkpointsDir = getModelTypeDirectory(ModelType::CHECKPOINT);
|
|
|
+ std::string diffusionModelsDir = getModelTypeDirectory(ModelType::DIFFUSION_MODELS);
|
|
|
+ std::string pathType = ModelPathSelector::selectPathType(
|
|
|
+ info.fullPath, checkpointsDir, diffusionModelsDir);
|
|
|
+
|
|
|
+ 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;
|
|
|
+ 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.suggestedParams["sampler"] = info.recommendedSampler;
|
|
|
+
|
|
|
+ std::cout << "Using folder-based detection for " << info.name
|
|
|
+ << " in " << pathType << std::endl;
|
|
|
+ } else {
|
|
|
+ // Perform full architecture detection
|
|
|
+ detectionSource = "architecture";
|
|
|
+ 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.recommendedSampler = "euler_a";
|
|
|
+ detectionSource = "fallback";
|
|
|
+ } else {
|
|
|
+ info.architecture = detection.architectureName;
|
|
|
+ info.recommendedVAE = detection.recommendedVAE;
|
|
|
+
|
|
|
+ // Parse recommended parameters
|
|
|
+ if (detection.suggestedParams.count("width")) {
|
|
|
+ info.recommendedWidth = std::stoi(detection.suggestedParams["width"]);
|
|
|
+ }
|
|
|
+ if (detection.suggestedParams.count("height")) {
|
|
|
+ info.recommendedHeight = std::stoi(detection.suggestedParams["height"]);
|
|
|
+ }
|
|
|
+ if (detection.suggestedParams.count("steps")) {
|
|
|
+ info.recommendedSteps = std::stoi(detection.suggestedParams["steps"]);
|
|
|
+ }
|
|
|
+ if (detection.suggestedParams.count("sampler")) {
|
|
|
+ info.recommendedSampler = detection.suggestedParams["sampler"];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ std::cout << "Using architecture-based detection for " << info.name << std::endl;
|
|
|
}
|
|
|
- if (detection.suggestedParams.count("sampler")) {
|
|
|
- info.recommendedSampler = detection.suggestedParams["sampler"];
|
|
|
+
|
|
|
+ // Build list of required models based on detection
|
|
|
+ if (detection.needsVAE && !detection.recommendedVAE.empty()) {
|
|
|
+ info.requiredModels.push_back("VAE: " + detection.recommendedVAE);
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- // Build list of required models based on architecture
|
|
|
- if (detection.needsVAE && !detection.recommendedVAE.empty()) {
|
|
|
- info.requiredModels.push_back("VAE: " + detection.recommendedVAE);
|
|
|
- }
|
|
|
+ // Add CLIP-L if required
|
|
|
+ if (detection.suggestedParams.count("clip_l_required")) {
|
|
|
+ info.requiredModels.push_back("CLIP-L: " + detection.suggestedParams.at("clip_l_required"));
|
|
|
+ }
|
|
|
|
|
|
- // Add CLIP-L if required
|
|
|
- if (detection.suggestedParams.count("clip_l_required")) {
|
|
|
- info.requiredModels.push_back("CLIP-L: " + detection.suggestedParams.at("clip_l_required"));
|
|
|
- }
|
|
|
+ // Add CLIP-G if required
|
|
|
+ if (detection.suggestedParams.count("clip_g_required")) {
|
|
|
+ info.requiredModels.push_back("CLIP-G: " + detection.suggestedParams.at("clip_g_required"));
|
|
|
+ }
|
|
|
|
|
|
- // Add CLIP-G if required
|
|
|
- if (detection.suggestedParams.count("clip_g_required")) {
|
|
|
- info.requiredModels.push_back("CLIP-G: " + detection.suggestedParams.at("clip_g_required"));
|
|
|
- }
|
|
|
+ // Add T5XXL if required
|
|
|
+ if (detection.suggestedParams.count("t5xxl_required")) {
|
|
|
+ info.requiredModels.push_back("T5XXL: " + detection.suggestedParams.at("t5xxl_required"));
|
|
|
+ }
|
|
|
|
|
|
- // Add T5XXL if required
|
|
|
- if (detection.suggestedParams.count("t5xxl_required")) {
|
|
|
- info.requiredModels.push_back("T5XXL: " + detection.suggestedParams.at("t5xxl_required"));
|
|
|
- }
|
|
|
+ // Add Qwen models if required
|
|
|
+ if (detection.suggestedParams.count("qwen2vl_required")) {
|
|
|
+ info.requiredModels.push_back("Qwen2-VL: " + detection.suggestedParams.at("qwen2vl_required"));
|
|
|
+ }
|
|
|
+ if (detection.suggestedParams.count("qwen2vl_vision_required")) {
|
|
|
+ info.requiredModels.push_back("Qwen2-VL-Vision: " + detection.suggestedParams.at("qwen2vl_vision_required"));
|
|
|
+ }
|
|
|
|
|
|
- // Add Qwen models if required
|
|
|
- if (detection.suggestedParams.count("qwen2vl_required")) {
|
|
|
- info.requiredModels.push_back("Qwen2-VL: " + detection.suggestedParams.at("qwen2vl_required"));
|
|
|
- }
|
|
|
- if (detection.suggestedParams.count("qwen2vl_vision_required")) {
|
|
|
- info.requiredModels.push_back("Qwen2-VL-Vision: " + detection.suggestedParams.at("qwen2vl_vision_required"));
|
|
|
+ // 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.useFolderBasedDetection = useFolderBasedDetection;
|
|
|
+ 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.recommendedSampler = "euler_a";
|
|
|
+ info.detectionSource = "fallback";
|
|
|
+
|
|
|
+ std::cerr << "Detection failed for " << info.name << ": " << e.what()
|
|
|
+ << ", using SD1.5 defaults" << std::endl;
|
|
|
}
|
|
|
- } 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.recommendedSampler = "euler_a";
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -547,6 +626,43 @@ bool ModelManager::loadModel(const std::string& name, const std::string& path, M
|
|
|
loadParams.modelPath = path;
|
|
|
loadParams.modelType = "f16"; // Default to f16 for better performance
|
|
|
|
|
|
+ // Try to detect model type automatically for checkpoint and diffusion models
|
|
|
+ if (type == ModelType::CHECKPOINT || type == ModelType::DIFFUSION_MODELS) {
|
|
|
+ try {
|
|
|
+ ModelDetectionResult detection = ModelDetector::detectModel(path);
|
|
|
+
|
|
|
+ // Apply detected model type and parameters
|
|
|
+ if (detection.architecture != ModelArchitecture::UNKNOWN) {
|
|
|
+ std::cout << "Detected model architecture: " << detection.architectureName << " for " << name << std::endl;
|
|
|
+
|
|
|
+ // Set model type from detection if available
|
|
|
+ if (detection.suggestedParams.count("model_type")) {
|
|
|
+ loadParams.modelType = detection.suggestedParams.at("model_type");
|
|
|
+ }
|
|
|
+
|
|
|
+ // Set additional model paths based on detection
|
|
|
+ if (detection.needsVAE && !detection.recommendedVAE.empty()) {
|
|
|
+ loadParams.vaePath = detection.recommendedVAE;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Apply other suggested parameters (only for fields that exist in GenerationParams)
|
|
|
+ for (const auto& [param, value] : detection.suggestedParams) {
|
|
|
+ if (param == "clip_l_path") {
|
|
|
+ loadParams.clipLPath = value;
|
|
|
+ } else if (param == "clip_g_path") {
|
|
|
+ loadParams.clipGPath = value;
|
|
|
+ }
|
|
|
+ // Note: t5xxl_path and qwen2vl_path are not available in GenerationParams structure
|
|
|
+ // These would need to be passed through the underlying stable-diffusion.cpp library directly
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ std::cout << "Could not detect model architecture for " << name << ", using defaults" << std::endl;
|
|
|
+ }
|
|
|
+ } catch (const std::exception& e) {
|
|
|
+ std::cerr << "Model detection failed for " << name << ": " << e.what() << " - using defaults" << std::endl;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// Try to load the model
|
|
|
if (!wrapper->loadModel(path, loadParams)) {
|
|
|
std::cerr << "Failed to load model '" << name << "': " << wrapper->getLastError() << std::endl;
|
|
|
@@ -1063,3 +1179,179 @@ std::string ModelManager::ensureModelHash(const std::string& modelName, bool for
|
|
|
|
|
|
return hash;
|
|
|
}
|
|
|
+
|
|
|
+// ModelPathSelector Implementation
|
|
|
+std::string ModelManager::ModelPathSelector::selectPathType(
|
|
|
+ const std::string& modelPath,
|
|
|
+ const std::string& checkpointsDir,
|
|
|
+ const std::string& diffusionModelsDir) {
|
|
|
+
|
|
|
+ std::cout << "Selecting path type for model: " << modelPath << std::endl;
|
|
|
+ std::cout << "Checkpoints directory: " << checkpointsDir << std::endl;
|
|
|
+ std::cout << "Diffusion models directory: " << diffusionModelsDir << std::endl;
|
|
|
+
|
|
|
+ // Check if model is in diffusion_models directory first (priority)
|
|
|
+ if (!diffusionModelsDir.empty() && isModelInDirectory(modelPath, diffusionModelsDir)) {
|
|
|
+ std::cout << "Model is in diffusion_models directory, using diffusion_model_path" << std::endl;
|
|
|
+ return "diffusion_model_path";
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check if model is in checkpoints directory
|
|
|
+ if (!checkpointsDir.empty() && isModelInDirectory(modelPath, checkpointsDir)) {
|
|
|
+ std::cout << "Model is in checkpoints directory, using model_path" << std::endl;
|
|
|
+ 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") {
|
|
|
+ std::cout << "Model is in diffusion_models directory (detected from path), using diffusion_model_path" << std::endl;
|
|
|
+ return "diffusion_model_path";
|
|
|
+ } else if (parentDir.filename().string() == "checkpoints") {
|
|
|
+ std::cout << "Model is in checkpoints directory (detected from path), using model_path" << std::endl;
|
|
|
+ return "model_path";
|
|
|
+ }
|
|
|
+
|
|
|
+ // Default fallback for unknown locations
|
|
|
+ std::cout << "Model location unknown, defaulting to model_path for backward compatibility" << std::endl;
|
|
|
+ return "model_path";
|
|
|
+}
|
|
|
+
|
|
|
+bool ModelManager::ModelPathSelector::isModelInDirectory(const std::string& modelPath, const std::string& directory) {
|
|
|
+ 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();
|
|
|
+
|
|
|
+ // Get relative path from directory to model
|
|
|
+ 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] != '/';
|
|
|
+
|
|
|
+ return isUnderDirectory;
|
|
|
+ } catch (const std::filesystem::filesystem_error& e) {
|
|
|
+ std::cerr << "Error checking if model is in directory: " << e.what() << std::endl;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// ModelDetectionCache Implementation
|
|
|
+std::map<std::string, ModelManager::ModelDetectionCache::CacheEntry> ModelManager::ModelDetectionCache::cache_;
|
|
|
+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
|
|
|
+ }
|
|
|
+
|
|
|
+ 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
|
|
|
+}
|
|
|
+
|
|
|
+void ModelManager::ModelDetectionCache::cacheDetectionResult(
|
|
|
+ const std::string& modelPath,
|
|
|
+ const ModelDetectionResult& detection,
|
|
|
+ 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.recommendedSampler = "";
|
|
|
+ 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") {
|
|
|
+ entry.recommendedWidth = std::stoi(value);
|
|
|
+ } else if (param == "height") {
|
|
|
+ entry.recommendedHeight = std::stoi(value);
|
|
|
+ } else if (param == "steps") {
|
|
|
+ entry.recommendedSteps = std::stoi(value);
|
|
|
+ } else if (param == "sampler") {
|
|
|
+ entry.recommendedSampler = value;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Build list of required models
|
|
|
+ if (detection.needsVAE && !detection.recommendedVAE.empty()) {
|
|
|
+ entry.requiredModels.push_back("VAE: " + detection.recommendedVAE);
|
|
|
+ }
|
|
|
+
|
|
|
+ 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"));
|
|
|
+ }
|
|
|
+
|
|
|
+ cache_[modelPath] = entry;
|
|
|
+ std::cout << "Cached detection result for: " << modelPath
|
|
|
+ << " (source: " << detectionSource << ", path type: " << pathType << ")" << std::endl;
|
|
|
+}
|
|
|
+
|
|
|
+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);
|
|
|
+ std::cout << "Invalidated cache for: " << modelPath << std::endl;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+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;
|
|
|
+}
|