Add smartFilter module
Browse files- src/smartFilter.ts +48 -0
src/smartFilter.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { MemoryConfig, DEFAULT_MEMORY_CONFIG } from './types';
|
| 2 |
+
|
| 3 |
+
type MessageIntent = 'declarative' | 'interrogative' | 'imperative' | 'exclamatory' | 'social';
|
| 4 |
+
|
| 5 |
+
export function classifyMessageIntent(text: string): MessageIntent {
|
| 6 |
+
const t = text.trim();
|
| 7 |
+
if (t.endsWith('?')) return 'interrogative';
|
| 8 |
+
if (/^(what|who|where|when|why|how|which|can|could|would|should|is|are|do|does|did|will|have|has)\s/i.test(t)) return 'interrogative';
|
| 9 |
+
if (/^(tell|show|give|find|get|make|let|help|explain|describe|list|suggest|recommend)\s/i.test(t)) return 'imperative';
|
| 10 |
+
if (/^(hi|hey|hello|bye|goodbye|thanks|thank\s+you|please|sorry|wow|oh|hmm|haha|lol|nice|cool|great|awesome)\b/i.test(t)) return 'social';
|
| 11 |
+
if (t.endsWith('!') && /\b(love|hate|amazing|terrible|awful|great|best|worst)\b/i.test(t)) return 'exclamatory';
|
| 12 |
+
return 'declarative';
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
export function scoreMessageForMemory(text: string, config: MemoryConfig = DEFAULT_MEMORY_CONFIG): { score: number; shouldStore: boolean; reason: string } {
|
| 16 |
+
let score = 0; const reasons: string[] = []; const t = text.trim();
|
| 17 |
+
if (t.length < config.minMessageLength) return { score: 0, shouldStore: false, reason: 'too_short' };
|
| 18 |
+
if (config.skipPatterns.some(p => p.test(t))) return { score: 0, shouldStore: false, reason: 'skip_pattern' };
|
| 19 |
+
const intent = classifyMessageIntent(t);
|
| 20 |
+
switch (intent) {
|
| 21 |
+
case 'declarative': score += 0.4; reasons.push('declarative'); break;
|
| 22 |
+
case 'exclamatory': score += 0.3; reasons.push('exclamatory'); break;
|
| 23 |
+
case 'interrogative': score += 0.05; reasons.push('question'); break;
|
| 24 |
+
case 'imperative': if (/\b(always|never|remember|don't\s+forget)\b/i.test(t)) { score += 0.45; reasons.push('procedural'); } else { score += 0.05; reasons.push('command'); } break;
|
| 25 |
+
case 'social': reasons.push('social'); break;
|
| 26 |
+
}
|
| 27 |
+
if (/\b(i\s+am|i'm|i\s+have|i've|i\s+live|i\s+work|my\s+\w+\s+is)\b/i.test(t)) { score += 0.25; reasons.push('personal_fact'); }
|
| 28 |
+
if (/\b(i\s+(like|love|enjoy|prefer|hate|dislike|always|never))\b/i.test(t)) { score += 0.2; reasons.push('preference'); }
|
| 29 |
+
if (/\b(allergic|can't\s+eat|don't\s+eat|vegetarian|vegan|intolerant|avoid)\b/i.test(t)) { score += 0.3; reasons.push('constraint'); }
|
| 30 |
+
if (/\b(always|never|remember\s+to|remind\s+me|don't\s+forget)\b/i.test(t)) { score += 0.2; reasons.push('procedural_keywords'); }
|
| 31 |
+
if (t.length > 40) score += 0.05;
|
| 32 |
+
if (t.length > 80) score += 0.05;
|
| 33 |
+
score = Math.min(score, 1.0);
|
| 34 |
+
return { score, shouldStore: score >= 0.5, reason: reasons.join(', ') };
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
export function shouldStoreUserMessage(text: string, config: MemoryConfig = DEFAULT_MEMORY_CONFIG): { store: boolean; textToStore: string; reason: string } {
|
| 38 |
+
const segments = text.split(/[.!?]+/).map(s => s.trim()).filter(s => s.length > 5);
|
| 39 |
+
if (segments.length <= 1) {
|
| 40 |
+
const result = scoreMessageForMemory(text, config);
|
| 41 |
+
if (result.reason.includes('constraint') || result.reason.includes('preference'))
|
| 42 |
+
return { store: true, textToStore: text, reason: result.reason };
|
| 43 |
+
return { store: result.shouldStore, textToStore: text, reason: result.reason };
|
| 44 |
+
}
|
| 45 |
+
const storeable = segments.filter(s => scoreMessageForMemory(s, config).shouldStore);
|
| 46 |
+
if (!storeable.length) return { store: false, textToStore: '', reason: 'no_storable_content' };
|
| 47 |
+
return { store: true, textToStore: storeable.join('. ') + '.', reason: 'compound' };
|
| 48 |
+
}
|