If you meant to summarize the most recent (already committed) changes on this branch (41 commits ahead of origin/master), let me know and I can help craft a message for a squash or a summary instead.
This commit is contained in:
@@ -536,9 +536,18 @@ const analyzeSingleMediaImage = async (
|
||||
? makeStickerCacheKey(image.stickerName)
|
||||
: makeImageCacheKey(image.image_url.url);
|
||||
|
||||
// Layer 0: In-memory LRU cache (fastest — no DB or network I/O)
|
||||
const lruCached = visionLruCache.get(cacheKey);
|
||||
if (lruCached) {
|
||||
log.debug({ cacheKey }, "Vision LRU cache HIT (in-memory)");
|
||||
return `[Media analysis for message ${messageId}] ${image.sourceLabel}: ${lruCached}`;
|
||||
}
|
||||
|
||||
// Layer 1: DB cache
|
||||
const cached = await getCachedMediaAnalysis(cacheKey);
|
||||
if (cached) {
|
||||
log.debug({ cacheKey }, "Media analysis cache HIT");
|
||||
visionLruCache.set(cacheKey, cached);
|
||||
log.debug({ cacheKey }, "Media analysis cache HIT (DB → LRU)");
|
||||
return `[Media analysis for message ${messageId}] ${image.sourceLabel}: ${cached}`;
|
||||
}
|
||||
|
||||
@@ -562,6 +571,38 @@ const analyzeSingleMediaImage = async (
|
||||
: buildGeneralImageVisionPrompt(image.sourceLabel, messageId);
|
||||
|
||||
const visionPromise = (async (): Promise<string | null> => {
|
||||
// Layer 2: Perceptual hash pre-check (before expensive vision API call)
|
||||
let phash: string | null = null;
|
||||
if (image.image_url.url.startsWith("data:")) {
|
||||
try {
|
||||
const base64Data = image.image_url.url.split(",")[1];
|
||||
if (base64Data) {
|
||||
const imgBuffer = Buffer.from(base64Data, "base64");
|
||||
phash = await computeImagePhash(imgBuffer);
|
||||
if (phash) {
|
||||
const phashCached = await getCachedMediaByPhash(phash);
|
||||
if (phashCached) {
|
||||
log.debug(
|
||||
{ cacheKey, phash: phash.slice(0, 16) },
|
||||
"Vision phash HIT — reusing analysis",
|
||||
);
|
||||
visionLruCache.set(cacheKey, phashCached);
|
||||
await upsertCachedMediaAnalysis(
|
||||
cacheKey,
|
||||
phashCached,
|
||||
"vision_llm",
|
||||
Date.now() + 24 * 60 * 60 * 1000,
|
||||
).catch(() => {});
|
||||
return phashCached;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// phash failed — continue with normal vision API flow
|
||||
phash = null;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const content = await llmVision(promptText, image.image_url);
|
||||
if (!content) return null;
|
||||
@@ -572,6 +613,16 @@ const analyzeSingleMediaImage = async (
|
||||
"vision_llm",
|
||||
Date.now() + 24 * 60 * 60 * 1000,
|
||||
);
|
||||
// Populate in-memory LRU and phash cache
|
||||
visionLruCache.set(cacheKey, content);
|
||||
if (phash) {
|
||||
upsertCachedMediaByPhash(
|
||||
phash,
|
||||
content,
|
||||
"vision_llm",
|
||||
Date.now() + 7 * 24 * 60 * 60 * 1000,
|
||||
).catch(() => {});
|
||||
}
|
||||
|
||||
return content;
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user