@cmdoss/memwal-sdk 0.6.1 → 0.7.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.
Files changed (148) hide show
  1. package/dist/ai-sdk/PDWVectorStore.d.ts.map +1 -1
  2. package/dist/ai-sdk/PDWVectorStore.js +4 -1
  3. package/dist/ai-sdk/PDWVectorStore.js.map +1 -1
  4. package/dist/ai-sdk/tools.d.ts +2 -2
  5. package/dist/ai-sdk/tools.js +2 -2
  6. package/dist/browser.d.ts +5 -6
  7. package/dist/browser.d.ts.map +1 -1
  8. package/dist/browser.js +7 -6
  9. package/dist/browser.js.map +1 -1
  10. package/dist/client/ClientMemoryManager.d.ts +1 -0
  11. package/dist/client/ClientMemoryManager.d.ts.map +1 -1
  12. package/dist/client/ClientMemoryManager.js +5 -1
  13. package/dist/client/ClientMemoryManager.js.map +1 -1
  14. package/dist/client/SimplePDWClient.d.ts +24 -1
  15. package/dist/client/SimplePDWClient.d.ts.map +1 -1
  16. package/dist/client/SimplePDWClient.js +31 -9
  17. package/dist/client/SimplePDWClient.js.map +1 -1
  18. package/dist/client/namespaces/EmbeddingsNamespace.d.ts +1 -1
  19. package/dist/client/namespaces/EmbeddingsNamespace.js +1 -1
  20. package/dist/client/namespaces/IndexNamespace.d.ts +38 -9
  21. package/dist/client/namespaces/IndexNamespace.d.ts.map +1 -1
  22. package/dist/client/namespaces/IndexNamespace.js +77 -10
  23. package/dist/client/namespaces/IndexNamespace.js.map +1 -1
  24. package/dist/client/namespaces/MemoryNamespace.d.ts +27 -0
  25. package/dist/client/namespaces/MemoryNamespace.d.ts.map +1 -1
  26. package/dist/client/namespaces/MemoryNamespace.js +104 -0
  27. package/dist/client/namespaces/MemoryNamespace.js.map +1 -1
  28. package/dist/client/namespaces/SearchNamespace.d.ts.map +1 -1
  29. package/dist/client/namespaces/SearchNamespace.js +25 -14
  30. package/dist/client/namespaces/SearchNamespace.js.map +1 -1
  31. package/dist/client/namespaces/consolidated/AINamespace.d.ts +2 -2
  32. package/dist/client/namespaces/consolidated/AINamespace.js +2 -2
  33. package/dist/client/namespaces/consolidated/BlockchainNamespace.d.ts.map +1 -1
  34. package/dist/client/namespaces/consolidated/BlockchainNamespace.js +69 -1
  35. package/dist/client/namespaces/consolidated/BlockchainNamespace.js.map +1 -1
  36. package/dist/client/namespaces/consolidated/StorageNamespace.d.ts +46 -0
  37. package/dist/client/namespaces/consolidated/StorageNamespace.d.ts.map +1 -1
  38. package/dist/client/namespaces/consolidated/StorageNamespace.js +34 -0
  39. package/dist/client/namespaces/consolidated/StorageNamespace.js.map +1 -1
  40. package/dist/graph/GraphService.js +2 -2
  41. package/dist/graph/GraphService.js.map +1 -1
  42. package/dist/index.d.ts +3 -1
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +3 -1
  45. package/dist/index.js.map +1 -1
  46. package/dist/permissions/ConsentRepository.browser.d.ts +56 -0
  47. package/dist/permissions/ConsentRepository.browser.d.ts.map +1 -0
  48. package/dist/permissions/ConsentRepository.browser.js +198 -0
  49. package/dist/permissions/ConsentRepository.browser.js.map +1 -0
  50. package/dist/retrieval/MemoryRetrievalService.d.ts +31 -0
  51. package/dist/retrieval/MemoryRetrievalService.d.ts.map +1 -1
  52. package/dist/retrieval/MemoryRetrievalService.js +44 -4
  53. package/dist/retrieval/MemoryRetrievalService.js.map +1 -1
  54. package/dist/services/EmbeddingService.d.ts +28 -1
  55. package/dist/services/EmbeddingService.d.ts.map +1 -1
  56. package/dist/services/EmbeddingService.js +54 -0
  57. package/dist/services/EmbeddingService.js.map +1 -1
  58. package/dist/services/GeminiAIService.d.ts.map +1 -1
  59. package/dist/services/GeminiAIService.js +283 -27
  60. package/dist/services/GeminiAIService.js.map +1 -1
  61. package/dist/services/IndexManager.d.ts +5 -1
  62. package/dist/services/IndexManager.d.ts.map +1 -1
  63. package/dist/services/IndexManager.js +17 -40
  64. package/dist/services/IndexManager.js.map +1 -1
  65. package/dist/services/MemoryIndexService.d.ts +31 -2
  66. package/dist/services/MemoryIndexService.d.ts.map +1 -1
  67. package/dist/services/MemoryIndexService.js +75 -3
  68. package/dist/services/MemoryIndexService.js.map +1 -1
  69. package/dist/services/QueryService.js +1 -1
  70. package/dist/services/QueryService.js.map +1 -1
  71. package/dist/services/StorageService.d.ts +10 -0
  72. package/dist/services/StorageService.d.ts.map +1 -1
  73. package/dist/services/StorageService.js +13 -0
  74. package/dist/services/StorageService.js.map +1 -1
  75. package/dist/services/storage/QuiltBatchManager.d.ts +111 -4
  76. package/dist/services/storage/QuiltBatchManager.d.ts.map +1 -1
  77. package/dist/services/storage/QuiltBatchManager.js +450 -38
  78. package/dist/services/storage/QuiltBatchManager.js.map +1 -1
  79. package/dist/services/storage/index.d.ts +1 -1
  80. package/dist/services/storage/index.d.ts.map +1 -1
  81. package/dist/services/storage/index.js.map +1 -1
  82. package/dist/utils/LRUCache.d.ts +106 -0
  83. package/dist/utils/LRUCache.d.ts.map +1 -0
  84. package/dist/utils/LRUCache.js +281 -0
  85. package/dist/utils/LRUCache.js.map +1 -0
  86. package/dist/utils/index.d.ts +1 -0
  87. package/dist/utils/index.d.ts.map +1 -1
  88. package/dist/utils/index.js +2 -0
  89. package/dist/utils/index.js.map +1 -1
  90. package/dist/utils/memoryIndexOnChain.d.ts +212 -0
  91. package/dist/utils/memoryIndexOnChain.d.ts.map +1 -0
  92. package/dist/utils/memoryIndexOnChain.js +312 -0
  93. package/dist/utils/memoryIndexOnChain.js.map +1 -0
  94. package/dist/utils/rebuildIndexNode.d.ts +29 -0
  95. package/dist/utils/rebuildIndexNode.d.ts.map +1 -1
  96. package/dist/utils/rebuildIndexNode.js +387 -45
  97. package/dist/utils/rebuildIndexNode.js.map +1 -1
  98. package/dist/vector/HnswWasmService.d.ts +20 -5
  99. package/dist/vector/HnswWasmService.d.ts.map +1 -1
  100. package/dist/vector/HnswWasmService.js +73 -40
  101. package/dist/vector/HnswWasmService.js.map +1 -1
  102. package/dist/vector/IHnswService.d.ts +10 -1
  103. package/dist/vector/IHnswService.d.ts.map +1 -1
  104. package/dist/vector/IHnswService.js.map +1 -1
  105. package/dist/vector/NodeHnswService.d.ts +16 -0
  106. package/dist/vector/NodeHnswService.d.ts.map +1 -1
  107. package/dist/vector/NodeHnswService.js +108 -10
  108. package/dist/vector/NodeHnswService.js.map +1 -1
  109. package/dist/vector/createHnswService.d.ts +1 -1
  110. package/dist/vector/createHnswService.js +1 -1
  111. package/dist/vector/index.d.ts +1 -1
  112. package/dist/vector/index.js +1 -1
  113. package/package.json +157 -157
  114. package/src/ai-sdk/PDWVectorStore.ts +4 -1
  115. package/src/ai-sdk/tools.ts +2 -2
  116. package/src/browser.ts +15 -10
  117. package/src/client/ClientMemoryManager.ts +6 -1
  118. package/src/client/SimplePDWClient.ts +63 -10
  119. package/src/client/namespaces/EmbeddingsNamespace.ts +1 -1
  120. package/src/client/namespaces/IndexNamespace.ts +89 -11
  121. package/src/client/namespaces/MemoryNamespace.ts +137 -0
  122. package/src/client/namespaces/SearchNamespace.ts +27 -14
  123. package/src/client/namespaces/consolidated/AINamespace.ts +2 -2
  124. package/src/client/namespaces/consolidated/BlockchainNamespace.ts +73 -1
  125. package/src/client/namespaces/consolidated/StorageNamespace.ts +57 -0
  126. package/src/core/types/index.ts +1 -1
  127. package/src/generated/pdw/capability.ts +319 -319
  128. package/src/graph/GraphService.ts +2 -2
  129. package/src/index.ts +25 -1
  130. package/src/permissions/ConsentRepository.browser.ts +249 -0
  131. package/src/retrieval/MemoryRetrievalService.ts +78 -4
  132. package/src/services/EmbeddingService.ts +66 -1
  133. package/src/services/GeminiAIService.ts +283 -27
  134. package/src/services/IndexManager.ts +18 -45
  135. package/src/services/MemoryIndexService.ts +85 -3
  136. package/src/services/QueryService.ts +1 -1
  137. package/src/services/StorageService.ts +15 -0
  138. package/src/services/storage/QuiltBatchManager.ts +538 -42
  139. package/src/services/storage/index.ts +6 -1
  140. package/src/utils/LRUCache.ts +378 -0
  141. package/src/utils/index.ts +8 -0
  142. package/src/utils/memoryIndexOnChain.ts +507 -0
  143. package/src/utils/rebuildIndexNode.ts +482 -52
  144. package/src/vector/HnswWasmService.ts +95 -43
  145. package/src/vector/IHnswService.ts +10 -1
  146. package/src/vector/NodeHnswService.ts +130 -10
  147. package/src/vector/createHnswService.ts +1 -1
  148. package/src/vector/index.ts +1 -1
