@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.
Files changed (68) hide show
  1. package/dist/cjs/conversational-agent.d.ts +67 -8
  2. package/dist/cjs/index.cjs +1 -1
  3. package/dist/cjs/index.cjs.map +1 -1
  4. package/dist/cjs/index.d.ts +1 -0
  5. package/dist/cjs/langchain-agent.d.ts +8 -0
  6. package/dist/cjs/memory/SmartMemoryManager.d.ts +58 -21
  7. package/dist/cjs/memory/index.d.ts +1 -1
  8. package/dist/esm/index.js +8 -0
  9. package/dist/esm/index.js.map +1 -1
  10. package/dist/esm/index12.js +124 -46
  11. package/dist/esm/index12.js.map +1 -1
  12. package/dist/esm/index13.js +178 -13
  13. package/dist/esm/index13.js.map +1 -1
  14. package/dist/esm/index14.js +604 -100
  15. package/dist/esm/index14.js.map +1 -1
  16. package/dist/esm/index15.js +464 -9
  17. package/dist/esm/index15.js.map +1 -1
  18. package/dist/esm/index16.js +44 -172
  19. package/dist/esm/index16.js.map +1 -1
  20. package/dist/esm/index17.js +11 -156
  21. package/dist/esm/index17.js.map +1 -1
  22. package/dist/esm/index18.js +106 -191
  23. package/dist/esm/index18.js.map +1 -1
  24. package/dist/esm/index19.js +9 -660
  25. package/dist/esm/index19.js.map +1 -1
  26. package/dist/esm/index2.js +22 -13
  27. package/dist/esm/index2.js.map +1 -1
  28. package/dist/esm/index20.js +150 -206
  29. package/dist/esm/index20.js.map +1 -1
  30. package/dist/esm/index21.js +140 -166
  31. package/dist/esm/index21.js.map +1 -1
  32. package/dist/esm/index22.js +47 -105
  33. package/dist/esm/index22.js.map +1 -1
  34. package/dist/esm/index23.js +24 -89
  35. package/dist/esm/index23.js.map +1 -1
  36. package/dist/esm/index24.js +83 -56
  37. package/dist/esm/index24.js.map +1 -1
  38. package/dist/esm/index25.js +236 -32
  39. package/dist/esm/index25.js.map +1 -1
  40. package/dist/esm/index5.js +1 -1
  41. package/dist/esm/index6.js +295 -17
  42. package/dist/esm/index6.js.map +1 -1
  43. package/dist/esm/index8.js +82 -8
  44. package/dist/esm/index8.js.map +1 -1
  45. package/dist/types/conversational-agent.d.ts +67 -8
  46. package/dist/types/index.d.ts +1 -0
  47. package/dist/types/langchain-agent.d.ts +8 -0
  48. package/dist/types/memory/SmartMemoryManager.d.ts +58 -21
  49. package/dist/types/memory/index.d.ts +1 -1
  50. package/package.json +3 -3
  51. package/src/context/ReferenceContextManager.ts +9 -4
  52. package/src/context/ReferenceResponseProcessor.ts +3 -4
  53. package/src/conversational-agent.ts +379 -31
  54. package/src/index.ts +2 -0
  55. package/src/langchain/ContentAwareAgentExecutor.ts +0 -1
  56. package/src/langchain-agent.ts +94 -11
  57. package/src/mcp/ContentProcessor.ts +13 -3
  58. package/src/mcp/adapters/langchain.ts +1 -9
  59. package/src/memory/ContentStorage.ts +3 -51
  60. package/src/memory/MemoryWindow.ts +4 -16
  61. package/src/memory/ReferenceIdGenerator.ts +0 -4
  62. package/src/memory/SmartMemoryManager.ts +400 -33
  63. package/src/memory/TokenCounter.ts +12 -16
  64. package/src/memory/index.ts +1 -1
  65. package/src/plugins/hcs-10/HCS10Plugin.ts +44 -14
  66. package/src/services/ContentStoreManager.ts +0 -3
  67. package/src/types/content-reference.ts +8 -8
  68. 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: memoryStats.totalActiveMessages + storageStats.totalMessages,
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
- // Update components with new configuration
209
- if (newConfig.maxTokens !== undefined || newConfig.reserveTokens !== undefined) {
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), // Last 5 active messages
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), // Last 10 stored messages
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
- // No specific maintenance needed currently
312
- // This method is reserved for future optimizations
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
- // Token overhead per message for chat completion format
14
- private static readonly MESSAGE_OVERHEAD = 3; // <|start|>role<|end|>content<|end|>
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 (error) {
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 as string);
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
- return messages.reduce((total, message) => {
73
- return total + this.countMessageTokens(message);
74
- }, 0);
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'; // Default fallback
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 (error) {
149
- console.warn('Error disposing encoding:', error);
144
+ } catch {
145
+ console.warn('Error disposing encoding');
150
146
  }
151
147
  }
152
148
  }
@@ -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';