UNPKG

@bramato/openrouter-mock-generator

Version:

AI-powered mock data generator using OpenRouter API with JSON mode support

467 lines β€’ 19.6 kB
"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