fix(automod): flow real LLM analysis + descriptive fallback
Build & Deploy (Nix) / build-and-deploy (backend) (push) Successful in 3m2s
Build & Deploy (Nix) / build-and-deploy (discord-gateway) (push) Successful in 2m25s
Build & Deploy (Nix) / build-and-deploy (proxy) (push) Successful in 2m40s
Build & Deploy (Nix) / build-and-deploy (backend) (push) Successful in 3m2s
Build & Deploy (Nix) / build-and-deploy (discord-gateway) (push) Successful in 2m25s
Build & Deploy (Nix) / build-and-deploy (proxy) (push) Successful in 2m40s
Root cause: ai-analysis-worker read llmResult.explanation and llmResult.toxicityScore — fields the LLM pipeline never produces (canonical AnalysisResult uses analysis/score). Every message fell back to the bare template "Tidak ada indikasi pelanggaran." and the stored score was always 0. - Map analysis/score correctly; fallback now quotes the message content - Prompt: ban generic analysis phrasing, require reply context - LLM context: include replied-to message content (metadata.reference) so the model can explain what the user is replying to - Frontend: show thread/channel names from metadata instead of raw IDs (message card, detail views, search overlay); detail panel now displays the ai_analysis text - Auto-delete log/DM include the descriptive analysis as the reason
This commit is contained in:
@@ -42,12 +42,44 @@ export function estimateTokens(text: string): number {
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats reference info for a message (reply/forward/crosspost)
|
||||
* Formats reference info for a message (reply/forward/crosspost).
|
||||
*
|
||||
* The replied-to content is stored in the message metadata
|
||||
* (`metadata.reference.content` / `repliedUsername`) at capture time, so
|
||||
* include it here — the LLM can then explain WHAT the user is replying to
|
||||
* instead of only seeing a raw message ID it cannot resolve.
|
||||
*/
|
||||
function formatReferenceInfo(msg: MessageRecord): string {
|
||||
const parts: string[] = [];
|
||||
|
||||
let repliedContent: string | null = null;
|
||||
let repliedUsername: string | null = null;
|
||||
try {
|
||||
const meta = JSON.parse(msg.metadata ?? "") as {
|
||||
reference?: {
|
||||
content?: string | null;
|
||||
repliedUsername?: string | null;
|
||||
} | null;
|
||||
};
|
||||
repliedContent = meta?.reference?.content ?? null;
|
||||
repliedUsername = meta?.reference?.repliedUsername ?? null;
|
||||
} catch {
|
||||
// metadata malformed — fall back to ID-only reference
|
||||
}
|
||||
|
||||
const repliedText = (repliedContent ?? "").trim();
|
||||
const repliedSnippet = repliedText
|
||||
? sanitizeDiscordTokens(
|
||||
repliedText.length > 200
|
||||
? `${repliedText.slice(0, 200)}…`
|
||||
: repliedText,
|
||||
)
|
||||
: null;
|
||||
|
||||
if (msg.is_reply && msg.reference_message_id) {
|
||||
parts.push(`[reply_to: ${msg.reference_message_id}]`);
|
||||
const who = repliedUsername ? ` oleh ${repliedUsername}` : "";
|
||||
const what = repliedSnippet ? `: "${repliedSnippet}"` : "";
|
||||
parts.push(`[reply_to: ${msg.reference_message_id}${who}${what}]`);
|
||||
if (msg.reference_channel_id) {
|
||||
parts.push(`(reply_channel: ${msg.reference_channel_id})`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user