package/src/index.ts CHANGED
@@ -33,7 +33,12 @@ import type { PipelineConfig, PipelineManagerConfig } from './pipeline';
33
33
  // ==================== SERVICES ====================
34
34
  // Business logic services
35
35
  export { StorageService } from './services/StorageService';
36
- export { EmbeddingService } from './services/EmbeddingService';
36
+ export {
37
+ EmbeddingService,
38
+ getSharedEmbeddingService,
39
+ clearSharedEmbeddingServices,
40
+ getSharedEmbeddingStats,
41
+ } from './services/EmbeddingService';
37
42
  export { GeminiAIService } from './services/GeminiAIService';
38
43
  export { QueryService } from './services/QueryService';
39
44
  export { ClassifierService } from './services/ClassifierService';
@@ -454,6 +459,25 @@ export type { RebuildIndexOptions, RebuildIndexResult } from './utils/rebuildInd
454
459
  export { rebuildIndexNode, hasExistingIndexNode, clearIndexNode } from './utils/rebuildIndexNode';
455
460
  export type { RebuildIndexNodeOptions, RebuildIndexNodeResult } from './utils/rebuildIndexNode';
456
461
 
462
+ // MemoryIndex on-chain utilities
463
+ export {
464
+ getMemoryIndex,
465
+ updateMemoryIndexOnChain,
466
+ createMemoryIndexOnChain,
467
+ syncIndexAndUpdateOnChain,
468
+ uploadPlaceholderToWalrus
469
+ } from './utils/memoryIndexOnChain';
470
+ export type {
471
+ OnChainMemoryIndex,
472
+ GetMemoryIndexOptions,
473
+ UpdateMemoryIndexOnChainOptions,
474
+ CreateMemoryIndexOnChainOptions,
475
+ UpdateMemoryIndexResult,
476
+ CreateMemoryIndexResult,
477
+ SyncAndUpdateOptions,
478
+ SyncAndUpdateResult
479
+ } from './utils/memoryIndexOnChain';
480
+
457
481
  // ==================== AI SDK INTEGRATION ====================
