weekly-learning-report.js
1 #!/usr/bin/env node 2 3 /** 4 * Weekly Learning Report Cron Job 5 * Analyzes prompt feedback patterns and flags low-performing prompts for human review 6 * Adds approximately weekly (not exact schedule) via cron_jobs table 7 */ 8 9 import { run } from './../utils/db.js'; 10 import Logger from '../utils/logger.js'; 11 import { generatePromptRecommendations } from '../utils/prompt-learning.js'; 12 13 const logger = new Logger('WeeklyLearningReport'); 14 15 // Approval rate threshold for flagging (70%) 16 const APPROVAL_THRESHOLD = 70; 17 18 // Prompt files to analyze 19 const PROMPT_FILES = [ 20 'PROPOSAL.md', 21 'CONVERSION-SCORING-VISION.md', 22 'CONVERSION-SCORING-NOVIS.md', 23 'CLASSIFICATION.md', 24 'FORM-FIELD-DETECTION', 25 ]; 26 27 /** 28 * Add item to human review queue 29 */ 30 async function addToReviewQueue(promptFile, reason, recommendations) { 31 try { 32 const recommendationText = recommendations 33 .map( 34 (rec, i) => 35 `${i + 1}. [${rec.priority.toUpperCase()}] ${rec.issue}\n → ${rec.recommendation}` 36 ) 37 .join('\n\n'); 38 39 await run( 40 `INSERT INTO human_review_queue (file, reason, type, priority, status) 41 VALUES ($1, $2, $3, $4, 'pending')`, 42 [ 43 `prompts/${promptFile}`, 44 `${reason}\n\n${recommendationText}`, 45 'prompt_quality', 46 'medium', 47 ] 48 ); 49 50 logger.info(`Added ${promptFile} to human review queue`); 51 } catch (err) { 52 logger.error(`Failed to add ${promptFile} to review queue: ${err.message}`); 53 } 54 } 55 56 /** 57 * Log cron job execution 58 */ 59 async function logCronExecution(status, details) { 60 try { 61 await run( 62 `INSERT INTO ops.cron_jobs (job_name, last_run, status, details) 63 VALUES ('weekly-learning-report', NOW(), $1, $2) 64 ON CONFLICT (job_name) DO UPDATE SET 65 last_run = NOW(), 66 status = EXCLUDED.status, 67 details = EXCLUDED.details`, 68 [status, details] 69 ); 70 } catch (err) { 71 logger.error(`Failed to log cron execution: ${err.message}`); 72 } 73 } 74 75 /** 76 * Run weekly learning report 77 */ 78 async function runWeeklyReport() { 79 const startTime = Date.now(); 80 81 logger.info('Starting weekly learning report...'); 82 83 const results = { 84 analyzed: 0, 85 flagged: 0, 86 skipped: 0, 87 errors: [], 88 }; 89 90 // Analyze each prompt file 91 for (const promptFile of PROMPT_FILES) { 92 try { 93 logger.info(`Analyzing ${promptFile}...`); 94 95 const analysis = generatePromptRecommendations(promptFile); 96 results.analyzed++; 97 98 // Check if data is available 99 if (!analysis || !analysis.stats || analysis.stats.total === 0) { 100 logger.warn(`No feedback data available for ${promptFile}`); 101 results.skipped++; 102 continue; 103 } 104 105 const { stats, recommendations } = analysis; 106 const { approvalRate } = stats; 107 108 logger.info(`${promptFile}: ${approvalRate}% approval rate (${stats.total} samples)`); 109 110 // Flag low-performing prompts 111 if (approvalRate < APPROVAL_THRESHOLD && recommendations.length > 0) { 112 const reason = 113 `Low approval rate: ${approvalRate}% (target: ${APPROVAL_THRESHOLD}%+)\n` + 114 `Total feedback: ${stats.total}\n` + 115 `Approved: ${stats.approved}, Rework: ${stats.rework}, Rejected: ${stats.rejected}\n\n` + 116 `This prompt needs review and improvement.`; 117 118 await addToReviewQueue(promptFile, reason, recommendations); 119 results.flagged++; 120 121 logger.warn(`Flagged ${promptFile} for review (${approvalRate}% approval)`); 122 } else if (approvalRate >= APPROVAL_THRESHOLD) { 123 logger.success(`${promptFile} performing well (${approvalRate}% approval)`); 124 } 125 } catch (err) { 126 logger.error(`Failed to analyze ${promptFile}: ${err.message}`); 127 results.errors.push(`${promptFile}: ${err.message}`); 128 } 129 } 130 131 const duration = Date.now() - startTime; 132 133 // Log execution 134 const details = JSON.stringify({ 135 analyzed: results.analyzed, 136 flagged: results.flagged, 137 skipped: results.skipped, 138 errors: results.errors.length, 139 duration_ms: duration, 140 }); 141 142 await logCronExecution('completed', details); 143 144 // Summary 145 logger.info('Weekly Learning Report Summary:'); 146 logger.info(` Analyzed: ${results.analyzed} prompts`); 147 logger.info(` Flagged for review: ${results.flagged} prompts`); 148 logger.info(` Skipped (no data): ${results.skipped} prompts`); 149 logger.info(` Errors: ${results.errors.length}`); 150 logger.info(` Duration: ${(duration / 1000).toFixed(2)}s`); 151 152 if (results.flagged > 0) { 153 logger.warn( 154 `${results.flagged} prompts flagged for human review. Check the Human Review dashboard.` 155 ); 156 } 157 158 if (results.errors.length > 0) { 159 logger.error('Errors encountered:'); 160 results.errors.forEach(err => logger.error(` - ${err}`)); 161 } 162 163 return results; 164 } 165 166 // Run if executed directly 167 if (import.meta.url === `file://${process.argv[1]}`) { 168 runWeeklyReport() 169 .then(() => { 170 logger.success('Weekly learning report completed'); 171 process.exit(0); 172 }) 173 .catch(err => { 174 logger.error(`Weekly learning report failed: ${err.message}`); 175 process.exit(1); 176 }); 177 } 178 179 export default runWeeklyReport;