|
@@ -33,7 +33,6 @@ public:
|
|
|
std::map<std::string, std::unique_ptr<StableDiffusionWrapper>> loadedModels;
|
|
std::map<std::string, std::unique_ptr<StableDiffusionWrapper>> loadedModels;
|
|
|
mutable std::shared_mutex modelsMutex;
|
|
mutable std::shared_mutex modelsMutex;
|
|
|
std::atomic<bool> scanCancelled{false};
|
|
std::atomic<bool> scanCancelled{false};
|
|
|
- bool legacyMode = true;
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* @brief Validate a directory path
|
|
* @brief Validate a directory path
|
|
@@ -99,12 +98,10 @@ public:
|
|
|
return it->second;
|
|
return it->second;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // If in legacy mode, construct default path
|
|
|
|
|
- if (legacyMode) {
|
|
|
|
|
- std::string defaultDir = getDefaultDirectoryName(type);
|
|
|
|
|
- if (!defaultDir.empty()) {
|
|
|
|
|
- return modelsDirectory + "/" + defaultDir;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // Always use explicit directory configuration
|
|
|
|
|
+ std::string defaultDir = getDefaultDirectoryName(type);
|
|
|
|
|
+ if (!defaultDir.empty()) {
|
|
|
|
|
+ return modelsDirectory + "/" + defaultDir;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return "";
|
|
return "";
|
|
@@ -195,11 +192,11 @@ public:
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // If in legacy mode or no configured directories matched, check default directory structure
|
|
|
|
|
- if (legacyMode || modelTypeDirectories.empty()) {
|
|
|
|
|
|
|
+ // Check default directory structure when no explicit directories configured
|
|
|
|
|
+ if (modelTypeDirectories.empty()) {
|
|
|
// Check the entire path hierarchy for model type directories
|
|
// Check the entire path hierarchy for model type directories
|
|
|
fs::path currentPath = filePath.parent_path();
|
|
fs::path currentPath = filePath.parent_path();
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
while (currentPath.has_filename()) {
|
|
while (currentPath.has_filename()) {
|
|
|
std::string dirName = currentPath.filename().string();
|
|
std::string dirName = currentPath.filename().string();
|
|
|
std::transform(dirName.begin(), dirName.end(), dirName.begin(), ::tolower);
|
|
std::transform(dirName.begin(), dirName.end(), dirName.begin(), ::tolower);
|
|
@@ -234,7 +231,7 @@ public:
|
|
|
return ModelType::EMBEDDING;
|
|
return ModelType::EMBEDDING;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// Move up to parent directory
|
|
// Move up to parent directory
|
|
|
currentPath = currentPath.parent_path();
|
|
currentPath = currentPath.parent_path();
|
|
|
}
|
|
}
|
|
@@ -323,7 +320,7 @@ public:
|
|
|
// Calculate relative path from the scanned directory (not base models directory)
|
|
// Calculate relative path from the scanned directory (not base models directory)
|
|
|
fs::path relativePath = fs::relative(filePath, directory);
|
|
fs::path relativePath = fs::relative(filePath, directory);
|
|
|
std::string modelName = relativePath.string();
|
|
std::string modelName = relativePath.string();
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// Normalize path separators for consistency
|
|
// Normalize path separators for consistency
|
|
|
std::replace(modelName.begin(), modelName.end(), '\\', '/');
|
|
std::replace(modelName.begin(), modelName.end(), '\\', '/');
|
|
|
|
|
|
|
@@ -463,38 +460,21 @@ bool ModelManager::scanModelsDirectory() {
|
|
|
// Create temporary map to store scan results (outside of lock)
|
|
// Create temporary map to store scan results (outside of lock)
|
|
|
std::map<std::string, ModelInfo> tempModels;
|
|
std::map<std::string, ModelInfo> tempModels;
|
|
|
|
|
|
|
|
- if (pImpl->legacyMode) {
|
|
|
|
|
- // Legacy mode: recursively scan the entire models directory and auto-detect all subdirectories
|
|
|
|
|
- fs::path modelsPath(pImpl->modelsDirectory);
|
|
|
|
|
-
|
|
|
|
|
- if (!fs::exists(modelsPath) || !fs::is_directory(modelsPath)) {
|
|
|
|
|
- std::cerr << "Models directory does not exist: " << pImpl->modelsDirectory << std::endl;
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Scan the entire models directory recursively
|
|
|
|
|
- // Model type will be determined by file extension and directory structure
|
|
|
|
|
- // Model names will include the full relative path from the base models directory
|
|
|
|
|
- if (!pImpl->scanDirectory(modelsPath, ModelType::NONE, tempModels)) {
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- // Explicit mode: scan configured directories for each model type
|
|
|
|
|
- std::vector<std::pair<ModelType, std::string>> directoriesToScan = {
|
|
|
|
|
- {ModelType::CHECKPOINT, pImpl->getModelTypeDirectory(ModelType::CHECKPOINT)},
|
|
|
|
|
- {ModelType::CONTROLNET, pImpl->getModelTypeDirectory(ModelType::CONTROLNET)},
|
|
|
|
|
- {ModelType::LORA, pImpl->getModelTypeDirectory(ModelType::LORA)},
|
|
|
|
|
- {ModelType::VAE, pImpl->getModelTypeDirectory(ModelType::VAE)},
|
|
|
|
|
- {ModelType::TAESD, pImpl->getModelTypeDirectory(ModelType::TAESD)},
|
|
|
|
|
- {ModelType::ESRGAN, pImpl->getModelTypeDirectory(ModelType::ESRGAN)},
|
|
|
|
|
- {ModelType::EMBEDDING, pImpl->getModelTypeDirectory(ModelType::EMBEDDING)}
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- for (const auto& [type, dirPath] : directoriesToScan) {
|
|
|
|
|
- if (!dirPath.empty()) {
|
|
|
|
|
- if (!pImpl->scanDirectory(dirPath, type, tempModels)) {
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // Explicit mode: scan configured directories for each model type
|
|
|
|
|
+ std::vector<std::pair<ModelType, std::string>> directoriesToScan = {
|
|
|
|
|
+ {ModelType::CHECKPOINT, pImpl->getModelTypeDirectory(ModelType::CHECKPOINT)},
|
|
|
|
|
+ {ModelType::CONTROLNET, pImpl->getModelTypeDirectory(ModelType::CONTROLNET)},
|
|
|
|
|
+ {ModelType::LORA, pImpl->getModelTypeDirectory(ModelType::LORA)},
|
|
|
|
|
+ {ModelType::VAE, pImpl->getModelTypeDirectory(ModelType::VAE)},
|
|
|
|
|
+ {ModelType::TAESD, pImpl->getModelTypeDirectory(ModelType::TAESD)},
|
|
|
|
|
+ {ModelType::ESRGAN, pImpl->getModelTypeDirectory(ModelType::ESRGAN)},
|
|
|
|
|
+ {ModelType::EMBEDDING, pImpl->getModelTypeDirectory(ModelType::EMBEDDING)}
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ for (const auto& [type, dirPath] : directoriesToScan) {
|
|
|
|
|
+ if (!dirPath.empty()) {
|
|
|
|
|
+ if (!pImpl->scanDirectory(dirPath, type, tempModels)) {
|
|
|
|
|
+ return false;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -755,7 +735,6 @@ bool ModelManager::setModelTypeDirectory(ModelType type, const std::string& path
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
pImpl->modelTypeDirectories[type] = path;
|
|
pImpl->modelTypeDirectories[type] = path;
|
|
|
- pImpl->legacyMode = false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
@@ -777,7 +756,6 @@ bool ModelManager::setAllModelTypeDirectories(const std::map<ModelType, std::str
|
|
|
|
|
|
|
|
// Set all directories
|
|
// Set all directories
|
|
|
pImpl->modelTypeDirectories = directories;
|
|
pImpl->modelTypeDirectories = directories;
|
|
|
- pImpl->legacyMode = false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
@@ -787,12 +765,8 @@ std::map<ModelType, std::string> ModelManager::getAllModelTypeDirectories() cons
|
|
|
return pImpl->modelTypeDirectories;
|
|
return pImpl->modelTypeDirectories;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-void ModelManager::resetToLegacyDirectories() {
|
|
|
|
|
- // Note: This method should be called with modelsMutex already locked
|
|
|
|
|
-
|
|
|
|
|
- pImpl->modelTypeDirectories.clear();
|
|
|
|
|
- pImpl->legacyMode = true;
|
|
|
|
|
-}
|
|
|
|
|
|
|
+ // Legacy resetToLegacyDirectories method removed
|
|
|
|
|
+ // Using explicit directory configuration only
|
|
|
|
|
|
|
|
bool ModelManager::configureFromServerConfig(const ServerConfig& config) {
|
|
bool ModelManager::configureFromServerConfig(const ServerConfig& config) {
|
|
|
std::unique_lock<std::shared_mutex> lock(pImpl->modelsMutex);
|
|
std::unique_lock<std::shared_mutex> lock(pImpl->modelsMutex);
|
|
@@ -800,49 +774,42 @@ bool ModelManager::configureFromServerConfig(const ServerConfig& config) {
|
|
|
// Set the base models directory
|
|
// Set the base models directory
|
|
|
pImpl->modelsDirectory = config.modelsDir;
|
|
pImpl->modelsDirectory = config.modelsDir;
|
|
|
|
|
|
|
|
- if (config.legacyMode) {
|
|
|
|
|
- // Legacy mode: use single models directory
|
|
|
|
|
- resetToLegacyDirectories();
|
|
|
|
|
- return true;
|
|
|
|
|
- } else {
|
|
|
|
|
- // Explicit mode: set per-type directories
|
|
|
|
|
- std::map<ModelType, std::string> directories;
|
|
|
|
|
|
|
+ // Always use explicit directory configuration
|
|
|
|
|
+ std::map<ModelType, std::string> directories;
|
|
|
|
|
|
|
|
- if (!config.checkpoints.empty()) {
|
|
|
|
|
- directories[ModelType::CHECKPOINT] = config.checkpoints;
|
|
|
|
|
- }
|
|
|
|
|
- if (!config.controlnetDir.empty()) {
|
|
|
|
|
- directories[ModelType::CONTROLNET] = config.controlnetDir;
|
|
|
|
|
- }
|
|
|
|
|
- if (!config.embeddingsDir.empty()) {
|
|
|
|
|
- directories[ModelType::EMBEDDING] = config.embeddingsDir;
|
|
|
|
|
- }
|
|
|
|
|
- if (!config.esrganDir.empty()) {
|
|
|
|
|
- directories[ModelType::ESRGAN] = config.esrganDir;
|
|
|
|
|
- }
|
|
|
|
|
- if (!config.loraDir.empty()) {
|
|
|
|
|
- directories[ModelType::LORA] = config.loraDir;
|
|
|
|
|
- }
|
|
|
|
|
- if (!config.taesdDir.empty()) {
|
|
|
|
|
- directories[ModelType::TAESD] = config.taesdDir;
|
|
|
|
|
- }
|
|
|
|
|
- if (!config.vaeDir.empty()) {
|
|
|
|
|
- directories[ModelType::VAE] = config.vaeDir;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (!config.checkpoints.empty()) {
|
|
|
|
|
+ directories[ModelType::CHECKPOINT] = config.checkpoints;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!config.controlnetDir.empty()) {
|
|
|
|
|
+ directories[ModelType::CONTROLNET] = config.controlnetDir;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!config.embeddingsDir.empty()) {
|
|
|
|
|
+ directories[ModelType::EMBEDDING] = config.embeddingsDir;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!config.esrganDir.empty()) {
|
|
|
|
|
+ directories[ModelType::ESRGAN] = config.esrganDir;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!config.loraDir.empty()) {
|
|
|
|
|
+ directories[ModelType::LORA] = config.loraDir;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!config.taesdDir.empty()) {
|
|
|
|
|
+ directories[ModelType::TAESD] = config.taesdDir;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!config.vaeDir.empty()) {
|
|
|
|
|
+ directories[ModelType::VAE] = config.vaeDir;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // Validate all directories first
|
|
|
|
|
- for (const auto& [type, path] : directories) {
|
|
|
|
|
- if (!path.empty() && !pImpl->validateDirectory(path)) {
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // Validate all directories first
|
|
|
|
|
+ for (const auto& [type, path] : directories) {
|
|
|
|
|
+ if (!path.empty() && !pImpl->validateDirectory(path)) {
|
|
|
|
|
+ return false;
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // Set all directories (inlined to avoid deadlock from calling setAllModelTypeDirectories)
|
|
|
|
|
- pImpl->modelTypeDirectories = directories;
|
|
|
|
|
- pImpl->legacyMode = false;
|
|
|
|
|
|
|
+ // Set all directories (inlined to avoid deadlock from calling setAllModelTypeDirectories)
|
|
|
|
|
+ pImpl->modelTypeDirectories = directories;
|
|
|
|
|
|
|
|
- return true;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void ModelManager::cancelScan() {
|
|
void ModelManager::cancelScan() {
|
|
@@ -852,16 +819,16 @@ void ModelManager::cancelScan() {
|
|
|
|
|
|
|
|
std::string ModelManager::computeModelHash(const std::string& modelName) {
|
|
std::string ModelManager::computeModelHash(const std::string& modelName) {
|
|
|
std::shared_lock<std::shared_mutex> lock(pImpl->modelsMutex);
|
|
std::shared_lock<std::shared_mutex> lock(pImpl->modelsMutex);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
auto it = pImpl->availableModels.find(modelName);
|
|
auto it = pImpl->availableModels.find(modelName);
|
|
|
if (it == pImpl->availableModels.end()) {
|
|
if (it == pImpl->availableModels.end()) {
|
|
|
std::cerr << "Model not found: " << modelName << std::endl;
|
|
std::cerr << "Model not found: " << modelName << std::endl;
|
|
|
return "";
|
|
return "";
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
std::string filePath = it->second.fullPath;
|
|
std::string filePath = it->second.fullPath;
|
|
|
lock.unlock();
|
|
lock.unlock();
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
std::ifstream file(filePath, std::ios::binary);
|
|
std::ifstream file(filePath, std::ios::binary);
|
|
|
if (!file.is_open()) {
|
|
if (!file.is_open()) {
|
|
|
std::cerr << "Failed to open file for hashing: " << filePath << std::endl;
|
|
std::cerr << "Failed to open file for hashing: " << filePath << std::endl;
|
|
@@ -922,75 +889,75 @@ std::string ModelManager::computeModelHash(const std::string& modelName) {
|
|
|
for (unsigned int i = 0; i < hashLen; i++) {
|
|
for (unsigned int i = 0; i < hashLen; i++) {
|
|
|
oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(hash[i]);
|
|
oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(hash[i]);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
std::string hashStr = oss.str();
|
|
std::string hashStr = oss.str();
|
|
|
std::cout << "Hash computed: " << hashStr.substr(0, 16) << "..." << std::endl;
|
|
std::cout << "Hash computed: " << hashStr.substr(0, 16) << "..." << std::endl;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
return hashStr;
|
|
return hashStr;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
std::string ModelManager::loadModelHashFromFile(const std::string& modelName) {
|
|
std::string ModelManager::loadModelHashFromFile(const std::string& modelName) {
|
|
|
std::shared_lock<std::shared_mutex> lock(pImpl->modelsMutex);
|
|
std::shared_lock<std::shared_mutex> lock(pImpl->modelsMutex);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
auto it = pImpl->availableModels.find(modelName);
|
|
auto it = pImpl->availableModels.find(modelName);
|
|
|
if (it == pImpl->availableModels.end()) {
|
|
if (it == pImpl->availableModels.end()) {
|
|
|
return "";
|
|
return "";
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
std::string jsonPath = it->second.fullPath + ".json";
|
|
std::string jsonPath = it->second.fullPath + ".json";
|
|
|
lock.unlock();
|
|
lock.unlock();
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (!fs::exists(jsonPath)) {
|
|
if (!fs::exists(jsonPath)) {
|
|
|
return "";
|
|
return "";
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
try {
|
|
try {
|
|
|
std::ifstream jsonFile(jsonPath);
|
|
std::ifstream jsonFile(jsonPath);
|
|
|
if (!jsonFile.is_open()) {
|
|
if (!jsonFile.is_open()) {
|
|
|
return "";
|
|
return "";
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
nlohmann::json j;
|
|
nlohmann::json j;
|
|
|
jsonFile >> j;
|
|
jsonFile >> j;
|
|
|
jsonFile.close();
|
|
jsonFile.close();
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (j.contains("sha256") && j["sha256"].is_string()) {
|
|
if (j.contains("sha256") && j["sha256"].is_string()) {
|
|
|
return j["sha256"].get<std::string>();
|
|
return j["sha256"].get<std::string>();
|
|
|
}
|
|
}
|
|
|
} catch (const std::exception& e) {
|
|
} catch (const std::exception& e) {
|
|
|
std::cerr << "Error loading hash from JSON: " << e.what() << std::endl;
|
|
std::cerr << "Error loading hash from JSON: " << e.what() << std::endl;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
return "";
|
|
return "";
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
bool ModelManager::saveModelHashToFile(const std::string& modelName, const std::string& hash) {
|
|
bool ModelManager::saveModelHashToFile(const std::string& modelName, const std::string& hash) {
|
|
|
std::shared_lock<std::shared_mutex> lock(pImpl->modelsMutex);
|
|
std::shared_lock<std::shared_mutex> lock(pImpl->modelsMutex);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
auto it = pImpl->availableModels.find(modelName);
|
|
auto it = pImpl->availableModels.find(modelName);
|
|
|
if (it == pImpl->availableModels.end()) {
|
|
if (it == pImpl->availableModels.end()) {
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
std::string jsonPath = it->second.fullPath + ".json";
|
|
std::string jsonPath = it->second.fullPath + ".json";
|
|
|
size_t fileSize = it->second.fileSize;
|
|
size_t fileSize = it->second.fileSize;
|
|
|
lock.unlock();
|
|
lock.unlock();
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
try {
|
|
try {
|
|
|
nlohmann::json j;
|
|
nlohmann::json j;
|
|
|
j["sha256"] = hash;
|
|
j["sha256"] = hash;
|
|
|
j["file_size"] = fileSize;
|
|
j["file_size"] = fileSize;
|
|
|
j["computed_at"] = std::chrono::system_clock::now().time_since_epoch().count();
|
|
j["computed_at"] = std::chrono::system_clock::now().time_since_epoch().count();
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
std::ofstream jsonFile(jsonPath);
|
|
std::ofstream jsonFile(jsonPath);
|
|
|
if (!jsonFile.is_open()) {
|
|
if (!jsonFile.is_open()) {
|
|
|
std::cerr << "Failed to open file for writing: " << jsonPath << std::endl;
|
|
std::cerr << "Failed to open file for writing: " << jsonPath << std::endl;
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
jsonFile << j.dump(2);
|
|
jsonFile << j.dump(2);
|
|
|
jsonFile.close();
|
|
jsonFile.close();
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
std::cout << "Saved hash to: " << jsonPath << std::endl;
|
|
std::cout << "Saved hash to: " << jsonPath << std::endl;
|
|
|
return true;
|
|
return true;
|
|
|
} catch (const std::exception& e) {
|
|
} catch (const std::exception& e) {
|
|
@@ -1004,20 +971,20 @@ std::string ModelManager::findModelByHash(const std::string& hash) {
|
|
|
std::cerr << "Hash must be at least 10 characters" << std::endl;
|
|
std::cerr << "Hash must be at least 10 characters" << std::endl;
|
|
|
return "";
|
|
return "";
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
std::shared_lock<std::shared_mutex> lock(pImpl->modelsMutex);
|
|
std::shared_lock<std::shared_mutex> lock(pImpl->modelsMutex);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
for (const auto& [name, info] : pImpl->availableModels) {
|
|
for (const auto& [name, info] : pImpl->availableModels) {
|
|
|
if (info.sha256.empty()) {
|
|
if (info.sha256.empty()) {
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// Support full or partial match (minimum 10 chars)
|
|
// Support full or partial match (minimum 10 chars)
|
|
|
if (info.sha256 == hash || info.sha256.substr(0, hash.length()) == hash) {
|
|
if (info.sha256 == hash || info.sha256.substr(0, hash.length()) == hash) {
|
|
|
return name;
|
|
return name;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
return "";
|
|
return "";
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1035,22 +1002,22 @@ std::string ModelManager::ensureModelHash(const std::string& modelName, bool for
|
|
|
return existingHash;
|
|
return existingHash;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// Compute new hash
|
|
// Compute new hash
|
|
|
std::string hash = computeModelHash(modelName);
|
|
std::string hash = computeModelHash(modelName);
|
|
|
if (hash.empty()) {
|
|
if (hash.empty()) {
|
|
|
return "";
|
|
return "";
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// Save to file
|
|
// Save to file
|
|
|
saveModelHashToFile(modelName, hash);
|
|
saveModelHashToFile(modelName, hash);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// Update in-memory model info
|
|
// Update in-memory model info
|
|
|
std::unique_lock<std::shared_mutex> lock(pImpl->modelsMutex);
|
|
std::unique_lock<std::shared_mutex> lock(pImpl->modelsMutex);
|
|
|
auto it = pImpl->availableModels.find(modelName);
|
|
auto it = pImpl->availableModels.find(modelName);
|
|
|
if (it != pImpl->availableModels.end()) {
|
|
if (it != pImpl->availableModels.end()) {
|
|
|
it->second.sha256 = hash;
|
|
it->second.sha256 = hash;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
return hash;
|
|
return hash;
|
|
|
}
|
|
}
|