Files
GMW/src/moderation/conversationContext.ts
T

73 lines
2.4 KiB
TypeScript
Raw Normal View History

import { formatModerationTextEvidenceForPrompt } from "./indonesianTextNormalizer.js";
import { formatMediaEvidenceForPrompt } from "./messageMetadata.js";
import type { MessageRecord } from "./types.js";
2026-05-14 19:10:13 +07:00
export interface ConversationContextInput {
contextBefore: MessageRecord[];
targets: MessageRecord[];
maxTokens: number;
}
/**
* Formats a timestamp to ISO 8601 string
*/
function formatTimestamp(ms: number): string {
return new Date(ms).toISOString();
}
/**
* Estimates token count for a string (pessimistic approximation for Indonesian slang & JSON overhead)
2026-05-14 19:10:13 +07:00
*/
export function estimateTokens(text: string): number {
return Math.ceil(text.length / 3) + 15;
2026-05-14 19:10:13 +07:00
}
/**
* Formats a single message for context or target display
2026-05-14 19:10:13 +07:00
*/
export async function formatMessageForPrompt(
msg: MessageRecord,
label: "context" | "target",
): Promise<string> {
const content = msg.edited_content ?? msg.content;
const timestamp = formatTimestamp(msg.created_at);
const textEvidence = await formatModerationTextEvidenceForPrompt(content);
const textSuffix = textEvidence ? ` ${textEvidence}` : "";
const mediaEvidence = formatMediaEvidenceForPrompt(msg.metadata);
const mediaSuffix = mediaEvidence ? ` ${mediaEvidence}` : "";
return `[${label}] id=${msg.id} time=${timestamp} user=${msg.username}: ${content}${textSuffix}${mediaSuffix}`;
}
/**
* Builds conversation historical context without including targets.
* Calculates how much token budget targets use, and fills the rest with context.
*/
export async function buildConversationContext(
2026-05-14 19:10:13 +07:00
input: ConversationContextInput,
): Promise<string[]> {
2026-05-14 19:10:13 +07:00
const { contextBefore, targets, maxTokens } = input;
// Calculate tokens used by targets (parallel)
const targetLines = await Promise.all(
targets.map((msg) => formatMessageForPrompt(msg, "target")),
);
let usedTokens = targetLines.reduce((sum, line) => sum + estimateTokens(line), 0);
2026-05-14 19:10:13 +07:00
const selectedContextLines: string[] = [];
2026-05-14 19:10:13 +07:00
// Go backwards through context, taking most recent first
for (let i = contextBefore.length - 1; i >= 0; i--) {
const msg = contextBefore[i];
const line = await formatMessageForPrompt(msg, "context");
2026-05-14 19:10:13 +07:00
const lineTokens = estimateTokens(line);
2026-05-14 19:10:13 +07:00
if (usedTokens + lineTokens <= maxTokens) {
// Unshift so oldest context is first in the array
selectedContextLines.unshift(line);
2026-05-14 19:10:13 +07:00
usedTokens += lineTokens;
}
}
return selectedContextLines;
2026-05-14 19:10:13 +07:00
}