@bramato/openrouter-mock-generator
Version:
AI-powered mock data generator using OpenRouter API with JSON mode support
467 lines β’ 19.6 kB
JavaScript
"use strict";
/**
* Servizio coordinatore per il post-processing delle immagini nei dati mock
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.PostProcessingOrchestrator = void 0;
const image_url_extractor_1 = require("./image-url-extractor");
const description_generator_1 = require("./description-generator");
const image_processing_analyzer_1 = require("./image-processing-analyzer");
const url_replacer_1 = require("./url-replacer");
const image_mock_generator_1 = require("./image-mock-generator");
class PostProcessingOrchestrator {
constructor(huggingfaceApiKey, options = {}) {
this.stats = [];
this.pathToUrlMap = new Map();
this.imageGenerator = new image_mock_generator_1.ImageMockGenerator(huggingfaceApiKey);
this.options = this.normalizeOptions(options);
}
/**
* Esegue il post-processing completo dei dati mock
*/
async processData(data) {
const startTime = Date.now();
const result = {
success: false,
originalImageCount: 0,
processedImageCount: 0,
generatedImageCount: 0,
optimizationSavings: 0,
processingTimeMs: 0,
extraction: { images: [], totalFound: 0, uniqueUrls: 0, duplicateGroups: new Map() },
optimization: {
groups: [],
totalImages: 0,
generatedImages: 0,
resizedImages: 0,
croppedImages: 0,
reusedImages: 0,
estimatedSavings: 0,
},
replacement: {
success: false,
replacedCount: 0,
failedCount: 0,
mappings: [],
modifiedData: data,
errors: [],
},
processedData: data,
errors: [],
warnings: [],
};
try {
if (!this.options.enableImageReplacement) {
this.log('π Image replacement disabled, skipping post-processing');
result.success = true;
result.processedData = data;
return result;
}
this.log('π Starting post-processing pipeline...\n');
// Fase 1: Estrazione URL immagini
result.extraction = await this.extractImageUrls(data);
if (result.extraction.totalFound === 0) {
this.log('βΉοΈ No Picsum images found, skipping processing');
result.success = true;
result.processedData = data;
return result;
}
// Fase 2: Generazione descrizioni
const descriptions = await this.generateDescriptions(result.extraction);
// Fase 3: Analisi e ottimizzazione
result.optimization = await this.optimizeProcessing(result.extraction, descriptions);
// Fase 4: Generazione immagini
const generatedImages = await this.generateImages(result.optimization);
// Fase 5: Sostituzione URL
result.replacement = await this.replaceUrls(data, generatedImages);
// Calcola statistiche finali
result.originalImageCount = result.extraction.totalFound;
result.processedImageCount = result.replacement.replacedCount;
result.generatedImageCount = generatedImages.filter(img => img.success).length;
result.optimizationSavings = result.optimization.estimatedSavings;
result.processedData = result.replacement.modifiedData;
result.success = result.replacement.success && result.errors.length === 0;
this.log(`\nβ
Post-processing completed successfully!`);
this.logFinalStats(result);
}
catch (error) {
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
result.errors.push(`Post-processing failed: ${errorMsg}`);
this.log(`β Post-processing failed: ${errorMsg}`);
result.success = false;
}
finally {
result.processingTimeMs = Date.now() - startTime;
}
return result;
}
/**
* Fase 1: Estrazione URL immagini Picsum
*/
async extractImageUrls(data) {
const phase = this.startPhase('Image URL Extraction');
try {
this.log('π Extracting Picsum URLs from data...');
const result = image_url_extractor_1.ImageUrlExtractor.extractPicsumUrls(data);
// Popola la mappa path -> URL originale
result.images.forEach(imageInfo => {
this.pathToUrlMap.set(imageInfo.path, imageInfo.url);
});
const stats = image_url_extractor_1.ImageUrlExtractor.getExtractionStats(result);
this.log(` ${stats.summary}`);
if (this.options.verbose) {
this.log(' π Dimensions distribution:');
stats.dimensionDistribution.forEach((count, dim) => {
this.log(` β’ ${dim}: ${count} images`);
});
}
this.endPhase(phase, result.totalFound, 0);
return result;
}
catch (error) {
this.endPhase(phase, 0, 1);
throw error;
}
}
/**
* Fase 2: Generazione descrizioni contestuali
*/
async generateDescriptions(extraction) {
const phase = this.startPhase('Description Generation');
try {
this.log('π Generating contextual descriptions...');
const descriptions = description_generator_1.DescriptionGenerator.generateDescriptions(extraction.images, this.options.descriptionOptions);
this.log(` Generated ${descriptions.size} contextual descriptions`);
if (this.options.verbose) {
const categories = new Map();
descriptions.forEach(desc => {
categories.set(desc.category, (categories.get(desc.category) || 0) + 1);
});
this.log(' π Categories distribution:');
categories.forEach((count, category) => {
this.log(` β’ ${category}: ${count} images`);
});
}
this.endPhase(phase, descriptions.size, 0);
return descriptions;
}
catch (error) {
this.endPhase(phase, 0, 1);
throw error;
}
}
/**
* Fase 3: Analisi e ottimizzazione processing
*/
async optimizeProcessing(extraction, descriptions) {
const phase = this.startPhase('Processing Optimization');
try {
this.log('βοΈ Analyzing and optimizing image processing...');
if (!this.options.enableOptimization) {
this.log(' Optimization disabled, creating basic plan');
// Crea piano base senza ottimizzazioni
return this.createBasicPlan(extraction.images, descriptions);
}
const result = image_processing_analyzer_1.ImageProcessingAnalyzer.createProcessingPlan(extraction.images, descriptions);
const stats = image_processing_analyzer_1.ImageProcessingAnalyzer.getOptimizationStats(result);
this.log(` ${stats.summary}`);
this.log(` Efficiency score: ${stats.efficiencyScore}%`);
if (this.options.verbose) {
this.log(' π Processing breakdown:');
Object.entries(stats.breakdown).forEach(([type, count]) => {
this.log(` β’ ${type}: ${count}`);
});
}
this.endPhase(phase, result.totalImages, 0);
return result;
}
catch (error) {
this.endPhase(phase, 0, 1);
throw error;
}
}
/**
* Fase 4: Generazione immagini AI
*/
async generateImages(optimization) {
const phase = this.startPhase('Image Generation');
try {
this.log('π¨ Generating AI images...');
const results = [];
// Ordina gruppi per prioritΓ
const sortedGroups = image_processing_analyzer_1.ImageProcessingAnalyzer.sortGroupsByPriority(optimization.groups);
const totalImages = optimization.totalImages;
let processedImages = 0;
// Processa gruppi con concorrenza limitata
const concurrencyLimit = this.options.maxConcurrentGenerations || 3;
const batches = this.createBatches(sortedGroups, concurrencyLimit);
for (let batchIndex = 0; batchIndex < batches.length; batchIndex++) {
const batch = batches[batchIndex];
this.log(` Processing batch ${batchIndex + 1}/${batches.length} (${batch.length} groups)`);
const batchPromises = batch.map(group => this.processGroup(group));
const batchResults = await Promise.allSettled(batchPromises);
batchResults.forEach((result, index) => {
if (result.status === 'fulfilled') {
results.push(...result.value);
processedImages += result.value.length;
}
else {
const group = batch[index];
this.log(` β Failed to process group ${group.masterImageId}: ${result.reason}`);
// Aggiungi risultati falliti
group.variants.forEach(variant => {
results.push({
originalUrl: '', // TODO: recupera URL originale
newUrl: '',
path: variant.imageId,
success: false,
error: result.reason?.toString(),
});
processedImages++;
});
}
});
// Callback di progress per le immagini
if (this.options.onImageProgress) {
this.options.onImageProgress(processedImages, totalImages, `Batch ${batchIndex + 1}/${batches.length} completato`);
}
// Pausa tra batch per evitare rate limiting
if (batchIndex < batches.length - 1) {
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
const successCount = results.filter(r => r.success).length;
this.log(` Generated ${successCount}/${results.length} images successfully`);
this.endPhase(phase, successCount, results.length - successCount);
return results;
}
catch (error) {
this.endPhase(phase, 0, 1);
throw error;
}
}
/**
* Fase 5: Sostituzione URL nei dati
*/
async replaceUrls(data, generatedImages) {
const phase = this.startPhase('URL Replacement');
try {
this.log('π Replacing URLs in data...');
const { urlMappings, cdnMappings } = url_replacer_1.UrlReplacer.createUrlMappings(generatedImages);
const result = await url_replacer_1.UrlReplacer.replaceUrls(data, urlMappings, cdnMappings, this.options.replacementOptions);
const stats = url_replacer_1.UrlReplacer.getReplacementStats(result);
this.log(` ${stats.summary}`);
this.endPhase(phase, result.replacedCount, result.failedCount);
return result;
}
catch (error) {
this.endPhase(phase, 0, 1);
throw error;
}
}
/**
* Processa un singolo gruppo di immagini
*/
async processGroup(group) {
const results = [];
// Prima genera l'immagine master
const masterPlan = group.variants.find(v => v.processingType === 'generate');
if (!masterPlan) {
throw new Error(`No master generation plan found for group ${group.masterImageId}`);
}
try {
// Forza upload se Γ¨ disponibile una configurazione cloud valida
const shouldUploadToCloud = this.options.uploadToCloud || this.hasCloudStorageConfigured();
const masterResult = await this.imageGenerator.generateImageMock({
prompt: masterPlan.description.enhancedPrompt,
model: 'black-forest-labs/FLUX.1-dev',
size: `${masterPlan.targetDimensions.width}x${masterPlan.targetDimensions.height}`,
uploadToCloud: shouldUploadToCloud,
});
if (!masterResult.imageUrl) {
throw new Error(`Failed to generate master image: No image URL returned`);
}
// Processa tutte le varianti
for (const variant of group.variants) {
if (variant.processingType === 'generate') {
// Γ l'immagine master giΓ generata
const originalUrl = this.getOriginalUrlFromPath(variant.imageId);
results.push({
originalUrl: originalUrl,
newUrl: masterResult.imageUrl,
cdnUrl: masterResult.cdnUrl,
path: variant.imageId,
success: true,
});
}
else {
// Implementa ridimensionamento/ritaglio in futuro
// Per ora riusa l'immagine master
const originalUrl = this.getOriginalUrlFromPath(variant.imageId);
results.push({
originalUrl: originalUrl,
newUrl: masterResult.imageUrl,
cdnUrl: masterResult.cdnUrl,
path: variant.imageId,
success: true,
});
}
}
}
catch (error) {
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
// Aggiungi risultati falliti per tutte le varianti - MANTIENI URL ORIGINALE
group.variants.forEach(variant => {
// Recupera l'URL originale dal mapping interno
const originalUrl = this.getOriginalUrlFromPath(variant.imageId);
results.push({
originalUrl: originalUrl, // URL Picsum originale
newUrl: originalUrl, // Mantieni l'URL originale quando la generazione fallisce
path: variant.imageId,
success: false,
error: errorMsg,
});
});
}
return results;
}
/**
* Recupera l'URL originale Picsum dal path interno
*/
getOriginalUrlFromPath(path) {
return this.pathToUrlMap.get(path) || path;
}
/**
* Verifica se Γ¨ disponibile una configurazione cloud storage
*/
hasCloudStorageConfigured() {
// Verifica configurazione DigitalOcean
const hasDOConfig = !!(process.env.DO_SPACES_ACCESS_KEY &&
process.env.DO_SPACES_SECRET_KEY &&
process.env.DO_SPACES_REGION &&
process.env.DO_SPACES_NAME);
// Verifica configurazione AWS S3
const hasS3Config = !!(process.env.AWS_ACCESS_KEY_ID &&
process.env.AWS_SECRET_ACCESS_KEY &&
process.env.AWS_REGION &&
process.env.AWS_S3_BUCKET_NAME);
return hasDOConfig || hasS3Config;
}
/**
* Crea piano base senza ottimizzazioni
*/
createBasicPlan(images, descriptions) {
const groups = images.map(img => {
const description = descriptions.get(img.path);
return {
masterImageId: img.path,
description,
targetDimensions: img.dimensions,
variants: [
{
imageId: img.path,
processingType: 'generate',
targetDimensions: img.dimensions,
description,
priority: 1,
estimatedCost: 1,
},
],
totalCost: 1,
};
});
return {
groups,
totalImages: images.length,
generatedImages: images.length,
resizedImages: 0,
croppedImages: 0,
reusedImages: 0,
estimatedSavings: 0,
};
}
/**
* Crea batch per elaborazione concorrente
*/
createBatches(items, batchSize) {
const batches = [];
for (let i = 0; i < items.length; i += batchSize) {
batches.push(items.slice(i, i + batchSize));
}
return batches;
}
/**
* Gestione fasi di processing
*/
startPhase(phase) {
const stats = {
phase,
startTime: Date.now(),
itemsProcessed: 0,
errors: 0,
};
this.stats.push(stats);
return stats;
}
endPhase(stats, itemsProcessed, errors) {
stats.endTime = Date.now();
stats.itemsProcessed = itemsProcessed;
stats.errors = errors;
}
/**
* Logging
*/
log(message) {
if (this.options.verbose) {
console.log(message);
}
}
logFinalStats(result) {
this.log(`\nπ Final Statistics:`);
this.log(` β’ Original images: ${result.originalImageCount}`);
this.log(` β’ Processed images: ${result.processedImageCount}`);
this.log(` β’ Generated images: ${result.generatedImageCount}`);
this.log(` β’ Optimization savings: ${result.optimizationSavings.toFixed(1)}%`);
this.log(` β’ Processing time: ${(result.processingTimeMs / 1000).toFixed(1)}s`);
if (result.errors.length > 0) {
this.log(` β’ Errors: ${result.errors.length}`);
}
if (result.warnings.length > 0) {
this.log(` β’ Warnings: ${result.warnings.length}`);
}
}
/**
* Normalizza opzioni con valori di default
*/
normalizeOptions(options) {
return {
enableImageReplacement: options.enableImageReplacement ?? true,
enableOptimization: options.enableOptimization ?? true,
maxConcurrentGenerations: options.maxConcurrentGenerations ?? 3,
descriptionOptions: options.descriptionOptions ?? {},
uploadToCloud: options.uploadToCloud ?? true,
storageProvider: options.storageProvider ?? 'digitalocean',
replacementOptions: options.replacementOptions ?? {},
verbose: options.verbose ?? true,
saveIntermediateResults: options.saveIntermediateResults ?? false,
onImageProgress: options.onImageProgress,
onUploadProgress: options.onUploadProgress,
customDescriptions: options.customDescriptions,
logFile: options.logFile,
};
}
/**
* Ottieni statistiche complete del processing
*/
getProcessingStats() {
return [...this.stats];
}
/**
* Reset statistiche
*/
resetStats() {
this.stats = [];
}
}
exports.PostProcessingOrchestrator = PostProcessingOrchestrator;
//# sourceMappingURL=post-processing-orchestrator.js.map