Product editor tweaks

This commit is contained in:
2026-01-30 22:21:44 -05:00
parent 003e1ddd61
commit ac39257a51
195 changed files with 1233 additions and 66705 deletions

View File

@@ -1,172 +0,0 @@
/**
* Name Validation Task
*
* Validates a product name for spelling, grammar, and naming conventions.
* Uses Groq with the smaller model for fast response times.
* Loads all prompts from the database (no hardcoded prompts).
*/
const { MODELS } = require('../providers/groqProvider');
const {
loadNameValidationPrompts,
validateRequiredPrompts
} = require('../prompts/promptLoader');
const {
buildNameUserPrompt,
parseNameResponse
} = require('../prompts/namePrompts');
const TASK_ID = 'validate.name';
/**
* Create the name validation task
*
* @returns {Object} Task definition
*/
function createNameValidationTask() {
return {
id: TASK_ID,
description: 'Validate product name for spelling, grammar, and conventions',
/**
* Run the name validation
*
* @param {Object} payload
* @param {Object} payload.product - Product data
* @param {string} payload.product.name - Product name to validate
* @param {string} [payload.product.company_name] - Company name
* @param {string} [payload.product.company_id] - Company ID for loading specific rules
* @param {string} [payload.product.line_name] - Product line
* @param {string} [payload.product.description] - Description for context
* @param {Object} payload.provider - Groq provider instance
* @param {Object} payload.pool - PostgreSQL pool
* @param {Object} [payload.logger] - Logger instance
* @returns {Promise<Object>}
*/
async run(payload) {
const { product, provider, pool, logger } = payload;
const log = logger || console;
// Validate required input
if (!product?.name) {
return {
isValid: true,
suggestion: null,
issues: [],
skipped: true,
reason: 'No name provided'
};
}
if (!provider) {
throw new Error('Groq provider not available');
}
if (!pool) {
throw new Error('Database pool not available');
}
try {
// Load prompts from database
const companyKey = product.company_id || product.company_name || product.company;
const prompts = await loadNameValidationPrompts(pool, companyKey);
// Debug: Log loaded prompts
log.info('[NameValidation] Loaded prompts:', {
hasSystem: !!prompts.system,
systemLength: prompts.system?.length || 0,
hasGeneral: !!prompts.general,
generalLength: prompts.general?.length || 0,
generalPreview: prompts.general?.substring(0, 100) || '(empty)',
hasCompanySpecific: !!prompts.companySpecific,
companyKey
});
// Validate required prompts exist
validateRequiredPrompts(prompts, 'name_validation');
// Build the user prompt with database-loaded prompts
const userPrompt = buildNameUserPrompt(product, prompts);
// Debug: Log the full user prompt being sent
log.info('[NameValidation] User prompt:', userPrompt.substring(0, 500));
let response;
let result;
try {
// Try with JSON mode first
response = await provider.chatCompletion({
messages: [
{ role: 'system', content: prompts.system },
{ role: 'user', content: userPrompt }
],
model: MODELS.LARGE, // openai/gpt-oss-120b - reasoning model
temperature: 0.2, // Low temperature for consistent results
maxTokens: 3000, // Reasoning models need extra tokens for thinking
responseFormat: { type: 'json_object' }
});
// Log full raw response for debugging
log.info('[NameValidation] Raw AI response:', {
parsed: response.parsed,
content: response.content,
contentLength: response.content?.length
});
// Parse the response
result = parseNameResponse(response.parsed, response.content);
} catch (jsonError) {
// If JSON mode failed, check if we have failedGeneration to parse
if (jsonError.failedGeneration) {
log.warn('[NameValidation] JSON mode failed, attempting to parse failed_generation:', {
failedGeneration: jsonError.failedGeneration
});
result = parseNameResponse(null, jsonError.failedGeneration);
response = { latencyMs: 0, usage: {}, model: MODELS.SMALL };
} else {
// Retry without JSON mode
log.warn('[NameValidation] JSON mode failed, retrying without JSON mode');
response = await provider.chatCompletion({
messages: [
{ role: 'system', content: prompts.system },
{ role: 'user', content: userPrompt }
],
model: MODELS.SMALL,
temperature: 0.2,
maxTokens: 1500 // Reasoning models need extra tokens for thinking
// No responseFormat - let the model respond freely
});
log.info('[NameValidation] Raw AI response (no JSON mode):', {
parsed: response.parsed,
content: response.content,
contentLength: response.content?.length
});
result = parseNameResponse(response.parsed, response.content);
}
}
log.info(`[NameValidation] Validated "${product.name}" in ${response.latencyMs}ms`, {
isValid: result.isValid,
hassuggestion: !!result.suggestion,
issueCount: result.issues.length
});
return {
...result,
latencyMs: response.latencyMs,
usage: response.usage,
model: response.model
};
} catch (error) {
log.error('[NameValidation] Error:', error.message);
throw error;
}
}
};
}
module.exports = {
TASK_ID,
createNameValidationTask
};