@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,462 +1,462 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* VectorService - Unified Vector Operations
|
|
3
|
-
*
|
|
4
|
-
* Consolidated service combining embedding generation and HNSW indexing
|
|
5
|
-
* with smart caching and Walrus persistence.
|
|
6
|
-
*
|
|
7
|
-
* Uses hybrid HNSW implementation:
|
|
8
|
-
* - Browser: hnswlib-wasm
|
|
9
|
-
* - Node.js: hnswlib-node
|
|
10
|
-
*
|
|
11
|
-
* Replaces: HnswIndexService + VectorManager
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { createHnswService, isBrowser, isNode } from '../vector/createHnswService';
|
|
15
|
-
import type { IHnswService, HnswServiceConfig } from '../vector/IHnswService';
|
|
16
|
-
import { EmbeddingService } from './EmbeddingService';
|
|
17
|
-
import { StorageService } from './StorageService';
|
|
18
|
-
import {
|
|
19
|
-
VectorEmbedding,
|
|
20
|
-
EmbeddingConfig,
|
|
21
|
-
HNSWIndexConfig,
|
|
22
|
-
BatchConfig,
|
|
23
|
-
VectorSearchOptions,
|
|
24
|
-
VectorSearchResult
|
|
25
|
-
} from '../embedding/types';
|
|
26
|
-
import type { MemoryMetadata } from './StorageService';
|
|
27
|
-
|
|
28
|
-
// Local VectorError class implementation
|
|
29
|
-
class VectorErrorImpl extends Error {
|
|
30
|
-
constructor(message: string) {
|
|
31
|
-
super(message);
|
|
32
|
-
this.name = 'VectorError';
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface VectorServiceConfig {
|
|
37
|
-
embedding: EmbeddingConfig;
|
|
38
|
-
index?: Partial<HNSWIndexConfig>;
|
|
39
|
-
batch?: Partial<BatchConfig>;
|
|
40
|
-
enableAutoIndex?: boolean;
|
|
41
|
-
enableMemoryCache?: boolean;
|
|
42
|
-
/** Pre-initialized HNSW service instance (shared singleton) */
|
|
43
|
-
hnswService?: IHnswService;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
interface IndexCacheEntry {
|
|
47
|
-
lastModified: Date;
|
|
48
|
-
pendingVectors: Map<number, number[]>;
|
|
49
|
-
isDirty: boolean;
|
|
50
|
-
version: number;
|
|
51
|
-
metadata: Map<number, any>;
|
|
52
|
-
/** Cached vectors for serialization (vectorId -> vector) */
|
|
53
|
-
vectors: Map<number, number[]>;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* VectorService provides unified vector operations including:
|
|
58
|
-
* - Embedding generation via EmbeddingService
|
|
59
|
-
* - HNSW vector indexing and search (hybrid: hnswlib-wasm for browser, hnswlib-node for Node.js)
|
|
60
|
-
* - Intelligent batching and caching
|
|
61
|
-
* - Persistence via Walrus storage
|
|
62
|
-
*/
|
|
63
|
-
export class VectorService {
|
|
64
|
-
private embeddingService: EmbeddingService;
|
|
65
|
-
private storageService: StorageService;
|
|
66
|
-
private indexCache: Map<string, IndexCacheEntry> = new Map();
|
|
67
|
-
private hnswService: IHnswService | null = null;
|
|
68
|
-
private hnswServicePromise: Promise<IHnswService> | null = null;
|
|
69
|
-
|
|
70
|
-
constructor(
|
|
71
|
-
private config: VectorServiceConfig,
|
|
72
|
-
embeddingService?: EmbeddingService,
|
|
73
|
-
storageService?: StorageService
|
|
74
|
-
) {
|
|
75
|
-
this.embeddingService = embeddingService || new EmbeddingService(config.embedding);
|
|
76
|
-
this.storageService = storageService || new StorageService({ packageId: '' }); // Will be properly configured
|
|
77
|
-
|
|
78
|
-
// Use pre-initialized HNSW service if provided (shared singleton pattern)
|
|
79
|
-
if (config.hnswService) {
|
|
80
|
-
this.hnswService = config.hnswService;
|
|
81
|
-
console.log('✅ VectorService using shared HNSW service instance');
|
|
82
|
-
} else {
|
|
83
|
-
const envType = isBrowser() ? 'browser (hnswlib-wasm)' : isNode() ? 'Node.js (hnswlib-node)' : 'unknown';
|
|
84
|
-
console.log(`✅ VectorService initializing with hybrid HNSW (${envType})`);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Get the HNSW service, initializing it if needed
|
|
90
|
-
*/
|
|
91
|
-
private async getHnswService(): Promise<IHnswService> {
|
|
92
|
-
if (this.hnswService) {
|
|
93
|
-
return this.hnswService;
|
|
94
|
-
}
|
|
95
|
-
if (this.hnswServicePromise) {
|
|
96
|
-
return this.hnswServicePromise;
|
|
97
|
-
}
|
|
98
|
-
throw new VectorErrorImpl('HNSW service not initialized. Call initialize() first.');
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Initialize the HNSW service (must be called before using any index operations)
|
|
103
|
-
* If a shared HNSW service was provided in config, this is a no-op.
|
|
104
|
-
*/
|
|
105
|
-
async initialize(): Promise<void> {
|
|
106
|
-
if (this.hnswService) {
|
|
107
|
-
return; // Already initialized (or using shared instance)
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (this.hnswServicePromise) {
|
|
111
|
-
await this.hnswServicePromise;
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Create own HNSW service only if not provided externally
|
|
116
|
-
this.hnswServicePromise = createHnswService({
|
|
117
|
-
indexConfig: {
|
|
118
|
-
dimension: this.config.index?.dimension || 3072,
|
|
119
|
-
maxElements: this.config.index?.maxElements || 10000,
|
|
120
|
-
efConstruction: this.config.index?.efConstruction || 200,
|
|
121
|
-
m: this.config.index?.m || 16
|
|
122
|
-
},
|
|
123
|
-
batchConfig: this.config.batch
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
try {
|
|
127
|
-
this.hnswService = await this.hnswServicePromise;
|
|
128
|
-
console.log('✅ VectorService HNSW service initialized successfully');
|
|
129
|
-
} catch (error) {
|
|
130
|
-
this.hnswServicePromise = null;
|
|
131
|
-
console.error('❌ Failed to initialize VectorService HNSW service:', error);
|
|
132
|
-
throw error;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Generate embeddings for text content
|
|
138
|
-
*/
|
|
139
|
-
async generateEmbedding(text: string): Promise<VectorEmbedding> {
|
|
140
|
-
const result = await this.embeddingService.embedText({ text });
|
|
141
|
-
return {
|
|
142
|
-
vector: result.vector,
|
|
143
|
-
dimension: result.dimension,
|
|
144
|
-
model: result.model
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Create or get HNSW index for a specific space
|
|
150
|
-
*/
|
|
151
|
-
async createIndex(spaceId: string, dimension?: number, config?: Partial<HNSWIndexConfig>): Promise<void> {
|
|
152
|
-
await this.initialize();
|
|
153
|
-
const hnswService = await this.getHnswService();
|
|
154
|
-
|
|
155
|
-
// Use IHnswService.getOrCreateIndex to create the index
|
|
156
|
-
await hnswService.getOrCreateIndex(spaceId);
|
|
157
|
-
|
|
158
|
-
// Initialize local cache entry for metadata tracking
|
|
159
|
-
if (!this.indexCache.has(spaceId)) {
|
|
160
|
-
this.indexCache.set(spaceId, {
|
|
161
|
-
lastModified: new Date(),
|
|
162
|
-
pendingVectors: new Map(),
|
|
163
|
-
isDirty: false,
|
|
164
|
-
version: 1,
|
|
165
|
-
metadata: new Map(),
|
|
166
|
-
vectors: new Map()
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Add vector to index
|
|
173
|
-
*/
|
|
174
|
-
async addVector(spaceId: string, vectorId: number, vector: number[], metadata?: any): Promise<void> {
|
|
175
|
-
const hnswService = await this.getHnswService();
|
|
176
|
-
|
|
177
|
-
// Add to HNSW index
|
|
178
|
-
await hnswService.addVector(spaceId, vectorId, vector, metadata);
|
|
179
|
-
|
|
180
|
-
// Update local cache entry for metadata tracking
|
|
181
|
-
let entry = this.indexCache.get(spaceId);
|
|
182
|
-
if (!entry) {
|
|
183
|
-
entry = {
|
|
184
|
-
lastModified: new Date(),
|
|
185
|
-
pendingVectors: new Map(),
|
|
186
|
-
isDirty: true,
|
|
187
|
-
version: 1,
|
|
188
|
-
metadata: new Map(),
|
|
189
|
-
vectors: new Map()
|
|
190
|
-
};
|
|
191
|
-
this.indexCache.set(spaceId, entry);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
if (metadata) {
|
|
195
|
-
entry.metadata.set(vectorId, metadata);
|
|
196
|
-
}
|
|
197
|
-
// Cache vector for serialization
|
|
198
|
-
entry.vectors.set(vectorId, vector);
|
|
199
|
-
entry.isDirty = true;
|
|
200
|
-
entry.lastModified = new Date();
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Get cached vector by ID
|
|
205
|
-
*/
|
|
206
|
-
getVector(spaceId: string, vectorId: number): number[] | undefined {
|
|
207
|
-
const entry = this.indexCache.get(spaceId);
|
|
208
|
-
return entry?.vectors.get(vectorId);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Get all cached vectors
|
|
213
|
-
*/
|
|
214
|
-
getAllCachedVectors(spaceId: string): Map<number, number[]> {
|
|
215
|
-
const entry = this.indexCache.get(spaceId);
|
|
216
|
-
return entry?.vectors || new Map();
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Search vectors in index
|
|
221
|
-
*/
|
|
222
|
-
async searchVectors(
|
|
223
|
-
spaceId: string,
|
|
224
|
-
queryVector: number[],
|
|
225
|
-
options?: Partial<VectorSearchOptions>
|
|
226
|
-
): Promise<VectorSearchResult> {
|
|
227
|
-
const hnswService = await this.getHnswService();
|
|
228
|
-
const entry = this.indexCache.get(spaceId);
|
|
229
|
-
|
|
230
|
-
const k = options?.k || 10;
|
|
231
|
-
const startTime = performance.now();
|
|
232
|
-
|
|
233
|
-
// Use IHnswService.search
|
|
234
|
-
const searchResults = await hnswService.search(spaceId, queryVector, {
|
|
235
|
-
k,
|
|
236
|
-
ef: options?.efSearch || 50
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
const searchTime = performance.now() - startTime;
|
|
240
|
-
|
|
241
|
-
// Get metadata - prefer HNSW service metadata (loaded from disk) over local cache
|
|
242
|
-
return {
|
|
243
|
-
results: searchResults.map((result) => ({
|
|
244
|
-
memoryId: result.vectorId.toString(),
|
|
245
|
-
vectorId: result.vectorId,
|
|
246
|
-
similarity: result.score,
|
|
247
|
-
distance: result.distance,
|
|
248
|
-
metadata: result.metadata || entry?.metadata.get(result.vectorId)
|
|
249
|
-
})),
|
|
250
|
-
searchStats: {
|
|
251
|
-
searchTime,
|
|
252
|
-
nodesVisited: searchResults.length,
|
|
253
|
-
exactMatches: searchResults.length,
|
|
254
|
-
approximateMatches: 0,
|
|
255
|
-
cacheHits: 0,
|
|
256
|
-
indexSize: 0 // TODO: Get from IHnswService
|
|
257
|
-
}
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Save index to Walrus storage
|
|
263
|
-
*/
|
|
264
|
-
async saveIndex(spaceId: string): Promise<string> {
|
|
265
|
-
const hnswService = await this.getHnswService();
|
|
266
|
-
const entry = this.indexCache.get(spaceId);
|
|
267
|
-
|
|
268
|
-
// Use IHnswService.saveIndex to save the index
|
|
269
|
-
await hnswService.saveIndex(spaceId);
|
|
270
|
-
|
|
271
|
-
// Also save metadata to Walrus for persistence
|
|
272
|
-
if (entry) {
|
|
273
|
-
const indexData = {
|
|
274
|
-
spaceId,
|
|
275
|
-
version: entry.version,
|
|
276
|
-
metadata: Array.from(entry.metadata.entries()),
|
|
277
|
-
vectors: Array.from(entry.vectors.entries()),
|
|
278
|
-
lastModified: entry.lastModified.toISOString()
|
|
279
|
-
};
|
|
280
|
-
|
|
281
|
-
const serializedData = JSON.stringify(indexData);
|
|
282
|
-
const metadata: MemoryMetadata = {
|
|
283
|
-
contentType: 'application/json',
|
|
284
|
-
contentSize: serializedData.length,
|
|
285
|
-
contentHash: '',
|
|
286
|
-
category: 'vector-index',
|
|
287
|
-
topic: spaceId,
|
|
288
|
-
importance: 8,
|
|
289
|
-
embeddingDimension: 3072,
|
|
290
|
-
createdTimestamp: Date.now(),
|
|
291
|
-
customMetadata: {
|
|
292
|
-
type: 'hnsw-index',
|
|
293
|
-
spaceId,
|
|
294
|
-
version: entry.version.toString()
|
|
295
|
-
}
|
|
296
|
-
};
|
|
297
|
-
|
|
298
|
-
const result = await this.storageService.upload(serializedData, metadata);
|
|
299
|
-
entry.isDirty = false;
|
|
300
|
-
return result.blobId;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
return '';
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* Load index from persistent storage or Walrus
|
|
308
|
-
* @param spaceId - Index space identifier
|
|
309
|
-
* @param blobId - Optional blob ID for Walrus storage (if provided, loads metadata from Walrus)
|
|
310
|
-
*/
|
|
311
|
-
async loadIndex(spaceId: string, blobId?: string): Promise<void> {
|
|
312
|
-
const hnswService = await this.getHnswService();
|
|
313
|
-
|
|
314
|
-
// Load the HNSW index using IHnswService (from local IndexedDB/filesystem)
|
|
315
|
-
await hnswService.loadIndex(spaceId);
|
|
316
|
-
|
|
317
|
-
// If blobId provided, also load metadata from Walrus
|
|
318
|
-
if (blobId) {
|
|
319
|
-
try {
|
|
320
|
-
const result = await this.storageService.retrieve(blobId);
|
|
321
|
-
const indexData = JSON.parse(new TextDecoder().decode(result.content));
|
|
322
|
-
|
|
323
|
-
// Initialize or update local cache entry
|
|
324
|
-
const entry = this.indexCache.get(spaceId) || {
|
|
325
|
-
lastModified: new Date(),
|
|
326
|
-
pendingVectors: new Map(),
|
|
327
|
-
isDirty: false,
|
|
328
|
-
version: 1,
|
|
329
|
-
metadata: new Map(),
|
|
330
|
-
vectors: new Map()
|
|
331
|
-
};
|
|
332
|
-
|
|
333
|
-
entry.version = indexData.version || 1;
|
|
334
|
-
entry.metadata = new Map(indexData.metadata || []);
|
|
335
|
-
entry.vectors = new Map(indexData.vectors || []);
|
|
336
|
-
entry.lastModified = new Date(indexData.lastModified || Date.now());
|
|
337
|
-
entry.isDirty = false;
|
|
338
|
-
|
|
339
|
-
this.indexCache.set(spaceId, entry);
|
|
340
|
-
|
|
341
|
-
// Re-add vectors to HNSW index from cached vectors
|
|
342
|
-
for (const [vectorId, vector] of entry.vectors.entries()) {
|
|
343
|
-
await hnswService.addVector(spaceId, vectorId, vector, entry.metadata.get(vectorId));
|
|
344
|
-
}
|
|
345
|
-
} catch (error) {
|
|
346
|
-
console.warn(`Failed to load metadata from Walrus for ${blobId}:`, error);
|
|
347
|
-
// Index is still loaded via IHnswService, just without local metadata
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
/**
|
|
353
|
-
* Process text to vector pipeline
|
|
354
|
-
*/
|
|
355
|
-
async processText(spaceId: string, text: string, vectorId: number, metadata?: any): Promise<VectorEmbedding> {
|
|
356
|
-
// Generate embedding
|
|
357
|
-
const embedding = await this.generateEmbedding(text);
|
|
358
|
-
|
|
359
|
-
// Add to index
|
|
360
|
-
await this.addVector(spaceId, vectorId, embedding.vector, metadata);
|
|
361
|
-
|
|
362
|
-
return embedding;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
/**
|
|
366
|
-
* Search by text query
|
|
367
|
-
*/
|
|
368
|
-
async searchByText(
|
|
369
|
-
spaceId: string,
|
|
370
|
-
query: string,
|
|
371
|
-
options?: Partial<VectorSearchOptions>
|
|
372
|
-
): Promise<VectorSearchResult> {
|
|
373
|
-
// Generate query embedding
|
|
374
|
-
const queryEmbedding = await this.generateEmbedding(query);
|
|
375
|
-
|
|
376
|
-
// Search vectors
|
|
377
|
-
return await this.searchVectors(spaceId, queryEmbedding.vector, options);
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
/**
|
|
381
|
-
* Get index statistics
|
|
382
|
-
*/
|
|
383
|
-
getIndexStats(spaceId: string): any {
|
|
384
|
-
const entry = this.indexCache.get(spaceId);
|
|
385
|
-
if (!entry) {
|
|
386
|
-
return null;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
return {
|
|
390
|
-
spaceId,
|
|
391
|
-
version: entry.version,
|
|
392
|
-
currentElements: entry.metadata.size,
|
|
393
|
-
maxElements: this.config.index?.maxElements || 10000,
|
|
394
|
-
isDirty: entry.isDirty,
|
|
395
|
-
lastModified: entry.lastModified,
|
|
396
|
-
metadataCount: entry.metadata.size,
|
|
397
|
-
vectorCount: entry.vectors.size
|
|
398
|
-
};
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
/**
|
|
402
|
-
* Get all vectors matching a category filter
|
|
403
|
-
*
|
|
404
|
-
* @param spaceId - Index space identifier
|
|
405
|
-
* @param category - Category to filter by
|
|
406
|
-
* @returns Array of metadata objects matching the category
|
|
407
|
-
*/
|
|
408
|
-
getVectorsByCategory(spaceId: string, category: string): Array<{ vectorId: number; metadata: any }> {
|
|
409
|
-
const entry = this.indexCache.get(spaceId);
|
|
410
|
-
if (!entry) {
|
|
411
|
-
return [];
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
const results: Array<{ vectorId: number; metadata: any }> = [];
|
|
415
|
-
for (const [vectorId, metadata] of entry.metadata.entries()) {
|
|
416
|
-
if (metadata?.category === category) {
|
|
417
|
-
results.push({ vectorId, metadata });
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
return results;
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
/**
|
|
424
|
-
* Get all vectors with their metadata
|
|
425
|
-
*
|
|
426
|
-
* @param spaceId - Index space identifier
|
|
427
|
-
* @returns Array of all vectors with metadata
|
|
428
|
-
*/
|
|
429
|
-
getAllVectors(spaceId: string): Array<{ vectorId: number; metadata: any }> {
|
|
430
|
-
const entry = this.indexCache.get(spaceId);
|
|
431
|
-
if (!entry) {
|
|
432
|
-
return [];
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
const results: Array<{ vectorId: number; metadata: any }> = [];
|
|
436
|
-
for (const [vectorId, metadata] of entry.metadata.entries()) {
|
|
437
|
-
results.push({ vectorId, metadata });
|
|
438
|
-
}
|
|
439
|
-
return results;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
/**
|
|
443
|
-
* Clean up resources
|
|
444
|
-
*/
|
|
445
|
-
async cleanup(): Promise<void> {
|
|
446
|
-
// Save all dirty indices
|
|
447
|
-
for (const [spaceId, entry] of this.indexCache.entries()) {
|
|
448
|
-
if (entry.isDirty) {
|
|
449
|
-
await this.saveIndex(spaceId);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
// Destroy HNSW service
|
|
454
|
-
if (this.hnswService) {
|
|
455
|
-
this.hnswService.destroy();
|
|
456
|
-
this.hnswService = null;
|
|
457
|
-
this.hnswServicePromise = null;
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
this.indexCache.clear();
|
|
461
|
-
}
|
|
462
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* VectorService - Unified Vector Operations
|
|
3
|
+
*
|
|
4
|
+
* Consolidated service combining embedding generation and HNSW indexing
|
|
5
|
+
* with smart caching and Walrus persistence.
|
|
6
|
+
*
|
|
7
|
+
* Uses hybrid HNSW implementation:
|
|
8
|
+
* - Browser: hnswlib-wasm
|
|
9
|
+
* - Node.js: hnswlib-node
|
|
10
|
+
*
|
|
11
|
+
* Replaces: HnswIndexService + VectorManager
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { createHnswService, isBrowser, isNode } from '../vector/createHnswService';
|
|
15
|
+
import type { IHnswService, HnswServiceConfig } from '../vector/IHnswService';
|
|
16
|
+
import { EmbeddingService } from './EmbeddingService';
|
|
17
|
+
import { StorageService } from './StorageService';
|
|
18
|
+
import {
|
|
19
|
+
VectorEmbedding,
|
|
20
|
+
EmbeddingConfig,
|
|
21
|
+
HNSWIndexConfig,
|
|
22
|
+
BatchConfig,
|
|
23
|
+
VectorSearchOptions,
|
|
24
|
+
VectorSearchResult
|
|
25
|
+
} from '../embedding/types';
|
|
26
|
+
import type { MemoryMetadata } from './StorageService';
|
|
27
|
+
|
|
28
|
+
// Local VectorError class implementation
|
|
29
|
+
class VectorErrorImpl extends Error {
|
|
30
|
+
constructor(message: string) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.name = 'VectorError';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface VectorServiceConfig {
|
|
37
|
+
embedding: EmbeddingConfig;
|
|
38
|
+
index?: Partial<HNSWIndexConfig>;
|
|
39
|
+
batch?: Partial<BatchConfig>;
|
|
40
|
+
enableAutoIndex?: boolean;
|
|
41
|
+
enableMemoryCache?: boolean;
|
|
42
|
+
/** Pre-initialized HNSW service instance (shared singleton) */
|
|
43
|
+
hnswService?: IHnswService;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface IndexCacheEntry {
|
|
47
|
+
lastModified: Date;
|
|
48
|
+
pendingVectors: Map<number, number[]>;
|
|
49
|
+
isDirty: boolean;
|
|
50
|
+
version: number;
|
|
51
|
+
metadata: Map<number, any>;
|
|
52
|
+
/** Cached vectors for serialization (vectorId -> vector) */
|
|
53
|
+
vectors: Map<number, number[]>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* VectorService provides unified vector operations including:
|
|
58
|
+
* - Embedding generation via EmbeddingService
|
|
59
|
+
* - HNSW vector indexing and search (hybrid: hnswlib-wasm for browser, hnswlib-node for Node.js)
|
|
60
|
+
* - Intelligent batching and caching
|
|
61
|
+
* - Persistence via Walrus storage
|
|
62
|
+
*/
|
|
63
|
+
export class VectorService {
|
|
64
|
+
private embeddingService: EmbeddingService;
|
|
65
|
+
private storageService: StorageService;
|
|
66
|
+
private indexCache: Map<string, IndexCacheEntry> = new Map();
|
|
67
|
+
private hnswService: IHnswService | null = null;
|
|
68
|
+
private hnswServicePromise: Promise<IHnswService> | null = null;
|
|
69
|
+
|
|
70
|
+
constructor(
|
|
71
|
+
private config: VectorServiceConfig,
|
|
72
|
+
embeddingService?: EmbeddingService,
|
|
73
|
+
storageService?: StorageService
|
|
74
|
+
) {
|
|
75
|
+
this.embeddingService = embeddingService || new EmbeddingService(config.embedding);
|
|
76
|
+
this.storageService = storageService || new StorageService({ packageId: '' }); // Will be properly configured
|
|
77
|
+
|
|
78
|
+
// Use pre-initialized HNSW service if provided (shared singleton pattern)
|
|
79
|
+
if (config.hnswService) {
|
|
80
|
+
this.hnswService = config.hnswService;
|
|
81
|
+
console.log('✅ VectorService using shared HNSW service instance');
|
|
82
|
+
} else {
|
|
83
|
+
const envType = isBrowser() ? 'browser (hnswlib-wasm)' : isNode() ? 'Node.js (hnswlib-node)' : 'unknown';
|
|
84
|
+
console.log(`✅ VectorService initializing with hybrid HNSW (${envType})`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get the HNSW service, initializing it if needed
|
|
90
|
+
*/
|
|
91
|
+
private async getHnswService(): Promise<IHnswService> {
|
|
92
|
+
if (this.hnswService) {
|
|
93
|
+
return this.hnswService;
|
|
94
|
+
}
|
|
95
|
+
if (this.hnswServicePromise) {
|
|
96
|
+
return this.hnswServicePromise;
|
|
97
|
+
}
|
|
98
|
+
throw new VectorErrorImpl('HNSW service not initialized. Call initialize() first.');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Initialize the HNSW service (must be called before using any index operations)
|
|
103
|
+
* If a shared HNSW service was provided in config, this is a no-op.
|
|
104
|
+
*/
|
|
105
|
+
async initialize(): Promise<void> {
|
|
106
|
+
if (this.hnswService) {
|
|
107
|
+
return; // Already initialized (or using shared instance)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (this.hnswServicePromise) {
|
|
111
|
+
await this.hnswServicePromise;
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Create own HNSW service only if not provided externally
|
|
116
|
+
this.hnswServicePromise = createHnswService({
|
|
117
|
+
indexConfig: {
|
|
118
|
+
dimension: this.config.index?.dimension || 3072,
|
|
119
|
+
maxElements: this.config.index?.maxElements || 10000,
|
|
120
|
+
efConstruction: this.config.index?.efConstruction || 200,
|
|
121
|
+
m: this.config.index?.m || 16
|
|
122
|
+
},
|
|
123
|
+
batchConfig: this.config.batch
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
this.hnswService = await this.hnswServicePromise;
|
|
128
|
+
console.log('✅ VectorService HNSW service initialized successfully');
|
|
129
|
+
} catch (error) {
|
|
130
|
+
this.hnswServicePromise = null;
|
|
131
|
+
console.error('❌ Failed to initialize VectorService HNSW service:', error);
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Generate embeddings for text content
|
|
138
|
+
*/
|
|
139
|
+
async generateEmbedding(text: string): Promise<VectorEmbedding> {
|
|
140
|
+
const result = await this.embeddingService.embedText({ text });
|
|
141
|
+
return {
|
|
142
|
+
vector: result.vector,
|
|
143
|
+
dimension: result.dimension,
|
|
144
|
+
model: result.model
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Create or get HNSW index for a specific space
|
|
150
|
+
*/
|
|
151
|
+
async createIndex(spaceId: string, dimension?: number, config?: Partial<HNSWIndexConfig>): Promise<void> {
|
|
152
|
+
await this.initialize();
|
|
153
|
+
const hnswService = await this.getHnswService();
|
|
154
|
+
|
|
155
|
+
// Use IHnswService.getOrCreateIndex to create the index
|
|
156
|
+
await hnswService.getOrCreateIndex(spaceId);
|
|
157
|
+
|
|
158
|
+
// Initialize local cache entry for metadata tracking
|
|
159
|
+
if (!this.indexCache.has(spaceId)) {
|
|
160
|
+
this.indexCache.set(spaceId, {
|
|
161
|
+
lastModified: new Date(),
|
|
162
|
+
pendingVectors: new Map(),
|
|
163
|
+
isDirty: false,
|
|
164
|
+
version: 1,
|
|
165
|
+
metadata: new Map(),
|
|
166
|
+
vectors: new Map()
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Add vector to index
|
|
173
|
+
*/
|
|
174
|
+
async addVector(spaceId: string, vectorId: number, vector: number[], metadata?: any): Promise<void> {
|
|
175
|
+
const hnswService = await this.getHnswService();
|
|
176
|
+
|
|
177
|
+
// Add to HNSW index
|
|
178
|
+
await hnswService.addVector(spaceId, vectorId, vector, metadata);
|
|
179
|
+
|
|
180
|
+
// Update local cache entry for metadata tracking
|
|
181
|
+
let entry = this.indexCache.get(spaceId);
|
|
182
|
+
if (!entry) {
|
|
183
|
+
entry = {
|
|
184
|
+
lastModified: new Date(),
|
|
185
|
+
pendingVectors: new Map(),
|
|
186
|
+
isDirty: true,
|
|
187
|
+
version: 1,
|
|
188
|
+
metadata: new Map(),
|
|
189
|
+
vectors: new Map()
|
|
190
|
+
};
|
|
191
|
+
this.indexCache.set(spaceId, entry);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (metadata) {
|
|
195
|
+
entry.metadata.set(vectorId, metadata);
|
|
196
|
+
}
|
|
197
|
+
// Cache vector for serialization
|
|
198
|
+
entry.vectors.set(vectorId, vector);
|
|
199
|
+
entry.isDirty = true;
|
|
200
|
+
entry.lastModified = new Date();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Get cached vector by ID
|
|
205
|
+
*/
|
|
206
|
+
getVector(spaceId: string, vectorId: number): number[] | undefined {
|
|
207
|
+
const entry = this.indexCache.get(spaceId);
|
|
208
|
+
return entry?.vectors.get(vectorId);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Get all cached vectors
|
|
213
|
+
*/
|
|
214
|
+
getAllCachedVectors(spaceId: string): Map<number, number[]> {
|
|
215
|
+
const entry = this.indexCache.get(spaceId);
|
|
216
|
+
return entry?.vectors || new Map();
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Search vectors in index
|
|
221
|
+
*/
|
|
222
|
+
async searchVectors(
|
|
223
|
+
spaceId: string,
|
|
224
|
+
queryVector: number[],
|
|
225
|
+
options?: Partial<VectorSearchOptions>
|
|
226
|
+
): Promise<VectorSearchResult> {
|
|
227
|
+
const hnswService = await this.getHnswService();
|
|
228
|
+
const entry = this.indexCache.get(spaceId);
|
|
229
|
+
|
|
230
|
+
const k = options?.k || 10;
|
|
231
|
+
const startTime = performance.now();
|
|
232
|
+
|
|
233
|
+
// Use IHnswService.search
|
|
234
|
+
const searchResults = await hnswService.search(spaceId, queryVector, {
|
|
235
|
+
k,
|
|
236
|
+
ef: options?.efSearch || 50
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
const searchTime = performance.now() - startTime;
|
|
240
|
+
|
|
241
|
+
// Get metadata - prefer HNSW service metadata (loaded from disk) over local cache
|
|
242
|
+
return {
|
|
243
|
+
results: searchResults.map((result) => ({
|
|
244
|
+
memoryId: result.vectorId.toString(),
|
|
245
|
+
vectorId: result.vectorId,
|
|
246
|
+
similarity: result.score,
|
|
247
|
+
distance: result.distance,
|
|
248
|
+
metadata: result.metadata || entry?.metadata.get(result.vectorId)
|
|
249
|
+
})),
|
|
250
|
+
searchStats: {
|
|
251
|
+
searchTime,
|
|
252
|
+
nodesVisited: searchResults.length,
|
|
253
|
+
exactMatches: searchResults.length,
|
|
254
|
+
approximateMatches: 0,
|
|
255
|
+
cacheHits: 0,
|
|
256
|
+
indexSize: 0 // TODO: Get from IHnswService
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Save index to Walrus storage
|
|
263
|
+
*/
|
|
264
|
+
async saveIndex(spaceId: string): Promise<string> {
|
|
265
|
+
const hnswService = await this.getHnswService();
|
|
266
|
+
const entry = this.indexCache.get(spaceId);
|
|
267
|
+
|
|
268
|
+
// Use IHnswService.saveIndex to save the index
|
|
269
|
+
await hnswService.saveIndex(spaceId);
|
|
270
|
+
|
|
271
|
+
// Also save metadata to Walrus for persistence
|
|
272
|
+
if (entry) {
|
|
273
|
+
const indexData = {
|
|
274
|
+
spaceId,
|
|
275
|
+
version: entry.version,
|
|
276
|
+
metadata: Array.from(entry.metadata.entries()),
|
|
277
|
+
vectors: Array.from(entry.vectors.entries()),
|
|
278
|
+
lastModified: entry.lastModified.toISOString()
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const serializedData = JSON.stringify(indexData);
|
|
282
|
+
const metadata: MemoryMetadata = {
|
|
283
|
+
contentType: 'application/json',
|
|
284
|
+
contentSize: serializedData.length,
|
|
285
|
+
contentHash: '',
|
|
286
|
+
category: 'vector-index',
|
|
287
|
+
topic: spaceId,
|
|
288
|
+
importance: 8,
|
|
289
|
+
embeddingDimension: 3072,
|
|
290
|
+
createdTimestamp: Date.now(),
|
|
291
|
+
customMetadata: {
|
|
292
|
+
type: 'hnsw-index',
|
|
293
|
+
spaceId,
|
|
294
|
+
version: entry.version.toString()
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
const result = await this.storageService.upload(serializedData, metadata);
|
|
299
|
+
entry.isDirty = false;
|
|
300
|
+
return result.blobId;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return '';
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Load index from persistent storage or Walrus
|
|
308
|
+
* @param spaceId - Index space identifier
|
|
309
|
+
* @param blobId - Optional blob ID for Walrus storage (if provided, loads metadata from Walrus)
|
|
310
|
+
*/
|
|
311
|
+
async loadIndex(spaceId: string, blobId?: string): Promise<void> {
|
|
312
|
+
const hnswService = await this.getHnswService();
|
|
313
|
+
|
|
314
|
+
// Load the HNSW index using IHnswService (from local IndexedDB/filesystem)
|
|
315
|
+
await hnswService.loadIndex(spaceId);
|
|
316
|
+
|
|
317
|
+
// If blobId provided, also load metadata from Walrus
|
|
318
|
+
if (blobId) {
|
|
319
|
+
try {
|
|
320
|
+
const result = await this.storageService.retrieve(blobId);
|
|
321
|
+
const indexData = JSON.parse(new TextDecoder().decode(result.content));
|
|
322
|
+
|
|
323
|
+
// Initialize or update local cache entry
|
|
324
|
+
const entry = this.indexCache.get(spaceId) || {
|
|
325
|
+
lastModified: new Date(),
|
|
326
|
+
pendingVectors: new Map(),
|
|
327
|
+
isDirty: false,
|
|
328
|
+
version: 1,
|
|
329
|
+
metadata: new Map(),
|
|
330
|
+
vectors: new Map()
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
entry.version = indexData.version || 1;
|
|
334
|
+
entry.metadata = new Map(indexData.metadata || []);
|
|
335
|
+
entry.vectors = new Map(indexData.vectors || []);
|
|
336
|
+
entry.lastModified = new Date(indexData.lastModified || Date.now());
|
|
337
|
+
entry.isDirty = false;
|
|
338
|
+
|
|
339
|
+
this.indexCache.set(spaceId, entry);
|
|
340
|
+
|
|
341
|
+
// Re-add vectors to HNSW index from cached vectors
|
|
342
|
+
for (const [vectorId, vector] of entry.vectors.entries()) {
|
|
343
|
+
await hnswService.addVector(spaceId, vectorId, vector, entry.metadata.get(vectorId));
|
|
344
|
+
}
|
|
345
|
+
} catch (error) {
|
|
346
|
+
console.warn(`Failed to load metadata from Walrus for ${blobId}:`, error);
|
|
347
|
+
// Index is still loaded via IHnswService, just without local metadata
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Process text to vector pipeline
|
|
354
|
+
*/
|
|
355
|
+
async processText(spaceId: string, text: string, vectorId: number, metadata?: any): Promise<VectorEmbedding> {
|
|
356
|
+
// Generate embedding
|
|
357
|
+
const embedding = await this.generateEmbedding(text);
|
|
358
|
+
|
|
359
|
+
// Add to index
|
|
360
|
+
await this.addVector(spaceId, vectorId, embedding.vector, metadata);
|
|
361
|
+
|
|
362
|
+
return embedding;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Search by text query
|
|
367
|
+
*/
|
|
368
|
+
async searchByText(
|
|
369
|
+
spaceId: string,
|
|
370
|
+
query: string,
|
|
371
|
+
options?: Partial<VectorSearchOptions>
|
|
372
|
+
): Promise<VectorSearchResult> {
|
|
373
|
+
// Generate query embedding
|
|
374
|
+
const queryEmbedding = await this.generateEmbedding(query);
|
|
375
|
+
|
|
376
|
+
// Search vectors
|
|
377
|
+
return await this.searchVectors(spaceId, queryEmbedding.vector, options);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Get index statistics
|
|
382
|
+
*/
|
|
383
|
+
getIndexStats(spaceId: string): any {
|
|
384
|
+
const entry = this.indexCache.get(spaceId);
|
|
385
|
+
if (!entry) {
|
|
386
|
+
return null;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return {
|
|
390
|
+
spaceId,
|
|
391
|
+
version: entry.version,
|
|
392
|
+
currentElements: entry.metadata.size,
|
|
393
|
+
maxElements: this.config.index?.maxElements || 10000,
|
|
394
|
+
isDirty: entry.isDirty,
|
|
395
|
+
lastModified: entry.lastModified,
|
|
396
|
+
metadataCount: entry.metadata.size,
|
|
397
|
+
vectorCount: entry.vectors.size
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Get all vectors matching a category filter
|
|
403
|
+
*
|
|
404
|
+
* @param spaceId - Index space identifier
|
|
405
|
+
* @param category - Category to filter by
|
|
406
|
+
* @returns Array of metadata objects matching the category
|
|
407
|
+
*/
|
|
408
|
+
getVectorsByCategory(spaceId: string, category: string): Array<{ vectorId: number; metadata: any }> {
|
|
409
|
+
const entry = this.indexCache.get(spaceId);
|
|
410
|
+
if (!entry) {
|
|
411
|
+
return [];
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const results: Array<{ vectorId: number; metadata: any }> = [];
|
|
415
|
+
for (const [vectorId, metadata] of entry.metadata.entries()) {
|
|
416
|
+
if (metadata?.category === category) {
|
|
417
|
+
results.push({ vectorId, metadata });
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return results;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Get all vectors with their metadata
|
|
425
|
+
*
|
|
426
|
+
* @param spaceId - Index space identifier
|
|
427
|
+
* @returns Array of all vectors with metadata
|
|
428
|
+
*/
|
|
429
|
+
getAllVectors(spaceId: string): Array<{ vectorId: number; metadata: any }> {
|
|
430
|
+
const entry = this.indexCache.get(spaceId);
|
|
431
|
+
if (!entry) {
|
|
432
|
+
return [];
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const results: Array<{ vectorId: number; metadata: any }> = [];
|
|
436
|
+
for (const [vectorId, metadata] of entry.metadata.entries()) {
|
|
437
|
+
results.push({ vectorId, metadata });
|
|
438
|
+
}
|
|
439
|
+
return results;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Clean up resources
|
|
444
|
+
*/
|
|
445
|
+
async cleanup(): Promise<void> {
|
|
446
|
+
// Save all dirty indices
|
|
447
|
+
for (const [spaceId, entry] of this.indexCache.entries()) {
|
|
448
|
+
if (entry.isDirty) {
|
|
449
|
+
await this.saveIndex(spaceId);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// Destroy HNSW service
|
|
454
|
+
if (this.hnswService) {
|
|
455
|
+
this.hnswService.destroy();
|
|
456
|
+
this.hnswService = null;
|
|
457
|
+
this.hnswServicePromise = null;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
this.indexCache.clear();
|
|
461
|
+
}
|
|
462
|
+
}
|