458
482
  // AI SDK tools and vector store for Vercel AI SDK integration
459
483
  export { pdwTools } from './ai-sdk/tools';
@@ -0,0 +1,249 @@
1
+ /**
2
+ * Browser-safe Consent Repository implementations
3
+ *
4
+ * This file contains only browser-compatible implementations.
5
+ * FileSystemConsentRepository is excluded as it requires Node.js fs/promises.
6
+ */
7
+
8
+ import {
9
+ ConsentRequestRecord,
10
+ ConsentStatus,
11
+ } from '../core/types/wallet.js';
12
+ import { normalizeSuiAddress } from '@mysten/sui/utils';
13
+
14
+ export interface ConsentRepository {
15
+ save(request: ConsentRequestRecord): Promise<void>;
16
+ updateStatus(requestId: string, status: ConsentStatus, updatedAt: number): Promise<void>;
17
+ getById(requestId: string): Promise<ConsentRequestRecord | null>;
18
+ listByTarget(targetWallet: string, status?: ConsentStatus): Promise<ConsentRequestRecord[]>;
19
+ listByRequester(requesterWallet: string, status?: ConsentStatus): Promise<ConsentRequestRecord[]>;
20
+ delete(requestId: string): Promise<void>;
21
+ }
22
+
23
+ interface StoredConsentRecord extends ConsentRequestRecord {}
24
+
25
+ function normalizeRecord(record: ConsentRequestRecord): StoredConsentRecord {
26
+ return {
27
+ ...record,
28
+ requesterWallet: normalizeSuiAddress(record.requesterWallet),
29
+ targetWallet: normalizeSuiAddress(record.targetWallet),
30
+ };
31
+ }
32
+
33
+ export class InMemoryConsentRepository implements ConsentRepository {
34
+ private store = new Map<string, StoredConsentRecord>();
35
+
36
+ async save(request: ConsentRequestRecord): Promise<void> {
37
+ const normalized = normalizeRecord(request);
38
+ this.store.set(normalized.requestId, normalized);
39
+ }
40
+
41
+ async updateStatus(requestId: string, status: ConsentStatus, updatedAt: number): Promise<void> {
42
+ const record = this.store.get(requestId);
43
+ if (!record) {
44
+ return;
45
+ }
46
+ this.store.set(requestId, {
47
+ ...record,
48
+ status,
49
+ updatedAt,
50
+ });
51
+ }
52
+
53
+ async getById(requestId: string): Promise<ConsentRequestRecord | null> {
54
+ const record = this.store.get(requestId);
55
+ return record ? { ...record } : null;
56
+ }
57
+
58
+ async listByTarget(targetWallet: string, status?: ConsentStatus): Promise<ConsentRequestRecord[]> {
59
+ const normalizedTarget = normalizeSuiAddress(targetWallet);
60
+ return Array.from(this.store.values())
61
+ .filter((record) => record.targetWallet === normalizedTarget)
62
+ .filter((record) => (status ? record.status === status : true))
63
+ .map((record) => ({ ...record }));
64
+ }
65
+
66
+ async listByRequester(requesterWallet: string, status?: ConsentStatus): Promise<ConsentRequestRecord[]> {
67
+ const normalizedRequester = normalizeSuiAddress(requesterWallet);
68
+ return Array.from(this.store.values())
69
+ .filter((record) => record.requesterWallet === normalizedRequester)
70
+ .filter((record) => (status ? record.status === status : true))
71
+ .map((record) => ({ ...record }));
72
+ }
73
+
74
+ async delete(requestId: string): Promise<void> {
75
+ this.store.delete(requestId);
76
+ }
77
+ }
78
+
79
+ /**
80
+ * IndexedDBConsentRepository - Browser-compatible implementation
81
+ * Uses IndexedDB for persistent storage in browser environments.
82
+ */
83
+ export class IndexedDBConsentRepository implements ConsentRepository {
84
+ private dbName = 'pdw-consent-store';
85
+ private storeName = 'consent-requests';
86
+ private dbVersion = 1;
87
+ private db: IDBDatabase | null = null;
88
+
89
+ constructor(options?: { dbName?: string }) {
90
+ if (options?.dbName) {
91
+ this.dbName = options.dbName;
92
+ }
93
+ }
94
+
95
+ private async getDB(): Promise<IDBDatabase> {
96
+ if (this.db) {
97
+ return this.db;
98
+ }
99
+
100
+ return new Promise((resolve, reject) => {
101
+ const request = indexedDB.open(this.dbName, this.dbVersion);
102
+
103
+ request.onerror = () => reject(request.error);
104
+ request.onsuccess = () => {
105
+ this.db = request.result;
106
+ resolve(request.result);
107
+ };
108
+
109
+ request.onupgradeneeded = (event) => {
110
+ const db = (event.target as IDBOpenDBRequest).result;
111
+ if (!db.objectStoreNames.contains(this.storeName)) {
112
+ const store = db.createObjectStore(this.storeName, { keyPath: 'requestId' });
113
+ store.createIndex('targetWallet', 'targetWallet', { unique: false });
114
+ store.createIndex('requesterWallet', 'requesterWallet', { unique: false });
115
+ store.createIndex('status', 'status', { unique: false });
116
+ }
117
+ };
118
+ });
119
+ }
120
+
121
+ async save(request: ConsentRequestRecord): Promise<void> {
122
+ const db = await this.getDB();
123
+ const normalized = normalizeRecord(request);
124
+
125
+ return new Promise((resolve, reject) => {
126
+ const tx = db.transaction(this.storeName, 'readwrite');
127
+ const store = tx.objectStore(this.storeName);
128
+ const req = store.put(normalized);
129
+
130
+ req.onerror = () => reject(req.error);
131
+ req.onsuccess = () => resolve();
132
+ });
133
+ }
134
+
135
+ async updateStatus(requestId: string, status: ConsentStatus, updatedAt: number): Promise<void> {
136
+ const record = await this.getById(requestId);
137
+ if (!record) {
138
+ return;
139
+ }
140
+
141
+ await this.save({
142
+ ...record,
143
+ status,
144
+ updatedAt,
145
+ });
146
+ }
147
+
148
+ async getById(requestId: string): Promise<ConsentRequestRecord | null> {
149
+ const db = await this.getDB();
150
+
151
+ return new Promise((resolve, reject) => {
152
+ const tx = db.transaction(this.storeName, 'readonly');
153
+ const store = tx.objectStore(this.storeName);
154
+ const req = store.get(requestId);
155
+
156
+ req.onerror = () => reject(req.error);
157
+ req.onsuccess = () => {
158
+ const record = req.result as StoredConsentRecord | undefined;
159
+ resolve(record ? { ...record } : null);
160
+ };
161
+ });
162
+ }
163
+
164
+ async listByTarget(targetWallet: string, status?: ConsentStatus): Promise<ConsentRequestRecord[]> {
165
+ const normalizedTarget = normalizeSuiAddress(targetWallet);
166
+ const db = await this.getDB();
167
+
168
+ return new Promise((resolve, reject) => {
169
+ const tx = db.transaction(this.storeName, 'readonly');
170
+ const store = tx.objectStore(this.storeName);
171
+ const index = store.index('targetWallet');
172
+ const req = index.getAll(normalizedTarget);
173
+
174
+ req.onerror = () => reject(req.error);
175
+ req.onsuccess = () => {
176
+ let records = req.result as StoredConsentRecord[];
177
+ if (status) {
178
+ records = records.filter((r) => r.status === status);
179
+ }
180
+ resolve(records.map((r) => ({ ...r })));
181
+ };
182
+ });
183
+ }
184
+
185
+ async listByRequester(requesterWallet: string, status?: ConsentStatus): Promise<ConsentRequestRecord[]> {
186
+ const normalizedRequester = normalizeSuiAddress(requesterWallet);
187
+ const db = await this.getDB();
188
+
189
+ return new Promise((resolve, reject) => {
190
+ const tx = db.transaction(this.storeName, 'readonly');
191
+ const store = tx.objectStore(this.storeName);
192
+ const index = store.index('requesterWallet');
193
+ const req = index.getAll(normalizedRequester);
194
+
195
+ req.onerror = () => reject(req.error);
196
+ req.onsuccess = () => {
197
+ let records = req.result as StoredConsentRecord[];
198
+ if (status) {
199
+ records = records.filter((r) => r.status === status);
200
+ }
201
+ resolve(records.map((r) => ({ ...r })));
202
+ };
203
+ });
204
+ }
205
+
206
+ async delete(requestId: string): Promise<void> {
207
+ const db = await this.getDB();
208
+
209
+ return new Promise((resolve, reject) => {
210
+ const tx = db.transaction(this.storeName, 'readwrite');
211
+ const store = tx.objectStore(this.storeName);
212
+ const req = store.delete(requestId);
213
+
214
+ req.onerror = () => reject(req.error);
215
+ req.onsuccess = () => resolve();
216
+ });
217
+ }
218
+
219
+ /**
220
+ * Close the database connection
221
+ */
222
+ close(): void {
223
+ if (this.db) {
224
+ this.db.close();
225
+ this.db = null;
226
+ }
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Factory function to create the appropriate ConsentRepository for browser
232
+ */
233
+ export function createBrowserConsentRepository(options?: {
234
+ dbName?: string;
235
+ forceInMemory?: boolean;
236
+ }): ConsentRepository {
237
+ // Force in-memory for testing
238
+ if (options?.forceInMemory) {
239
+ return new InMemoryConsentRepository();
240
+ }
241
+
242
+ // Browser environment - use IndexedDB
243
+ if (typeof window !== 'undefined' && typeof indexedDB !== 'undefined') {
244
+ return new IndexedDBConsentRepository({ dbName: options?.dbName });
245
+ }
246
+
247
+ // Fallback to in-memory
248
+ return new InMemoryConsentRepository();
249
+ }
@@ -182,10 +182,15 @@ export class MemoryRetrievalService {
182
182
  private contentCache = new Map<string, { content: string; metadata: any; timestamp: number }>();
183
183
  private analyticsCache = new Map<string, { analytics: any; timestamp: number }>();
184
184
 
185
- // Cache TTL settings
186
- private readonly QUERY_CACHE_TTL = 5 * 60 * 1000; // 5 minutes
187
- private readonly CONTENT_CACHE_TTL = 30 * 60 * 1000; // 30 minutes
188
- private readonly ANALYTICS_CACHE_TTL = 60 * 60 * 1000; // 1 hour
185
+ // Cache TTL settings (configurable)
186
+ private readonly QUERY_CACHE_TTL: number;
187
+ private readonly CONTENT_CACHE_TTL: number;
188
+ private readonly ANALYTICS_CACHE_TTL: number;
189
+
190
+ // Cache size limits to prevent memory leaks (configurable)
191
+ private readonly MAX_QUERY_CACHE_SIZE: number;
192
+ private readonly MAX_CONTENT_CACHE_SIZE: number;
193
+ private readonly MAX_ANALYTICS_CACHE_SIZE: number;
189
194
 
190
195
  constructor(config?: {
191
196
  embeddingService?: EmbeddingService;
@@ -196,7 +201,29 @@ export class MemoryRetrievalService {
196
201
  encryptionService?: EncryptionService;
197
202
  batchManager?: BatchManager;
198
203
  decryptionConfig?: DecryptionConfig;
204
+ /** Cache configuration for memory management */
205
+ cacheConfig?: {
206
+ /** Query cache TTL in ms (default: 5 minutes) */
207
+ queryCacheTtlMs?: number;
208
+ /** Content cache TTL in ms (default: 30 minutes) */
209
+ contentCacheTtlMs?: number;
210
+ /** Analytics cache TTL in ms (default: 1 hour) */
211
+ analyticsCacheTtlMs?: number;
212
+ /** Max query cache entries (default: 100) */
213
+ maxQueryCacheSize?: number;
214
+ /** Max content cache entries (default: 50) */
215
+ maxContentCacheSize?: number;
216
+ /** Max analytics cache entries (default: 100) */
217
+ maxAnalyticsCacheSize?: number;
218
+ };
199
219
  }) {
220
+ // Initialize cache settings with configurable defaults
221
+ this.QUERY_CACHE_TTL = config?.cacheConfig?.queryCacheTtlMs ?? 5 * 60 * 1000; // 5 minutes
222
+ this.CONTENT_CACHE_TTL = config?.cacheConfig?.contentCacheTtlMs ?? 30 * 60 * 1000; // 30 minutes
223
+ this.ANALYTICS_CACHE_TTL = config?.cacheConfig?.analyticsCacheTtlMs ?? 60 * 60 * 1000; // 1 hour
224
+ this.MAX_QUERY_CACHE_SIZE = config?.cacheConfig?.maxQueryCacheSize ?? 100;
225
+ this.MAX_CONTENT_CACHE_SIZE = config?.cacheConfig?.maxContentCacheSize ?? 50; // Content can be large
226
+ this.MAX_ANALYTICS_CACHE_SIZE = config?.cacheConfig?.maxAnalyticsCacheSize ?? 100;
200
227
  // Initialize services (can be injected or created with default configs)
201
228
  this.embeddingService = config?.embeddingService ?? new EmbeddingService();
202
229
  this.storageManager = config?.storageManager ?? new StorageManager();
@@ -684,9 +711,56 @@ export class MemoryRetrievalService {
684
711
  }
685
712
 
686
713
  private cacheQuery(key: string, result: RetrievalContext): void {
714
+ this.enforceCacheLimit(this.queryCache, this.MAX_QUERY_CACHE_SIZE, this.QUERY_CACHE_TTL);
687
715
  this.queryCache.set(key, { result, timestamp: Date.now() });
688
716
  }
689
717
 
718
+ /**
719
+ * Cache content with size limit enforcement
720
+ */
721
+ private cacheContent(key: string, content: string, metadata: any): void {
722
+ this.enforceCacheLimit(this.contentCache, this.MAX_CONTENT_CACHE_SIZE, this.CONTENT_CACHE_TTL);
723
+ this.contentCache.set(key, { content, metadata, timestamp: Date.now() });
724
+ }
725
+
726
+ /**
727
+ * Cache analytics with size limit enforcement
728
+ */
729
+ private cacheAnalytics(key: string, analytics: any): void {
730
+ this.enforceCacheLimit(this.analyticsCache, this.MAX_ANALYTICS_CACHE_SIZE, this.ANALYTICS_CACHE_TTL);
731
+ this.analyticsCache.set(key, { analytics, timestamp: Date.now() });
732
+ }
733
+
734
+ /**
735
+ * Enforce cache size limit using LRU eviction (oldest entries first)
736
+ * Also removes expired entries based on TTL
737
+ */
738
+ private enforceCacheLimit<T extends { timestamp: number }>(
739
+ cache: Map<string, T>,
740
+ maxSize: number,
741
+ ttl: number
742
+ ): void {
743
+ // First, clean up expired entries
744
+ const now = Date.now();
745
+
746
+ for (const [key, value] of cache.entries()) {
747
+ if (now - value.timestamp > ttl) {
748
+ cache.delete(key);
749
+ }
750
+ }
751
+
752
+ // If still over limit, remove oldest entries (LRU)
753
+ if (cache.size >= maxSize) {
754
+ const entries = Array.from(cache.entries())
755
+ .sort((a, b) => a[1].timestamp - b[1].timestamp);
756
+
757
+ const toRemove = cache.size - maxSize + 1; // +1 to make room for new entry
758
+ for (let i = 0; i < toRemove && i < entries.length; i++) {
759
+ cache.delete(entries[i][0]);
760
+ }
761
+ }
762
+ }
763
+
690
764
  // ==================== UTILITY METHODS ====================
691
765
 
692
766
  private calculateCacheHitRate(): number {
@@ -83,7 +83,7 @@ export interface EmbeddingConfig {
83
83
 
84
84
  /**
85
85
  * Embedding dimensions (optional, provider-dependent)
86
- * - Google: Up to 768
86
+ * - Google: Up to 3072
87
87
  * - OpenAI: 256, 512, 1024, 1536, 3072 (depending on model)
88
88
  * - OpenRouter: Depends on the underlying model
89
89
  * - Cohere: Model-specific
@@ -704,3 +704,68 @@ export class EmbeddingService {
704
704
  }
705
705
 
706
706
  export default EmbeddingService;
707
+
708
+ // ==================== Singleton Pattern ====================
709
+
710
+ /**
711
+ * Generate config key for singleton cache
712
+ */
713
+ function getConfigKey(config: EmbeddingConfig): string {
714
+ const provider = config.provider || 'google';
715
+ const modelName = typeof config.model === 'string'
716
+ ? config.model
717
+ : (config.modelName || 'default');
718
+ const dimensions = config.dimensions || 'default';
719
+ return `${provider}:${modelName}:${dimensions}`;
720
+ }
721
+
722
+ /** Singleton cache */
723
+ const sharedInstances = new Map<string, EmbeddingService>();
724
+
725
+ /**
726
+ * Get or create a shared EmbeddingService instance (Singleton)
727
+ *
728
+ * All clients with same provider/model/dimensions share one instance.
729
+ * Reduces memory usage and connection overhead.
730
+ *
731
+ * @example
732
+ * ```typescript
733
+ * // Instead of: new EmbeddingService({ apiKey, modelName })
734
+ * const embedding = getSharedEmbeddingService({ apiKey, modelName });
735
+ * ```
736
+ */
737
+ export function getSharedEmbeddingService(config: EmbeddingConfig): EmbeddingService {
738
+ const key = getConfigKey(config);
739
+
740
+ let instance = sharedInstances.get(key);
741
+ if (!instance) {
742
+ console.log(`🔧 [Singleton] Creating shared EmbeddingService: ${key}`);
743
+ instance = new EmbeddingService(config);
744
+ sharedInstances.set(key, instance);
745
+ }
746
+
747
+ return instance;
748
+ }
749
+
750
+ /**
751
+ * Clear all shared instances (for testing)
752
+ */
753
+ export function clearSharedEmbeddingServices(): void {
754
+ sharedInstances.clear();
755
+ }
756
+
757
+ /**
758
+ * Get singleton stats
759
+ */
760
+ export function getSharedEmbeddingStats(): {
761
+ instanceCount: number;
762
+ instances: Array<{ key: string; stats: ReturnType<EmbeddingService['getStats']> }>;
763
+ } {
764
+ return {
765
+ instanceCount: sharedInstances.size,
766
+ instances: Array.from(sharedInstances.entries()).map(([key, svc]) => ({
767
+ key,
768
+ stats: svc.getStats(),
769
+ })),
770
+ };
771
+ }