@hashgraphonline/conversational-agent 0.1.208 → 0.1.209
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/dist/cjs/conversational-agent.d.ts +67 -8
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/langchain-agent.d.ts +8 -0
- package/dist/cjs/memory/SmartMemoryManager.d.ts +58 -21
- package/dist/cjs/memory/index.d.ts +1 -1
- package/dist/esm/index.js +8 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index12.js +124 -46
- package/dist/esm/index12.js.map +1 -1
- package/dist/esm/index13.js +178 -13
- package/dist/esm/index13.js.map +1 -1
- package/dist/esm/index14.js +604 -100
- package/dist/esm/index14.js.map +1 -1
- package/dist/esm/index15.js +464 -9
- package/dist/esm/index15.js.map +1 -1
- package/dist/esm/index16.js +44 -172
- package/dist/esm/index16.js.map +1 -1
- package/dist/esm/index17.js +11 -156
- package/dist/esm/index17.js.map +1 -1
- package/dist/esm/index18.js +106 -191
- package/dist/esm/index18.js.map +1 -1
- package/dist/esm/index19.js +9 -660
- package/dist/esm/index19.js.map +1 -1
- package/dist/esm/index2.js +22 -13
- package/dist/esm/index2.js.map +1 -1
- package/dist/esm/index20.js +150 -206
- package/dist/esm/index20.js.map +1 -1
- package/dist/esm/index21.js +140 -166
- package/dist/esm/index21.js.map +1 -1
- package/dist/esm/index22.js +47 -105
- package/dist/esm/index22.js.map +1 -1
- package/dist/esm/index23.js +24 -89
- package/dist/esm/index23.js.map +1 -1
- package/dist/esm/index24.js +83 -56
- package/dist/esm/index24.js.map +1 -1
- package/dist/esm/index25.js +236 -32
- package/dist/esm/index25.js.map +1 -1
- package/dist/esm/index5.js +1 -1
- package/dist/esm/index6.js +295 -17
- package/dist/esm/index6.js.map +1 -1
- package/dist/esm/index8.js +82 -8
- package/dist/esm/index8.js.map +1 -1
- package/dist/types/conversational-agent.d.ts +67 -8
- package/dist/types/index.d.ts +1 -0
- package/dist/types/langchain-agent.d.ts +8 -0
- package/dist/types/memory/SmartMemoryManager.d.ts +58 -21
- package/dist/types/memory/index.d.ts +1 -1
- package/package.json +3 -3
- package/src/context/ReferenceContextManager.ts +9 -4
- package/src/context/ReferenceResponseProcessor.ts +3 -4
- package/src/conversational-agent.ts +379 -31
- package/src/index.ts +2 -0
- package/src/langchain/ContentAwareAgentExecutor.ts +0 -1
- package/src/langchain-agent.ts +94 -11
- package/src/mcp/ContentProcessor.ts +13 -3
- package/src/mcp/adapters/langchain.ts +1 -9
- package/src/memory/ContentStorage.ts +3 -51
- package/src/memory/MemoryWindow.ts +4 -16
- package/src/memory/ReferenceIdGenerator.ts +0 -4
- package/src/memory/SmartMemoryManager.ts +400 -33
- package/src/memory/TokenCounter.ts +12 -16
- package/src/memory/index.ts +1 -1
- package/src/plugins/hcs-10/HCS10Plugin.ts +44 -14
- package/src/services/ContentStoreManager.ts +0 -3
- package/src/types/content-reference.ts +8 -8
- package/src/types/index.ts +0 -1
|
@@ -3,6 +3,34 @@ import { MemoryWindow } from './MemoryWindow';
|
|
|
3
3
|
import { ContentStorage } from './ContentStorage';
|
|
4
4
|
import { TokenCounter } from './TokenCounter';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Entity association for storing blockchain entity contexts
|
|
8
|
+
*/
|
|
9
|
+
export interface EntityAssociation {
|
|
10
|
+
/** The blockchain entity ID (e.g., tokenId, accountId, topicId) */
|
|
11
|
+
entityId: string;
|
|
12
|
+
/** User-provided or derived friendly name */
|
|
13
|
+
entityName: string;
|
|
14
|
+
/** Type of entity (token, account, topic, schedule, etc.) */
|
|
15
|
+
entityType: string;
|
|
16
|
+
/** When the entity was created/associated */
|
|
17
|
+
createdAt: Date;
|
|
18
|
+
/** Transaction ID that created this entity */
|
|
19
|
+
transactionId?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Options for resolving entity references
|
|
24
|
+
*/
|
|
25
|
+
export interface EntityResolutionOptions {
|
|
26
|
+
/** Filter by specific entity type */
|
|
27
|
+
entityType?: string;
|
|
28
|
+
/** Maximum number of results to return */
|
|
29
|
+
limit?: number;
|
|
30
|
+
/** Whether to use fuzzy matching for natural language queries */
|
|
31
|
+
fuzzyMatch?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
6
34
|
/**
|
|
7
35
|
* Configuration for SmartMemoryManager
|
|
8
36
|
*/
|
|
@@ -47,6 +75,8 @@ export interface MemoryStats {
|
|
|
47
75
|
usagePercentage: number;
|
|
48
76
|
}
|
|
49
77
|
|
|
78
|
+
const IS_ENTITY_ASSOCIATION_FLAG = '"isEntityAssociation":true';
|
|
79
|
+
|
|
50
80
|
/**
|
|
51
81
|
* Smart memory manager that combines active memory window with long-term storage
|
|
52
82
|
* Provides context-aware memory management with automatic pruning and searchable history
|
|
@@ -57,18 +87,16 @@ export class SmartMemoryManager {
|
|
|
57
87
|
private tokenCounter: TokenCounter;
|
|
58
88
|
private config: Required<SmartMemoryConfig>;
|
|
59
89
|
|
|
60
|
-
// Default configuration values
|
|
61
90
|
private static readonly DEFAULT_CONFIG: Required<SmartMemoryConfig> = {
|
|
62
91
|
maxTokens: 8000,
|
|
63
92
|
reserveTokens: 1000,
|
|
64
93
|
modelName: 'gpt-4o',
|
|
65
|
-
storageLimit: 1000
|
|
94
|
+
storageLimit: 1000,
|
|
66
95
|
};
|
|
67
96
|
|
|
68
97
|
constructor(config: SmartMemoryConfig = {}) {
|
|
69
98
|
this.config = { ...SmartMemoryManager.DEFAULT_CONFIG, ...config };
|
|
70
|
-
|
|
71
|
-
// Initialize components
|
|
99
|
+
|
|
72
100
|
this.tokenCounter = new TokenCounter(this.config.modelName as any);
|
|
73
101
|
this.contentStorage = new ContentStorage(this.config.storageLimit);
|
|
74
102
|
this.memoryWindow = new MemoryWindow(
|
|
@@ -85,8 +113,7 @@ export class SmartMemoryManager {
|
|
|
85
113
|
*/
|
|
86
114
|
addMessage(message: BaseMessage): void {
|
|
87
115
|
const result = this.memoryWindow.addMessage(message);
|
|
88
|
-
|
|
89
|
-
// Store any pruned messages in content storage
|
|
116
|
+
|
|
90
117
|
if (result.prunedMessages.length > 0) {
|
|
91
118
|
this.contentStorage.storeMessages(result.prunedMessages);
|
|
92
119
|
}
|
|
@@ -106,7 +133,7 @@ export class SmartMemoryManager {
|
|
|
106
133
|
*/
|
|
107
134
|
clear(clearStorage: boolean = false): void {
|
|
108
135
|
this.memoryWindow.clear();
|
|
109
|
-
|
|
136
|
+
|
|
110
137
|
if (clearStorage) {
|
|
111
138
|
this.contentStorage.clear();
|
|
112
139
|
}
|
|
@@ -162,14 +189,14 @@ export class SmartMemoryManager {
|
|
|
162
189
|
*/
|
|
163
190
|
getMemoryStats(): MemoryStats {
|
|
164
191
|
const windowStats = this.memoryWindow.getStats();
|
|
165
|
-
|
|
192
|
+
|
|
166
193
|
return {
|
|
167
194
|
totalActiveMessages: windowStats.totalMessages,
|
|
168
195
|
currentTokenCount: windowStats.currentTokens,
|
|
169
196
|
maxTokens: windowStats.maxTokens,
|
|
170
197
|
remainingCapacity: windowStats.remainingCapacity,
|
|
171
198
|
systemPromptTokens: windowStats.systemPromptTokens,
|
|
172
|
-
usagePercentage: windowStats.usagePercentage
|
|
199
|
+
usagePercentage: windowStats.usagePercentage,
|
|
173
200
|
};
|
|
174
201
|
}
|
|
175
202
|
|
|
@@ -177,7 +204,7 @@ export class SmartMemoryManager {
|
|
|
177
204
|
* Get statistics about the content storage
|
|
178
205
|
* @returns Storage usage statistics
|
|
179
206
|
*/
|
|
180
|
-
getStorageStats() {
|
|
207
|
+
getStorageStats(): ReturnType<ContentStorage['getStorageStats']> {
|
|
181
208
|
return this.contentStorage.getStorageStats();
|
|
182
209
|
}
|
|
183
210
|
|
|
@@ -185,16 +212,23 @@ export class SmartMemoryManager {
|
|
|
185
212
|
* Get combined statistics for both active memory and storage
|
|
186
213
|
* @returns Combined memory and storage statistics
|
|
187
214
|
*/
|
|
188
|
-
getOverallStats() {
|
|
215
|
+
getOverallStats(): {
|
|
216
|
+
activeMemory: MemoryStats;
|
|
217
|
+
storage: ReturnType<ContentStorage['getStorageStats']>;
|
|
218
|
+
totalMessagesManaged: number;
|
|
219
|
+
activeMemoryUtilization: number;
|
|
220
|
+
storageUtilization: number;
|
|
221
|
+
} {
|
|
189
222
|
const memoryStats = this.getMemoryStats();
|
|
190
223
|
const storageStats = this.getStorageStats();
|
|
191
|
-
|
|
224
|
+
|
|
192
225
|
return {
|
|
193
226
|
activeMemory: memoryStats,
|
|
194
227
|
storage: storageStats,
|
|
195
|
-
totalMessagesManaged:
|
|
228
|
+
totalMessagesManaged:
|
|
229
|
+
memoryStats.totalActiveMessages + storageStats.totalMessages,
|
|
196
230
|
activeMemoryUtilization: memoryStats.usagePercentage,
|
|
197
|
-
storageUtilization: storageStats.usagePercentage
|
|
231
|
+
storageUtilization: storageStats.usagePercentage,
|
|
198
232
|
};
|
|
199
233
|
}
|
|
200
234
|
|
|
@@ -204,21 +238,21 @@ export class SmartMemoryManager {
|
|
|
204
238
|
*/
|
|
205
239
|
updateConfig(newConfig: Partial<SmartMemoryConfig>): void {
|
|
206
240
|
this.config = { ...this.config, ...newConfig };
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
241
|
+
|
|
242
|
+
if (
|
|
243
|
+
newConfig.maxTokens !== undefined ||
|
|
244
|
+
newConfig.reserveTokens !== undefined
|
|
245
|
+
) {
|
|
210
246
|
this.memoryWindow.updateLimits(
|
|
211
247
|
this.config.maxTokens,
|
|
212
248
|
this.config.reserveTokens
|
|
213
249
|
);
|
|
214
250
|
}
|
|
215
|
-
|
|
251
|
+
|
|
216
252
|
if (newConfig.storageLimit !== undefined) {
|
|
217
253
|
this.contentStorage.updateStorageLimit(this.config.storageLimit);
|
|
218
254
|
}
|
|
219
|
-
|
|
220
|
-
// Note: Model name changes would require recreating the token counter
|
|
221
|
-
// This is not implemented to avoid disrupting ongoing operations
|
|
255
|
+
|
|
222
256
|
}
|
|
223
257
|
|
|
224
258
|
/**
|
|
@@ -262,17 +296,24 @@ export class SmartMemoryManager {
|
|
|
262
296
|
* Export the current state for persistence or analysis
|
|
263
297
|
* @returns Serializable representation of memory state
|
|
264
298
|
*/
|
|
265
|
-
exportState() {
|
|
299
|
+
exportState(): {
|
|
300
|
+
config: Required<SmartMemoryConfig>;
|
|
301
|
+
activeMessages: Array<{ content: unknown; type: string }>;
|
|
302
|
+
systemPrompt: string;
|
|
303
|
+
memoryStats: MemoryStats;
|
|
304
|
+
storageStats: ReturnType<ContentStorage['getStorageStats']>;
|
|
305
|
+
storedMessages: ReturnType<ContentStorage['exportMessages']>;
|
|
306
|
+
} {
|
|
266
307
|
return {
|
|
267
308
|
config: this.config,
|
|
268
|
-
activeMessages: this.memoryWindow.getMessages().map(msg => ({
|
|
309
|
+
activeMessages: this.memoryWindow.getMessages().map((msg) => ({
|
|
269
310
|
content: msg.content,
|
|
270
|
-
type: msg._getType()
|
|
311
|
+
type: msg._getType(),
|
|
271
312
|
})),
|
|
272
313
|
systemPrompt: this.memoryWindow.getSystemPrompt(),
|
|
273
314
|
memoryStats: this.getMemoryStats(),
|
|
274
315
|
storageStats: this.getStorageStats(),
|
|
275
|
-
storedMessages: this.contentStorage.exportMessages()
|
|
316
|
+
storedMessages: this.contentStorage.exportMessages(),
|
|
276
317
|
};
|
|
277
318
|
}
|
|
278
319
|
|
|
@@ -282,21 +323,29 @@ export class SmartMemoryManager {
|
|
|
282
323
|
* @param includeStoredContext - Whether to include recent stored messages
|
|
283
324
|
* @returns Context summary object
|
|
284
325
|
*/
|
|
285
|
-
getContextSummary(includeStoredContext: boolean = false) {
|
|
326
|
+
getContextSummary(includeStoredContext: boolean = false): {
|
|
327
|
+
activeMessageCount: number;
|
|
328
|
+
systemPrompt: string;
|
|
329
|
+
recentMessages: BaseMessage[];
|
|
330
|
+
memoryUtilization: number;
|
|
331
|
+
hasStoredHistory: boolean;
|
|
332
|
+
recentStoredMessages?: BaseMessage[];
|
|
333
|
+
storageStats?: ReturnType<ContentStorage['getStorageStats']>;
|
|
334
|
+
} {
|
|
286
335
|
const activeMessages = this.getMessages();
|
|
287
336
|
const summary = {
|
|
288
337
|
activeMessageCount: activeMessages.length,
|
|
289
338
|
systemPrompt: this.getSystemPrompt(),
|
|
290
|
-
recentMessages: activeMessages.slice(-5),
|
|
339
|
+
recentMessages: activeMessages.slice(-5),
|
|
291
340
|
memoryUtilization: this.getMemoryStats().usagePercentage,
|
|
292
|
-
hasStoredHistory: this.getStorageStats().totalMessages > 0
|
|
341
|
+
hasStoredHistory: this.getStorageStats().totalMessages > 0,
|
|
293
342
|
};
|
|
294
343
|
|
|
295
344
|
if (includeStoredContext) {
|
|
296
345
|
return {
|
|
297
346
|
...summary,
|
|
298
|
-
recentStoredMessages: this.getRecentHistory(10),
|
|
299
|
-
storageStats: this.getStorageStats()
|
|
347
|
+
recentStoredMessages: this.getRecentHistory(10),
|
|
348
|
+
storageStats: this.getStorageStats(),
|
|
300
349
|
};
|
|
301
350
|
}
|
|
302
351
|
|
|
@@ -308,8 +357,326 @@ export class SmartMemoryManager {
|
|
|
308
357
|
* Optimizes storage and cleans up resources
|
|
309
358
|
*/
|
|
310
359
|
performMaintenance(): void {
|
|
311
|
-
|
|
312
|
-
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Store an entity association for later resolution
|
|
365
|
+
* @param entityId - The blockchain entity ID
|
|
366
|
+
* @param entityName - User-provided or derived friendly name
|
|
367
|
+
* @param entityType - Type of entity (token, account, topic, etc.)
|
|
368
|
+
* @param transactionId - Optional transaction ID that created this entity
|
|
369
|
+
*/
|
|
370
|
+
storeEntityAssociation(
|
|
371
|
+
entityId: string,
|
|
372
|
+
entityName: string,
|
|
373
|
+
entityType: string,
|
|
374
|
+
transactionId?: string
|
|
375
|
+
): void {
|
|
376
|
+
try {
|
|
377
|
+
if (
|
|
378
|
+
!entityId ||
|
|
379
|
+
typeof entityId !== 'string' ||
|
|
380
|
+
entityId.trim().length === 0
|
|
381
|
+
) {
|
|
382
|
+
console.warn(
|
|
383
|
+
'[SmartMemoryManager] Invalid entityId provided:',
|
|
384
|
+
entityId
|
|
385
|
+
);
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (
|
|
390
|
+
!entityName ||
|
|
391
|
+
typeof entityName !== 'string' ||
|
|
392
|
+
entityName.trim().length === 0
|
|
393
|
+
) {
|
|
394
|
+
console.warn(
|
|
395
|
+
'[SmartMemoryManager] Invalid entityName provided:',
|
|
396
|
+
entityName
|
|
397
|
+
);
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (
|
|
402
|
+
!entityType ||
|
|
403
|
+
typeof entityType !== 'string' ||
|
|
404
|
+
entityType.trim().length === 0
|
|
405
|
+
) {
|
|
406
|
+
console.warn(
|
|
407
|
+
'[SmartMemoryManager] Invalid entityType provided:',
|
|
408
|
+
entityType
|
|
409
|
+
);
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const sanitizedEntityId = entityId.trim();
|
|
414
|
+
const sanitizedEntityName = entityName.trim().substring(0, 100);
|
|
415
|
+
const sanitizedEntityType = entityType.trim().toLowerCase();
|
|
416
|
+
|
|
417
|
+
const association: EntityAssociation = {
|
|
418
|
+
entityId: sanitizedEntityId,
|
|
419
|
+
entityName: sanitizedEntityName,
|
|
420
|
+
entityType: sanitizedEntityType,
|
|
421
|
+
createdAt: new Date(),
|
|
422
|
+
...(transactionId !== undefined && transactionId !== null && transactionId.trim() !== ''
|
|
423
|
+
? { transactionId: transactionId.trim() }
|
|
424
|
+
: {})
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
const content = JSON.stringify(association);
|
|
428
|
+
type LangChainLikeMessage = {
|
|
429
|
+
_getType: () => string;
|
|
430
|
+
content: unknown;
|
|
431
|
+
id: string;
|
|
432
|
+
name?: string;
|
|
433
|
+
additional_kwargs?: Record<string, unknown>;
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
const entityMessage: LangChainLikeMessage = {
|
|
437
|
+
_getType: () => 'system',
|
|
438
|
+
content: content,
|
|
439
|
+
id: `entity_${sanitizedEntityId}_${Date.now()}`,
|
|
440
|
+
name: 'entity_association',
|
|
441
|
+
additional_kwargs: {
|
|
442
|
+
entityId: sanitizedEntityId,
|
|
443
|
+
entityName: sanitizedEntityName,
|
|
444
|
+
entityType: sanitizedEntityType,
|
|
445
|
+
isEntityAssociation: true,
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
this.contentStorage.storeMessages([entityMessage as any]);
|
|
450
|
+
|
|
451
|
+
console.debug(
|
|
452
|
+
`[SmartMemoryManager] Stored entity association: ${sanitizedEntityName} (${sanitizedEntityType}) -> ${sanitizedEntityId}`
|
|
453
|
+
);
|
|
454
|
+
} catch (_error) {
|
|
455
|
+
console.error(
|
|
456
|
+
'[SmartMemoryManager] Failed to store entity association:',
|
|
457
|
+
_error
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Resolve entity references from natural language queries
|
|
464
|
+
* @param query - Search query (entity name or natural language reference)
|
|
465
|
+
* @param options - Resolution options for filtering and fuzzy matching
|
|
466
|
+
* @returns Array of matching entity associations
|
|
467
|
+
*/
|
|
468
|
+
resolveEntityReference(
|
|
469
|
+
query: string,
|
|
470
|
+
options: EntityResolutionOptions = {}
|
|
471
|
+
): EntityAssociation[] {
|
|
472
|
+
try {
|
|
473
|
+
if (!query || typeof query !== 'string') {
|
|
474
|
+
console.warn(
|
|
475
|
+
'[SmartMemoryManager] Invalid query provided for entity resolution:',
|
|
476
|
+
query
|
|
477
|
+
);
|
|
478
|
+
return [];
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const sanitizedQuery = query.trim();
|
|
482
|
+
if (sanitizedQuery.length === 0) {
|
|
483
|
+
return [];
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
if (sanitizedQuery.length > 200) {
|
|
487
|
+
console.warn(
|
|
488
|
+
'[SmartMemoryManager] Query too long, truncating:',
|
|
489
|
+
sanitizedQuery.length
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const { entityType, limit = 10, fuzzyMatch = true } = options;
|
|
494
|
+
|
|
495
|
+
const safeLimit = Math.max(1, Math.min(limit || 10, 100));
|
|
496
|
+
|
|
497
|
+
const searchResults = this.contentStorage.searchMessages(
|
|
498
|
+
sanitizedQuery.substring(0, 200),
|
|
499
|
+
{
|
|
500
|
+
caseSensitive: false,
|
|
501
|
+
limit: safeLimit * 2,
|
|
502
|
+
}
|
|
503
|
+
);
|
|
504
|
+
|
|
505
|
+
const associations: EntityAssociation[] = [];
|
|
506
|
+
|
|
507
|
+
for (const message of searchResults) {
|
|
508
|
+
try {
|
|
509
|
+
const content = message.content as string;
|
|
510
|
+
if (
|
|
511
|
+
content.includes(IS_ENTITY_ASSOCIATION_FLAG) ||
|
|
512
|
+
content.includes('entityId')
|
|
513
|
+
) {
|
|
514
|
+
const parsed = JSON.parse(content);
|
|
515
|
+
if (parsed.entityId && parsed.entityName && parsed.entityType) {
|
|
516
|
+
if (entityType && parsed.entityType !== entityType) {
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
associations.push(parsed as EntityAssociation);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
} catch {
|
|
524
|
+
continue;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
if (fuzzyMatch && associations.length === 0) {
|
|
529
|
+
const fuzzyQueries = [
|
|
530
|
+
query.toLowerCase(),
|
|
531
|
+
`token`,
|
|
532
|
+
`account`,
|
|
533
|
+
entityType || '',
|
|
534
|
+
].filter(Boolean);
|
|
535
|
+
|
|
536
|
+
for (const fuzzyQuery of fuzzyQueries) {
|
|
537
|
+
if (fuzzyQuery === query.toLowerCase()) continue;
|
|
538
|
+
|
|
539
|
+
const fuzzyResults = this.contentStorage.searchMessages(fuzzyQuery, {
|
|
540
|
+
caseSensitive: false,
|
|
541
|
+
limit: limit,
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
for (const message of fuzzyResults) {
|
|
545
|
+
try {
|
|
546
|
+
const content = message.content as string;
|
|
547
|
+
if (content.includes(IS_ENTITY_ASSOCIATION_FLAG)) {
|
|
548
|
+
const parsed = JSON.parse(content);
|
|
549
|
+
if (parsed.entityId && parsed.entityName && parsed.entityType) {
|
|
550
|
+
if (entityType && parsed.entityType !== entityType) {
|
|
551
|
+
continue;
|
|
552
|
+
}
|
|
553
|
+
associations.push(parsed as EntityAssociation);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
} catch {
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
const uniqueAssociations = associations
|
|
564
|
+
.filter(
|
|
565
|
+
(assoc, index, arr) =>
|
|
566
|
+
arr.findIndex((a) => a.entityId === assoc.entityId) === index
|
|
567
|
+
)
|
|
568
|
+
.sort(
|
|
569
|
+
(a, b) =>
|
|
570
|
+
new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
|
|
571
|
+
);
|
|
572
|
+
|
|
573
|
+
const results = uniqueAssociations.slice(0, safeLimit);
|
|
574
|
+
|
|
575
|
+
if (results.length > 1) {
|
|
576
|
+
console.debug(
|
|
577
|
+
`[SmartMemoryManager] Multiple entities found for "${sanitizedQuery}":`,
|
|
578
|
+
results.map((r) => `${r.entityName} (${r.entityType})`).join(', ')
|
|
579
|
+
);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
return results;
|
|
583
|
+
} catch (_error) {
|
|
584
|
+
console.error(
|
|
585
|
+
'[SmartMemoryManager] Failed to resolve entity reference:',
|
|
586
|
+
_error
|
|
587
|
+
);
|
|
588
|
+
return [];
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Get all entity associations, optionally filtered by type
|
|
594
|
+
* @param entityType - Optional filter by entity type
|
|
595
|
+
* @returns Array of entity associations
|
|
596
|
+
*/
|
|
597
|
+
getEntityAssociations(entityType?: string): EntityAssociation[] {
|
|
598
|
+
try {
|
|
599
|
+
const sanitizedEntityType = entityType
|
|
600
|
+
? entityType.trim().toLowerCase()
|
|
601
|
+
: undefined;
|
|
602
|
+
|
|
603
|
+
if (
|
|
604
|
+
entityType &&
|
|
605
|
+
(!sanitizedEntityType || sanitizedEntityType.length === 0)
|
|
606
|
+
) {
|
|
607
|
+
console.warn(
|
|
608
|
+
'[SmartMemoryManager] Invalid entityType filter provided:',
|
|
609
|
+
entityType
|
|
610
|
+
);
|
|
611
|
+
return [];
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const SEARCH_ANY_ENTITY = 'entityId';
|
|
615
|
+
const searchQuery = sanitizedEntityType || SEARCH_ANY_ENTITY;
|
|
616
|
+
const searchResults = this.contentStorage.searchMessages(searchQuery, {
|
|
617
|
+
caseSensitive: false,
|
|
618
|
+
limit: 100,
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
const associations: EntityAssociation[] = [];
|
|
622
|
+
|
|
623
|
+
for (const message of searchResults) {
|
|
624
|
+
try {
|
|
625
|
+
const content = message.content as string;
|
|
626
|
+
if (content.includes(IS_ENTITY_ASSOCIATION_FLAG)) {
|
|
627
|
+
const parsed = JSON.parse(content);
|
|
628
|
+
|
|
629
|
+
if (parsed.entityId && parsed.entityName && parsed.entityType) {
|
|
630
|
+
if (
|
|
631
|
+
sanitizedEntityType &&
|
|
632
|
+
parsed.entityType !== sanitizedEntityType
|
|
633
|
+
) {
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
if (parsed.createdAt && typeof parsed.createdAt === 'string') {
|
|
638
|
+
parsed.createdAt = new Date(parsed.createdAt);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
associations.push(parsed as EntityAssociation);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
} catch (_parseError) {
|
|
645
|
+
console.debug(
|
|
646
|
+
'[SmartMemoryManager] Skipped malformed association data:',
|
|
647
|
+
_parseError
|
|
648
|
+
);
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
const results = associations
|
|
654
|
+
.filter(
|
|
655
|
+
(assoc, index, arr) =>
|
|
656
|
+
arr.findIndex((a) => a.entityId === assoc.entityId) === index
|
|
657
|
+
)
|
|
658
|
+
.sort((a, b): number => {
|
|
659
|
+
const getTime = (d: Date | string): number =>
|
|
660
|
+
d instanceof Date ? d.getTime() : new Date(d).getTime();
|
|
661
|
+
const aTime = getTime(a.createdAt);
|
|
662
|
+
const bTime = getTime(b.createdAt);
|
|
663
|
+
return bTime - aTime;
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
console.debug(
|
|
667
|
+
`[SmartMemoryManager] Retrieved ${results.length} entity associations${
|
|
668
|
+
sanitizedEntityType ? ` of type '${sanitizedEntityType}'` : ''
|
|
669
|
+
}`
|
|
670
|
+
);
|
|
671
|
+
|
|
672
|
+
return results;
|
|
673
|
+
} catch (_error) {
|
|
674
|
+
console.error(
|
|
675
|
+
'[SmartMemoryManager] Failed to get entity associations:',
|
|
676
|
+
_error
|
|
677
|
+
);
|
|
678
|
+
return [];
|
|
679
|
+
}
|
|
313
680
|
}
|
|
314
681
|
|
|
315
682
|
/**
|
|
@@ -320,4 +687,4 @@ export class SmartMemoryManager {
|
|
|
320
687
|
this.contentStorage.dispose();
|
|
321
688
|
this.tokenCounter.dispose();
|
|
322
689
|
}
|
|
323
|
-
}
|
|
690
|
+
}
|
|
@@ -10,16 +10,14 @@ export class TokenCounter {
|
|
|
10
10
|
private encoding: ReturnType<typeof encoding_for_model>;
|
|
11
11
|
private modelName: TiktokenModel;
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
private static readonly
|
|
15
|
-
private static readonly ROLE_OVERHEAD = 1; // Additional token for role specification
|
|
13
|
+
private static readonly MESSAGE_OVERHEAD = 3;
|
|
14
|
+
private static readonly ROLE_OVERHEAD = 1;
|
|
16
15
|
|
|
17
16
|
constructor(modelName: TiktokenModel = 'gpt-4o') {
|
|
18
17
|
this.modelName = modelName;
|
|
19
18
|
try {
|
|
20
19
|
this.encoding = encoding_for_model(modelName);
|
|
21
|
-
} catch
|
|
22
|
-
// Fallback to gpt-4o if specific model encoding is not available
|
|
20
|
+
} catch {
|
|
23
21
|
console.warn(`Model ${modelName} not found, falling back to gpt-4o encoding`);
|
|
24
22
|
this.encoding = encoding_for_model('gpt-4o');
|
|
25
23
|
this.modelName = 'gpt-4o';
|
|
@@ -41,7 +39,6 @@ export class TokenCounter {
|
|
|
41
39
|
return tokens.length;
|
|
42
40
|
} catch (error) {
|
|
43
41
|
console.warn('Error counting tokens, falling back to word-based estimation:', error);
|
|
44
|
-
// Fallback: rough estimation based on words (typically 1.3 tokens per word)
|
|
45
42
|
return Math.ceil(text.split(/\s+/).length * 1.3);
|
|
46
43
|
}
|
|
47
44
|
}
|
|
@@ -52,10 +49,9 @@ export class TokenCounter {
|
|
|
52
49
|
* @returns Number of tokens including message formatting overhead
|
|
53
50
|
*/
|
|
54
51
|
countMessageTokens(message: BaseMessage): number {
|
|
55
|
-
const contentTokens = this.countTokens(message.content
|
|
52
|
+
const contentTokens = this.countTokens(String(message.content ?? ''));
|
|
56
53
|
const roleTokens = this.countTokens(this.getMessageRole(message));
|
|
57
54
|
|
|
58
|
-
// Add overhead for message structure and role
|
|
59
55
|
return contentTokens + roleTokens + TokenCounter.MESSAGE_OVERHEAD + TokenCounter.ROLE_OVERHEAD;
|
|
60
56
|
}
|
|
61
57
|
|
|
@@ -69,9 +65,11 @@ export class TokenCounter {
|
|
|
69
65
|
return 0;
|
|
70
66
|
}
|
|
71
67
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
68
|
+
let total = 0;
|
|
69
|
+
for (const message of messages) {
|
|
70
|
+
total += this.countMessageTokens(message);
|
|
71
|
+
}
|
|
72
|
+
return total;
|
|
75
73
|
}
|
|
76
74
|
|
|
77
75
|
/**
|
|
@@ -88,7 +86,6 @@ export class TokenCounter {
|
|
|
88
86
|
const contentTokens = this.countTokens(systemPrompt);
|
|
89
87
|
const roleTokens = this.countTokens('system');
|
|
90
88
|
|
|
91
|
-
// System messages have similar overhead to regular messages
|
|
92
89
|
return contentTokens + roleTokens + TokenCounter.MESSAGE_OVERHEAD + TokenCounter.ROLE_OVERHEAD;
|
|
93
90
|
}
|
|
94
91
|
|
|
@@ -102,7 +99,6 @@ export class TokenCounter {
|
|
|
102
99
|
const systemTokens = this.estimateSystemPromptTokens(systemPrompt);
|
|
103
100
|
const messageTokens = this.countMessagesTokens(messages);
|
|
104
101
|
|
|
105
|
-
// Add a small buffer for chat completion overhead
|
|
106
102
|
const completionOverhead = 10;
|
|
107
103
|
|
|
108
104
|
return systemTokens + messageTokens + completionOverhead;
|
|
@@ -127,7 +123,7 @@ export class TokenCounter {
|
|
|
127
123
|
case 'tool':
|
|
128
124
|
return 'tool';
|
|
129
125
|
default:
|
|
130
|
-
return 'user';
|
|
126
|
+
return 'user';
|
|
131
127
|
}
|
|
132
128
|
}
|
|
133
129
|
|
|
@@ -145,8 +141,8 @@ export class TokenCounter {
|
|
|
145
141
|
dispose(): void {
|
|
146
142
|
try {
|
|
147
143
|
this.encoding.free();
|
|
148
|
-
} catch
|
|
149
|
-
console.warn('Error disposing encoding
|
|
144
|
+
} catch {
|
|
145
|
+
console.warn('Error disposing encoding');
|
|
150
146
|
}
|
|
151
147
|
}
|
|
152
148
|
}
|
package/src/memory/index.ts
CHANGED
|
@@ -3,6 +3,6 @@ export { MemoryWindow } from './MemoryWindow';
|
|
|
3
3
|
export { ContentStorage } from './ContentStorage';
|
|
4
4
|
export { SmartMemoryManager } from './SmartMemoryManager';
|
|
5
5
|
|
|
6
|
-
export type { SmartMemoryConfig, SearchOptions, MemoryStats } from './SmartMemoryManager';
|
|
6
|
+
export type { SmartMemoryConfig, SearchOptions, MemoryStats, EntityAssociation, EntityResolutionOptions } from './SmartMemoryManager';
|
|
7
7
|
export type { AddMessageResult } from './MemoryWindow';
|
|
8
8
|
export type { StorageStats } from './ContentStorage';
|