Add prompts table and settings page to create/read/update/delete from it, incorporate company specific prompts into ai validation

This commit is contained in:
2025-03-24 11:30:15 -04:00
parent 7eb4077224
commit dd4b3f7145
10 changed files with 1360 additions and 132 deletions

View File

@@ -289,7 +289,76 @@ async function generateDebugResponse(productsToUse, res) {
});
try {
const prompt = await loadPrompt(promptConnection, productsToUse);
// Get the local PostgreSQL pool to fetch prompts
const pool = res.app.locals.pool;
if (!pool) {
console.warn("⚠️ Local database pool not available for prompts");
throw new Error("Database connection not available");
}
// First, fetch the general prompt
const generalPromptResult = await pool.query(`
SELECT * FROM ai_prompts
WHERE prompt_type = 'general'
`);
if (generalPromptResult.rows.length === 0) {
console.warn("⚠️ No general prompt found in database");
throw new Error("No general prompt found in database");
}
// Get the general prompt text and info
const generalPrompt = generalPromptResult.rows[0];
console.log("📝 Loaded general prompt from database, ID:", generalPrompt.id);
// Fetch company-specific prompts if we have products to validate
let companyPrompts = [];
if (productsToUse && Array.isArray(productsToUse)) {
// Extract unique company IDs from products
const companyIds = new Set();
productsToUse.forEach(product => {
if (product.company) {
companyIds.add(String(product.company));
}
});
if (companyIds.size > 0) {
console.log(`🔍 Found ${companyIds.size} unique companies in products:`, Array.from(companyIds));
// Fetch company-specific prompts
const companyPromptsResult = await pool.query(`
SELECT * FROM ai_prompts
WHERE prompt_type = 'company_specific'
AND company = ANY($1)
`, [Array.from(companyIds)]);
companyPrompts = companyPromptsResult.rows;
console.log(`📝 Loaded ${companyPrompts.length} company-specific prompts`);
}
}
// Find company names from taxonomy
const companyPromptsWithNames = companyPrompts.map(prompt => {
let companyName = "Unknown Company";
if (taxonomy.companies && Array.isArray(taxonomy.companies)) {
const companyData = taxonomy.companies.find(company =>
String(company[0]) === String(prompt.company)
);
if (companyData && companyData[1]) {
companyName = companyData[1];
}
}
return {
id: prompt.id,
company: prompt.company,
companyName: companyName,
prompt_text: prompt.prompt_text
};
});
// Now use loadPrompt to get the actual combined prompt
const prompt = await loadPrompt(promptConnection, productsToUse, res.app.locals.pool);
const fullPrompt = prompt + "\n" + JSON.stringify(productsToUse);
// Create the response with taxonomy stats
@@ -330,9 +399,16 @@ async function generateDebugResponse(productsToUse, res) {
: null,
}
: null,
basePrompt: prompt,
basePrompt: generalPrompt.prompt_text,
sampleFullPrompt: fullPrompt,
promptLength: fullPrompt.length,
promptSources: {
generalPrompt: {
id: generalPrompt.id,
prompt_text: generalPrompt.prompt_text
},
companyPrompts: companyPromptsWithNames
}
};
console.log("Sending response with taxonomy stats:", response.taxonomyStats);
@@ -513,22 +589,107 @@ SELECT t.cat_id,t.name,null as master_cat_id,1 AS level_order FROM product_categ
}
}
// Load the prompt from file and inject taxonomy data
async function loadPrompt(connection, productsToValidate = null) {
// Load prompts from database and inject taxonomy data
async function loadPrompt(connection, productsToValidate = null, appPool = null) {
try {
const promptPath = path.join(
__dirname,
"..",
"prompts",
"product-validation.txt"
);
const basePrompt = await fs.readFile(promptPath, "utf8");
// Get taxonomy data using the provided MySQL connection
const taxonomy = await getTaxonomyData(connection);
// Add system instructions to the prompt
// Initialize default system instructions
const systemInstructions = `You are a specialized e-commerce product data processor for a crafting supplies website tasked with providing complete, correct, appealing, and SEO-friendly product listings. You should write professionally, but in a friendly and engaging tone. You have meticulous attention to detail and are a master at your craft.`;
// Use the provided pool parameter instead of global.app
const pool = appPool;
if (!pool) {
console.warn("⚠️ Local database pool not available for prompts");
throw new Error("Database connection not available");
}
// Fetch the general prompt
const generalPromptResult = await pool.query(`
SELECT * FROM ai_prompts
WHERE prompt_type = 'general'
`);
if (generalPromptResult.rows.length === 0) {
console.warn("⚠️ No general prompt found in database");
throw new Error("No general prompt found in database");
}
// Get the general prompt text
const basePrompt = generalPromptResult.rows[0].prompt_text;
console.log("📝 Loaded general prompt from database");
// Fetch company-specific prompts if we have products to validate
let companyPrompts = [];
if (productsToValidate && Array.isArray(productsToValidate)) {
// Extract unique company IDs from products
const companyIds = new Set();
productsToValidate.forEach(product => {
if (product.company) {
companyIds.add(String(product.company));
}
});
if (companyIds.size > 0) {
console.log(`🔍 Found ${companyIds.size} unique companies in products:`, Array.from(companyIds));
// Fetch company-specific prompts
const companyPromptsResult = await pool.query(`
SELECT * FROM ai_prompts
WHERE prompt_type = 'company_specific'
AND company = ANY($1)
`, [Array.from(companyIds)]);
companyPrompts = companyPromptsResult.rows;
console.log(`📝 Loaded ${companyPrompts.length} company-specific prompts`);
}
}
// Find company names from taxonomy for the validation endpoint
const companyPromptsWithNames = companyPrompts.map(prompt => {
let companyName = "Unknown Company";
if (taxonomy.companies && Array.isArray(taxonomy.companies)) {
const companyData = taxonomy.companies.find(company =>
String(company[0]) === String(prompt.company)
);
if (companyData && companyData[1]) {
companyName = companyData[1];
}
}
return {
id: prompt.id,
company: prompt.company,
companyName: companyName,
prompt_text: prompt.prompt_text
};
});
// Combine prompts - start with the general prompt
let combinedPrompt = basePrompt;
// Add any company-specific prompts with annotations
if (companyPrompts.length > 0) {
combinedPrompt += "\n\n--- COMPANY-SPECIFIC INSTRUCTIONS ---\n";
for (const prompt of companyPrompts) {
// Find company name from taxonomy
let companyName = "Unknown Company";
if (taxonomy.companies && Array.isArray(taxonomy.companies)) {
const companyData = taxonomy.companies.find(company =>
String(company[0]) === String(prompt.company)
);
if (companyData && companyData[1]) {
companyName = companyData[1];
}
}
combinedPrompt += `\n[SPECIFIC TO COMPANY: ${companyName} (ID: ${prompt.company})]:\n${prompt.prompt_text}\n`;
}
combinedPrompt += "\n--- END COMPANY-SPECIFIC INSTRUCTIONS ---\n";
}
// If we have products to validate, create a filtered prompt
if (productsToValidate) {
@@ -656,7 +817,7 @@ ${JSON.stringify(mixedTaxonomy.sizeCategories)}${
----------Here is the product data to validate----------`;
// Return the filtered prompt
return systemInstructions + basePrompt + "\n" + taxonomySection;
return systemInstructions + combinedPrompt + "\n" + taxonomySection;
}
// Generate the full unfiltered prompt
@@ -687,7 +848,7 @@ ${JSON.stringify(taxonomy.artists)}
Here is the product data to validate:`;
return systemInstructions + basePrompt + "\n" + taxonomySection;
return systemInstructions + combinedPrompt + "\n" + taxonomySection;
} catch (error) {
console.error("Error loading prompt:", error);
throw error; // Re-throw to be handled by the calling function
@@ -735,7 +896,7 @@ router.post("/validate", async (req, res) => {
// Load the prompt with the products data to filter taxonomy
console.log("🔄 Loading prompt with filtered taxonomy...");
const prompt = await loadPrompt(connection, products);
const prompt = await loadPrompt(connection, products, req.app.locals.pool);
const fullPrompt = prompt + "\n" + JSON.stringify(products);
promptLength = fullPrompt.length; // Store prompt length for performance metrics
console.log("📝 Generated prompt length:", promptLength);
@@ -884,7 +1045,72 @@ router.post("/validate", async (req, res) => {
console.error("⚠️ Failed to record performance metrics:", metricError);
}
// Include performance metrics in the response
// Get sources of the prompts for tracking
let promptSources = null;
try {
// Get general prompt
const generalPromptResult = await pool.query(`
SELECT * FROM ai_prompts WHERE prompt_type = 'general'
`);
// Extract unique company IDs from products
const companyIds = new Set();
products.forEach(product => {
if (product.company) {
companyIds.add(String(product.company));
}
});
let companyPrompts = [];
if (companyIds.size > 0) {
// Fetch company-specific prompts
const companyPromptsResult = await pool.query(`
SELECT * FROM ai_prompts
WHERE prompt_type = 'company_specific'
AND company = ANY($1)
`, [Array.from(companyIds)]);
companyPrompts = companyPromptsResult.rows;
}
// Find company names from taxonomy
const companyPromptsWithNames = companyPrompts.map(prompt => {
let companyName = "Unknown Company";
if (taxonomy.companies && Array.isArray(taxonomy.companies)) {
const companyData = taxonomy.companies.find(company =>
String(company[0]) === String(prompt.company)
);
if (companyData && companyData[1]) {
companyName = companyData[1];
}
}
return {
id: prompt.id,
company: prompt.company,
companyName: companyName,
prompt_text: prompt.prompt_text
};
});
// Set prompt sources
if (generalPromptResult.rows.length > 0) {
const generalPrompt = generalPromptResult.rows[0];
promptSources = {
generalPrompt: {
id: generalPrompt.id,
prompt_text: generalPrompt.prompt_text
},
companyPrompts: companyPromptsWithNames
};
}
} catch (promptSourceError) {
console.error("⚠️ Error getting prompt sources:", promptSourceError);
// Don't fail the entire validation if just prompt sources retrieval fails
}
// Include prompt sources in the response
res.json({
success: true,
changeDetails: changeDetails,
@@ -895,6 +1121,7 @@ router.post("/validate", async (req, res) => {
isEstimate: true,
productCount: products.length
},
promptSources: promptSources,
...aiResponse,
});
} catch (parseError) {