/** * Prompt Loader * * Utilities to load AI prompts from the ai_prompts PostgreSQL table. * Supports loading prompts by base type (e.g., 'name_validation' loads * name_validation_system, name_validation_general, and optionally * name_validation_company_specific). */ /** * Load a single prompt by exact type * @param {Object} pool - PostgreSQL pool * @param {string} promptType - Exact prompt type (e.g., 'name_validation_system') * @param {string} [company] - Company identifier (for company_specific types) * @returns {Promise} Prompt text or null if not found */ async function loadPromptByType(pool, promptType, company = null) { try { let result; if (company) { result = await pool.query( 'SELECT prompt_text FROM ai_prompts WHERE prompt_type = $1 AND company = $2', [promptType, company] ); } else { result = await pool.query( 'SELECT prompt_text FROM ai_prompts WHERE prompt_type = $1 AND company IS NULL', [promptType] ); } return result.rows[0]?.prompt_text || null; } catch (error) { console.error(`[PromptLoader] Error loading ${promptType} prompt:`, error.message); return null; } } /** * Load all prompts for a task type (system, general, and optionally company-specific) * * @param {Object} pool - PostgreSQL pool * @param {string} baseType - Base type name (e.g., 'name_validation', 'description_validation') * @param {string|null} [company] - Optional company ID for company-specific prompts * @returns {Promise<{system: string|null, general: string|null, companySpecific: string|null}>} */ async function loadPromptsByType(pool, baseType, company = null) { const systemType = `${baseType}_system`; const generalType = `${baseType}_general`; const companyType = `${baseType}_company_specific`; // Load system and general prompts in parallel const [system, general] = await Promise.all([ loadPromptByType(pool, systemType), loadPromptByType(pool, generalType) ]); // Load company-specific prompt if company is provided let companySpecific = null; if (company) { companySpecific = await loadPromptByType(pool, companyType, company); } return { system, general, companySpecific }; } /** * Load name validation prompts * @param {Object} pool - PostgreSQL pool * @param {string|null} [company] - Optional company ID * @returns {Promise<{system: string|null, general: string|null, companySpecific: string|null}>} */ async function loadNameValidationPrompts(pool, company = null) { return loadPromptsByType(pool, 'name_validation', company); } /** * Load description validation prompts * @param {Object} pool - PostgreSQL pool * @param {string|null} [company] - Optional company ID * @returns {Promise<{system: string|null, general: string|null, companySpecific: string|null}>} */ async function loadDescriptionValidationPrompts(pool, company = null) { return loadPromptsByType(pool, 'description_validation', company); } /** * Load sanity check prompts (no company-specific variant) * @param {Object} pool - PostgreSQL pool * @returns {Promise<{system: string|null, general: string|null, companySpecific: null}>} */ async function loadSanityCheckPrompts(pool) { return loadPromptsByType(pool, 'sanity_check', null); } /** * Load bulk validation prompts (GPT-5 validation) * @param {Object} pool - PostgreSQL pool * @param {string|null} [company] - Optional company ID * @returns {Promise<{system: string|null, general: string|null, companySpecific: string|null}>} */ async function loadBulkValidationPrompts(pool, company = null) { return loadPromptsByType(pool, 'bulk_validation', company); } /** * Load bulk validation prompts for multiple companies at once * @param {Object} pool - PostgreSQL pool * @param {string[]} companyIds - Array of company IDs * @returns {Promise<{system: string|null, general: string|null, companyPrompts: Map}>} */ async function loadBulkValidationPromptsForCompanies(pool, companyIds = []) { // Load system and general prompts const [system, general] = await Promise.all([ loadPromptByType(pool, 'bulk_validation_system'), loadPromptByType(pool, 'bulk_validation_general') ]); // Load company-specific prompts for all provided companies const companyPrompts = new Map(); if (companyIds.length > 0) { try { const result = await pool.query( `SELECT company, prompt_text FROM ai_prompts WHERE prompt_type = 'bulk_validation_company_specific' AND company = ANY($1)`, [companyIds] ); for (const row of result.rows) { companyPrompts.set(row.company, row.prompt_text); } } catch (error) { console.error('[PromptLoader] Error loading company-specific prompts:', error.message); } } return { system, general, companyPrompts }; } /** * Validate that required prompts exist, throw error if missing * @param {Object} prompts - Prompts object from loadPromptsByType * @param {string} baseType - Base type for error messages * @param {Object} options - Validation options * @param {boolean} [options.requireSystem=true] - Require system prompt * @param {boolean} [options.requireGeneral=true] - Require general prompt * @throws {Error} If required prompts are missing */ function validateRequiredPrompts(prompts, baseType, options = {}) { const { requireSystem = true, requireGeneral = true } = options; const missing = []; if (requireSystem && !prompts.system) { missing.push(`${baseType}_system`); } if (requireGeneral && !prompts.general) { missing.push(`${baseType}_general`); } if (missing.length > 0) { throw new Error( `Missing required AI prompts: ${missing.join(', ')}. ` + `Please add these prompts in Settings > AI Validation Prompts.` ); } } module.exports = { // Core loader loadPromptByType, loadPromptsByType, // Task-specific loaders loadNameValidationPrompts, loadDescriptionValidationPrompts, loadSanityCheckPrompts, loadBulkValidationPrompts, loadBulkValidationPromptsForCompanies, // Validation validateRequiredPrompts };