UNPKG

perchance-ai-prompt-library

Version:

🎨 Complete AI prompt library and generator with advanced CLI, batch processing, analytics, and multi-format export capabilities

257 lines (225 loc) • 8.71 kB
const stylesData = require('../data/styles.json'); const { randomChoice, replacePlaceholders, validateConfig } = require('../utils/helpers'); const PhotographyGenerator = require('./PhotographyGenerator'); class PromptGenerator { constructor(options = {}) { this.styles = stylesData; this.photographyGenerator = new PhotographyGenerator(); this.options = { includeQuality: true, includeNegativePrompt: true, randomizeVariables: false, enablePhotographyMode: false, ...options }; } /** * Get a random style from the available styles * @param {Array} [filter] - Optional array of style keys to filter by * @returns {Object} A random style object */ getRandomStyle(filter = []) { let availableStyles = this.styles; // Filter styles if filter array is provided if (filter.length > 0) { availableStyles = this.styles.filter(style => filter.includes(style.key)); if (availableStyles.length === 0) { availableStyles = this.styles; // Fallback to all styles if filter returns none } } return randomChoice(availableStyles); } /** * Generate a prompt based on the provided configuration * @param {Object} config - Configuration object * @param {string} [config.style] - Style key (optional if customPrompt is provided) * @param {string} [config.customPrompt] - Custom prompt text (optional) * @param {Object} [config.variables] - Variables to replace in the prompt * @param {boolean} [config.includeQuality] - Whether to include quality modifiers * @param {boolean} [config.includeNegativePrompt] - Whether to include negative prompts * @param {boolean} [config.randomizeVariables] - Whether to randomize variables * @param {string} [config.randomFromStyles] - Comma-separated list of style keys to randomize from * @returns {Object} Generated prompt and metadata */ generate(config = {}) { // If customPrompt is provided, use it directly if (config.customPrompt) { let promptText = config.customPrompt; // Apply photography enhancements if in photography mode if (this.options.enablePhotographyMode && config.photographyOptions) { promptText = this.enhanceWithPhotographyStandards(promptText, config.photographyOptions); } return { prompt: promptText, style: 'custom', variables: {}, negativePrompt: '' }; } // If randomFromStyles is provided, select a random style from the specified list if (config.randomFromStyles) { const styleKeys = config.randomFromStyles.split(',').map(s => s.trim()); const randomStyle = this.getRandomStyle(styleKeys); config.style = randomStyle.key; } const validation = validateConfig(config); if (!validation.valid) { throw new Error(`Invalid configuration: ${validation.errors.join(', ')}`); } // Find the style by key (supports both string keys and array indices) let style; if (Array.isArray(this.styles)) { style = this.styles.find(s => s.key === config.style); } else { style = this.styles[config.style]; } // If style not found, use a random style if (!style) { style = this.getRandomStyle(); } if (!style) { const availableStyles = Array.isArray(this.styles) ? this.styles.map(s => s.key).join(', ') : Object.keys(this.styles).join(', '); throw new Error(`Style "${config.style}" not found. Available: ${availableStyles}`); } // Ensure style has required properties if (!style.formula || !style.variables) { throw new Error(`Invalid style configuration for "${config.style}". Missing required properties.`); } // Initialize prompt with the style's formula let prompt = style.formula; // Prepare variables for placeholder replacement const variables = { ...config }; // Handle variables defined in the style if (Array.isArray(style.variables)) { // If variables is an array of variable names style.variables.forEach(varName => { if (!variables[varName] && style.examples && style.examples[0]) { // Use the first example as a default value if available variables[varName] = style.examples[0]; } }); } else if (typeof style.variables === 'object' && style.variables !== null) { // If variables is an object with variable names as keys and possible values as arrays Object.keys(style.variables).forEach(key => { if (!variables[key] && Array.isArray(style.variables[key]) && style.variables[key].length > 0) { variables[key] = this.options.randomizeVariables ? randomChoice(style.variables[key]) : style.variables[key][0]; } }); } // Ensure subject is set if (config.subject) { variables.subject = config.subject; } else if (!variables.subject) { // If no subject is provided, use a default based on the style variables.subject = style.examples && style.examples.length > 0 ? style.examples[0] : 'a character'; } // Replace placeholders in the prompt prompt = replacePlaceholders(prompt, variables); // Add quality modifiers if enabled if (this.options.includeQuality && style.quality_modifiers && Array.isArray(style.quality_modifiers)) { const qualityMods = style.quality_modifiers.slice(0, 3).join(', '); prompt += `, ${qualityMods}`; } const result = { text: prompt, style: style.key || config.style, // Use the style's key or the provided style name variables: variables, timestamp: new Date().toISOString(), metadata: { wordCount: prompt.split(' ').length, characterCount: prompt.length } }; if (this.options.includeNegativePrompt && style.negative_prompt) { result.negativePrompt = style.negative_prompt; } return result; } generateVariations(style, config, count = 3) { const variations = []; for (let i = 0; i < count; i++) { const originalRandomize = this.options.randomizeVariables; this.options.randomizeVariables = true; try { const variation = this.generate({ ...config, style: style }); variation.variationNumber = i + 1; variations.push(variation); } catch (error) { console.warn(`Failed to generate variation ${i + 1}:`, error.message); } this.options.randomizeVariables = originalRandomize; } return variations; } getStats() { const styleCount = Object.keys(this.styles).length; let totalVariables = 0; Object.values(this.styles).forEach(style => { totalVariables += Object.keys(style.variables).length; }); return { totalStyles: styleCount, totalVariables: totalVariables, availableStyles: Object.keys(this.styles) }; } /** * Enhance a prompt with professional photography standards * @param {string} basePrompt - The base prompt text * @param {Object} options - Photography enhancement options * @returns {string} Enhanced prompt with photography standards */ enhanceWithPhotographyStandards(basePrompt, options = {}) { const { style = 'standard', format = 'landscape', includeSettings = true, includeEquipment = false, termCount = 3, customTerms = [] } = options; // Generate photography-specific parts const photoPrompt = this.photographyGenerator.generatePrompt({ subject: basePrompt, style, format, includeSettings, includeEquipment, termCount, customTerms }); return photoPrompt; } /** * Enable or disable photography mode * @param {boolean} enabled - Whether to enable photography mode */ setPhotographyMode(enabled = true) { this.options.enablePhotographyMode = enabled; return this; } /** * Get a random camera setting * @param {string} type - Type of setting to get (aperture, shutter_speed, iso, focal_length) * @returns {string} Random setting value */ getRandomCameraSetting(type) { return this.photographyGenerator.getCameraSetting(type); } /** * Get a random resolution * @param {string} type - Resolution type (standard, social_media, print) * @param {string} format - Format (portrait, landscape, square, etc.) * @returns {string} Random resolution in WxH format */ getRandomResolution(type = 'standard', format = 'landscape') { return this.photographyGenerator.getResolution(type, format); } } module.exports = PromptGenerator;