@cmdoss/memwal-sdk 0.6.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +547 -547
- package/BENCHMARKS.md +238 -238
- package/README.md +310 -181
- package/dist/ai-sdk/tools.d.ts +2 -2
- package/dist/ai-sdk/tools.js +2 -2
- package/dist/client/ClientMemoryManager.js +2 -2
- package/dist/client/ClientMemoryManager.js.map +1 -1
- package/dist/client/PersonalDataWallet.d.ts.map +1 -1
- package/dist/client/SimplePDWClient.d.ts +29 -1
- package/dist/client/SimplePDWClient.d.ts.map +1 -1
- package/dist/client/SimplePDWClient.js +45 -13
- package/dist/client/SimplePDWClient.js.map +1 -1
- package/dist/client/namespaces/EmbeddingsNamespace.d.ts +1 -1
- package/dist/client/namespaces/EmbeddingsNamespace.js +1 -1
- package/dist/client/namespaces/MemoryNamespace.d.ts +31 -0
- package/dist/client/namespaces/MemoryNamespace.d.ts.map +1 -1
- package/dist/client/namespaces/MemoryNamespace.js +272 -39
- package/dist/client/namespaces/MemoryNamespace.js.map +1 -1
- package/dist/client/namespaces/consolidated/AINamespace.d.ts +2 -2
- package/dist/client/namespaces/consolidated/AINamespace.js +2 -2
- package/dist/client/namespaces/consolidated/BlockchainNamespace.d.ts +12 -2
- package/dist/client/namespaces/consolidated/BlockchainNamespace.d.ts.map +1 -1
- package/dist/client/namespaces/consolidated/BlockchainNamespace.js +62 -4
- package/dist/client/namespaces/consolidated/BlockchainNamespace.js.map +1 -1
- package/dist/client/namespaces/consolidated/StorageNamespace.d.ts +67 -2
- package/dist/client/namespaces/consolidated/StorageNamespace.d.ts.map +1 -1
- package/dist/client/namespaces/consolidated/StorageNamespace.js +549 -16
- package/dist/client/namespaces/consolidated/StorageNamespace.js.map +1 -1
- package/dist/config/ConfigurationHelper.js +61 -61
- package/dist/config/defaults.js +2 -2
- package/dist/config/defaults.js.map +1 -1
- package/dist/graph/GraphService.js +21 -21
- package/dist/graph/GraphService.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/seal/EncryptionService.d.ts +9 -5
- package/dist/infrastructure/seal/EncryptionService.d.ts.map +1 -1
- package/dist/infrastructure/seal/EncryptionService.js +37 -15
- package/dist/infrastructure/seal/EncryptionService.js.map +1 -1
- package/dist/infrastructure/seal/SealService.d.ts +13 -5
- package/dist/infrastructure/seal/SealService.d.ts.map +1 -1
- package/dist/infrastructure/seal/SealService.js +36 -34
- package/dist/infrastructure/seal/SealService.js.map +1 -1
- package/dist/langchain/createPDWRAG.js +30 -30
- package/dist/retrieval/MemoryDecryptionPipeline.d.ts.map +1 -1
- package/dist/retrieval/MemoryDecryptionPipeline.js +2 -1
- package/dist/retrieval/MemoryDecryptionPipeline.js.map +1 -1
- package/dist/retrieval/MemoryRetrievalService.d.ts +31 -0
- package/dist/retrieval/MemoryRetrievalService.d.ts.map +1 -1
- package/dist/retrieval/MemoryRetrievalService.js +44 -4
- package/dist/retrieval/MemoryRetrievalService.js.map +1 -1
- package/dist/services/CapabilityService.d.ts.map +1 -1
- package/dist/services/CapabilityService.js +30 -14
- package/dist/services/CapabilityService.js.map +1 -1
- package/dist/services/CrossContextPermissionService.d.ts.map +1 -1
- package/dist/services/CrossContextPermissionService.js +9 -7
- package/dist/services/CrossContextPermissionService.js.map +1 -1
- package/dist/services/EmbeddingService.d.ts +28 -1
- package/dist/services/EmbeddingService.d.ts.map +1 -1
- package/dist/services/EmbeddingService.js +54 -0
- package/dist/services/EmbeddingService.js.map +1 -1
- package/dist/services/EncryptionService.d.ts.map +1 -1
- package/dist/services/EncryptionService.js +6 -5
- package/dist/services/EncryptionService.js.map +1 -1
- package/dist/services/GeminiAIService.js +309 -309
- package/dist/services/IndexManager.d.ts +5 -1
- package/dist/services/IndexManager.d.ts.map +1 -1
- package/dist/services/IndexManager.js +17 -40
- package/dist/services/IndexManager.js.map +1 -1
- package/dist/services/QueryService.js +1 -1
- package/dist/services/QueryService.js.map +1 -1
- package/dist/services/StorageService.d.ts +11 -0
- package/dist/services/StorageService.d.ts.map +1 -1
- package/dist/services/StorageService.js +73 -10
- package/dist/services/StorageService.js.map +1 -1
- package/dist/services/TransactionService.d.ts +20 -0
- package/dist/services/TransactionService.d.ts.map +1 -1
- package/dist/services/TransactionService.js +43 -0
- package/dist/services/TransactionService.js.map +1 -1
- package/dist/services/ViewService.js +2 -2
- package/dist/services/ViewService.js.map +1 -1
- package/dist/services/storage/QuiltBatchManager.d.ts +101 -1
- package/dist/services/storage/QuiltBatchManager.d.ts.map +1 -1
- package/dist/services/storage/QuiltBatchManager.js +410 -20
- package/dist/services/storage/QuiltBatchManager.js.map +1 -1
- package/dist/services/storage/index.d.ts +1 -1
- package/dist/services/storage/index.d.ts.map +1 -1
- package/dist/services/storage/index.js.map +1 -1
- package/dist/utils/LRUCache.d.ts +106 -0
- package/dist/utils/LRUCache.d.ts.map +1 -0
- package/dist/utils/LRUCache.js +281 -0
- package/dist/utils/LRUCache.js.map +1 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +2 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/memoryIndexOnChain.d.ts +212 -0
- package/dist/utils/memoryIndexOnChain.d.ts.map +1 -0
- package/dist/utils/memoryIndexOnChain.js +312 -0
- package/dist/utils/memoryIndexOnChain.js.map +1 -0
- package/dist/utils/rebuildIndexNode.d.ts +29 -0
- package/dist/utils/rebuildIndexNode.d.ts.map +1 -1
- package/dist/utils/rebuildIndexNode.js +366 -98
- package/dist/utils/rebuildIndexNode.js.map +1 -1
- package/dist/vector/HnswWasmService.d.ts +20 -5
- package/dist/vector/HnswWasmService.d.ts.map +1 -1
- package/dist/vector/HnswWasmService.js +73 -40
- package/dist/vector/HnswWasmService.js.map +1 -1
- package/dist/vector/IHnswService.d.ts +10 -1
- package/dist/vector/IHnswService.d.ts.map +1 -1
- package/dist/vector/IHnswService.js.map +1 -1
- package/dist/vector/NodeHnswService.d.ts +16 -0
- package/dist/vector/NodeHnswService.d.ts.map +1 -1
- package/dist/vector/NodeHnswService.js +84 -5
- package/dist/vector/NodeHnswService.js.map +1 -1
- package/dist/vector/createHnswService.d.ts +1 -1
- package/dist/vector/createHnswService.js +1 -1
- package/dist/vector/index.d.ts +1 -1
- package/dist/vector/index.js +1 -1
- package/package.json +157 -157
- package/src/access/PermissionService.ts +635 -635
- package/src/aggregation/AggregationService.ts +389 -389
- package/src/ai-sdk/PDWVectorStore.ts +715 -715
- package/src/ai-sdk/index.ts +65 -65
- package/src/ai-sdk/tools.ts +460 -460
- package/src/ai-sdk/types.ts +404 -404
- package/src/batch/BatchManager.ts +597 -597
- package/src/batch/BatchingService.ts +429 -429
- package/src/batch/MemoryProcessingCache.ts +492 -492
- package/src/batch/index.ts +30 -30
- package/src/browser.ts +200 -200
- package/src/client/ClientMemoryManager.ts +987 -987
- package/src/client/PersonalDataWallet.ts +345 -345
- package/src/client/SimplePDWClient.ts +1289 -1222
- package/src/client/factory.ts +154 -154
- package/src/client/namespaces/AnalyticsNamespace.ts +377 -377
- package/src/client/namespaces/BatchNamespace.ts +356 -356
- package/src/client/namespaces/CacheNamespace.ts +123 -123
- package/src/client/namespaces/CapabilityNamespace.ts +217 -217
- package/src/client/namespaces/ClassifyNamespace.ts +169 -169
- package/src/client/namespaces/ContextNamespace.ts +297 -297
- package/src/client/namespaces/EmbeddingsNamespace.ts +99 -99
- package/src/client/namespaces/EncryptionNamespace.ts +221 -221
- package/src/client/namespaces/GraphNamespace.ts +468 -468
- package/src/client/namespaces/IndexNamespace.ts +361 -361
- package/src/client/namespaces/MemoryNamespace.ts +1422 -1135
- package/src/client/namespaces/PermissionsNamespace.ts +254 -254
- package/src/client/namespaces/PipelineNamespace.ts +220 -220
- package/src/client/namespaces/SearchNamespace.ts +1049 -1049
- package/src/client/namespaces/StorageNamespace.ts +458 -458
- package/src/client/namespaces/TxNamespace.ts +260 -260
- package/src/client/namespaces/WalletNamespace.ts +243 -243
- package/src/client/namespaces/consolidated/AINamespace.ts +449 -449
- package/src/client/namespaces/consolidated/BlockchainNamespace.ts +607 -546
- package/src/client/namespaces/consolidated/SecurityNamespace.ts +648 -648
- package/src/client/namespaces/consolidated/StorageNamespace.ts +1141 -497
- package/src/client/namespaces/consolidated/index.ts +39 -39
- package/src/client/signers/KeypairSigner.ts +108 -108
- package/src/client/signers/UnifiedSigner.ts +110 -110
- package/src/client/signers/WalletAdapterSigner.ts +159 -159
- package/src/client/signers/index.ts +26 -26
- package/src/config/ConfigurationHelper.ts +412 -412
- package/src/config/defaults.ts +51 -51
- package/src/config/index.ts +8 -8
- package/src/config/validation.ts +70 -70
- package/src/core/index.ts +14 -14
- package/src/core/interfaces/IService.ts +307 -307
- package/src/core/interfaces/index.ts +8 -8
- package/src/core/types/capability.ts +297 -297
- package/src/core/types/index.ts +870 -870
- package/src/core/types/wallet.ts +270 -270
- package/src/core/types.ts +9 -9
- package/src/core/wallet.ts +222 -222
- package/src/embedding/index.ts +19 -19
- package/src/embedding/types.ts +357 -357
- package/src/errors/index.ts +602 -602
- package/src/errors/recovery.ts +461 -461
- package/src/errors/validation.ts +567 -567
- package/src/generated/pdw/capability.ts +319 -319
- package/src/graph/GraphService.ts +887 -887
- package/src/graph/KnowledgeGraphManager.ts +728 -728
- package/src/graph/index.ts +25 -25
- package/src/index.ts +498 -474
- package/src/infrastructure/index.ts +22 -22
- package/src/infrastructure/seal/EncryptionService.ts +628 -603
- package/src/infrastructure/seal/SealService.ts +613 -615
- package/src/infrastructure/seal/index.ts +9 -9
- package/src/infrastructure/sui/BlockchainManager.ts +627 -627
- package/src/infrastructure/sui/SuiService.ts +888 -888
- package/src/infrastructure/sui/index.ts +9 -9
- package/src/infrastructure/walrus/StorageManager.ts +604 -604
- package/src/infrastructure/walrus/WalrusStorageService.ts +612 -612
- package/src/infrastructure/walrus/index.ts +9 -9
- package/src/langchain/PDWEmbeddings.ts +145 -145
- package/src/langchain/PDWVectorStore.ts +456 -456
- package/src/langchain/createPDWRAG.ts +303 -303
- package/src/langchain/index.ts +47 -47
- package/src/permissions/ConsentRepository.browser.ts +249 -249
- package/src/permissions/ConsentRepository.ts +364 -364
- package/src/pipeline/MemoryPipeline.ts +862 -862
- package/src/pipeline/PipelineManager.ts +683 -683
- package/src/pipeline/index.ts +26 -26
- package/src/retrieval/AdvancedSearchService.ts +629 -629
- package/src/retrieval/MemoryAnalyticsService.ts +711 -711
- package/src/retrieval/MemoryDecryptionPipeline.ts +825 -824
- package/src/retrieval/MemoryRetrievalService.ts +904 -830
- package/src/retrieval/index.ts +42 -42
- package/src/services/BatchService.ts +352 -352
- package/src/services/CapabilityService.ts +464 -448
- package/src/services/ClassifierService.ts +465 -465
- package/src/services/CrossContextPermissionService.ts +486 -484
- package/src/services/EmbeddingService.ts +771 -706
- package/src/services/EncryptionService.ts +712 -711
- package/src/services/GeminiAIService.ts +753 -753
- package/src/services/IndexManager.ts +977 -1004
- package/src/services/MemoryIndexService.ts +1003 -1003
- package/src/services/MemoryService.ts +369 -369
- package/src/services/QueryService.ts +890 -890
- package/src/services/StorageService.ts +1182 -1111
- package/src/services/TransactionService.ts +838 -790
- package/src/services/VectorService.ts +462 -462
- package/src/services/ViewService.ts +484 -484
- package/src/services/index.ts +25 -25
- package/src/services/storage/BlobAttributesManager.ts +333 -333
- package/src/services/storage/KnowledgeGraphManager.ts +425 -425
- package/src/services/storage/MemorySearchManager.ts +387 -387
- package/src/services/storage/QuiltBatchManager.ts +1130 -660
- package/src/services/storage/WalrusMetadataManager.ts +268 -268
- package/src/services/storage/WalrusStorageManager.ts +287 -287
- package/src/services/storage/index.ts +57 -52
- package/src/types/index.ts +13 -13
- package/src/utils/LRUCache.ts +378 -0
- package/src/utils/index.ts +76 -68
- package/src/utils/memoryIndexOnChain.ts +507 -0
- package/src/utils/rebuildIndex.ts +290 -290
- package/src/utils/rebuildIndexNode.ts +771 -424
- package/src/vector/BrowserHnswIndexService.ts +758 -758
- package/src/vector/HnswWasmService.ts +731 -679
- package/src/vector/IHnswService.ts +233 -224
- package/src/vector/NodeHnswService.ts +833 -735
- package/src/vector/VectorManager.ts +478 -478
- package/src/vector/createHnswService.ts +135 -135
- package/src/vector/index.ts +56 -56
- package/src/wallet/ContextWalletService.ts +656 -656
- package/src/wallet/MainWalletService.ts +317 -317
|
@@ -1,466 +1,466 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ClassifierService - Content Classification and Filtering
|
|
3
|
-
*
|
|
4
|
-
* Determines if content should be saved as memory using pattern matching
|
|
5
|
-
* and AI classification. Provides category classification for content organization.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { EmbeddingService } from './EmbeddingService';
|
|
9
|
-
import type { ClientWithCoreApi, PDWConfig } from '../types';
|
|
10
|
-
|
|
11
|
-
export interface ClassificationResult {
|
|
12
|
-
shouldSave: boolean;
|
|
13
|
-
confidence: number;
|
|
14
|
-
category: string;
|
|
15
|
-
reasoning: string;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface ClassificationOptions {
|
|
19
|
-
useAI?: boolean;
|
|
20
|
-
confidenceThreshold?: number;
|
|
21
|
-
categories?: string[];
|
|
22
|
-
patterns?: RegExp[];
|
|
23
|
-
/**
|
|
24
|
-
* If true, only explicit memory commands trigger save (e.g., "remember that", "store in memory")
|
|
25
|
-
* Default: true - to give users control over what gets saved
|
|
26
|
-
*/
|
|
27
|
-
explicitOnly?: boolean;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface PatternAnalysisResult {
|
|
31
|
-
patterns: string[];
|
|
32
|
-
categories: string[];
|
|
33
|
-
matches: Array<{
|
|
34
|
-
pattern: string;
|
|
35
|
-
match: string;
|
|
36
|
-
category: string;
|
|
37
|
-
confidence: number;
|
|
38
|
-
}>;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Content classification service for determining memory worthiness
|
|
43
|
-
*/
|
|
44
|
-
export class ClassifierService {
|
|
45
|
-
private embeddingService?: EmbeddingService;
|
|
46
|
-
private aiApiKey?: string;
|
|
47
|
-
|
|
48
|
-
// Explicit memory command patterns - only these trigger by default
|
|
49
|
-
private readonly explicitPatterns: RegExp[] = [
|
|
50
|
-
// Explicit memory requests
|
|
51
|
-
/remember that ([^.!?]+)/i,
|
|
52
|
-
/remember:?\s*([^.!?]+)/i,
|
|
53
|
-
/don't forget that ([^.!?]+)/i,
|
|
54
|
-
/don't forget:?\s*([^.!?]+)/i,
|
|
55
|
-
/please remember ([^.!?]+)/i,
|
|
56
|
-
/store (?:this )?(?:in|to) memory:?\s*([^.!?]*)/i,
|
|
57
|
-
/save (?:this )?(?:in|to) memory:?\s*([^.!?]*)/i,
|
|
58
|
-
/add (?:this )?to (?:my )?memory:?\s*([^.!?]*)/i,
|
|
59
|
-
/keep in mind:?\s*([^.!?]+)/i,
|
|
60
|
-
/note that ([^.!?]+)/i,
|
|
61
|
-
];
|
|
62
|
-
|
|
63
|
-
// Full regex patterns for detecting factual statements (used when explicitOnly=false)
|
|
64
|
-
private readonly factPatterns: RegExp[] = [
|
|
65
|
-
// Include explicit patterns first
|
|
66
|
-
...this.explicitPatterns,
|
|
67
|
-
|
|
68
|
-
// Personal information
|
|
69
|
-
/my name is ([a-zA-Z\s]+)/i,
|
|
70
|
-
/my email is ([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/i,
|
|
71
|
-
/i live in ([a-zA-Z\s,]+)/i,
|
|
72
|
-
/i work at ([a-zA-Z\s,&]+)/i,
|
|
73
|
-
/i am from ([a-zA-Z\s,]+)/i,
|
|
74
|
-
/i was born in ([a-zA-Z0-9\s,]+)/i,
|
|
75
|
-
/my birthday is ([a-zA-Z0-9\s,]+)/i,
|
|
76
|
-
/my phone (?:number|#) is ([0-9+\-\s()]+)/i,
|
|
77
|
-
/my address is ([a-zA-Z0-9\s,]+)/i,
|
|
78
|
-
|
|
79
|
-
// Preferences and likes/dislikes
|
|
80
|
-
/i love ([^.!?]+)/i,
|
|
81
|
-
/i like ([^.!?]+)/i,
|
|
82
|
-
/i enjoy ([^.!?]+)/i,
|
|
83
|
-
/i prefer ([^.!?]+)/i,
|
|
84
|
-
/i hate ([^.!?]+)/i,
|
|
85
|
-
/i dislike ([^.!?]+)/i,
|
|
86
|
-
/i don't like ([^.!?]+)/i,
|
|
87
|
-
/my favorite ([^.!?]+) is ([^.!?]+)/i,
|
|
88
|
-
/my favourite ([^.!?]+) is ([^.!?]+)/i,
|
|
89
|
-
|
|
90
|
-
// Personal facts
|
|
91
|
-
/i am ([^.!?]+)/i,
|
|
92
|
-
/i have ([^.!?]+)/i,
|
|
93
|
-
/i own ([^.!?]+)/i,
|
|
94
|
-
/i studied ([^.!?]+)/i,
|
|
95
|
-
/i graduated from ([^.!?]+)/i,
|
|
96
|
-
];
|
|
97
|
-
|
|
98
|
-
// Map of regex patterns to categories (adapted from backend)
|
|
99
|
-
private readonly categoryMap: Record<string, string> = {
|
|
100
|
-
// Personal information
|
|
101
|
-
'my name is': 'personal_info',
|
|
102
|
-
'my email is': 'contact',
|
|
103
|
-
'i live in': 'location',
|
|
104
|
-
'i work at': 'career',
|
|
105
|
-
'i am from': 'background',
|
|
106
|
-
'i was born': 'background',
|
|
107
|
-
'my birthday': 'personal_info',
|
|
108
|
-
'my phone': 'contact',
|
|
109
|
-
'my address': 'contact',
|
|
110
|
-
|
|
111
|
-
// Preferences
|
|
112
|
-
'i love': 'preference',
|
|
113
|
-
'i like': 'preference',
|
|
114
|
-
'i enjoy': 'preference',
|
|
115
|
-
'i prefer': 'preference',
|
|
116
|
-
'i hate': 'preference',
|
|
117
|
-
'i dislike': 'preference',
|
|
118
|
-
"i don't like": 'preference',
|
|
119
|
-
'my favorite': 'preference',
|
|
120
|
-
'my favourite': 'preference',
|
|
121
|
-
|
|
122
|
-
// Explicit memory requests
|
|
123
|
-
'remember that': 'custom',
|
|
124
|
-
'remember': 'custom',
|
|
125
|
-
"don't forget": 'custom',
|
|
126
|
-
'please remember': 'custom',
|
|
127
|
-
'store': 'custom',
|
|
128
|
-
'save': 'custom',
|
|
129
|
-
'add': 'custom',
|
|
130
|
-
'keep in mind': 'custom',
|
|
131
|
-
'note that': 'custom',
|
|
132
|
-
|
|
133
|
-
// Personal facts
|
|
134
|
-
'i am': 'personal_info',
|
|
135
|
-
'i have': 'personal_info',
|
|
136
|
-
'i own': 'personal_info',
|
|
137
|
-
'i studied': 'education',
|
|
138
|
-
'i graduated': 'education',
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
constructor(
|
|
142
|
-
private client?: ClientWithCoreApi,
|
|
143
|
-
private config?: PDWConfig,
|
|
144
|
-
embeddingService?: EmbeddingService,
|
|
145
|
-
aiApiKey?: string
|
|
146
|
-
) {
|
|
147
|
-
this.embeddingService = embeddingService;
|
|
148
|
-
this.aiApiKey = aiApiKey;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Determine if a message contains information worth saving
|
|
153
|
-
* @param message User message to classify
|
|
154
|
-
* @param options Classification options
|
|
155
|
-
* @returns Classification result
|
|
156
|
-
*/
|
|
157
|
-
async shouldSaveMemory(message: string, options: ClassificationOptions = {}): Promise<ClassificationResult> {
|
|
158
|
-
try {
|
|
159
|
-
// Input validation
|
|
160
|
-
if (!message || typeof message !== 'string' || message.trim().length === 0) {
|
|
161
|
-
return {
|
|
162
|
-
shouldSave: false,
|
|
163
|
-
confidence: 0,
|
|
164
|
-
category: 'general',
|
|
165
|
-
reasoning: 'Empty or invalid message'
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const {
|
|
170
|
-
useAI = true,
|
|
171
|
-
confidenceThreshold = 0.7,
|
|
172
|
-
explicitOnly = true, // Default to explicit-only mode for user control
|
|
173
|
-
patterns = explicitOnly ? this.explicitPatterns : this.factPatterns
|
|
174
|
-
} = options;
|
|
175
|
-
|
|
176
|
-
// Step 1: Check for obvious patterns using regex
|
|
177
|
-
const patternResult = this.matchPatterns(message, patterns);
|
|
178
|
-
if (patternResult.matched && patternResult.confidence >= confidenceThreshold) {
|
|
179
|
-
return {
|
|
180
|
-
shouldSave: true,
|
|
181
|
-
confidence: patternResult.confidence,
|
|
182
|
-
category: patternResult.category,
|
|
183
|
-
reasoning: `Matched pattern: ${patternResult.pattern}`
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Step 2: Use AI for more complex classification (if enabled and available)
|
|
188
|
-
if (useAI && (this.embeddingService || this.aiApiKey)) {
|
|
189
|
-
try {
|
|
190
|
-
const aiResult = await this.classifyWithAI(message, options);
|
|
191
|
-
if (aiResult.confidence >= confidenceThreshold) {
|
|
192
|
-
return aiResult;
|
|
193
|
-
}
|
|
194
|
-
} catch (aiError) {
|
|
195
|
-
if (process.env.NODE_ENV === 'development') {
|
|
196
|
-
const errorMessage = aiError instanceof Error ? aiError.message : String(aiError);
|
|
197
|
-
console.warn('AI classification failed, falling back to pattern-only result:', errorMessage);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Step 3: Return pattern result even if below threshold
|
|
203
|
-
if (patternResult.matched) {
|
|
204
|
-
return {
|
|
205
|
-
shouldSave: patternResult.confidence >= confidenceThreshold,
|
|
206
|
-
confidence: patternResult.confidence,
|
|
207
|
-
category: patternResult.category,
|
|
208
|
-
reasoning: `Pattern match (confidence: ${patternResult.confidence})`
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// Step 4: Default to not saving
|
|
213
|
-
return {
|
|
214
|
-
shouldSave: false,
|
|
215
|
-
confidence: 0.1,
|
|
216
|
-
category: 'general',
|
|
217
|
-
reasoning: 'No significant patterns detected and AI classification unavailable or inconclusive'
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
} catch (error) {
|
|
221
|
-
if (process.env.NODE_ENV === 'development') {
|
|
222
|
-
console.error('Error in classification:', error);
|
|
223
|
-
}
|
|
224
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
225
|
-
return {
|
|
226
|
-
shouldSave: false,
|
|
227
|
-
confidence: 0,
|
|
228
|
-
category: 'error',
|
|
229
|
-
reasoning: `Classification error: ${errorMessage}`
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Classify content into categories
|
|
236
|
-
* @param content Content to classify
|
|
237
|
-
* @param options Classification options
|
|
238
|
-
* @returns Category string
|
|
239
|
-
*/
|
|
240
|
-
async classifyContent(content: string, options: ClassificationOptions = {}): Promise<string> {
|
|
241
|
-
const result = await this.shouldSaveMemory(content, options);
|
|
242
|
-
return result.category;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Analyze patterns in text
|
|
247
|
-
* @param text Text to analyze
|
|
248
|
-
* @param patterns Optional custom patterns
|
|
249
|
-
* @returns Pattern analysis result
|
|
250
|
-
*/
|
|
251
|
-
async analyzePatterns(text: string, patterns: RegExp[] = this.factPatterns): Promise<PatternAnalysisResult> {
|
|
252
|
-
const matches: Array<{
|
|
253
|
-
pattern: string;
|
|
254
|
-
match: string;
|
|
255
|
-
category: string;
|
|
256
|
-
confidence: number;
|
|
257
|
-
}> = [];
|
|
258
|
-
const foundPatterns: string[] = [];
|
|
259
|
-
const foundCategories: string[] = [];
|
|
260
|
-
|
|
261
|
-
for (const pattern of patterns) {
|
|
262
|
-
const match = text.match(pattern);
|
|
263
|
-
if (match) {
|
|
264
|
-
const patternStr = pattern.toString();
|
|
265
|
-
const category = this.getCategoryForPattern(patternStr);
|
|
266
|
-
const confidence = this.calculatePatternConfidence(pattern, match[0]);
|
|
267
|
-
|
|
268
|
-
matches.push({
|
|
269
|
-
pattern: patternStr,
|
|
270
|
-
match: match[0],
|
|
271
|
-
category,
|
|
272
|
-
confidence
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
if (!foundPatterns.includes(patternStr)) {
|
|
276
|
-
foundPatterns.push(patternStr);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
if (!foundCategories.includes(category)) {
|
|
280
|
-
foundCategories.push(category);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
return {
|
|
286
|
-
patterns: foundPatterns,
|
|
287
|
-
categories: foundCategories,
|
|
288
|
-
matches
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Match patterns against text
|
|
294
|
-
* @param text Text to match
|
|
295
|
-
* @param patterns Patterns to use
|
|
296
|
-
* @returns Match result
|
|
297
|
-
*/
|
|
298
|
-
private matchPatterns(text: string, patterns: RegExp[]): {
|
|
299
|
-
matched: boolean;
|
|
300
|
-
pattern?: string;
|
|
301
|
-
category: string;
|
|
302
|
-
confidence: number;
|
|
303
|
-
} {
|
|
304
|
-
// Handle null/undefined text input
|
|
305
|
-
if (!text || typeof text !== 'string') {
|
|
306
|
-
return {
|
|
307
|
-
matched: false,
|
|
308
|
-
category: 'general',
|
|
309
|
-
confidence: 0.1
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
for (const pattern of patterns) {
|
|
314
|
-
try {
|
|
315
|
-
const match = text.match(pattern);
|
|
316
|
-
if (match) {
|
|
317
|
-
const patternStr = pattern.toString();
|
|
318
|
-
const category = this.getCategoryForPattern(patternStr);
|
|
319
|
-
const confidence = this.calculatePatternConfidence(pattern, match[0]);
|
|
320
|
-
|
|
321
|
-
return {
|
|
322
|
-
matched: true,
|
|
323
|
-
pattern: patternStr,
|
|
324
|
-
category,
|
|
325
|
-
confidence
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
} catch (error) {
|
|
329
|
-
// Skip invalid patterns
|
|
330
|
-
if (process.env.NODE_ENV === 'development') {
|
|
331
|
-
console.warn(`Pattern matching error for pattern ${pattern}:`, error);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
return {
|
|
337
|
-
matched: false,
|
|
338
|
-
category: 'general',
|
|
339
|
-
confidence: 0.1
|
|
340
|
-
};
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
/**
|
|
344
|
-
* Get category for a regex pattern
|
|
345
|
-
* @param patternString String representation of the regex
|
|
346
|
-
* @returns Category string
|
|
347
|
-
*/
|
|
348
|
-
private getCategoryForPattern(patternString: string): string {
|
|
349
|
-
// Extract the key part of the pattern for lookup
|
|
350
|
-
const patternCore = patternString.toLowerCase()
|
|
351
|
-
.replace(/^\/|\/[a-z]*$/g, '') // Remove regex delimiters
|
|
352
|
-
.replace(/\([^)]*\)/g, '') // Remove capture groups
|
|
353
|
-
.replace(/\[[^\]]*\]/g, '') // Remove character classes
|
|
354
|
-
.replace(/[+*?{}|\\^$]/g, '') // Remove regex metacharacters
|
|
355
|
-
.trim();
|
|
356
|
-
|
|
357
|
-
// Find the best matching category
|
|
358
|
-
for (const [key, category] of Object.entries(this.categoryMap)) {
|
|
359
|
-
if (patternCore.includes(key.toLowerCase()) || key.toLowerCase().includes(patternCore)) {
|
|
360
|
-
return category;
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
return 'custom';
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
/**
|
|
368
|
-
* Calculate confidence score for pattern match
|
|
369
|
-
* @param pattern The regex pattern
|
|
370
|
-
* @param match The matched text
|
|
371
|
-
* @returns Confidence score (0-1)
|
|
372
|
-
*/
|
|
373
|
-
private calculatePatternConfidence(pattern: RegExp, match: string): number {
|
|
374
|
-
// Base confidence for any pattern match
|
|
375
|
-
let confidence = 0.85;
|
|
376
|
-
|
|
377
|
-
// Highest confidence for explicit memory commands
|
|
378
|
-
if (pattern.source.includes('remember') ||
|
|
379
|
-
pattern.source.includes('forget') ||
|
|
380
|
-
pattern.source.includes('store') ||
|
|
381
|
-
pattern.source.includes('save') ||
|
|
382
|
-
pattern.source.includes('note that') ||
|
|
383
|
-
pattern.source.includes('keep in mind')) {
|
|
384
|
-
confidence = 0.98;
|
|
385
|
-
}
|
|
386
|
-
// High confidence for specific personal info
|
|
387
|
-
else if (pattern.source.includes('my name is')) {
|
|
388
|
-
confidence = 0.95;
|
|
389
|
-
}
|
|
390
|
-
// Higher confidence for specific patterns (email, phone, etc.)
|
|
391
|
-
else if (pattern.source.includes('@') || pattern.source.includes('[0-9]')) {
|
|
392
|
-
confidence = 0.92;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
// Adjust based on match length (longer matches tend to be more specific)
|
|
396
|
-
if (match.length > 30) {
|
|
397
|
-
confidence = Math.min(confidence + 0.02, 1.0);
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
return confidence;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
/**
|
|
404
|
-
* Use AI for classification (fallback/enhancement)
|
|
405
|
-
* @param message Message to classify
|
|
406
|
-
* @param options Classification options
|
|
407
|
-
* @returns Classification result
|
|
408
|
-
*/
|
|
409
|
-
private async classifyWithAI(message: string, options: ClassificationOptions): Promise<ClassificationResult> {
|
|
410
|
-
// For now, this is a placeholder that could integrate with:
|
|
411
|
-
// 1. EmbeddingService for semantic similarity
|
|
412
|
-
// 2. External AI APIs (OpenAI, Gemini, etc.)
|
|
413
|
-
// 3. Local AI models
|
|
414
|
-
|
|
415
|
-
if (this.embeddingService) {
|
|
416
|
-
// Use embedding-based classification
|
|
417
|
-
return await this.classifyWithEmbeddings(message, options);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
// If no AI service is available, return low-confidence result
|
|
421
|
-
return {
|
|
422
|
-
shouldSave: false,
|
|
423
|
-
confidence: 0.3,
|
|
424
|
-
category: 'general',
|
|
425
|
-
reasoning: 'AI classification not available'
|
|
426
|
-
};
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
/**
|
|
430
|
-
* Classify using embeddings (semantic similarity approach)
|
|
431
|
-
* @param message Message to classify
|
|
432
|
-
* @param options Classification options
|
|
433
|
-
* @returns Classification result
|
|
434
|
-
*/
|
|
435
|
-
private async classifyWithEmbeddings(message: string, options: ClassificationOptions): Promise<ClassificationResult> {
|
|
436
|
-
if (!this.embeddingService) {
|
|
437
|
-
throw new Error('EmbeddingService not available');
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
try {
|
|
441
|
-
// Generate embedding for the message
|
|
442
|
-
const result = await this.embeddingService.embedText({ text: message, type: 'content' });
|
|
443
|
-
|
|
444
|
-
// For now, use simple heuristics based on common memory-worthy patterns
|
|
445
|
-
// In a full implementation, this would compare against a database of
|
|
446
|
-
// known memory-worthy content embeddings
|
|
447
|
-
|
|
448
|
-
const messageWords = message.toLowerCase().split(/\s+/);
|
|
449
|
-
const memoryKeywords = ['i', 'my', 'me', 'like', 'love', 'hate', 'prefer', 'enjoy', 'am', 'work', 'live'];
|
|
450
|
-
const memoryWordCount = messageWords.filter(word => memoryKeywords.includes(word)).length;
|
|
451
|
-
|
|
452
|
-
const confidence = Math.min(memoryWordCount * 0.15 + 0.1, 0.85);
|
|
453
|
-
const shouldSave = confidence > 0.6;
|
|
454
|
-
|
|
455
|
-
return {
|
|
456
|
-
shouldSave,
|
|
457
|
-
confidence,
|
|
458
|
-
category: shouldSave ? 'personal_info' : 'general',
|
|
459
|
-
reasoning: `Embedding-based classification (${memoryWordCount} memory keywords found)`
|
|
460
|
-
};
|
|
461
|
-
} catch (error) {
|
|
462
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
463
|
-
throw new Error(`Embedding classification failed: ${errorMessage}`);
|
|
464
|
-
}
|
|
465
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* ClassifierService - Content Classification and Filtering
|
|
3
|
+
*
|
|
4
|
+
* Determines if content should be saved as memory using pattern matching
|
|
5
|
+
* and AI classification. Provides category classification for content organization.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { EmbeddingService } from './EmbeddingService';
|
|
9
|
+
import type { ClientWithCoreApi, PDWConfig } from '../types';
|
|
10
|
+
|
|
11
|
+
export interface ClassificationResult {
|
|
12
|
+
shouldSave: boolean;
|
|
13
|
+
confidence: number;
|
|
14
|
+
category: string;
|
|
15
|
+
reasoning: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ClassificationOptions {
|
|
19
|
+
useAI?: boolean;
|
|
20
|
+
confidenceThreshold?: number;
|
|
21
|
+
categories?: string[];
|
|
22
|
+
patterns?: RegExp[];
|
|
23
|
+
/**
|
|
24
|
+
* If true, only explicit memory commands trigger save (e.g., "remember that", "store in memory")
|
|
25
|
+
* Default: true - to give users control over what gets saved
|
|
26
|
+
*/
|
|
27
|
+
explicitOnly?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface PatternAnalysisResult {
|
|
31
|
+
patterns: string[];
|
|
32
|
+
categories: string[];
|
|
33
|
+
matches: Array<{
|
|
34
|
+
pattern: string;
|
|
35
|
+
match: string;
|
|
36
|
+
category: string;
|
|
37
|
+
confidence: number;
|
|
38
|
+
}>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Content classification service for determining memory worthiness
|
|
43
|
+
*/
|
|
44
|
+
export class ClassifierService {
|
|
45
|
+
private embeddingService?: EmbeddingService;
|
|
46
|
+
private aiApiKey?: string;
|
|
47
|
+
|
|
48
|
+
// Explicit memory command patterns - only these trigger by default
|
|
49
|
+
private readonly explicitPatterns: RegExp[] = [
|
|
50
|
+
// Explicit memory requests
|
|
51
|
+
/remember that ([^.!?]+)/i,
|
|
52
|
+
/remember:?\s*([^.!?]+)/i,
|
|
53
|
+
/don't forget that ([^.!?]+)/i,
|
|
54
|
+
/don't forget:?\s*([^.!?]+)/i,
|
|
55
|
+
/please remember ([^.!?]+)/i,
|
|
56
|
+
/store (?:this )?(?:in|to) memory:?\s*([^.!?]*)/i,
|
|
57
|
+
/save (?:this )?(?:in|to) memory:?\s*([^.!?]*)/i,
|
|
58
|
+
/add (?:this )?to (?:my )?memory:?\s*([^.!?]*)/i,
|
|
59
|
+
/keep in mind:?\s*([^.!?]+)/i,
|
|
60
|
+
/note that ([^.!?]+)/i,
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
// Full regex patterns for detecting factual statements (used when explicitOnly=false)
|
|
64
|
+
private readonly factPatterns: RegExp[] = [
|
|
65
|
+
// Include explicit patterns first
|
|
66
|
+
...this.explicitPatterns,
|
|
67
|
+
|
|
68
|
+
// Personal information
|
|
69
|
+
/my name is ([a-zA-Z\s]+)/i,
|
|
70
|
+
/my email is ([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/i,
|
|
71
|
+
/i live in ([a-zA-Z\s,]+)/i,
|
|
72
|
+
/i work at ([a-zA-Z\s,&]+)/i,
|
|
73
|
+
/i am from ([a-zA-Z\s,]+)/i,
|
|
74
|
+
/i was born in ([a-zA-Z0-9\s,]+)/i,
|
|
75
|
+
/my birthday is ([a-zA-Z0-9\s,]+)/i,
|
|
76
|
+
/my phone (?:number|#) is ([0-9+\-\s()]+)/i,
|
|
77
|
+
/my address is ([a-zA-Z0-9\s,]+)/i,
|
|
78
|
+
|
|
79
|
+
// Preferences and likes/dislikes
|
|
80
|
+
/i love ([^.!?]+)/i,
|
|
81
|
+
/i like ([^.!?]+)/i,
|
|
82
|
+
/i enjoy ([^.!?]+)/i,
|
|
83
|
+
/i prefer ([^.!?]+)/i,
|
|
84
|
+
/i hate ([^.!?]+)/i,
|
|
85
|
+
/i dislike ([^.!?]+)/i,
|
|
86
|
+
/i don't like ([^.!?]+)/i,
|
|
87
|
+
/my favorite ([^.!?]+) is ([^.!?]+)/i,
|
|
88
|
+
/my favourite ([^.!?]+) is ([^.!?]+)/i,
|
|
89
|
+
|
|
90
|
+
// Personal facts
|
|
91
|
+
/i am ([^.!?]+)/i,
|
|
92
|
+
/i have ([^.!?]+)/i,
|
|
93
|
+
/i own ([^.!?]+)/i,
|
|
94
|
+
/i studied ([^.!?]+)/i,
|
|
95
|
+
/i graduated from ([^.!?]+)/i,
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
// Map of regex patterns to categories (adapted from backend)
|
|
99
|
+
private readonly categoryMap: Record<string, string> = {
|
|
100
|
+
// Personal information
|
|
101
|
+
'my name is': 'personal_info',
|
|
102
|
+
'my email is': 'contact',
|
|
103
|
+
'i live in': 'location',
|
|
104
|
+
'i work at': 'career',
|
|
105
|
+
'i am from': 'background',
|
|
106
|
+
'i was born': 'background',
|
|
107
|
+
'my birthday': 'personal_info',
|
|
108
|
+
'my phone': 'contact',
|
|
109
|
+
'my address': 'contact',
|
|
110
|
+
|
|
111
|
+
// Preferences
|
|
112
|
+
'i love': 'preference',
|
|
113
|
+
'i like': 'preference',
|
|
114
|
+
'i enjoy': 'preference',
|
|
115
|
+
'i prefer': 'preference',
|
|
116
|
+
'i hate': 'preference',
|
|
117
|
+
'i dislike': 'preference',
|
|
118
|
+
"i don't like": 'preference',
|
|
119
|
+
'my favorite': 'preference',
|
|
120
|
+
'my favourite': 'preference',
|
|
121
|
+
|
|
122
|
+
// Explicit memory requests
|
|
123
|
+
'remember that': 'custom',
|
|
124
|
+
'remember': 'custom',
|
|
125
|
+
"don't forget": 'custom',
|
|
126
|
+
'please remember': 'custom',
|
|
127
|
+
'store': 'custom',
|
|
128
|
+
'save': 'custom',
|
|
129
|
+
'add': 'custom',
|
|
130
|
+
'keep in mind': 'custom',
|
|
131
|
+
'note that': 'custom',
|
|
132
|
+
|
|
133
|
+
// Personal facts
|
|
134
|
+
'i am': 'personal_info',
|
|
135
|
+
'i have': 'personal_info',
|
|
136
|
+
'i own': 'personal_info',
|
|
137
|
+
'i studied': 'education',
|
|
138
|
+
'i graduated': 'education',
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
constructor(
|
|
142
|
+
private client?: ClientWithCoreApi,
|
|
143
|
+
private config?: PDWConfig,
|
|
144
|
+
embeddingService?: EmbeddingService,
|
|
145
|
+
aiApiKey?: string
|
|
146
|
+
) {
|
|
147
|
+
this.embeddingService = embeddingService;
|
|
148
|
+
this.aiApiKey = aiApiKey;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Determine if a message contains information worth saving
|
|
153
|
+
* @param message User message to classify
|
|
154
|
+
* @param options Classification options
|
|
155
|
+
* @returns Classification result
|
|
156
|
+
*/
|
|
157
|
+
async shouldSaveMemory(message: string, options: ClassificationOptions = {}): Promise<ClassificationResult> {
|
|
158
|
+
try {
|
|
159
|
+
// Input validation
|
|
160
|
+
if (!message || typeof message !== 'string' || message.trim().length === 0) {
|
|
161
|
+
return {
|
|
162
|
+
shouldSave: false,
|
|
163
|
+
confidence: 0,
|
|
164
|
+
category: 'general',
|
|
165
|
+
reasoning: 'Empty or invalid message'
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const {
|
|
170
|
+
useAI = true,
|
|
171
|
+
confidenceThreshold = 0.7,
|
|
172
|
+
explicitOnly = true, // Default to explicit-only mode for user control
|
|
173
|
+
patterns = explicitOnly ? this.explicitPatterns : this.factPatterns
|
|
174
|
+
} = options;
|
|
175
|
+
|
|
176
|
+
// Step 1: Check for obvious patterns using regex
|
|
177
|
+
const patternResult = this.matchPatterns(message, patterns);
|
|
178
|
+
if (patternResult.matched && patternResult.confidence >= confidenceThreshold) {
|
|
179
|
+
return {
|
|
180
|
+
shouldSave: true,
|
|
181
|
+
confidence: patternResult.confidence,
|
|
182
|
+
category: patternResult.category,
|
|
183
|
+
reasoning: `Matched pattern: ${patternResult.pattern}`
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Step 2: Use AI for more complex classification (if enabled and available)
|
|
188
|
+
if (useAI && (this.embeddingService || this.aiApiKey)) {
|
|
189
|
+
try {
|
|
190
|
+
const aiResult = await this.classifyWithAI(message, options);
|
|
191
|
+
if (aiResult.confidence >= confidenceThreshold) {
|
|
192
|
+
return aiResult;
|
|
193
|
+
}
|
|
194
|
+
} catch (aiError) {
|
|
195
|
+
if (process.env.NODE_ENV === 'development') {
|
|
196
|
+
const errorMessage = aiError instanceof Error ? aiError.message : String(aiError);
|
|
197
|
+
console.warn('AI classification failed, falling back to pattern-only result:', errorMessage);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Step 3: Return pattern result even if below threshold
|
|
203
|
+
if (patternResult.matched) {
|
|
204
|
+
return {
|
|
205
|
+
shouldSave: patternResult.confidence >= confidenceThreshold,
|
|
206
|
+
confidence: patternResult.confidence,
|
|
207
|
+
category: patternResult.category,
|
|
208
|
+
reasoning: `Pattern match (confidence: ${patternResult.confidence})`
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Step 4: Default to not saving
|
|
213
|
+
return {
|
|
214
|
+
shouldSave: false,
|
|
215
|
+
confidence: 0.1,
|
|
216
|
+
category: 'general',
|
|
217
|
+
reasoning: 'No significant patterns detected and AI classification unavailable or inconclusive'
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
} catch (error) {
|
|
221
|
+
if (process.env.NODE_ENV === 'development') {
|
|
222
|
+
console.error('Error in classification:', error);
|
|
223
|
+
}
|
|
224
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
225
|
+
return {
|
|
226
|
+
shouldSave: false,
|
|
227
|
+
confidence: 0,
|
|
228
|
+
category: 'error',
|
|
229
|
+
reasoning: `Classification error: ${errorMessage}`
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Classify content into categories
|
|
236
|
+
* @param content Content to classify
|
|
237
|
+
* @param options Classification options
|
|
238
|
+
* @returns Category string
|
|
239
|
+
*/
|
|
240
|
+
async classifyContent(content: string, options: ClassificationOptions = {}): Promise<string> {
|
|
241
|
+
const result = await this.shouldSaveMemory(content, options);
|
|
242
|
+
return result.category;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Analyze patterns in text
|
|
247
|
+
* @param text Text to analyze
|
|
248
|
+
* @param patterns Optional custom patterns
|
|
249
|
+
* @returns Pattern analysis result
|
|
250
|
+
*/
|
|
251
|
+
async analyzePatterns(text: string, patterns: RegExp[] = this.factPatterns): Promise<PatternAnalysisResult> {
|
|
252
|
+
const matches: Array<{
|
|
253
|
+
pattern: string;
|
|
254
|
+
match: string;
|
|
255
|
+
category: string;
|
|
256
|
+
confidence: number;
|
|
257
|
+
}> = [];
|
|
258
|
+
const foundPatterns: string[] = [];
|
|
259
|
+
const foundCategories: string[] = [];
|
|
260
|
+
|
|
261
|
+
for (const pattern of patterns) {
|
|
262
|
+
const match = text.match(pattern);
|
|
263
|
+
if (match) {
|
|
264
|
+
const patternStr = pattern.toString();
|
|
265
|
+
const category = this.getCategoryForPattern(patternStr);
|
|
266
|
+
const confidence = this.calculatePatternConfidence(pattern, match[0]);
|
|
267
|
+
|
|
268
|
+
matches.push({
|
|
269
|
+
pattern: patternStr,
|
|
270
|
+
match: match[0],
|
|
271
|
+
category,
|
|
272
|
+
confidence
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
if (!foundPatterns.includes(patternStr)) {
|
|
276
|
+
foundPatterns.push(patternStr);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (!foundCategories.includes(category)) {
|
|
280
|
+
foundCategories.push(category);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return {
|
|
286
|
+
patterns: foundPatterns,
|
|
287
|
+
categories: foundCategories,
|
|
288
|
+
matches
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Match patterns against text
|
|
294
|
+
* @param text Text to match
|
|
295
|
+
* @param patterns Patterns to use
|
|
296
|
+
* @returns Match result
|
|
297
|
+
*/
|
|
298
|
+
private matchPatterns(text: string, patterns: RegExp[]): {
|
|
299
|
+
matched: boolean;
|
|
300
|
+
pattern?: string;
|
|
301
|
+
category: string;
|
|
302
|
+
confidence: number;
|
|
303
|
+
} {
|
|
304
|
+
// Handle null/undefined text input
|
|
305
|
+
if (!text || typeof text !== 'string') {
|
|
306
|
+
return {
|
|
307
|
+
matched: false,
|
|
308
|
+
category: 'general',
|
|
309
|
+
confidence: 0.1
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
for (const pattern of patterns) {
|
|
314
|
+
try {
|
|
315
|
+
const match = text.match(pattern);
|
|
316
|
+
if (match) {
|
|
317
|
+
const patternStr = pattern.toString();
|
|
318
|
+
const category = this.getCategoryForPattern(patternStr);
|
|
319
|
+
const confidence = this.calculatePatternConfidence(pattern, match[0]);
|
|
320
|
+
|
|
321
|
+
return {
|
|
322
|
+
matched: true,
|
|
323
|
+
pattern: patternStr,
|
|
324
|
+
category,
|
|
325
|
+
confidence
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
} catch (error) {
|
|
329
|
+
// Skip invalid patterns
|
|
330
|
+
if (process.env.NODE_ENV === 'development') {
|
|
331
|
+
console.warn(`Pattern matching error for pattern ${pattern}:`, error);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return {
|
|
337
|
+
matched: false,
|
|
338
|
+
category: 'general',
|
|
339
|
+
confidence: 0.1
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Get category for a regex pattern
|
|
345
|
+
* @param patternString String representation of the regex
|
|
346
|
+
* @returns Category string
|
|
347
|
+
*/
|
|
348
|
+
private getCategoryForPattern(patternString: string): string {
|
|
349
|
+
// Extract the key part of the pattern for lookup
|
|
350
|
+
const patternCore = patternString.toLowerCase()
|
|
351
|
+
.replace(/^\/|\/[a-z]*$/g, '') // Remove regex delimiters
|
|
352
|
+
.replace(/\([^)]*\)/g, '') // Remove capture groups
|
|
353
|
+
.replace(/\[[^\]]*\]/g, '') // Remove character classes
|
|
354
|
+
.replace(/[+*?{}|\\^$]/g, '') // Remove regex metacharacters
|
|
355
|
+
.trim();
|
|
356
|
+
|
|
357
|
+
// Find the best matching category
|
|
358
|
+
for (const [key, category] of Object.entries(this.categoryMap)) {
|
|
359
|
+
if (patternCore.includes(key.toLowerCase()) || key.toLowerCase().includes(patternCore)) {
|
|
360
|
+
return category;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return 'custom';
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Calculate confidence score for pattern match
|
|
369
|
+
* @param pattern The regex pattern
|
|
370
|
+
* @param match The matched text
|
|
371
|
+
* @returns Confidence score (0-1)
|
|
372
|
+
*/
|
|
373
|
+
private calculatePatternConfidence(pattern: RegExp, match: string): number {
|
|
374
|
+
// Base confidence for any pattern match
|
|
375
|
+
let confidence = 0.85;
|
|
376
|
+
|
|
377
|
+
// Highest confidence for explicit memory commands
|
|
378
|
+
if (pattern.source.includes('remember') ||
|
|
379
|
+
pattern.source.includes('forget') ||
|
|
380
|
+
pattern.source.includes('store') ||
|
|
381
|
+
pattern.source.includes('save') ||
|
|
382
|
+
pattern.source.includes('note that') ||
|
|
383
|
+
pattern.source.includes('keep in mind')) {
|
|
384
|
+
confidence = 0.98;
|
|
385
|
+
}
|
|
386
|
+
// High confidence for specific personal info
|
|
387
|
+
else if (pattern.source.includes('my name is')) {
|
|
388
|
+
confidence = 0.95;
|
|
389
|
+
}
|
|
390
|
+
// Higher confidence for specific patterns (email, phone, etc.)
|
|
391
|
+
else if (pattern.source.includes('@') || pattern.source.includes('[0-9]')) {
|
|
392
|
+
confidence = 0.92;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Adjust based on match length (longer matches tend to be more specific)
|
|
396
|
+
if (match.length > 30) {
|
|
397
|
+
confidence = Math.min(confidence + 0.02, 1.0);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return confidence;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Use AI for classification (fallback/enhancement)
|
|
405
|
+
* @param message Message to classify
|
|
406
|
+
* @param options Classification options
|
|
407
|
+
* @returns Classification result
|
|
408
|
+
*/
|
|
409
|
+
private async classifyWithAI(message: string, options: ClassificationOptions): Promise<ClassificationResult> {
|
|
410
|
+
// For now, this is a placeholder that could integrate with:
|
|
411
|
+
// 1. EmbeddingService for semantic similarity
|
|
412
|
+
// 2. External AI APIs (OpenAI, Gemini, etc.)
|
|
413
|
+
// 3. Local AI models
|
|
414
|
+
|
|
415
|
+
if (this.embeddingService) {
|
|
416
|
+
// Use embedding-based classification
|
|
417
|
+
return await this.classifyWithEmbeddings(message, options);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// If no AI service is available, return low-confidence result
|
|
421
|
+
return {
|
|
422
|
+
shouldSave: false,
|
|
423
|
+
confidence: 0.3,
|
|
424
|
+
category: 'general',
|
|
425
|
+
reasoning: 'AI classification not available'
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Classify using embeddings (semantic similarity approach)
|
|
431
|
+
* @param message Message to classify
|
|
432
|
+
* @param options Classification options
|
|
433
|
+
* @returns Classification result
|
|
434
|
+
*/
|
|
435
|
+
private async classifyWithEmbeddings(message: string, options: ClassificationOptions): Promise<ClassificationResult> {
|
|
436
|
+
if (!this.embeddingService) {
|
|
437
|
+
throw new Error('EmbeddingService not available');
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
try {
|
|
441
|
+
// Generate embedding for the message
|
|
442
|
+
const result = await this.embeddingService.embedText({ text: message, type: 'content' });
|
|
443
|
+
|
|
444
|
+
// For now, use simple heuristics based on common memory-worthy patterns
|
|
445
|
+
// In a full implementation, this would compare against a database of
|
|
446
|
+
// known memory-worthy content embeddings
|
|
447
|
+
|
|
448
|
+
const messageWords = message.toLowerCase().split(/\s+/);
|
|
449
|
+
const memoryKeywords = ['i', 'my', 'me', 'like', 'love', 'hate', 'prefer', 'enjoy', 'am', 'work', 'live'];
|
|
450
|
+
const memoryWordCount = messageWords.filter(word => memoryKeywords.includes(word)).length;
|
|
451
|
+
|
|
452
|
+
const confidence = Math.min(memoryWordCount * 0.15 + 0.1, 0.85);
|
|
453
|
+
const shouldSave = confidence > 0.6;
|
|
454
|
+
|
|
455
|
+
return {
|
|
456
|
+
shouldSave,
|
|
457
|
+
confidence,
|
|
458
|
+
category: shouldSave ? 'personal_info' : 'general',
|
|
459
|
+
reasoning: `Embedding-based classification (${memoryWordCount} memory keywords found)`
|
|
460
|
+
};
|
|
461
|
+
} catch (error) {
|
|
462
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
463
|
+
throw new Error(`Embedding classification failed: ${errorMessage}`);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
466
|
}
|