← back to Wallco Ai
lib/bad-aesthetic.js
73 lines
// lib/bad-aesthetic.js — closes the marquee-Delete-and-learn loop.
//
// Reads data/bad-aesthetic-patterns.jsonl (written by both the Ghost Review UI's
// bulk-delete endpoint and scripts/learn-and-purge-flagged.js) and exposes a
// tiny API the generators can call before spending a Gemini/SDXL call on a
// category that keeps producing defects.
//
// Schema per line:
// { id, category, prompt, motifs, reason, ts }
//
// Behavior:
// getCategoryFailureCount(category) → integer
// isCategoryCooldown(category, n=50) → boolean ("skip this tick entirely")
// getAvoidanceAddendum(category) → string to append to a fresh prompt
// when the category has ≥1 prior failure
//
// File is reloaded on every call (small file, append-only — millisecond cost).
// If the file is missing the lib returns the no-failures defaults.
'use strict';
const fs = require('fs');
const path = require('path');
const FILE = path.join(__dirname, '..', 'data', 'bad-aesthetic-patterns.jsonl');
function loadRows() {
try {
const raw = fs.readFileSync(FILE, 'utf8');
const rows = [];
for (const line of raw.split('\n')) {
if (!line.trim()) continue;
try { rows.push(JSON.parse(line)); } catch {}
}
return rows;
} catch { return []; }
}
function getCategoryFailureCount(category) {
if (!category) return 0;
const want = String(category).toLowerCase();
return loadRows().filter(r => (r.category || '').toLowerCase() === want).length;
}
function isCategoryCooldown(category, threshold = 50) {
return getCategoryFailureCount(category) >= threshold;
}
// Build a category-targeted avoidance addendum from the reason strings actually
// observed in past failures. Falls back to a generic anti-ghost addendum if the
// category has prior failures but no parseable reasons.
function getAvoidanceAddendum(category) {
const rows = loadRows().filter(r => (r.category || '').toLowerCase() === String(category || '').toLowerCase());
if (!rows.length) return '';
const reasons = [...new Set(rows.map(r => r.reason || '').filter(Boolean))].slice(0, 3);
const n = rows.length;
return (
`\n\n[LEARNED FROM PRIOR FAILURES — ${n} prior generation${n === 1 ? '' : 's'} in this ` +
`"${category}" category were rejected with these reasons: ${reasons.join(' · ')}. ` +
`Take EXTRA CARE to avoid: ghost-layer / faded duplicate motifs / atmospheric haze over ` +
`flat patterns / multi-opacity copies of the same motif / "halo" pale silhouettes echoing ` +
`sharp ones. Render as a SINGLE flat layer, hard razor-sharp silhouette edges, uniform ` +
`opacity across every motif, ground is one flat solid color edge-to-edge.]`
);
}
module.exports = {
loadRows,
getCategoryFailureCount,
isCategoryCooldown,
getAvoidanceAddendum,
};