From 3b221823e7d0d27107be3820a5276b5d89f10679 Mon Sep 17 00:00:00 2001 From: asepharyana Date: Wed, 12 Aug 2026 19:22:56 +0700 Subject: [PATCH] feat(gateway): add observability logging for vision cache hits/misses Add debug logging to trace cacheKey + messageId + content length on every vision cache HIT and MISS, so we can detect if the vision model returns duplicate analysis for different images (provider issue vs cache collision). Includes the phash on cache miss (new analysis cached). Follow-up to 9f7ce7d which fixed makeImageCacheKey to hash full data URL instead of just first 128 chars (root cause of all images sharing the same cached 'konten judi' verdict due to hash collision). --- .../src/modules/ai-moderation/visionAnalyzer.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/services/discord-gateway/src/modules/ai-moderation/visionAnalyzer.ts b/services/discord-gateway/src/modules/ai-moderation/visionAnalyzer.ts index 4439876..e1b5aee 100644 --- a/services/discord-gateway/src/modules/ai-moderation/visionAnalyzer.ts +++ b/services/discord-gateway/src/modules/ai-moderation/visionAnalyzer.ts @@ -163,7 +163,10 @@ export const analyzeSingleMediaImage = async ( const cached = await getCachedMediaAnalysis(cacheKey); if (cached && !isNoImageSeenText(cached)) { visionLruCache.set(cacheKey, cached); - log.debug({ cacheKey }, "Media analysis cache HIT (DB → LRU)"); + log.debug( + { cacheKey, messageId, cachedLen: cached.length }, + "Media analysis cache HIT (DB → LRU)", + ); return `[Media analysis for message ${messageId}] ${image.sourceLabel}: ${cached}`; } if (cached) { @@ -246,6 +249,13 @@ export const analyzeSingleMediaImage = async ( try { const content = await llmVision(promptText, image.image_url); if (content && !isNoImageSeenText(content)) { + // Defensive: log when a vision analysis is cached so we can trace + // if the SAME analysis text is being stored for DIFFERENT cache keys + // (which would indicate the vision model is returning duplicates). + log.debug( + { cacheKey, phash, messageId, contentLen: content.length }, + "Vision analysis cached (new entry)", + ); await upsertCachedMediaAnalysis( cacheKey, content,