92 lines
2.2 KiB
TypeScript
92 lines
2.2 KiB
TypeScript
export interface SelectedCompactMemory {
|
|
content: string;
|
|
keywords: string[];
|
|
memoryKind: string | null;
|
|
}
|
|
|
|
const MAX_SELECTED_MEMORIES = 3;
|
|
|
|
const MEMORY_PATTERNS: Array<{
|
|
kind: string | null;
|
|
regex: RegExp;
|
|
keyword: string;
|
|
}> = [
|
|
{
|
|
kind: 'room_norm',
|
|
regex:
|
|
/(규칙|원칙|금지|반드시|하지 않|세션 시작 시에만|새 세션 시작 시에만)/i,
|
|
keyword: 'rule',
|
|
},
|
|
{
|
|
kind: 'preference',
|
|
regex: /(선호|원함|원한다|원하는|원했다)/i,
|
|
keyword: 'preference',
|
|
},
|
|
{
|
|
kind: 'decision',
|
|
regex: /(합의|결정|방향|기준|우선|책임지는 방향)/i,
|
|
keyword: 'decision',
|
|
},
|
|
{
|
|
kind: 'project_fact',
|
|
regex:
|
|
/(owner|reviewer|trigger|모드|메모리|기억|세션 리셋|recall|persist|compact)/i,
|
|
keyword: 'memory',
|
|
},
|
|
];
|
|
|
|
function normalizeSentence(raw: string): string | null {
|
|
const normalized = raw
|
|
.trim()
|
|
.replace(/^[-*]\s*/, '')
|
|
.replace(/\s+/g, ' ');
|
|
return normalized ? normalized : null;
|
|
}
|
|
|
|
function splitSummaryIntoSentences(summary: string): string[] {
|
|
return summary
|
|
.split(/\n+|(?<=[.!?。])\s+/)
|
|
.map(normalizeSentence)
|
|
.filter((value): value is string => Boolean(value));
|
|
}
|
|
|
|
function classifyMemorySentence(content: string): SelectedCompactMemory | null {
|
|
if (!content) return null;
|
|
|
|
const matchedPatterns = MEMORY_PATTERNS.filter(({ regex }) =>
|
|
regex.test(content),
|
|
);
|
|
if (matchedPatterns.length === 0) return null;
|
|
|
|
const primary = matchedPatterns[0];
|
|
const keywords = [
|
|
...new Set(
|
|
matchedPatterns.map((pattern) => pattern.keyword).filter(Boolean),
|
|
),
|
|
];
|
|
|
|
return {
|
|
content,
|
|
keywords,
|
|
memoryKind: primary?.kind ?? null,
|
|
};
|
|
}
|
|
|
|
export function selectCompactMemoriesFromSummary(
|
|
summary: string,
|
|
roomKey: string,
|
|
): SelectedCompactMemory[] {
|
|
if (!summary.trim()) return [];
|
|
|
|
const selected = splitSummaryIntoSentences(summary)
|
|
.map((sentence) => classifyMemorySentence(sentence))
|
|
.filter((value): value is SelectedCompactMemory => Boolean(value))
|
|
.slice(0, MAX_SELECTED_MEMORIES)
|
|
.map((entry) => ({
|
|
...entry,
|
|
keywords: [...new Set([roomKey, ...entry.keywords])],
|
|
}));
|
|
|
|
return selected;
|
|
}
|