|
@@ -11,6 +11,7 @@
|
|
|
#include <fstream>
|
|
#include <fstream>
|
|
|
#include <filesystem>
|
|
#include <filesystem>
|
|
|
#include <nlohmann/json.hpp>
|
|
#include <nlohmann/json.hpp>
|
|
|
|
|
+#include <vector>
|
|
|
|
|
|
|
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
|
|
#include "../stable-diffusion.cpp-src/thirdparty/stb_image_write.h"
|
|
#include "../stable-diffusion.cpp-src/thirdparty/stb_image_write.h"
|
|
@@ -122,20 +123,39 @@ public:
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- auto startTime = std::chrono::steady_clock::now();
|
|
|
|
|
|
|
+ auto startTime = std::chrono::system_clock::now();
|
|
|
|
|
|
|
|
- // Update job status to PROCESSING
|
|
|
|
|
|
|
+ // Update job status to PROCESSING (only if not already in PROCESSING from model loading)
|
|
|
{
|
|
{
|
|
|
std::lock_guard<std::mutex> lock(jobsMutex);
|
|
std::lock_guard<std::mutex> lock(jobsMutex);
|
|
|
if (activeJobs.find(request.id) != activeJobs.end()) {
|
|
if (activeJobs.find(request.id) != activeJobs.end()) {
|
|
|
- activeJobs[request.id].status = GenerationStatus::PROCESSING;
|
|
|
|
|
|
|
+ // Only change status if it was QUEUED (not MODEL_LOADING)
|
|
|
|
|
+ if (activeJobs[request.id].status == GenerationStatus::QUEUED) {
|
|
|
|
|
+ activeJobs[request.id].status = GenerationStatus::PROCESSING;
|
|
|
|
|
+ activeJobs[request.id].progress = 0.0f;
|
|
|
|
|
+ activeJobs[request.id].modelLoadProgress = 0.0f;
|
|
|
|
|
+ activeJobs[request.id].generationProgress = 0.0f;
|
|
|
|
|
+ activeJobs[request.id].firstGenerationCallback = true; // Initialize first callback flag
|
|
|
|
|
+ } else if (activeJobs[request.id].status == GenerationStatus::MODEL_LOADING) {
|
|
|
|
|
+ // Transition from MODEL_LOADING to PROCESSING
|
|
|
|
|
+ activeJobs[request.id].status = GenerationStatus::PROCESSING;
|
|
|
|
|
+ // Model loading should already be complete at this point
|
|
|
|
|
+ activeJobs[request.id].modelLoadProgress = 1.0f;
|
|
|
|
|
+ activeJobs[request.id].firstGenerationCallback = true; // Initialize first callback flag
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
activeJobs[request.id].startTime = startTime;
|
|
activeJobs[request.id].startTime = startTime;
|
|
|
- activeJobs[request.id].progress = 0.0f;
|
|
|
|
|
activeJobs[request.id].currentStep = 0;
|
|
activeJobs[request.id].currentStep = 0;
|
|
|
activeJobs[request.id].totalSteps = 0;
|
|
activeJobs[request.id].totalSteps = 0;
|
|
|
- activeJobs[request.id].timeElapsed = 0;
|
|
|
|
|
- activeJobs[request.id].timeRemaining = 0;
|
|
|
|
|
- activeJobs[request.id].speed = 0.0f;
|
|
|
|
|
|
|
+ if (activeJobs[request.id].timeElapsed == 0) {
|
|
|
|
|
+ activeJobs[request.id].timeElapsed = 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (activeJobs[request.id].timeRemaining == 0) {
|
|
|
|
|
+ activeJobs[request.id].timeRemaining = 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (activeJobs[request.id].speed == 0.0f) {
|
|
|
|
|
+ activeJobs[request.id].speed = 0.0f;
|
|
|
|
|
+ }
|
|
|
saveJobToFile(activeJobs[request.id]);
|
|
saveJobToFile(activeJobs[request.id]);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -149,7 +169,7 @@ public:
|
|
|
// Real generation logic using stable-diffusion.cpp with progress tracking
|
|
// Real generation logic using stable-diffusion.cpp with progress tracking
|
|
|
GenerationResult result = performActualGeneration(request, request.id);
|
|
GenerationResult result = performActualGeneration(request, request.id);
|
|
|
|
|
|
|
|
- auto endTime = std::chrono::steady_clock::now();
|
|
|
|
|
|
|
+ auto endTime = std::chrono::system_clock::now();
|
|
|
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime);
|
|
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime);
|
|
|
result.generationTime = duration.count();
|
|
result.generationTime = duration.count();
|
|
|
|
|
|
|
@@ -158,14 +178,14 @@ public:
|
|
|
std::lock_guard<std::mutex> lock(jobsMutex);
|
|
std::lock_guard<std::mutex> lock(jobsMutex);
|
|
|
if (activeJobs.find(request.id) != activeJobs.end()) {
|
|
if (activeJobs.find(request.id) != activeJobs.end()) {
|
|
|
float finalProgress = activeJobs[request.id].progress;
|
|
float finalProgress = activeJobs[request.id].progress;
|
|
|
- auto completionTime = std::chrono::steady_clock::now();
|
|
|
|
|
|
|
+ auto completionTime = std::chrono::system_clock::now();
|
|
|
auto totalGenerationTime = std::chrono::duration_cast<std::chrono::milliseconds>(completionTime - startTime).count();
|
|
auto totalGenerationTime = std::chrono::duration_cast<std::chrono::milliseconds>(completionTime - startTime).count();
|
|
|
|
|
|
|
|
- std::cout << "[TIMING_ANALYSIS] Job " << request.id
|
|
|
|
|
- << " - Total generation time: " << totalGenerationTime << "ms" << std::endl;
|
|
|
|
|
- std::cout << "[JOB_COMPLETION] Job " << request.id
|
|
|
|
|
- << " - Status changing to '" << (result.success ? "completed" : "failed") << "'"
|
|
|
|
|
- << " - Progress at completion: " << std::fixed << std::setprecision(2) << (finalProgress * 100.0f) << "%" << std::endl;
|
|
|
|
|
|
|
+ LOG_DEBUG("[TIMING_ANALYSIS] Job " + request.id +
|
|
|
|
|
+ " - Total generation time: " + std::to_string(totalGenerationTime) + "ms");
|
|
|
|
|
+ LOG_DEBUG("[JOB_COMPLETION] Job " + request.id +
|
|
|
|
|
+ " - Status changing to '" + (result.success ? "completed" : "failed") + "'" +
|
|
|
|
|
+ " - Progress at completion: " + std::to_string(static_cast<int>(finalProgress * 100.0f)) + "%");
|
|
|
|
|
|
|
|
activeJobs[request.id].status = result.success ? GenerationStatus::COMPLETED : GenerationStatus::FAILED;
|
|
activeJobs[request.id].status = result.success ? GenerationStatus::COMPLETED : GenerationStatus::FAILED;
|
|
|
activeJobs[request.id].endTime = endTime;
|
|
activeJobs[request.id].endTime = endTime;
|
|
@@ -178,9 +198,11 @@ public:
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Store output files and error message
|
|
|
|
|
|
|
+ // Store output files, error message, and generation results
|
|
|
activeJobs[request.id].outputFiles = result.imagePaths;
|
|
activeJobs[request.id].outputFiles = result.imagePaths;
|
|
|
activeJobs[request.id].errorMessage = result.errorMessage;
|
|
activeJobs[request.id].errorMessage = result.errorMessage;
|
|
|
|
|
+ activeJobs[request.id].actualSeed = result.actualSeed;
|
|
|
|
|
+ activeJobs[request.id].generationTime = result.generationTime;
|
|
|
|
|
|
|
|
{
|
|
{
|
|
|
std::ostringstream oss;
|
|
std::ostringstream oss;
|
|
@@ -225,16 +247,63 @@ public:
|
|
|
LOG_INFO(completionMsg);
|
|
LOG_INFO(completionMsg);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Progress callback that updates the job info
|
|
|
|
|
- void updateJobProgress(const std::string& jobId, int step, int totalSteps, float progress, uint64_t timeElapsed) {
|
|
|
|
|
|
|
+ // Progress callback that updates model loading progress
|
|
|
|
|
+ void updateModelLoadProgress(const std::string& jobId, float modelProgress, uint64_t timeElapsed) {
|
|
|
std::lock_guard<std::mutex> lock(jobsMutex);
|
|
std::lock_guard<std::mutex> lock(jobsMutex);
|
|
|
auto it = activeJobs.find(jobId);
|
|
auto it = activeJobs.find(jobId);
|
|
|
if (it != activeJobs.end()) {
|
|
if (it != activeJobs.end()) {
|
|
|
- it->second.progress = progress;
|
|
|
|
|
|
|
+ it->second.modelLoadProgress = modelProgress;
|
|
|
|
|
+ it->second.generationProgress = 0.0f;
|
|
|
|
|
+ // Overall progress during model loading is just the model loading progress
|
|
|
|
|
+ it->second.progress = modelProgress;
|
|
|
|
|
+ it->second.timeElapsed = static_cast<int64_t>(timeElapsed);
|
|
|
|
|
+
|
|
|
|
|
+ // Update status message to reflect model loading
|
|
|
|
|
+ if (!it->second.prompt.empty()) {
|
|
|
|
|
+ size_t bracketPos = it->second.prompt.find(" [Loading model:");
|
|
|
|
|
+ if (bracketPos == std::string::npos) {
|
|
|
|
|
+ it->second.prompt = it->second.prompt + " [Loading model: " + std::to_string(static_cast<int>(modelProgress * 100)) + "%]";
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // Update existing loading message
|
|
|
|
|
+ it->second.prompt = it->second.prompt.substr(0, bracketPos) + " [Loading model: " + std::to_string(static_cast<int>(modelProgress * 100)) + "%]";
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ saveJobToFile(it->second);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Progress callback that updates generation progress
|
|
|
|
|
+ void updateGenerationProgress(const std::string& jobId, int step, int totalSteps, float genProgress, uint64_t timeElapsed) {
|
|
|
|
|
+ std::lock_guard<std::mutex> lock(jobsMutex);
|
|
|
|
|
+ auto it = activeJobs.find(jobId);
|
|
|
|
|
+ if (it != activeJobs.end()) {
|
|
|
|
|
+ // Clamp generation progress to valid range [0.0, 1.0]
|
|
|
|
|
+ genProgress = std::max(0.0f, std::min(1.0f, genProgress));
|
|
|
|
|
+
|
|
|
|
|
+ it->second.generationProgress = genProgress;
|
|
|
it->second.currentStep = step;
|
|
it->second.currentStep = step;
|
|
|
it->second.totalSteps = totalSteps;
|
|
it->second.totalSteps = totalSteps;
|
|
|
it->second.timeElapsed = static_cast<int64_t>(timeElapsed);
|
|
it->second.timeElapsed = static_cast<int64_t>(timeElapsed);
|
|
|
|
|
|
|
|
|
|
+ // Handle first generation callback specially
|
|
|
|
|
+ if (it->second.firstGenerationCallback) {
|
|
|
|
|
+ // This is the first callback, reset generation progress to start from 0.0
|
|
|
|
|
+ // and set overall progress to exactly 50% (model loading complete)
|
|
|
|
|
+ it->second.generationProgress = 0.0f;
|
|
|
|
|
+ it->second.progress = 0.5f;
|
|
|
|
|
+ it->second.firstGenerationCallback = false; // Mark that we've handled the first callback
|
|
|
|
|
+
|
|
|
|
|
+ LOG_DEBUG("First generation callback for job " + jobId +
|
|
|
|
|
+ " - Reset generation progress to 0.0 and overall progress to 50%");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // For subsequent callbacks, calculate overall progress normally: 50% for model loading + 50% for generation
|
|
|
|
|
+ it->second.progress = 0.5f + (genProgress * 0.5f);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Clamp overall progress to valid range [0.0, 1.0]
|
|
|
|
|
+ it->second.progress = std::max(0.0f, std::min(1.0f, it->second.progress));
|
|
|
|
|
+
|
|
|
// Calculate time remaining and speed
|
|
// Calculate time remaining and speed
|
|
|
if (step > 0 && timeElapsed > 0) {
|
|
if (step > 0 && timeElapsed > 0) {
|
|
|
double avgStepTime = static_cast<double>(timeElapsed) / step;
|
|
double avgStepTime = static_cast<double>(timeElapsed) / step;
|
|
@@ -243,8 +312,16 @@ public:
|
|
|
it->second.speed = 1000.0 / avgStepTime; // steps per second
|
|
it->second.speed = 1000.0 / avgStepTime; // steps per second
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Clean up loading message from prompt
|
|
|
|
|
+ if (!it->second.prompt.empty()) {
|
|
|
|
|
+ size_t bracketPos = it->second.prompt.find(" [Loading model:");
|
|
|
|
|
+ if (bracketPos != std::string::npos) {
|
|
|
|
|
+ it->second.prompt = it->second.prompt.substr(0, bracketPos);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// Save progress to file periodically (every 10 steps or on significant progress changes)
|
|
// Save progress to file periodically (every 10 steps or on significant progress changes)
|
|
|
- if (step % 10 == 0 || progress >= 0.99f) {
|
|
|
|
|
|
|
+ if (step % 10 == 0 || genProgress >= 0.99f) {
|
|
|
saveJobToFile(it->second);
|
|
saveJobToFile(it->second);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -261,10 +338,66 @@ public:
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Check if the model is loaded (DO NOT auto-load)
|
|
|
|
|
|
|
+ // Update job status to MODEL_LOADING if model needs to be loaded
|
|
|
|
|
+ bool modelNeededLoading = false;
|
|
|
if (!modelManager->isModelLoaded(request.modelName)) {
|
|
if (!modelManager->isModelLoaded(request.modelName)) {
|
|
|
- result.errorMessage = "Model not loaded: " + request.modelName + ". Please load the model first using POST /api/models/{hash}/load";
|
|
|
|
|
- return result;
|
|
|
|
|
|
|
+ // Update status to show model is being loaded
|
|
|
|
|
+ {
|
|
|
|
|
+ std::lock_guard<std::mutex> lock(jobsMutex);
|
|
|
|
|
+ if (activeJobs.find(request.id) != activeJobs.end()) {
|
|
|
|
|
+ activeJobs[request.id].status = GenerationStatus::MODEL_LOADING;
|
|
|
|
|
+ activeJobs[request.id].progress = 0.0f; // Start at 0% for model loading
|
|
|
|
|
+ saveJobToFile(activeJobs[request.id]);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Auto-load the model with progress tracking
|
|
|
|
|
+ auto modelLoadStartTime = std::chrono::system_clock::now();
|
|
|
|
|
+
|
|
|
|
|
+ // Create a progress callback for model loading
|
|
|
|
|
+ auto modelLoadProgressCallback = [this, jobId = request.id, modelLoadStartTime](float loadProgress) {
|
|
|
|
|
+ auto currentTime = std::chrono::system_clock::now();
|
|
|
|
|
+ uint64_t timeElapsed = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - modelLoadStartTime).count();
|
|
|
|
|
+
|
|
|
|
|
+ updateModelLoadProgress(jobId, loadProgress, timeElapsed);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ bool loadSuccess = modelManager->loadModel(request.modelName, modelLoadProgressCallback);
|
|
|
|
|
+
|
|
|
|
|
+ if (!loadSuccess || !modelManager->isModelLoaded(request.modelName)) {
|
|
|
|
|
+ result.errorMessage = "Failed to load model: " + request.modelName;
|
|
|
|
|
+
|
|
|
|
|
+ // Update job status to failed
|
|
|
|
|
+ {
|
|
|
|
|
+ std::lock_guard<std::mutex> lock(jobsMutex);
|
|
|
|
|
+ if (activeJobs.find(request.id) != activeJobs.end()) {
|
|
|
|
|
+ activeJobs[request.id].status = GenerationStatus::FAILED;
|
|
|
|
|
+ activeJobs[request.id].errorMessage = result.errorMessage;
|
|
|
|
|
+ saveJobToFile(activeJobs[request.id]);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ modelNeededLoading = true;
|
|
|
|
|
+
|
|
|
|
|
+ // Reset status after successful model loading
|
|
|
|
|
+ {
|
|
|
|
|
+ std::lock_guard<std::mutex> lock(jobsMutex);
|
|
|
|
|
+ if (activeJobs.find(request.id) != activeJobs.end()) {
|
|
|
|
|
+ // Remove the loading progress suffix from prompt
|
|
|
|
|
+ size_t bracketPos = activeJobs[request.id].prompt.find(" [Loading model:");
|
|
|
|
|
+ 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)
|
|
|
|
|
+ activeJobs[request.id].modelLoadProgress = 1.0f;
|
|
|
|
|
+ activeJobs[request.id].generationProgress = 0.0f;
|
|
|
|
|
+ activeJobs[request.id].progress = 0.5f;
|
|
|
|
|
+ activeJobs[request.id].firstGenerationCallback = true; // Initialize first callback flag
|
|
|
|
|
+ saveJobToFile(activeJobs[request.id]);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Get the model wrapper from the shared model manager
|
|
// Get the model wrapper from the shared model manager
|
|
@@ -328,18 +461,16 @@ public:
|
|
|
// Create progress callback that updates job info
|
|
// Create progress callback that updates job info
|
|
|
auto progressCallback = [this, jobId](int step, int totalSteps, float progress, void* userData) {
|
|
auto progressCallback = [this, jobId](int step, int totalSteps, float progress, void* userData) {
|
|
|
// Calculate time elapsed from start time (stored in userData)
|
|
// Calculate time elapsed from start time (stored in userData)
|
|
|
- auto startTime = userData ? *static_cast<std::chrono::steady_clock::time_point*>(userData) : std::chrono::steady_clock::now();
|
|
|
|
|
- auto currentTime = std::chrono::steady_clock::now();
|
|
|
|
|
|
|
+ auto startTime = userData ? *static_cast<std::chrono::system_clock::time_point*>(userData) : std::chrono::system_clock::now();
|
|
|
|
|
+ auto currentTime = std::chrono::system_clock::now();
|
|
|
uint64_t timeElapsed = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - startTime).count();
|
|
uint64_t timeElapsed = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - startTime).count();
|
|
|
-
|
|
|
|
|
- // FIX: Convert progress from decimal fraction (0.0-1.0) to percentage (0-100)
|
|
|
|
|
- float progressPercentage = progress * 100.0f;
|
|
|
|
|
|
|
|
|
|
- updateJobProgress(jobId, step, totalSteps, progressPercentage, timeElapsed);
|
|
|
|
|
|
|
+ // Use the new generation progress update function
|
|
|
|
|
+ updateGenerationProgress(jobId, step, totalSteps, progress, timeElapsed);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// Store start time to pass as user data
|
|
// Store start time to pass as user data
|
|
|
- auto generationStartTime = std::chrono::steady_clock::now();
|
|
|
|
|
|
|
+ auto generationStartTime = std::chrono::system_clock::now();
|
|
|
|
|
|
|
|
switch (request.requestType) {
|
|
switch (request.requestType) {
|
|
|
case GenerationRequest::RequestType::TEXT2IMG:
|
|
case GenerationRequest::RequestType::TEXT2IMG:
|
|
@@ -434,19 +565,19 @@ public:
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Save generated images to files
|
|
// Save generated images to files
|
|
|
- std::cout << "[TIMING_ANALYSIS] Job " << request.id << " - Starting post-processing (image saving) phase" << std::endl;
|
|
|
|
|
- auto postProcessingStart = std::chrono::steady_clock::now();
|
|
|
|
|
|
|
+ LOG_DEBUG("[TIMING_ANALYSIS] Job " + request.id + " - Starting post-processing (image saving) phase");
|
|
|
|
|
+ auto postProcessingStart = std::chrono::system_clock::now();
|
|
|
|
|
|
|
|
for (size_t i = 0; i < generatedImages.size(); i++) {
|
|
for (size_t i = 0; i < generatedImages.size(); i++) {
|
|
|
const auto& image = generatedImages[i];
|
|
const auto& image = generatedImages[i];
|
|
|
- std::cout << "[TIMING_ANALYSIS] Job " << request.id << " - Saving image " << (i+1) << "/" << generatedImages.size() << std::endl;
|
|
|
|
|
- auto imageSaveStart = std::chrono::steady_clock::now();
|
|
|
|
|
|
|
+ LOG_DEBUG("[TIMING_ANALYSIS] Job " + request.id + " - Saving image " + std::to_string(i+1) + "/" + std::to_string(generatedImages.size()));
|
|
|
|
|
+ auto imageSaveStart = std::chrono::system_clock::now();
|
|
|
|
|
|
|
|
std::string imagePath = saveImageToFile(image, request.id, i);
|
|
std::string imagePath = saveImageToFile(image, request.id, i);
|
|
|
|
|
|
|
|
- auto imageSaveEnd = std::chrono::steady_clock::now();
|
|
|
|
|
|
|
+ auto imageSaveEnd = std::chrono::system_clock::now();
|
|
|
auto imageSaveTime = std::chrono::duration_cast<std::chrono::milliseconds>(imageSaveEnd - imageSaveStart).count();
|
|
auto imageSaveTime = std::chrono::duration_cast<std::chrono::milliseconds>(imageSaveEnd - imageSaveStart).count();
|
|
|
- std::cout << "[TIMING_ANALYSIS] Job " << request.id << " - Image " << (i+1) << " save took " << imageSaveTime << "ms" << std::endl;
|
|
|
|
|
|
|
+ LOG_DEBUG("[TIMING_ANALYSIS] Job " + request.id + " - Image " + std::to_string(i+1) + " save took " + std::to_string(imageSaveTime) + "ms");
|
|
|
|
|
|
|
|
if (!imagePath.empty()) {
|
|
if (!imagePath.empty()) {
|
|
|
result.imagePaths.push_back(imagePath);
|
|
result.imagePaths.push_back(imagePath);
|
|
@@ -456,9 +587,9 @@ public:
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- auto postProcessingEnd = std::chrono::steady_clock::now();
|
|
|
|
|
|
|
+ auto postProcessingEnd = std::chrono::system_clock::now();
|
|
|
auto postProcessingTime = std::chrono::duration_cast<std::chrono::milliseconds>(postProcessingEnd - postProcessingStart).count();
|
|
auto postProcessingTime = std::chrono::duration_cast<std::chrono::milliseconds>(postProcessingEnd - postProcessingStart).count();
|
|
|
- std::cout << "[TIMING_ANALYSIS] Job " << request.id << " - Post-processing completed in " << postProcessingTime << "ms" << std::endl;
|
|
|
|
|
|
|
+ LOG_DEBUG("[TIMING_ANALYSIS] Job " + request.id + " - Post-processing completed in " + std::to_string(postProcessingTime) + "ms");
|
|
|
|
|
|
|
|
result.success = true;
|
|
result.success = true;
|
|
|
result.generationTime = generatedImages.empty() ? 0 : generatedImages[0].generationTime;
|
|
result.generationTime = generatedImages.empty() ? 0 : generatedImages[0].generationTime;
|
|
@@ -486,7 +617,7 @@ public:
|
|
|
ss << jobOutputDir << "/" << requestId << "_" << index << ".png";
|
|
ss << jobOutputDir << "/" << requestId << "_" << index << ".png";
|
|
|
|
|
|
|
|
std::string filename = ss.str();
|
|
std::string filename = ss.str();
|
|
|
- std::cout << "Attempting to save image to: " << filename << std::endl;
|
|
|
|
|
|
|
+ LOG_DEBUG("Attempting to save image to: " + filename);
|
|
|
|
|
|
|
|
// Check if image data is valid
|
|
// Check if image data is valid
|
|
|
if (image.data.empty() || image.width <= 0 || image.height <= 0) {
|
|
if (image.data.empty() || image.width <= 0 || image.height <= 0) {
|
|
@@ -510,17 +641,17 @@ public:
|
|
|
// Check if we can write to the directory
|
|
// Check if we can write to the directory
|
|
|
std::ofstream testFile(filename + ".test");
|
|
std::ofstream testFile(filename + ".test");
|
|
|
if (!testFile.is_open()) {
|
|
if (!testFile.is_open()) {
|
|
|
- std::cerr << "Cannot write to directory " << jobOutputDir
|
|
|
|
|
- << ": permission denied or disk full" << std::endl;
|
|
|
|
|
|
|
+ LOG_ERROR("Cannot write to directory " + jobOutputDir +
|
|
|
|
|
+ ": permission denied or disk full");
|
|
|
return "";
|
|
return "";
|
|
|
}
|
|
}
|
|
|
testFile.close();
|
|
testFile.close();
|
|
|
std::filesystem::remove(filename + ".test");
|
|
std::filesystem::remove(filename + ".test");
|
|
|
|
|
|
|
|
// Write PNG file using stb_image_write with detailed error logging
|
|
// Write PNG file using stb_image_write with detailed error logging
|
|
|
- std::cout << "Writing PNG file: " << filename
|
|
|
|
|
- << " (size: " << image.width << "x" << image.height
|
|
|
|
|
- << "x" << image.channels << ")" << std::endl;
|
|
|
|
|
|
|
+ LOG_DEBUG("Writing PNG file: " + filename +
|
|
|
|
|
+ " (size: " + std::to_string(image.width) + "x" + std::to_string(image.height) +
|
|
|
|
|
+ "x" + std::to_string(image.channels) + ")");
|
|
|
|
|
|
|
|
int result = stbi_write_png(
|
|
int result = stbi_write_png(
|
|
|
filename.c_str(),
|
|
filename.c_str(),
|
|
@@ -532,33 +663,33 @@ public:
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
if (result == 0) {
|
|
if (result == 0) {
|
|
|
- std::cerr << "stbi_write_png failed for " << filename << std::endl;
|
|
|
|
|
|
|
+ LOG_ERROR("stbi_write_png failed for " + filename);
|
|
|
|
|
|
|
|
// Try to get more detailed error information
|
|
// Try to get more detailed error information
|
|
|
- std::cerr << "Image details:" << std::endl;
|
|
|
|
|
- std::cerr << " Dimensions: " << image.width << "x" << image.height << std::endl;
|
|
|
|
|
- std::cerr << " Channels: " << image.channels << std::endl;
|
|
|
|
|
- std::cerr << " Data size: " << image.data.size() << " bytes" << std::endl;
|
|
|
|
|
- std::cerr << " Expected size: " << expectedDataSize << " bytes" << std::endl;
|
|
|
|
|
- std::cerr << " Stride: " << (image.width * image.channels) << " bytes" << std::endl;
|
|
|
|
|
|
|
+ LOG_ERROR("Image details:");
|
|
|
|
|
+ LOG_ERROR(" Dimensions: " + std::to_string(image.width) + "x" + std::to_string(image.height));
|
|
|
|
|
+ LOG_ERROR(" Channels: " + std::to_string(image.channels));
|
|
|
|
|
+ LOG_ERROR(" Data size: " + std::to_string(image.data.size()) + " bytes");
|
|
|
|
|
+ LOG_ERROR(" Expected size: " + std::to_string(expectedDataSize) + " bytes");
|
|
|
|
|
+ LOG_ERROR(" Stride: " + std::to_string(image.width * image.channels) + " bytes");
|
|
|
|
|
|
|
|
// Check if file was created but is empty
|
|
// Check if file was created but is empty
|
|
|
if (std::filesystem::exists(filename)) {
|
|
if (std::filesystem::exists(filename)) {
|
|
|
auto fileSize = std::filesystem::file_size(filename);
|
|
auto fileSize = std::filesystem::file_size(filename);
|
|
|
- std::cerr << " File exists but size is: " << fileSize << " bytes" << std::endl;
|
|
|
|
|
|
|
+ LOG_ERROR(" File exists but size is: " + std::to_string(fileSize) + " bytes");
|
|
|
if (fileSize == 0) {
|
|
if (fileSize == 0) {
|
|
|
- std::cerr << " ERROR: Zero-byte file created - stbi_write_png returned false but file exists" << std::endl;
|
|
|
|
|
|
|
+ LOG_ERROR(" ERROR: Zero-byte file created - stbi_write_png returned false but file exists");
|
|
|
}
|
|
}
|
|
|
} else {
|
|
} else {
|
|
|
- std::cerr << " File was not created" << std::endl;
|
|
|
|
|
|
|
+ LOG_ERROR(" File was not created");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Check disk space
|
|
// Check disk space
|
|
|
try {
|
|
try {
|
|
|
auto space = std::filesystem::space(jobOutputDir);
|
|
auto space = std::filesystem::space(jobOutputDir);
|
|
|
- std::cerr << " Available disk space: " << (space.available / (1024 * 1024)) << " MB" << std::endl;
|
|
|
|
|
|
|
+ LOG_ERROR(" Available disk space: " + std::to_string(space.available / (1024 * 1024)) + " MB");
|
|
|
} catch (const std::exception& e) {
|
|
} catch (const std::exception& e) {
|
|
|
- std::cerr << " Could not check disk space: " << e.what() << std::endl;
|
|
|
|
|
|
|
+ LOG_ERROR(" Could not check disk space: " + std::string(e.what()));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return "";
|
|
return "";
|
|
@@ -566,21 +697,21 @@ public:
|
|
|
|
|
|
|
|
// Verify the file was created successfully and has content
|
|
// Verify the file was created successfully and has content
|
|
|
if (!std::filesystem::exists(filename)) {
|
|
if (!std::filesystem::exists(filename)) {
|
|
|
- std::cerr << "ERROR: stbi_write_png returned success but file does not exist: " << filename << std::endl;
|
|
|
|
|
|
|
+ LOG_ERROR("ERROR: stbi_write_png returned success but file does not exist: " + filename);
|
|
|
return "";
|
|
return "";
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
auto fileSize = std::filesystem::file_size(filename);
|
|
auto fileSize = std::filesystem::file_size(filename);
|
|
|
if (fileSize == 0) {
|
|
if (fileSize == 0) {
|
|
|
- std::cerr << "ERROR: stbi_write_png returned success but created zero-byte file: " << filename << std::endl;
|
|
|
|
|
|
|
+ LOG_ERROR("ERROR: stbi_write_png returned success but created zero-byte file: " + filename);
|
|
|
return "";
|
|
return "";
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- std::cout << "Successfully saved generated image to: " << filename
|
|
|
|
|
- << " (" << image.width << "x" << image.height
|
|
|
|
|
- << ", " << image.channels << " channels, "
|
|
|
|
|
- << image.data.size() << " data bytes, "
|
|
|
|
|
- << fileSize << " file bytes)" << std::endl;
|
|
|
|
|
|
|
+ LOG_DEBUG("Successfully saved generated image to: " + filename +
|
|
|
|
|
+ " (" + std::to_string(image.width) + "x" + std::to_string(image.height) +
|
|
|
|
|
+ ", " + std::to_string(image.channels) + " channels, "
|
|
|
|
|
+ + std::to_string(image.data.size()) + " data bytes, "
|
|
|
|
|
+ + std::to_string(fileSize) + " file bytes)");
|
|
|
return filename;
|
|
return filename;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -619,6 +750,7 @@ public:
|
|
|
std::string jobStatusToString(GenerationStatus status) {
|
|
std::string jobStatusToString(GenerationStatus status) {
|
|
|
switch (status) {
|
|
switch (status) {
|
|
|
case GenerationStatus::QUEUED: return "queued";
|
|
case GenerationStatus::QUEUED: return "queued";
|
|
|
|
|
+ case GenerationStatus::MODEL_LOADING: return "loading";
|
|
|
case GenerationStatus::PROCESSING: return "processing";
|
|
case GenerationStatus::PROCESSING: return "processing";
|
|
|
case GenerationStatus::COMPLETED: return "completed";
|
|
case GenerationStatus::COMPLETED: return "completed";
|
|
|
case GenerationStatus::FAILED: return "failed";
|
|
case GenerationStatus::FAILED: return "failed";
|
|
@@ -628,6 +760,7 @@ public:
|
|
|
|
|
|
|
|
GenerationStatus stringToJobStatus(const std::string& status) {
|
|
GenerationStatus stringToJobStatus(const std::string& status) {
|
|
|
if (status == "queued") return GenerationStatus::QUEUED;
|
|
if (status == "queued") return GenerationStatus::QUEUED;
|
|
|
|
|
+ if (status == "loading") return GenerationStatus::MODEL_LOADING;
|
|
|
if (status == "processing") return GenerationStatus::PROCESSING;
|
|
if (status == "processing") return GenerationStatus::PROCESSING;
|
|
|
if (status == "completed") return GenerationStatus::COMPLETED;
|
|
if (status == "completed") return GenerationStatus::COMPLETED;
|
|
|
if (status == "failed") return GenerationStatus::FAILED;
|
|
if (status == "failed") return GenerationStatus::FAILED;
|
|
@@ -648,13 +781,43 @@ public:
|
|
|
return JobType::GENERATION;
|
|
return JobType::GENERATION;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ SamplingMethod stringToSamplingMethod(const std::string& method) {
|
|
|
|
|
+ if (method == "euler") return SamplingMethod::EULER;
|
|
|
|
|
+ if (method == "euler_a") return SamplingMethod::EULER_A;
|
|
|
|
|
+ if (method == "heun") return SamplingMethod::HEUN;
|
|
|
|
|
+ if (method == "dpm2") return SamplingMethod::DPM2;
|
|
|
|
|
+ if (method == "dpmpp2s_a") return SamplingMethod::DPMPP2S_A;
|
|
|
|
|
+ if (method == "dpmpp2m") return SamplingMethod::DPMPP2M;
|
|
|
|
|
+ if (method == "dpmpp2mv2") return SamplingMethod::DPMPP2MV2;
|
|
|
|
|
+ if (method == "ipndm") return SamplingMethod::IPNDM;
|
|
|
|
|
+ if (method == "ipndm_v") return SamplingMethod::IPNDM_V;
|
|
|
|
|
+ if (method == "lcm") return SamplingMethod::LCM;
|
|
|
|
|
+ if (method == "ddim_trailing") return SamplingMethod::DDIM_TRAILING;
|
|
|
|
|
+ if (method == "tcd") return SamplingMethod::TCD;
|
|
|
|
|
+ return SamplingMethod::DEFAULT;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Scheduler stringToScheduler(const std::string& scheduler) {
|
|
|
|
|
+ if (scheduler == "discrete") return Scheduler::DISCRETE;
|
|
|
|
|
+ if (scheduler == "karras") return Scheduler::KARRAS;
|
|
|
|
|
+ if (scheduler == "exponential") return Scheduler::EXPONENTIAL;
|
|
|
|
|
+ if (scheduler == "ays") return Scheduler::AYS;
|
|
|
|
|
+ if (scheduler == "gits") return Scheduler::GITS;
|
|
|
|
|
+ if (scheduler == "smoothstep") return Scheduler::SMOOTHSTEP;
|
|
|
|
|
+ if (scheduler == "sgm_uniform") return Scheduler::SGM_UNIFORM;
|
|
|
|
|
+ if (scheduler == "simple") return Scheduler::SIMPLE;
|
|
|
|
|
+ return Scheduler::DEFAULT;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
void saveJobToFile(const JobInfo& job) {
|
|
void saveJobToFile(const JobInfo& job) {
|
|
|
try {
|
|
try {
|
|
|
// Create queue directory if it doesn't exist
|
|
// Create queue directory if it doesn't exist
|
|
|
std::filesystem::create_directories(queueDir);
|
|
std::filesystem::create_directories(queueDir);
|
|
|
|
|
|
|
|
- // Create JSON object
|
|
|
|
|
|
|
+ // Create JSON object with enhanced fields
|
|
|
nlohmann::json jobJson;
|
|
nlohmann::json jobJson;
|
|
|
|
|
+
|
|
|
|
|
+ // Basic fields
|
|
|
jobJson["id"] = job.id;
|
|
jobJson["id"] = job.id;
|
|
|
jobJson["type"] = jobTypeToString(job.type);
|
|
jobJson["type"] = jobTypeToString(job.type);
|
|
|
jobJson["status"] = jobStatusToString(job.status);
|
|
jobJson["status"] = jobStatusToString(job.status);
|
|
@@ -678,6 +841,89 @@ public:
|
|
|
jobJson["end_time"] = endMs;
|
|
jobJson["end_time"] = endMs;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Enhanced fields for repeatable generation
|
|
|
|
|
+ if (!job.modelName.empty()) {
|
|
|
|
|
+ jobJson["model_name"] = job.modelName;
|
|
|
|
|
+ jobJson["model_hash"] = job.modelHash;
|
|
|
|
|
+ jobJson["model_path"] = job.modelPath;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!job.negativePrompt.empty()) {
|
|
|
|
|
+ jobJson["negative_prompt"] = job.negativePrompt;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ jobJson["width"] = job.width;
|
|
|
|
|
+ jobJson["height"] = job.height;
|
|
|
|
|
+ jobJson["batch_count"] = job.batchCount;
|
|
|
|
|
+ jobJson["steps"] = job.steps;
|
|
|
|
|
+ jobJson["cfg_scale"] = job.cfgScale;
|
|
|
|
|
+ jobJson["sampling_method"] = samplingMethodToString(job.samplingMethod);
|
|
|
|
|
+ jobJson["scheduler"] = schedulerToString(job.scheduler);
|
|
|
|
|
+ jobJson["seed"] = job.seed;
|
|
|
|
|
+ jobJson["actual_seed"] = job.actualSeed;
|
|
|
|
|
+ jobJson["request_type"] = job.requestType;
|
|
|
|
|
+ jobJson["strength"] = job.strength;
|
|
|
|
|
+ jobJson["control_strength"] = job.controlStrength;
|
|
|
|
|
+ jobJson["clip_skip"] = job.clipSkip;
|
|
|
|
|
+ jobJson["n_threads"] = job.nThreads;
|
|
|
|
|
+ jobJson["offload_params_to_cpu"] = job.offloadParamsToCpu;
|
|
|
|
|
+ jobJson["clip_on_cpu"] = job.clipOnCpu;
|
|
|
|
|
+ jobJson["vae_on_cpu"] = job.vaeOnCpu;
|
|
|
|
|
+ jobJson["diffusion_flash_attn"] = job.diffusionFlashAttn;
|
|
|
|
|
+ jobJson["diffusion_conv_direct"] = job.diffusionConvDirect;
|
|
|
|
|
+ jobJson["vae_conv_direct"] = job.vaeConvDirect;
|
|
|
|
|
+ jobJson["generation_time"] = job.generationTime;
|
|
|
|
|
+
|
|
|
|
|
+ // Image paths for complex operations
|
|
|
|
|
+ if (!job.initImageData.empty()) {
|
|
|
|
|
+ jobJson["init_image_path"] = job.initImageData;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!job.controlImageData.empty()) {
|
|
|
|
|
+ jobJson["control_image_path"] = job.controlImageData;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!job.maskImageData.empty()) {
|
|
|
|
|
+ jobJson["mask_image_path"] = job.maskImageData;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Model paths for advanced usage
|
|
|
|
|
+ if (!job.clipLPath.empty()) {
|
|
|
|
|
+ jobJson["clip_l_path"] = job.clipLPath;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!job.clipGPath.empty()) {
|
|
|
|
|
+ jobJson["clip_g_path"] = job.clipGPath;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!job.vaePath.empty()) {
|
|
|
|
|
+ jobJson["vae_path"] = job.vaePath;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!job.taesdPath.empty()) {
|
|
|
|
|
+ jobJson["taesd_path"] = job.taesdPath;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!job.controlNetPath.empty()) {
|
|
|
|
|
+ jobJson["controlnet_path"] = job.controlNetPath;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!job.embeddingDir.empty()) {
|
|
|
|
|
+ jobJson["embedding_dir"] = job.embeddingDir;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!job.loraModelDir.empty()) {
|
|
|
|
|
+ jobJson["lora_model_dir"] = job.loraModelDir;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!job.esrganPath.empty()) {
|
|
|
|
|
+ jobJson["esrgan_path"] = job.esrganPath;
|
|
|
|
|
+ }
|
|
|
|
|
+ jobJson["upscale_factor"] = job.upscaleFactor;
|
|
|
|
|
+
|
|
|
|
|
+ // Progress and timing information
|
|
|
|
|
+ jobJson["progress"] = job.progress;
|
|
|
|
|
+ jobJson["model_load_progress"] = job.modelLoadProgress;
|
|
|
|
|
+ jobJson["generation_progress"] = job.generationProgress;
|
|
|
|
|
+ jobJson["current_step"] = job.currentStep;
|
|
|
|
|
+ jobJson["total_steps"] = job.totalSteps;
|
|
|
|
|
+ jobJson["time_elapsed"] = job.timeElapsed;
|
|
|
|
|
+ jobJson["time_remaining"] = job.timeRemaining;
|
|
|
|
|
+ jobJson["speed"] = job.speed;
|
|
|
|
|
+ jobJson["first_generation_callback"] = job.firstGenerationCallback;
|
|
|
|
|
+
|
|
|
|
|
+ // Output information
|
|
|
jobJson["output_files"] = job.outputFiles;
|
|
jobJson["output_files"] = job.outputFiles;
|
|
|
jobJson["error_message"] = job.errorMessage;
|
|
jobJson["error_message"] = job.errorMessage;
|
|
|
|
|
|
|
@@ -689,7 +935,7 @@ public:
|
|
|
file.close();
|
|
file.close();
|
|
|
}
|
|
}
|
|
|
} catch (const std::exception& e) {
|
|
} catch (const std::exception& e) {
|
|
|
- std::cerr << "Error saving job to file: " << e.what() << std::endl;
|
|
|
|
|
|
|
+ LOG_ERROR("Error saving job to file: " + std::string(e.what()));
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -726,18 +972,18 @@ public:
|
|
|
|
|
|
|
|
// Reconstruct time points
|
|
// Reconstruct time points
|
|
|
auto queuedMs = jobJson["queued_time"].get<int64_t>();
|
|
auto queuedMs = jobJson["queued_time"].get<int64_t>();
|
|
|
- job.queuedTime = std::chrono::steady_clock::time_point(
|
|
|
|
|
|
|
+ job.queuedTime = std::chrono::system_clock::time_point(
|
|
|
std::chrono::milliseconds(queuedMs));
|
|
std::chrono::milliseconds(queuedMs));
|
|
|
|
|
|
|
|
if (jobJson.contains("start_time")) {
|
|
if (jobJson.contains("start_time")) {
|
|
|
auto startMs = jobJson["start_time"].get<int64_t>();
|
|
auto startMs = jobJson["start_time"].get<int64_t>();
|
|
|
- job.startTime = std::chrono::steady_clock::time_point(
|
|
|
|
|
|
|
+ job.startTime = std::chrono::system_clock::time_point(
|
|
|
std::chrono::milliseconds(startMs));
|
|
std::chrono::milliseconds(startMs));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (jobJson.contains("end_time")) {
|
|
if (jobJson.contains("end_time")) {
|
|
|
auto endMs = jobJson["end_time"].get<int64_t>();
|
|
auto endMs = jobJson["end_time"].get<int64_t>();
|
|
|
- job.endTime = std::chrono::steady_clock::time_point(
|
|
|
|
|
|
|
+ job.endTime = std::chrono::system_clock::time_point(
|
|
|
std::chrono::milliseconds(endMs));
|
|
std::chrono::milliseconds(endMs));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -749,12 +995,86 @@ public:
|
|
|
job.errorMessage = jobJson["error_message"];
|
|
job.errorMessage = jobJson["error_message"];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Load enhanced fields (with backward compatibility)
|
|
|
|
|
+ if (jobJson.contains("model_name")) {
|
|
|
|
|
+ job.modelName = jobJson["model_name"];
|
|
|
|
|
+ job.modelHash = jobJson.value("model_hash", "");
|
|
|
|
|
+ job.modelPath = jobJson.value("model_path", "");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (jobJson.contains("negative_prompt")) {
|
|
|
|
|
+ job.negativePrompt = jobJson["negative_prompt"];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (jobJson.contains("width")) job.width = jobJson["width"];
|
|
|
|
|
+ if (jobJson.contains("height")) job.height = jobJson["height"];
|
|
|
|
|
+ if (jobJson.contains("batch_count")) job.batchCount = jobJson["batch_count"];
|
|
|
|
|
+ if (jobJson.contains("steps")) job.steps = jobJson["steps"];
|
|
|
|
|
+ if (jobJson.contains("cfg_scale")) job.cfgScale = jobJson["cfg_scale"];
|
|
|
|
|
+
|
|
|
|
|
+ if (jobJson.contains("sampling_method")) {
|
|
|
|
|
+ std::string samplingMethodStr = jobJson["sampling_method"];
|
|
|
|
|
+ job.samplingMethod = stringToSamplingMethod(samplingMethodStr);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (jobJson.contains("scheduler")) {
|
|
|
|
|
+ std::string schedulerStr = jobJson["scheduler"];
|
|
|
|
|
+ job.scheduler = stringToScheduler(schedulerStr);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (jobJson.contains("seed")) job.seed = jobJson["seed"];
|
|
|
|
|
+ if (jobJson.contains("actual_seed")) job.actualSeed = jobJson["actual_seed"];
|
|
|
|
|
+ if (jobJson.contains("request_type")) job.requestType = jobJson["request_type"];
|
|
|
|
|
+ if (jobJson.contains("strength")) job.strength = jobJson["strength"];
|
|
|
|
|
+ if (jobJson.contains("control_strength")) job.controlStrength = jobJson["control_strength"];
|
|
|
|
|
+ if (jobJson.contains("clip_skip")) job.clipSkip = jobJson["clip_skip"];
|
|
|
|
|
+ if (jobJson.contains("n_threads")) job.nThreads = jobJson["n_threads"];
|
|
|
|
|
+ if (jobJson.contains("offload_params_to_cpu")) job.offloadParamsToCpu = jobJson["offload_params_to_cpu"];
|
|
|
|
|
+ if (jobJson.contains("clip_on_cpu")) job.clipOnCpu = jobJson["clip_on_cpu"];
|
|
|
|
|
+ if (jobJson.contains("vae_on_cpu")) job.vaeOnCpu = jobJson["vae_on_cpu"];
|
|
|
|
|
+ if (jobJson.contains("diffusion_flash_attn")) job.diffusionFlashAttn = jobJson["diffusion_flash_attn"];
|
|
|
|
|
+ if (jobJson.contains("diffusion_conv_direct")) job.diffusionConvDirect = jobJson["diffusion_conv_direct"];
|
|
|
|
|
+ if (jobJson.contains("vae_conv_direct")) job.vaeConvDirect = jobJson["vae_conv_direct"];
|
|
|
|
|
+ if (jobJson.contains("generation_time")) job.generationTime = jobJson["generation_time"];
|
|
|
|
|
+
|
|
|
|
|
+ // Image paths for complex operations
|
|
|
|
|
+ if (jobJson.contains("init_image_path")) job.initImageData = jobJson["init_image_path"];
|
|
|
|
|
+ if (jobJson.contains("control_image_path")) job.controlImageData = jobJson["control_image_path"];
|
|
|
|
|
+ if (jobJson.contains("mask_image_path")) job.maskImageData = jobJson["mask_image_path"];
|
|
|
|
|
+
|
|
|
|
|
+ // Model paths for advanced usage
|
|
|
|
|
+ if (jobJson.contains("clip_l_path")) job.clipLPath = jobJson["clip_l_path"];
|
|
|
|
|
+ if (jobJson.contains("clip_g_path")) job.clipGPath = jobJson["clip_g_path"];
|
|
|
|
|
+ if (jobJson.contains("vae_path")) job.vaePath = jobJson["vae_path"];
|
|
|
|
|
+ if (jobJson.contains("taesd_path")) job.taesdPath = jobJson["taesd_path"];
|
|
|
|
|
+ if (jobJson.contains("controlnet_path")) job.controlNetPath = jobJson["controlnet_path"];
|
|
|
|
|
+ if (jobJson.contains("embedding_dir")) job.embeddingDir = jobJson["embedding_dir"];
|
|
|
|
|
+ if (jobJson.contains("lora_model_dir")) job.loraModelDir = jobJson["lora_model_dir"];
|
|
|
|
|
+ if (jobJson.contains("esrgan_path")) job.esrganPath = jobJson["esrgan_path"];
|
|
|
|
|
+ if (jobJson.contains("upscale_factor")) job.upscaleFactor = jobJson["upscale_factor"];
|
|
|
|
|
+
|
|
|
|
|
+ // Progress and timing information
|
|
|
|
|
+ if (jobJson.contains("progress")) job.progress = jobJson["progress"];
|
|
|
|
|
+ if (jobJson.contains("model_load_progress")) job.modelLoadProgress = jobJson["model_load_progress"];
|
|
|
|
|
+ if (jobJson.contains("generation_progress")) job.generationProgress = jobJson["generation_progress"];
|
|
|
|
|
+ if (jobJson.contains("current_step")) job.currentStep = jobJson["current_step"];
|
|
|
|
|
+ if (jobJson.contains("total_steps")) job.totalSteps = jobJson["total_steps"];
|
|
|
|
|
+ if (jobJson.contains("time_elapsed")) job.timeElapsed = jobJson["time_elapsed"];
|
|
|
|
|
+ if (jobJson.contains("time_remaining")) job.timeRemaining = jobJson["time_remaining"];
|
|
|
|
|
+ if (jobJson.contains("speed")) job.speed = jobJson["speed"];
|
|
|
|
|
+ if (jobJson.contains("first_generation_callback")) {
|
|
|
|
|
+ job.firstGenerationCallback = jobJson["first_generation_callback"];
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // For backward compatibility, default to true for loaded jobs
|
|
|
|
|
+ job.firstGenerationCallback = true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// Clean up stale processing jobs from server restart
|
|
// Clean up stale processing jobs from server restart
|
|
|
if (job.status == GenerationStatus::PROCESSING) {
|
|
if (job.status == GenerationStatus::PROCESSING) {
|
|
|
job.status = GenerationStatus::FAILED;
|
|
job.status = GenerationStatus::FAILED;
|
|
|
job.errorMessage = "Server restarted while job was processing";
|
|
job.errorMessage = "Server restarted while job was processing";
|
|
|
- job.endTime = std::chrono::steady_clock::now();
|
|
|
|
|
- std::cout << "Marked stale job as failed: " << job.id << std::endl;
|
|
|
|
|
|
|
+ job.endTime = std::chrono::system_clock::now();
|
|
|
|
|
+ LOG_DEBUG("Marked stale job as failed: " + job.id);
|
|
|
// Persist updated status to disk
|
|
// Persist updated status to disk
|
|
|
saveJobToFile(job);
|
|
saveJobToFile(job);
|
|
|
}
|
|
}
|
|
@@ -765,7 +1085,7 @@ public:
|
|
|
loadedCount++;
|
|
loadedCount++;
|
|
|
|
|
|
|
|
} catch (const std::exception& e) {
|
|
} catch (const std::exception& e) {
|
|
|
- std::cerr << "Error loading job from " << entry.path() << ": " << e.what() << std::endl;
|
|
|
|
|
|
|
+ LOG_ERROR("Error loading job from " + entry.path().string() + ": " + std::string(e.what()));
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -783,7 +1103,7 @@ public:
|
|
|
result.success = false;
|
|
result.success = false;
|
|
|
result.modelsHashed = 0;
|
|
result.modelsHashed = 0;
|
|
|
|
|
|
|
|
- auto startTime = std::chrono::steady_clock::now();
|
|
|
|
|
|
|
+ auto startTime = std::chrono::system_clock::now();
|
|
|
|
|
|
|
|
if (!modelManager) {
|
|
if (!modelManager) {
|
|
|
result.errorMessage = "Model manager not available";
|
|
result.errorMessage = "Model manager not available";
|
|
@@ -805,7 +1125,7 @@ public:
|
|
|
modelsToHash = request.modelNames;
|
|
modelsToHash = request.modelNames;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- std::cout << "Hashing " << modelsToHash.size() << " model(s)..." << std::endl;
|
|
|
|
|
|
|
+ LOG_DEBUG("Hashing " + std::to_string(modelsToHash.size()) + " model(s)...");
|
|
|
|
|
|
|
|
// Hash each model
|
|
// Hash each model
|
|
|
for (const auto& modelName : modelsToHash) {
|
|
for (const auto& modelName : modelsToHash) {
|
|
@@ -814,11 +1134,11 @@ public:
|
|
|
result.modelHashes[modelName] = hash;
|
|
result.modelHashes[modelName] = hash;
|
|
|
result.modelsHashed++;
|
|
result.modelsHashed++;
|
|
|
} else {
|
|
} else {
|
|
|
- std::cerr << "Failed to hash model: " << modelName << std::endl;
|
|
|
|
|
|
|
+ LOG_ERROR("Failed to hash model: " + modelName);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- auto endTime = std::chrono::steady_clock::now();
|
|
|
|
|
|
|
+ auto endTime = std::chrono::system_clock::now();
|
|
|
result.hashingTime = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
result.hashingTime = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
|
endTime - startTime).count();
|
|
endTime - startTime).count();
|
|
|
|
|
|
|
@@ -837,7 +1157,7 @@ public:
|
|
|
result.requestId = request.id;
|
|
result.requestId = request.id;
|
|
|
result.success = false;
|
|
result.success = false;
|
|
|
|
|
|
|
|
- auto startTime = std::chrono::steady_clock::now();
|
|
|
|
|
|
|
+ auto startTime = std::chrono::system_clock::now();
|
|
|
|
|
|
|
|
// Conversion start output removed from stdout
|
|
// Conversion start output removed from stdout
|
|
|
|
|
|
|
@@ -888,7 +1208,7 @@ public:
|
|
|
|
|
|
|
|
int exitCode = pclose(pipe);
|
|
int exitCode = pclose(pipe);
|
|
|
|
|
|
|
|
- auto endTime = std::chrono::steady_clock::now();
|
|
|
|
|
|
|
+ auto endTime = std::chrono::system_clock::now();
|
|
|
result.conversionTime = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
result.conversionTime = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
|
endTime - startTime).count();
|
|
endTime - startTime).count();
|
|
|
|
|
|
|
@@ -920,14 +1240,14 @@ public:
|
|
|
result.status = GenerationStatus::COMPLETED;
|
|
result.status = GenerationStatus::COMPLETED;
|
|
|
result.outputPath = request.outputPath;
|
|
result.outputPath = request.outputPath;
|
|
|
|
|
|
|
|
- std::cout << "Conversion completed successfully!" << std::endl;
|
|
|
|
|
- std::cout << " Original size: " << result.originalSize << std::endl;
|
|
|
|
|
- std::cout << " Converted size: " << result.convertedSize << std::endl;
|
|
|
|
|
- std::cout << " Time: " << result.conversionTime << "ms" << std::endl;
|
|
|
|
|
|
|
+ LOG_DEBUG("Conversion completed successfully!");
|
|
|
|
|
+ LOG_DEBUG(" Original size: " + result.originalSize);
|
|
|
|
|
+ LOG_DEBUG(" Converted size: " + result.convertedSize);
|
|
|
|
|
+ LOG_DEBUG(" Time: " + std::to_string(result.conversionTime) + "ms");
|
|
|
|
|
|
|
|
// Trigger model rescan after successful conversion
|
|
// Trigger model rescan after successful conversion
|
|
|
if (modelManager) {
|
|
if (modelManager) {
|
|
|
- std::cout << "Triggering model rescan..." << std::endl;
|
|
|
|
|
|
|
+ LOG_DEBUG("Triggering model rescan...");
|
|
|
modelManager->scanModelsDirectory();
|
|
modelManager->scanModelsDirectory();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -948,6 +1268,75 @@ public:
|
|
|
ss << std::fixed << std::setprecision(2) << size << " " << units[unitIndex];
|
|
ss << std::fixed << std::setprecision(2) << size << " " << units[unitIndex];
|
|
|
return ss.str();
|
|
return ss.str();
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // Helper function to populate JobInfo from GenerationRequest
|
|
|
|
|
+ void populateJobInfoFromRequest(JobInfo& jobInfo, const GenerationRequest& request) {
|
|
|
|
|
+ // Model information
|
|
|
|
|
+ jobInfo.modelName = request.modelName;
|
|
|
|
|
+ jobInfo.modelHash = modelManager ? modelManager->getModelInfo(request.modelName).sha256 : "";
|
|
|
|
|
+ jobInfo.modelPath = modelManager ? modelManager->getModelInfo(request.modelName).path : "";
|
|
|
|
|
+
|
|
|
|
|
+ // Generation parameters
|
|
|
|
|
+ jobInfo.negativePrompt = request.negativePrompt;
|
|
|
|
|
+ jobInfo.width = request.width;
|
|
|
|
|
+ jobInfo.height = request.height;
|
|
|
|
|
+ jobInfo.batchCount = request.batchCount;
|
|
|
|
|
+ jobInfo.steps = request.steps;
|
|
|
|
|
+ jobInfo.cfgScale = request.cfgScale;
|
|
|
|
|
+ jobInfo.samplingMethod = request.samplingMethod;
|
|
|
|
|
+ jobInfo.scheduler = request.scheduler;
|
|
|
|
|
+ jobInfo.seed = request.seed;
|
|
|
|
|
+ jobInfo.strength = request.strength;
|
|
|
|
|
+ jobInfo.controlStrength = request.controlStrength;
|
|
|
|
|
+ jobInfo.clipSkip = request.clipSkip;
|
|
|
|
|
+ jobInfo.nThreads = request.nThreads;
|
|
|
|
|
+ jobInfo.offloadParamsToCpu = request.offloadParamsToCpu;
|
|
|
|
|
+ jobInfo.clipOnCpu = request.clipOnCpu;
|
|
|
|
|
+ jobInfo.vaeOnCpu = request.vaeOnCpu;
|
|
|
|
|
+ jobInfo.diffusionFlashAttn = request.diffusionFlashAttn;
|
|
|
|
|
+ jobInfo.diffusionConvDirect = request.diffusionConvDirect;
|
|
|
|
|
+ jobInfo.vaeConvDirect = request.vaeConvDirect;
|
|
|
|
|
+
|
|
|
|
|
+ // Request type - store image paths instead of image data
|
|
|
|
|
+ switch (request.requestType) {
|
|
|
|
|
+ case GenerationRequest::RequestType::TEXT2IMG:
|
|
|
|
|
+ jobInfo.requestType = "text2img";
|
|
|
|
|
+ break;
|
|
|
|
|
+ case GenerationRequest::RequestType::IMG2IMG:
|
|
|
|
|
+ jobInfo.requestType = "img2img";
|
|
|
|
|
+ // Store path to init image instead of the image data
|
|
|
|
|
+ jobInfo.initImageData = request.initImagePath; // Store path, not base64
|
|
|
|
|
+ break;
|
|
|
|
|
+ case GenerationRequest::RequestType::CONTROLNET:
|
|
|
|
|
+ jobInfo.requestType = "controlnet";
|
|
|
|
|
+ // Store path to control image instead of the image data
|
|
|
|
|
+ jobInfo.controlImageData = request.controlImagePath; // Store path, not base64
|
|
|
|
|
+ break;
|
|
|
|
|
+ case GenerationRequest::RequestType::UPSCALER:
|
|
|
|
|
+ jobInfo.requestType = "upscaler";
|
|
|
|
|
+ // Store path to input image instead of the image data
|
|
|
|
|
+ jobInfo.initImageData = request.initImagePath; // Store path, not base64
|
|
|
|
|
+ break;
|
|
|
|
|
+ case GenerationRequest::RequestType::INPAINTING:
|
|
|
|
|
+ jobInfo.requestType = "inpainting";
|
|
|
|
|
+ // Store paths to images instead of the image data
|
|
|
|
|
+ jobInfo.initImageData = request.initImagePath; // Store path, not base64
|
|
|
|
|
+ jobInfo.maskImageData = request.maskImagePath; // Store path, not base64
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Model paths
|
|
|
|
|
+ jobInfo.clipLPath = request.clipLPath;
|
|
|
|
|
+ jobInfo.clipGPath = request.clipGPath;
|
|
|
|
|
+ jobInfo.vaePath = request.vaePath;
|
|
|
|
|
+ jobInfo.taesdPath = request.taesdPath;
|
|
|
|
|
+ jobInfo.controlNetPath = request.controlNetPath;
|
|
|
|
|
+ jobInfo.embeddingDir = request.embeddingDir;
|
|
|
|
|
+ jobInfo.loraModelDir = request.loraModelDir;
|
|
|
|
|
+ jobInfo.esrganPath = request.esrganPath;
|
|
|
|
|
+ jobInfo.upscaleFactor = request.upscaleFactor;
|
|
|
|
|
+ }
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
GenerationQueue::GenerationQueue(ModelManager* modelManager, int maxConcurrentGenerations,
|
|
GenerationQueue::GenerationQueue(ModelManager* modelManager, int maxConcurrentGenerations,
|
|
@@ -972,12 +1361,12 @@ GenerationQueue::~GenerationQueue() {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
std::future<GenerationResult> GenerationQueue::enqueueRequest(const GenerationRequest& request) {
|
|
std::future<GenerationResult> GenerationQueue::enqueueRequest(const GenerationRequest& request) {
|
|
|
- std::cout << "Enqueuing generation request: " << request.id << std::endl;
|
|
|
|
|
- std::cout << " Prompt: " << request.prompt.substr(0, 100)
|
|
|
|
|
- << (request.prompt.length() > 100 ? "..." : "") << std::endl;
|
|
|
|
|
- std::cout << " Model: " << request.modelName << std::endl;
|
|
|
|
|
- std::cout << " Size: " << request.width << "x" << request.height << std::endl;
|
|
|
|
|
- std::cout << " Steps: " << request.steps << ", CFG: " << request.cfgScale << std::endl;
|
|
|
|
|
|
|
+ LOG_DEBUG("Enqueuing generation request: " + request.id);
|
|
|
|
|
+ LOG_DEBUG(" Prompt: " + request.prompt.substr(0, 100) +
|
|
|
|
|
+ (request.prompt.length() > 100 ? "..." : ""));
|
|
|
|
|
+ LOG_DEBUG(" Model: " + request.modelName);
|
|
|
|
|
+ LOG_DEBUG(" Size: " + std::to_string(request.width) + "x" + std::to_string(request.height));
|
|
|
|
|
+ LOG_DEBUG(" Steps: " + std::to_string(request.steps) + ", CFG: " + std::to_string(request.cfgScale));
|
|
|
|
|
|
|
|
// Create promise and future
|
|
// Create promise and future
|
|
|
auto promise = std::make_shared<std::promise<GenerationResult>>();
|
|
auto promise = std::make_shared<std::promise<GenerationResult>>();
|
|
@@ -993,15 +1382,18 @@ std::future<GenerationResult> GenerationQueue::enqueueRequest(const GenerationRe
|
|
|
{
|
|
{
|
|
|
std::lock_guard<std::mutex> lock(pImpl->queueMutex);
|
|
std::lock_guard<std::mutex> lock(pImpl->queueMutex);
|
|
|
|
|
|
|
|
- // Create job info
|
|
|
|
|
|
|
+ // Create job info with enhanced data
|
|
|
JobInfo jobInfo;
|
|
JobInfo jobInfo;
|
|
|
jobInfo.id = request.id;
|
|
jobInfo.id = request.id;
|
|
|
jobInfo.type = JobType::GENERATION;
|
|
jobInfo.type = JobType::GENERATION;
|
|
|
jobInfo.status = GenerationStatus::QUEUED;
|
|
jobInfo.status = GenerationStatus::QUEUED;
|
|
|
jobInfo.prompt = request.prompt; // Store full prompt
|
|
jobInfo.prompt = request.prompt; // Store full prompt
|
|
|
- jobInfo.queuedTime = std::chrono::steady_clock::now();
|
|
|
|
|
|
|
+ jobInfo.queuedTime = std::chrono::system_clock::now();
|
|
|
jobInfo.position = pImpl->requestQueue.size() + 1;
|
|
jobInfo.position = pImpl->requestQueue.size() + 1;
|
|
|
|
|
|
|
|
|
|
+ // Populate all enhanced fields from the request
|
|
|
|
|
+ pImpl->populateJobInfoFromRequest(jobInfo, request);
|
|
|
|
|
+
|
|
|
// Store job info
|
|
// Store job info
|
|
|
{
|
|
{
|
|
|
std::lock_guard<std::mutex> jobsLock(pImpl->jobsMutex);
|
|
std::lock_guard<std::mutex> jobsLock(pImpl->jobsMutex);
|
|
@@ -1040,7 +1432,7 @@ std::future<HashResult> GenerationQueue::enqueueHashRequest(const HashRequest& r
|
|
|
pImpl->requestQueue.push(hashJobPlaceholder);
|
|
pImpl->requestQueue.push(hashJobPlaceholder);
|
|
|
pImpl->queueCondition.notify_one();
|
|
pImpl->queueCondition.notify_one();
|
|
|
|
|
|
|
|
- std::cout << "Enqueued hash request: " << request.id << std::endl;
|
|
|
|
|
|
|
+ LOG_DEBUG("Enqueued hash request: " + request.id);
|
|
|
|
|
|
|
|
return future;
|
|
return future;
|
|
|
}
|
|
}
|
|
@@ -1064,7 +1456,7 @@ std::future<ConversionResult> GenerationQueue::enqueueConversionRequest(const Co
|
|
|
pImpl->requestQueue.push(conversionJobPlaceholder);
|
|
pImpl->requestQueue.push(conversionJobPlaceholder);
|
|
|
pImpl->queueCondition.notify_one();
|
|
pImpl->queueCondition.notify_one();
|
|
|
|
|
|
|
|
- std::cout << "Enqueued conversion request: " << request.id << " (model: " << request.modelName << ", type: " << request.quantizationType << ")" << std::endl;
|
|
|
|
|
|
|
+ LOG_DEBUG("Enqueued conversion request: " + request.id + " (model: " + request.modelName + ", type: " + request.quantizationType + ")");
|
|
|
|
|
|
|
|
return future;
|
|
return future;
|
|
|
}
|
|
}
|
|
@@ -1128,7 +1520,7 @@ bool GenerationQueue::cancelJob(const std::string& jobId) {
|
|
|
auto it = pImpl->activeJobs.find(jobId);
|
|
auto it = pImpl->activeJobs.find(jobId);
|
|
|
if (it != pImpl->activeJobs.end()) {
|
|
if (it != pImpl->activeJobs.end()) {
|
|
|
it->second.status = GenerationStatus::FAILED;
|
|
it->second.status = GenerationStatus::FAILED;
|
|
|
- it->second.endTime = std::chrono::steady_clock::now();
|
|
|
|
|
|
|
+ it->second.endTime = std::chrono::system_clock::now();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Set promise with cancellation error
|
|
// Set promise with cancellation error
|
|
@@ -1154,7 +1546,7 @@ bool GenerationQueue::cancelJob(const std::string& jobId) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void GenerationQueue::clearQueue() {
|
|
void GenerationQueue::clearQueue() {
|
|
|
- std::cout << "Clearing generation queue" << std::endl;
|
|
|
|
|
|
|
+ LOG_DEBUG("Clearing generation queue");
|
|
|
|
|
|
|
|
std::lock_guard<std::mutex> queueLock(pImpl->queueMutex);
|
|
std::lock_guard<std::mutex> queueLock(pImpl->queueMutex);
|
|
|
std::lock_guard<std::mutex> jobsLock(pImpl->jobsMutex);
|
|
std::lock_guard<std::mutex> jobsLock(pImpl->jobsMutex);
|
|
@@ -1168,7 +1560,7 @@ void GenerationQueue::clearQueue() {
|
|
|
auto it = pImpl->activeJobs.find(request.id);
|
|
auto it = pImpl->activeJobs.find(request.id);
|
|
|
if (it != pImpl->activeJobs.end()) {
|
|
if (it != pImpl->activeJobs.end()) {
|
|
|
it->second.status = GenerationStatus::FAILED;
|
|
it->second.status = GenerationStatus::FAILED;
|
|
|
- it->second.endTime = std::chrono::steady_clock::now();
|
|
|
|
|
|
|
+ it->second.endTime = std::chrono::system_clock::now();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Set promise with cancellation error
|
|
// Set promise with cancellation error
|
|
@@ -1189,7 +1581,7 @@ void GenerationQueue::clearQueue() {
|
|
|
|
|
|
|
|
void GenerationQueue::start() {
|
|
void GenerationQueue::start() {
|
|
|
if (pImpl->running.load()) {
|
|
if (pImpl->running.load()) {
|
|
|
- std::cout << "GenerationQueue is already running" << std::endl;
|
|
|
|
|
|
|
+ LOG_DEBUG("GenerationQueue is already running");
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1197,7 +1589,7 @@ void GenerationQueue::start() {
|
|
|
pImpl->stopRequested.store(false);
|
|
pImpl->stopRequested.store(false);
|
|
|
pImpl->workerThread = std::thread(&Impl::workerThreadFunction, pImpl.get());
|
|
pImpl->workerThread = std::thread(&Impl::workerThreadFunction, pImpl.get());
|
|
|
|
|
|
|
|
- std::cout << "GenerationQueue started" << std::endl;
|
|
|
|
|
|
|
+ LOG_DEBUG("GenerationQueue started");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void GenerationQueue::stop() {
|
|
void GenerationQueue::stop() {
|
|
@@ -1205,7 +1597,7 @@ void GenerationQueue::stop() {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- std::cout << "Stopping GenerationQueue..." << std::endl;
|
|
|
|
|
|
|
+ LOG_DEBUG("Stopping GenerationQueue...");
|
|
|
|
|
|
|
|
pImpl->stopRequested.store(true);
|
|
pImpl->stopRequested.store(true);
|
|
|
pImpl->queueCondition.notify_all();
|
|
pImpl->queueCondition.notify_all();
|
|
@@ -1228,7 +1620,7 @@ void GenerationQueue::stop() {
|
|
|
}
|
|
}
|
|
|
pImpl->jobPromises.clear();
|
|
pImpl->jobPromises.clear();
|
|
|
|
|
|
|
|
- std::cout << "GenerationQueue stopped" << std::endl;
|
|
|
|
|
|
|
+ LOG_DEBUG("GenerationQueue stopped");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
bool GenerationQueue::isRunning() const {
|
|
bool GenerationQueue::isRunning() const {
|
|
@@ -1237,5 +1629,5 @@ bool GenerationQueue::isRunning() const {
|
|
|
|
|
|
|
|
void GenerationQueue::setMaxConcurrentGenerations(int maxConcurrent) {
|
|
void GenerationQueue::setMaxConcurrentGenerations(int maxConcurrent) {
|
|
|
pImpl->maxConcurrentGenerations = maxConcurrent;
|
|
pImpl->maxConcurrentGenerations = maxConcurrent;
|
|
|
- std::cout << "GenerationQueue max concurrent generations set to: " << maxConcurrent << std::endl;
|
|
|
|
|
|
|
+ LOG_DEBUG("GenerationQueue max concurrent generations set to: " + std::to_string(maxConcurrent));
|
|
|
}
|
|
}
|