/** * 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} */ 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 };