fix(ai-moderation): skip caching error results to prevent false positives from transient failures

This commit is contained in:
MythEclipse
2026-06-04 17:42:23 +07:00
parent 4996acfacf
commit 946343ec8c
@@ -1685,24 +1685,38 @@ export async function runModerationAnalysis(
try { try {
const cached = await getCachedUserModeration(cacheKey); const cached = await getCachedUserModeration(cacheKey);
if (cached) { if (cached) {
cacheHits.push({ // Safety: skip cache entries that are artifacts of API/parse errors.
messageId: target.id, // A previous bug cached error results as "flagged", causing 24h false positives.
status: cached.status, // This guards against both legacy corrupt entries and any future write-path bugs.
flags: cached.flags, if (
score: cached.score, cached.flags.some((f) =>
analysis: cached.analysis, ["analysis_api_failed", "analysis_parse_failed", "analysis_incomplete"].includes(f),
categories: cached.categories, )
severity: cached.severity as AnalysisResult["severity"], ) {
confidence: cached.confidence, log.warn(
recommendedAction: cached.recommendedAction as AnalysisResult["recommendedAction"], { messageId: target.id, cacheKey },
policyVersion: "cached-user-moderation-2026-06", "Cache entry contains error artifact — treating as miss",
evidence: [], );
}); } else {
log.debug( cacheHits.push({
{ messageId: target.id, userId: target.user_id, cacheKey }, messageId: target.id,
"User moderation cache HIT — reusing previous result", status: cached.status,
); flags: cached.flags,
continue; score: cached.score,
analysis: cached.analysis,
categories: cached.categories,
severity: cached.severity as AnalysisResult["severity"],
confidence: cached.confidence,
recommendedAction: cached.recommendedAction as AnalysisResult["recommendedAction"],
policyVersion: "cached-user-moderation-2026-06",
evidence: [],
});
log.debug(
{ messageId: target.id, userId: target.user_id, cacheKey },
"User moderation cache HIT — reusing previous result",
);
continue;
}
} }
} catch { } catch {
// Cache lookup failed — proceed with uncached path // Cache lookup failed — proceed with uncached path
@@ -1767,6 +1781,10 @@ export async function runModerationAnalysis(
const rawContent = target.edited_content ?? target.content; const rawContent = target.edited_content ?? target.content;
if (!rawContent.trim()) continue; if (!rawContent.trim()) continue;
// Do NOT cache error results (API failures, parse failures, incomplete).
// Caching a transient error would turn it into a 24h false positive.
if (result.status === "error") continue;
const cacheKey = makeUserModerationCacheKey(target.user_id, rawContent); const cacheKey = makeUserModerationCacheKey(target.user_id, rawContent);
setCachedUserModeration(cacheKey, { setCachedUserModeration(cacheKey, {
flags: result.flags ?? [], flags: result.flags ?? [],
@@ -1776,7 +1794,7 @@ export async function runModerationAnalysis(
severity: result.severity ?? "none", severity: result.severity ?? "none",
confidence: result.confidence ?? result.score ?? 0, confidence: result.confidence ?? result.score ?? 0,
recommendedAction: result.recommendedAction ?? "none", recommendedAction: result.recommendedAction ?? "none",
status: result.status === "error" ? "flagged" : result.status, status: result.status,
}).catch(() => {}); }).catch(() => {});
} }