fix(gateway): mediaAnalysis ffprobe path, fallback error-log, generic closer sanitize
Build & Deploy (Nix) / build-and-deploy (proxy) (push) Successful in 3m47s
Build & Deploy (Nix) / build-and-deploy (backend) (push) Successful in 4m11s
Build & Deploy (Nix) / build-and-deploy (discord-gateway) (push) Successful in 11m36s
Build & Deploy (Nix) / build-and-deploy (proxy) (push) Successful in 3m47s
Build & Deploy (Nix) / build-and-deploy (backend) (push) Successful in 4m11s
Build & Deploy (Nix) / build-and-deploy (discord-gateway) (push) Successful in 11m36s
Audit log produksi (sejak deploy13:38) menemukan 3 isu:
1. mediaDownloader.ts spawn /usr/bin/ffprobe + /usr/bin/ffmpeg (path keras) —
ENOENT di Nix karena binary cuma di ffmpeg-headless closure. Pakai
PATH-resolved ('ffprobe'/'ffmpeg') seperti voice-recording module
(ffmpegProcess.ts/transmitter.ts) — 5 media warning hilang.
2. individualFallbackProcessor log error 'Success' di level50 tiap fallback
BERHASIL (logModerationError dengan new Error('Success')) — ganti
logger.info dengan verdict yang sama; error log cuma untuk error asli.
3. moderationResponseParser: strip frasa penutup generik ('Tidak ada
indikasi pelanggaran.') yang masih sering dikeluarkan LLM walau prompt
melarang (277/1486 analisis mengandung frasa, termasuk hari ini).
sanitizeGenericCleanCloser hanya mencocok frasa di AKHIR, teks substantif
tetap utuh. Unit test: 6/6 pass.
This commit is contained in:
@@ -155,13 +155,16 @@ async function processIndividualFallback(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const resultSummary = analysisResult.results[0];
|
const resultSummary = analysisResult.results[0];
|
||||||
logModerationError([messageId], config.AI_LLM_MODEL, new Error("Success"), {
|
logger.info(
|
||||||
phase: "individual_fallback",
|
{
|
||||||
status: resultSummary?.status,
|
phase: "individual_fallback",
|
||||||
flags: resultSummary?.flags,
|
status: resultSummary?.status,
|
||||||
severity: resultSummary?.severity,
|
flags: resultSummary?.flags,
|
||||||
confidence: resultSummary?.confidence,
|
severity: resultSummary?.severity,
|
||||||
});
|
confidence: resultSummary?.confidence,
|
||||||
|
},
|
||||||
|
`Individual fallback moderation complete for ${messageId} (${resultSummary?.status})`,
|
||||||
|
);
|
||||||
|
|
||||||
individualConsecutiveErrors = 0;
|
individualConsecutiveErrors = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ async function extractVideoFrames(
|
|||||||
try {
|
try {
|
||||||
await writeFile(inputPath, videoBytes);
|
await writeFile(inputPath, videoBytes);
|
||||||
const { stdout: durationStr } = await execFileAsync(
|
const { stdout: durationStr } = await execFileAsync(
|
||||||
"/usr/bin/ffprobe",
|
"ffprobe",
|
||||||
[
|
[
|
||||||
"-v",
|
"-v",
|
||||||
"error",
|
"error",
|
||||||
@@ -213,7 +213,7 @@ async function extractVideoFrames(
|
|||||||
const duration = parseFloat(durationStr.trim()) || 1;
|
const duration = parseFloat(durationStr.trim()) || 1;
|
||||||
const fps = (3 / duration).toFixed(6);
|
const fps = (3 / duration).toFixed(6);
|
||||||
await execFileAsync(
|
await execFileAsync(
|
||||||
"/usr/bin/ffmpeg",
|
"ffmpeg",
|
||||||
[
|
[
|
||||||
"-i",
|
"-i",
|
||||||
inputPath,
|
inputPath,
|
||||||
|
|||||||
@@ -157,6 +157,22 @@ export function sanitizeErrorMessage(
|
|||||||
return `Analisis gagal dan memerlukan pemeriksaan manual. Error code: MOD_${Date.now().toString(36).slice(0, 6)}`;
|
return `Analisis gagal dan memerlukan pemeriksaan manual. Error code: MOD_${Date.now().toString(36).slice(0, 6)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strip generic clean-verdict closing phrases the LLM sometimes appends
|
||||||
|
* ("Tidak ada indikasi pelanggaran.") even when it already described the
|
||||||
|
* message content specifically. Keeps the substance, drops the boilerplate.
|
||||||
|
* Only matches a TRAILING standalone phrase — never mid-analysis text.
|
||||||
|
*/
|
||||||
|
export const GENERIC_CLEAN_CLOSER =
|
||||||
|
/\s*(?:(?:tidak ada (?:indikasi|tanda|temuan)(?: adanya)? pelanggaran(?: kebijakan| aturan)?)|(?:tidak ada (?:indikasi|tanda|temuan)(?: konten| pesan)? (?:yang )?melanggar)|(?:pesan (?:dianggap|tergolong|dinilai) bersih)|(?:tidak ditemukan (?:adanya )?pelanggaran))\.?\s*$/i;
|
||||||
|
|
||||||
|
export function sanitizeGenericCleanCloser(analysis: string): string {
|
||||||
|
if (!analysis) return analysis;
|
||||||
|
const cleaned = analysis.replace(GENERIC_CLEAN_CLOSER, "").trim();
|
||||||
|
// Drop a dangling separator the removal may have left behind.
|
||||||
|
return cleaned.replace(/(\s[.\-–—]\s*)$/, "").trim();
|
||||||
|
}
|
||||||
|
|
||||||
export function parseModerationResponse(
|
export function parseModerationResponse(
|
||||||
content: string,
|
content: string,
|
||||||
targetIds: string[],
|
targetIds: string[],
|
||||||
@@ -250,7 +266,7 @@ export function parseModerationResponse(
|
|||||||
status: status as "clean" | "warn" | "flagged",
|
status: status as "clean" | "warn" | "flagged",
|
||||||
flags: flags ?? [],
|
flags: flags ?? [],
|
||||||
score: normalizedScore,
|
score: normalizedScore,
|
||||||
analysis: coalescedAnalysis,
|
analysis: sanitizeGenericCleanCloser(coalescedAnalysis),
|
||||||
categories: categories ?? flags ?? [],
|
categories: categories ?? flags ?? [],
|
||||||
severity: normalizedSeverity,
|
severity: normalizedSeverity,
|
||||||
confidence: normalizedConfidence,
|
confidence: normalizedConfidence,
|
||||||
|
|||||||
Reference in New Issue
Block a user