@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,598 +1,598 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BatchManager - Central orchestrator for all batch processing operations
|
|
3
|
-
*
|
|
4
|
-
* Coordinates embedding generation, vector indexing, knowledge graph updates,
|
|
5
|
-
* and Walrus operations through intelligent batching and caching.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import EventEmitter from 'eventemitter3';
|
|
9
|
-
import { BatchingService, BatchProcessor, BatchItem } from './BatchingService';
|
|
10
|
-
import { MemoryProcessingCache } from './MemoryProcessingCache';
|
|
11
|
-
import { EmbeddingService } from '../services/EmbeddingService';
|
|
12
|
-
import type { IHnswService } from '../vector/IHnswService';
|
|
13
|
-
import {
|
|
14
|
-
Memory,
|
|
15
|
-
ProcessedMemory,
|
|
16
|
-
BatchConfig,
|
|
17
|
-
BatchStats,
|
|
18
|
-
MemoryBatchResult
|
|
19
|
-
} from '../embedding/types';
|
|
20
|
-
|
|
21
|
-
export interface BatchManagerConfig {
|
|
22
|
-
embedding?: {
|
|
23
|
-
batchSize?: number;
|
|
24
|
-
delayMs?: number;
|
|
25
|
-
};
|
|
26
|
-
indexing?: {
|
|
27
|
-
batchSize?: number;
|
|
28
|
-
delayMs?: number;
|
|
29
|
-
};
|
|
30
|
-
walrus?: {
|
|
31
|
-
batchSize?: number;
|
|
32
|
-
delayMs?: number;
|
|
33
|
-
};
|
|
34
|
-
cache?: {
|
|
35
|
-
maxSize?: number;
|
|
36
|
-
ttlMs?: number;
|
|
37
|
-
};
|
|
38
|
-
enableMetrics?: boolean;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface BatchManagerStats extends BatchStats {
|
|
42
|
-
managers: {
|
|
43
|
-
embedding: BatchStats;
|
|
44
|
-
indexing: BatchStats;
|
|
45
|
-
walrus: BatchStats;
|
|
46
|
-
};
|
|
47
|
-
cache: any; // MemoryCacheStats
|
|
48
|
-
performance: {
|
|
49
|
-
totalProcessingTime: number;
|
|
50
|
-
averageMemoryProcessingTime: number;
|
|
51
|
-
successfulBatches: number;
|
|
52
|
-
failedBatches: number;
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export interface BatchJobStatus {
|
|
57
|
-
id: string;
|
|
58
|
-
type: 'embedding' | 'indexing' | 'walrus' | 'pipeline';
|
|
59
|
-
status: 'pending' | 'processing' | 'completed' | 'failed';
|
|
60
|
-
itemCount: number;
|
|
61
|
-
startTime?: Date;
|
|
62
|
-
endTime?: Date;
|
|
63
|
-
error?: string;
|
|
64
|
-
processingTimeMs?: number;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Central batch processing manager with integrated caching and monitoring
|
|
69
|
-
*/
|
|
70
|
-
export class BatchManager extends EventEmitter {
|
|
71
|
-
private embeddingBatcher!: BatchingService<Memory>;
|
|
72
|
-
private indexingBatcher!: BatchingService<ProcessedMemory>;
|
|
73
|
-
private walrusBatcher!: BatchingService<ProcessedMemory>;
|
|
74
|
-
private cache!: MemoryProcessingCache;
|
|
75
|
-
|
|
76
|
-
private embeddingService?: EmbeddingService;
|
|
77
|
-
private indexService?: IHnswService;
|
|
78
|
-
|
|
79
|
-
private readonly config: Required<BatchManagerConfig>;
|
|
80
|
-
private jobStatuses = new Map<string, BatchJobStatus>();
|
|
81
|
-
private metrics = {
|
|
82
|
-
totalProcessingTime: 0,
|
|
83
|
-
averageMemoryProcessingTime: 0,
|
|
84
|
-
successfulBatches: 0,
|
|
85
|
-
failedBatches: 0,
|
|
86
|
-
totalMemoriesProcessed: 0
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
constructor(config: BatchManagerConfig = {}) {
|
|
90
|
-
super();
|
|
91
|
-
|
|
92
|
-
this.config = {
|
|
93
|
-
embedding: {
|
|
94
|
-
batchSize: config.embedding?.batchSize || 20,
|
|
95
|
-
delayMs: config.embedding?.delayMs || 5000
|
|
96
|
-
},
|
|
97
|
-
indexing: {
|
|
98
|
-
batchSize: config.indexing?.batchSize || 50,
|
|
99
|
-
delayMs: config.indexing?.delayMs || 3000
|
|
100
|
-
},
|
|
101
|
-
walrus: {
|
|
102
|
-
batchSize: config.walrus?.batchSize || 10,
|
|
103
|
-
delayMs: config.walrus?.delayMs || 8000
|
|
104
|
-
},
|
|
105
|
-
cache: {
|
|
106
|
-
maxSize: config.cache?.maxSize || 5000,
|
|
107
|
-
ttlMs: config.cache?.ttlMs || 60 * 60 * 1000
|
|
108
|
-
},
|
|
109
|
-
enableMetrics: config.enableMetrics !== false
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
// Initialize services
|
|
113
|
-
this.initializeBatchingServices();
|
|
114
|
-
this.cache = new MemoryProcessingCache(this.config.cache);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// ==================== SERVICE INITIALIZATION ====================
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Initialize with required services
|
|
121
|
-
*/
|
|
122
|
-
initialize(services: {
|
|
123
|
-
embeddingService?: EmbeddingService;
|
|
124
|
-
indexService?: IHnswService;
|
|
125
|
-
}): void {
|
|
126
|
-
this.embeddingService = services.embeddingService;
|
|
127
|
-
this.indexService = services.indexService;
|
|
128
|
-
|
|
129
|
-
console.log('BatchManager initialized with services');
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// ==================== MEMORY PROCESSING PIPELINE ====================
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Add memory to complete processing pipeline
|
|
136
|
-
*/
|
|
137
|
-
async addMemoryToPipeline(
|
|
138
|
-
memory: Memory,
|
|
139
|
-
options: {
|
|
140
|
-
priority?: 'low' | 'normal' | 'high';
|
|
141
|
-
skipCache?: boolean;
|
|
142
|
-
immediateProcessing?: boolean;
|
|
143
|
-
} = {}
|
|
144
|
-
): Promise<string> {
|
|
145
|
-
const jobId = this.generateJobId();
|
|
146
|
-
|
|
147
|
-
// Check cache first (unless skipped)
|
|
148
|
-
if (!options.skipCache) {
|
|
149
|
-
const cached = this.cache.getCachedMemory(memory.id);
|
|
150
|
-
if (cached?.processingState === 'completed') {
|
|
151
|
-
this.emit('memory:cached', { memoryId: memory.id, cached });
|
|
152
|
-
return jobId;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Create job status
|
|
157
|
-
this.jobStatuses.set(jobId, {
|
|
158
|
-
id: jobId,
|
|
159
|
-
type: 'pipeline',
|
|
160
|
-
status: 'pending',
|
|
161
|
-
itemCount: 1,
|
|
162
|
-
startTime: new Date()
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
try {
|
|
166
|
-
// Cache memory as pending
|
|
167
|
-
this.cache.cacheMemory(memory);
|
|
168
|
-
this.cache.updateMemoryState(memory.id, 'pending');
|
|
169
|
-
|
|
170
|
-
// Add to processing pipeline
|
|
171
|
-
if (options.immediateProcessing) {
|
|
172
|
-
await this.processMemoryImmediate(memory);
|
|
173
|
-
} else {
|
|
174
|
-
// Add to embedding batch
|
|
175
|
-
this.embeddingBatcher.addToBatch('memories', {
|
|
176
|
-
id: memory.id,
|
|
177
|
-
data: memory,
|
|
178
|
-
timestamp: new Date(),
|
|
179
|
-
priority: this.getPriorityValue(options.priority),
|
|
180
|
-
metadata: { originalJobId: jobId }
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
this.updateJobStatus(jobId, 'processing');
|
|
185
|
-
this.emit('memory:queued', { memoryId: memory.id, jobId });
|
|
186
|
-
|
|
187
|
-
} catch (error) {
|
|
188
|
-
this.updateJobStatus(jobId, 'failed', error as Error);
|
|
189
|
-
this.cache.updateMemoryState(memory.id, 'failed', (error as Error).message);
|
|
190
|
-
throw error;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
return jobId;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Process multiple memories in batch
|
|
198
|
-
*/
|
|
199
|
-
async addMemoriesToPipeline(
|
|
200
|
-
memories: Memory[],
|
|
201
|
-
options: {
|
|
202
|
-
priority?: 'low' | 'normal' | 'high';
|
|
203
|
-
batchSize?: number;
|
|
204
|
-
} = {}
|
|
205
|
-
): Promise<string[]> {
|
|
206
|
-
const batchSize = options.batchSize || this.config.embedding.batchSize;
|
|
207
|
-
const jobIds: string[] = [];
|
|
208
|
-
|
|
209
|
-
// Process in smaller batches to avoid overwhelming the system
|
|
210
|
-
const safeBatchSize = batchSize || 10;
|
|
211
|
-
for (let i = 0; i < memories.length; i += safeBatchSize) {
|
|
212
|
-
const batch = memories.slice(i, i + safeBatchSize);
|
|
213
|
-
|
|
214
|
-
for (const memory of batch) {
|
|
215
|
-
try {
|
|
216
|
-
const jobId = await this.addMemoryToPipeline(memory, options);
|
|
217
|
-
jobIds.push(jobId);
|
|
218
|
-
} catch (error) {
|
|
219
|
-
console.error(`Failed to add memory ${memory.id} to pipeline:`, error);
|
|
220
|
-
// Continue with other memories
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// Small delay between batches
|
|
225
|
-
if (i + safeBatchSize < memories.length) {
|
|
226
|
-
await this.delay(100);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
return jobIds;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// ==================== BATCH PROCESSING CONTROL ====================
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* Process all pending batches immediately
|
|
237
|
-
*/
|
|
238
|
-
async processAllBatches(): Promise<void> {
|
|
239
|
-
console.log('Processing all pending batches...');
|
|
240
|
-
|
|
241
|
-
try {
|
|
242
|
-
await Promise.all([
|
|
243
|
-
this.embeddingBatcher.processAllBatches(),
|
|
244
|
-
this.indexingBatcher.processAllBatches(),
|
|
245
|
-
this.walrusBatcher.processAllBatches()
|
|
246
|
-
]);
|
|
247
|
-
|
|
248
|
-
console.log('All batches processed successfully');
|
|
249
|
-
} catch (error) {
|
|
250
|
-
console.error('Error processing batches:', error);
|
|
251
|
-
throw error;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Get status of specific job
|
|
257
|
-
*/
|
|
258
|
-
getJobStatus(jobId: string): BatchJobStatus | undefined {
|
|
259
|
-
return this.jobStatuses.get(jobId);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Get all job statuses
|
|
264
|
-
*/
|
|
265
|
-
getAllJobStatuses(): BatchJobStatus[] {
|
|
266
|
-
return Array.from(this.jobStatuses.values());
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Get pending jobs count by type
|
|
271
|
-
*/
|
|
272
|
-
getPendingJobsCount(): Record<string, number> {
|
|
273
|
-
const counts = { embedding: 0, indexing: 0, walrus: 0, pipeline: 0 };
|
|
274
|
-
|
|
275
|
-
for (const job of this.jobStatuses.values()) {
|
|
276
|
-
if (job.status === 'pending' || job.status === 'processing') {
|
|
277
|
-
counts[job.type]++;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
return counts;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// ==================== CACHING OPERATIONS ====================
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Get cached memory
|
|
288
|
-
*/
|
|
289
|
-
getCachedMemory(memoryId: string) {
|
|
290
|
-
return this.cache.getCachedMemory(memoryId);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* Find similar memories
|
|
295
|
-
*/
|
|
296
|
-
findSimilarMemories(memoryId: string, limit?: number) {
|
|
297
|
-
return this.cache.findSimilarMemories(memoryId, limit);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Get cache statistics
|
|
302
|
-
*/
|
|
303
|
-
getCacheStats() {
|
|
304
|
-
return this.cache.getStats();
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
/**
|
|
308
|
-
* Clear all caches
|
|
309
|
-
*/
|
|
310
|
-
clearCaches(): void {
|
|
311
|
-
this.cache.clearAll();
|
|
312
|
-
this.jobStatuses.clear();
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// ==================== STATISTICS & MONITORING ====================
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* Get comprehensive batch manager statistics
|
|
319
|
-
*/
|
|
320
|
-
getStats(): BatchManagerStats {
|
|
321
|
-
const embeddingStats = this.embeddingBatcher.getBatchStats();
|
|
322
|
-
const indexingStats = this.indexingBatcher.getBatchStats();
|
|
323
|
-
const walrusStats = this.walrusBatcher.getBatchStats();
|
|
324
|
-
const cacheStats = this.cache.getStats();
|
|
325
|
-
|
|
326
|
-
return {
|
|
327
|
-
// Aggregate stats
|
|
328
|
-
totalUsers: Math.max(embeddingStats.totalUsers, indexingStats.totalUsers, walrusStats.totalUsers),
|
|
329
|
-
totalPendingVectors: embeddingStats.totalPendingVectors + indexingStats.totalPendingVectors,
|
|
330
|
-
activeBatchJobs: embeddingStats.activeBatchJobs + indexingStats.activeBatchJobs + walrusStats.activeBatchJobs,
|
|
331
|
-
cacheHitRate: cacheStats.performance.hitRateMemories,
|
|
332
|
-
averageBatchSize: (embeddingStats.averageBatchSize + indexingStats.averageBatchSize + walrusStats.averageBatchSize) / 3,
|
|
333
|
-
averageProcessingTime: this.metrics.averageMemoryProcessingTime,
|
|
334
|
-
|
|
335
|
-
// Individual managers
|
|
336
|
-
managers: {
|
|
337
|
-
embedding: embeddingStats,
|
|
338
|
-
indexing: indexingStats,
|
|
339
|
-
walrus: walrusStats
|
|
340
|
-
},
|
|
341
|
-
|
|
342
|
-
// Cache stats
|
|
343
|
-
cache: cacheStats,
|
|
344
|
-
|
|
345
|
-
// Performance metrics
|
|
346
|
-
performance: {
|
|
347
|
-
totalProcessingTime: this.metrics.totalProcessingTime,
|
|
348
|
-
averageMemoryProcessingTime: this.metrics.averageMemoryProcessingTime,
|
|
349
|
-
successfulBatches: this.metrics.successfulBatches,
|
|
350
|
-
failedBatches: this.metrics.failedBatches
|
|
351
|
-
}
|
|
352
|
-
};
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
* Cleanup and destroy batch manager
|
|
357
|
-
*/
|
|
358
|
-
destroy(): void {
|
|
359
|
-
// Destroy all batching services
|
|
360
|
-
this.embeddingBatcher.destroy();
|
|
361
|
-
this.indexingBatcher.destroy();
|
|
362
|
-
this.walrusBatcher.destroy();
|
|
363
|
-
|
|
364
|
-
// Destroy cache
|
|
365
|
-
this.cache.destroy();
|
|
366
|
-
|
|
367
|
-
// Clear job statuses
|
|
368
|
-
this.jobStatuses.clear();
|
|
369
|
-
|
|
370
|
-
// Remove all listeners
|
|
371
|
-
this.removeAllListeners();
|
|
372
|
-
|
|
373
|
-
console.log('BatchManager destroyed');
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
// ==================== PRIVATE METHODS ====================
|
|
377
|
-
|
|
378
|
-
private initializeBatchingServices(): void {
|
|
379
|
-
// Initialize embedding batcher
|
|
380
|
-
this.embeddingBatcher = new BatchingService<Memory>(
|
|
381
|
-
{
|
|
382
|
-
maxBatchSize: this.config.embedding.batchSize,
|
|
383
|
-
batchDelayMs: this.config.embedding.delayMs
|
|
384
|
-
}
|
|
385
|
-
);
|
|
386
|
-
|
|
387
|
-
// Initialize indexing batcher
|
|
388
|
-
this.indexingBatcher = new BatchingService<ProcessedMemory>(
|
|
389
|
-
{
|
|
390
|
-
maxBatchSize: this.config.indexing.batchSize,
|
|
391
|
-
batchDelayMs: this.config.indexing.delayMs
|
|
392
|
-
}
|
|
393
|
-
);
|
|
394
|
-
|
|
395
|
-
// Initialize Walrus batcher
|
|
396
|
-
this.walrusBatcher = new BatchingService<ProcessedMemory>(
|
|
397
|
-
{
|
|
398
|
-
maxBatchSize: this.config.walrus.batchSize,
|
|
399
|
-
batchDelayMs: this.config.walrus.delayMs
|
|
400
|
-
}
|
|
401
|
-
);
|
|
402
|
-
|
|
403
|
-
// Register processors
|
|
404
|
-
this.registerBatchProcessors();
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
private registerBatchProcessors(): void {
|
|
408
|
-
// Embedding processor
|
|
409
|
-
this.embeddingBatcher.registerProcessor('memories', {
|
|
410
|
-
process: async (items: BatchItem<Memory>[]) => {
|
|
411
|
-
await this.processEmbeddingBatch(items.map(item => item.data));
|
|
412
|
-
}
|
|
413
|
-
});
|
|
414
|
-
|
|
415
|
-
// Indexing processor
|
|
416
|
-
this.indexingBatcher.registerProcessor('processed-memories', {
|
|
417
|
-
process: async (items: BatchItem<ProcessedMemory>[]) => {
|
|
418
|
-
await this.processIndexingBatch(items.map(item => item.data));
|
|
419
|
-
}
|
|
420
|
-
});
|
|
421
|
-
|
|
422
|
-
// Walrus processor
|
|
423
|
-
this.walrusBatcher.registerProcessor('walrus-uploads', {
|
|
424
|
-
process: async (items: BatchItem<ProcessedMemory>[]) => {
|
|
425
|
-
await this.processWalrusBatch(items.map(item => item.data));
|
|
426
|
-
}
|
|
427
|
-
});
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
private async processEmbeddingBatch(memories: Memory[]): Promise<void> {
|
|
431
|
-
if (!this.embeddingService) {
|
|
432
|
-
console.warn('EmbeddingService not initialized');
|
|
433
|
-
return;
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
console.log(`Processing embedding batch of ${memories.length} memories`);
|
|
437
|
-
const startTime = Date.now();
|
|
438
|
-
|
|
439
|
-
try {
|
|
440
|
-
// Generate embeddings in batch
|
|
441
|
-
const results = await this.embeddingService.embedBatch(
|
|
442
|
-
memories.map(m => m.content)
|
|
443
|
-
);
|
|
444
|
-
|
|
445
|
-
// Process results
|
|
446
|
-
for (let i = 0; i < memories.length; i++) {
|
|
447
|
-
const memory = memories[i];
|
|
448
|
-
const embedding = results.vectors[i];
|
|
449
|
-
|
|
450
|
-
if (embedding && embedding.length > 0) {
|
|
451
|
-
const processedMemory: ProcessedMemory = {
|
|
452
|
-
...memory,
|
|
453
|
-
embedding: embedding,
|
|
454
|
-
embeddingModel: 'gemini-embedding',
|
|
455
|
-
processedAt: new Date()
|
|
456
|
-
};
|
|
457
|
-
|
|
458
|
-
// Update cache
|
|
459
|
-
this.cache.cacheMemory(memory, processedMemory);
|
|
460
|
-
this.cache.updateMemoryState(memory.id, 'completed');
|
|
461
|
-
|
|
462
|
-
// Add to indexing batch
|
|
463
|
-
this.indexingBatcher.addToBatch('processed-memories', {
|
|
464
|
-
id: memory.id,
|
|
465
|
-
data: processedMemory,
|
|
466
|
-
timestamp: new Date()
|
|
467
|
-
});
|
|
468
|
-
|
|
469
|
-
this.emit('memory:embedded', { memoryId: memory.id, embedding: embedding });
|
|
470
|
-
} else {
|
|
471
|
-
this.cache.updateMemoryState(memory.id, 'failed', 'Embedding generation failed');
|
|
472
|
-
this.emit('memory:embedding-failed', { memoryId: memory.id, error: 'Embedding generation failed' });
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
const processingTime = Date.now() - startTime;
|
|
477
|
-
this.updateMetrics(memories.length, processingTime, true);
|
|
478
|
-
|
|
479
|
-
} catch (error) {
|
|
480
|
-
console.error('Embedding batch processing failed:', error);
|
|
481
|
-
|
|
482
|
-
// Update all memories as failed
|
|
483
|
-
for (const memory of memories) {
|
|
484
|
-
this.cache.updateMemoryState(memory.id, 'failed', (error as Error).message);
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
this.updateMetrics(memories.length, Date.now() - startTime, false);
|
|
488
|
-
throw error;
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
private async processIndexingBatch(processedMemories: ProcessedMemory[]): Promise<void> {
|
|
493
|
-
if (!this.indexService) {
|
|
494
|
-
console.warn('IHnswService not initialized');
|
|
495
|
-
return;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
console.log(`Processing indexing batch of ${processedMemories.length} memories`);
|
|
499
|
-
|
|
500
|
-
try {
|
|
501
|
-
// Add to HNSW index
|
|
502
|
-
for (const memory of processedMemories) {
|
|
503
|
-
await this.indexService.addVector(
|
|
504
|
-
memory.userId || 'default-user',
|
|
505
|
-
parseInt(memory.id) || Date.now(),
|
|
506
|
-
memory.embedding || [],
|
|
507
|
-
{
|
|
508
|
-
content: memory.content,
|
|
509
|
-
category: memory.category,
|
|
510
|
-
timestamp: memory.createdAt
|
|
511
|
-
}
|
|
512
|
-
);
|
|
513
|
-
|
|
514
|
-
this.emit('memory:indexed', { memoryId: memory.id });
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
// Flush pending vectors
|
|
518
|
-
if (this.indexService) {
|
|
519
|
-
await this.indexService.flushBatch('batch-operation');
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
} catch (error) {
|
|
523
|
-
console.error('Indexing batch processing failed:', error);
|
|
524
|
-
throw error;
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
private async processWalrusBatch(processedMemories: ProcessedMemory[]): Promise<void> {
|
|
529
|
-
console.log(`Processing Walrus batch of ${processedMemories.length} memories`);
|
|
530
|
-
|
|
531
|
-
try {
|
|
532
|
-
// TODO: Implement Walrus batch operations
|
|
533
|
-
// This would involve:
|
|
534
|
-
// 1. Batch upload processed memories to Walrus
|
|
535
|
-
// 2. Update Sui blockchain records
|
|
536
|
-
// 3. Sync with knowledge graph
|
|
537
|
-
|
|
538
|
-
for (const memory of processedMemories) {
|
|
539
|
-
this.emit('memory:stored', { memoryId: memory.id });
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
} catch (error) {
|
|
543
|
-
console.error('Walrus batch processing failed:', error);
|
|
544
|
-
throw error;
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
private async processMemoryImmediate(memory: Memory): Promise<void> {
|
|
549
|
-
// Process single memory immediately (bypass batching)
|
|
550
|
-
await this.processEmbeddingBatch([memory]);
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
private updateJobStatus(jobId: string, status: BatchJobStatus['status'], error?: Error): void {
|
|
554
|
-
const job = this.jobStatuses.get(jobId);
|
|
555
|
-
if (job) {
|
|
556
|
-
job.status = status;
|
|
557
|
-
|
|
558
|
-
if (status === 'completed' || status === 'failed') {
|
|
559
|
-
job.endTime = new Date();
|
|
560
|
-
job.processingTimeMs = job.endTime.getTime() - (job.startTime?.getTime() || 0);
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
if (error) {
|
|
564
|
-
job.error = error.message;
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
this.emit('job:status-changed', { jobId, status, error });
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
private updateMetrics(itemCount: number, processingTimeMs: number, success: boolean): void {
|
|
572
|
-
if (success) {
|
|
573
|
-
this.metrics.successfulBatches++;
|
|
574
|
-
} else {
|
|
575
|
-
this.metrics.failedBatches++;
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
this.metrics.totalMemoriesProcessed += itemCount;
|
|
579
|
-
this.metrics.totalProcessingTime += processingTimeMs;
|
|
580
|
-
this.metrics.averageMemoryProcessingTime =
|
|
581
|
-
this.metrics.totalProcessingTime / this.metrics.totalMemoriesProcessed;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
private getPriorityValue(priority?: 'low' | 'normal' | 'high'): number {
|
|
585
|
-
const priorityMap = { low: 0, normal: 1, high: 2 };
|
|
586
|
-
return priorityMap[priority || 'normal'];
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
private generateJobId(): string {
|
|
590
|
-
return `job_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
private delay(ms: number): Promise<void> {
|
|
594
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
|
|
1
|
+
/**
|
|
2
|
+
* BatchManager - Central orchestrator for all batch processing operations
|
|
3
|
+
*
|
|
4
|
+
* Coordinates embedding generation, vector indexing, knowledge graph updates,
|
|
5
|
+
* and Walrus operations through intelligent batching and caching.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import EventEmitter from 'eventemitter3';
|
|
9
|
+
import { BatchingService, BatchProcessor, BatchItem } from './BatchingService';
|
|
10
|
+
import { MemoryProcessingCache } from './MemoryProcessingCache';
|
|
11
|
+
import { EmbeddingService } from '../services/EmbeddingService';
|
|
12
|
+
import type { IHnswService } from '../vector/IHnswService';
|
|
13
|
+
import {
|
|
14
|
+
Memory,
|
|
15
|
+
ProcessedMemory,
|
|
16
|
+
BatchConfig,
|
|
17
|
+
BatchStats,
|
|
18
|
+
MemoryBatchResult
|
|
19
|
+
} from '../embedding/types';
|
|
20
|
+
|
|
21
|
+
export interface BatchManagerConfig {
|
|
22
|
+
embedding?: {
|
|
23
|
+
batchSize?: number;
|
|
24
|
+
delayMs?: number;
|
|
25
|
+
};
|
|
26
|
+
indexing?: {
|
|
27
|
+
batchSize?: number;
|
|
28
|
+
delayMs?: number;
|
|
29
|
+
};
|
|
30
|
+
walrus?: {
|
|
31
|
+
batchSize?: number;
|
|
32
|
+
delayMs?: number;
|
|
33
|
+
};
|
|
34
|
+
cache?: {
|
|
35
|
+
maxSize?: number;
|
|
36
|
+
ttlMs?: number;
|
|
37
|
+
};
|
|
38
|
+
enableMetrics?: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface BatchManagerStats extends BatchStats {
|
|
42
|
+
managers: {
|
|
43
|
+
embedding: BatchStats;
|
|
44
|
+
indexing: BatchStats;
|
|
45
|
+
walrus: BatchStats;
|
|
46
|
+
};
|
|
47
|
+
cache: any; // MemoryCacheStats
|
|
48
|
+
performance: {
|
|
49
|
+
totalProcessingTime: number;
|
|
50
|
+
averageMemoryProcessingTime: number;
|
|
51
|
+
successfulBatches: number;
|
|
52
|
+
failedBatches: number;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface BatchJobStatus {
|
|
57
|
+
id: string;
|
|
58
|
+
type: 'embedding' | 'indexing' | 'walrus' | 'pipeline';
|
|
59
|
+
status: 'pending' | 'processing' | 'completed' | 'failed';
|
|
60
|
+
itemCount: number;
|
|
61
|
+
startTime?: Date;
|
|
62
|
+
endTime?: Date;
|
|
63
|
+
error?: string;
|
|
64
|
+
processingTimeMs?: number;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Central batch processing manager with integrated caching and monitoring
|
|
69
|
+
*/
|
|
70
|
+
export class BatchManager extends EventEmitter {
|
|
71
|
+
private embeddingBatcher!: BatchingService<Memory>;
|
|
72
|
+
private indexingBatcher!: BatchingService<ProcessedMemory>;
|
|
73
|
+
private walrusBatcher!: BatchingService<ProcessedMemory>;
|
|
74
|
+
private cache!: MemoryProcessingCache;
|
|
75
|
+
|
|
76
|
+
private embeddingService?: EmbeddingService;
|
|
77
|
+
private indexService?: IHnswService;
|
|
78
|
+
|
|
79
|
+
private readonly config: Required<BatchManagerConfig>;
|
|
80
|
+
private jobStatuses = new Map<string, BatchJobStatus>();
|
|
81
|
+
private metrics = {
|
|
82
|
+
totalProcessingTime: 0,
|
|
83
|
+
averageMemoryProcessingTime: 0,
|
|
84
|
+
successfulBatches: 0,
|
|
85
|
+
failedBatches: 0,
|
|
86
|
+
totalMemoriesProcessed: 0
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
constructor(config: BatchManagerConfig = {}) {
|
|
90
|
+
super();
|
|
91
|
+
|
|
92
|
+
this.config = {
|
|
93
|
+
embedding: {
|
|
94
|
+
batchSize: config.embedding?.batchSize || 20,
|
|
95
|
+
delayMs: config.embedding?.delayMs || 5000
|
|
96
|
+
},
|
|
97
|
+
indexing: {
|
|
98
|
+
batchSize: config.indexing?.batchSize || 50,
|
|
99
|
+
delayMs: config.indexing?.delayMs || 3000
|
|
100
|
+
},
|
|
101
|
+
walrus: {
|
|
102
|
+
batchSize: config.walrus?.batchSize || 10,
|
|
103
|
+
delayMs: config.walrus?.delayMs || 8000
|
|
104
|
+
},
|
|
105
|
+
cache: {
|
|
106
|
+
maxSize: config.cache?.maxSize || 5000,
|
|
107
|
+
ttlMs: config.cache?.ttlMs || 60 * 60 * 1000
|
|
108
|
+
},
|
|
109
|
+
enableMetrics: config.enableMetrics !== false
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// Initialize services
|
|
113
|
+
this.initializeBatchingServices();
|
|
114
|
+
this.cache = new MemoryProcessingCache(this.config.cache);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ==================== SERVICE INITIALIZATION ====================
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Initialize with required services
|
|
121
|
+
*/
|
|
122
|
+
initialize(services: {
|
|
123
|
+
embeddingService?: EmbeddingService;
|
|
124
|
+
indexService?: IHnswService;
|
|
125
|
+
}): void {
|
|
126
|
+
this.embeddingService = services.embeddingService;
|
|
127
|
+
this.indexService = services.indexService;
|
|
128
|
+
|
|
129
|
+
console.log('BatchManager initialized with services');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ==================== MEMORY PROCESSING PIPELINE ====================
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Add memory to complete processing pipeline
|
|
136
|
+
*/
|
|
137
|
+
async addMemoryToPipeline(
|
|
138
|
+
memory: Memory,
|
|
139
|
+
options: {
|
|
140
|
+
priority?: 'low' | 'normal' | 'high';
|
|
141
|
+
skipCache?: boolean;
|
|
142
|
+
immediateProcessing?: boolean;
|
|
143
|
+
} = {}
|
|
144
|
+
): Promise<string> {
|
|
145
|
+
const jobId = this.generateJobId();
|
|
146
|
+
|
|
147
|
+
// Check cache first (unless skipped)
|
|
148
|
+
if (!options.skipCache) {
|
|
149
|
+
const cached = this.cache.getCachedMemory(memory.id);
|
|
150
|
+
if (cached?.processingState === 'completed') {
|
|
151
|
+
this.emit('memory:cached', { memoryId: memory.id, cached });
|
|
152
|
+
return jobId;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Create job status
|
|
157
|
+
this.jobStatuses.set(jobId, {
|
|
158
|
+
id: jobId,
|
|
159
|
+
type: 'pipeline',
|
|
160
|
+
status: 'pending',
|
|
161
|
+
itemCount: 1,
|
|
162
|
+
startTime: new Date()
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
// Cache memory as pending
|
|
167
|
+
this.cache.cacheMemory(memory);
|
|
168
|
+
this.cache.updateMemoryState(memory.id, 'pending');
|
|
169
|
+
|
|
170
|
+
// Add to processing pipeline
|
|
171
|
+
if (options.immediateProcessing) {
|
|
172
|
+
await this.processMemoryImmediate(memory);
|
|
173
|
+
} else {
|
|
174
|
+
// Add to embedding batch
|
|
175
|
+
this.embeddingBatcher.addToBatch('memories', {
|
|
176
|
+
id: memory.id,
|
|
177
|
+
data: memory,
|
|
178
|
+
timestamp: new Date(),
|
|
179
|
+
priority: this.getPriorityValue(options.priority),
|
|
180
|
+
metadata: { originalJobId: jobId }
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
this.updateJobStatus(jobId, 'processing');
|
|
185
|
+
this.emit('memory:queued', { memoryId: memory.id, jobId });
|
|
186
|
+
|
|
187
|
+
} catch (error) {
|
|
188
|
+
this.updateJobStatus(jobId, 'failed', error as Error);
|
|
189
|
+
this.cache.updateMemoryState(memory.id, 'failed', (error as Error).message);
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return jobId;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Process multiple memories in batch
|
|
198
|
+
*/
|
|
199
|
+
async addMemoriesToPipeline(
|
|
200
|
+
memories: Memory[],
|
|
201
|
+
options: {
|
|
202
|
+
priority?: 'low' | 'normal' | 'high';
|
|
203
|
+
batchSize?: number;
|
|
204
|
+
} = {}
|
|
205
|
+
): Promise<string[]> {
|
|
206
|
+
const batchSize = options.batchSize || this.config.embedding.batchSize;
|
|
207
|
+
const jobIds: string[] = [];
|
|
208
|
+
|
|
209
|
+
// Process in smaller batches to avoid overwhelming the system
|
|
210
|
+
const safeBatchSize = batchSize || 10;
|
|
211
|
+
for (let i = 0; i < memories.length; i += safeBatchSize) {
|
|
212
|
+
const batch = memories.slice(i, i + safeBatchSize);
|
|
213
|
+
|
|
214
|
+
for (const memory of batch) {
|
|
215
|
+
try {
|
|
216
|
+
const jobId = await this.addMemoryToPipeline(memory, options);
|
|
217
|
+
jobIds.push(jobId);
|
|
218
|
+
} catch (error) {
|
|
219
|
+
console.error(`Failed to add memory ${memory.id} to pipeline:`, error);
|
|
220
|
+
// Continue with other memories
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Small delay between batches
|
|
225
|
+
if (i + safeBatchSize < memories.length) {
|
|
226
|
+
await this.delay(100);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return jobIds;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// ==================== BATCH PROCESSING CONTROL ====================
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Process all pending batches immediately
|
|
237
|
+
*/
|
|
238
|
+
async processAllBatches(): Promise<void> {
|
|
239
|
+
console.log('Processing all pending batches...');
|
|
240
|
+
|
|
241
|
+
try {
|
|
242
|
+
await Promise.all([
|
|
243
|
+
this.embeddingBatcher.processAllBatches(),
|
|
244
|
+
this.indexingBatcher.processAllBatches(),
|
|
245
|
+
this.walrusBatcher.processAllBatches()
|
|
246
|
+
]);
|
|
247
|
+
|
|
248
|
+
console.log('All batches processed successfully');
|
|
249
|
+
} catch (error) {
|
|
250
|
+
console.error('Error processing batches:', error);
|
|
251
|
+
throw error;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Get status of specific job
|
|
257
|
+
*/
|
|
258
|
+
getJobStatus(jobId: string): BatchJobStatus | undefined {
|
|
259
|
+
return this.jobStatuses.get(jobId);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Get all job statuses
|
|
264
|
+
*/
|
|
265
|
+
getAllJobStatuses(): BatchJobStatus[] {
|
|
266
|
+
return Array.from(this.jobStatuses.values());
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Get pending jobs count by type
|
|
271
|
+
*/
|
|
272
|
+
getPendingJobsCount(): Record<string, number> {
|
|
273
|
+
const counts = { embedding: 0, indexing: 0, walrus: 0, pipeline: 0 };
|
|
274
|
+
|
|
275
|
+
for (const job of this.jobStatuses.values()) {
|
|
276
|
+
if (job.status === 'pending' || job.status === 'processing') {
|
|
277
|
+
counts[job.type]++;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return counts;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// ==================== CACHING OPERATIONS ====================
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Get cached memory
|
|
288
|
+
*/
|
|
289
|
+
getCachedMemory(memoryId: string) {
|
|
290
|
+
return this.cache.getCachedMemory(memoryId);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Find similar memories
|
|
295
|
+
*/
|
|
296
|
+
findSimilarMemories(memoryId: string, limit?: number) {
|
|
297
|
+
return this.cache.findSimilarMemories(memoryId, limit);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Get cache statistics
|
|
302
|
+
*/
|
|
303
|
+
getCacheStats() {
|
|
304
|
+
return this.cache.getStats();
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Clear all caches
|
|
309
|
+
*/
|
|
310
|
+
clearCaches(): void {
|
|
311
|
+
this.cache.clearAll();
|
|
312
|
+
this.jobStatuses.clear();
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// ==================== STATISTICS & MONITORING ====================
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Get comprehensive batch manager statistics
|
|
319
|
+
*/
|
|
320
|
+
getStats(): BatchManagerStats {
|
|
321
|
+
const embeddingStats = this.embeddingBatcher.getBatchStats();
|
|
322
|
+
const indexingStats = this.indexingBatcher.getBatchStats();
|
|
323
|
+
const walrusStats = this.walrusBatcher.getBatchStats();
|
|
324
|
+
const cacheStats = this.cache.getStats();
|
|
325
|
+
|
|
326
|
+
return {
|
|
327
|
+
// Aggregate stats
|
|
328
|
+
totalUsers: Math.max(embeddingStats.totalUsers, indexingStats.totalUsers, walrusStats.totalUsers),
|
|
329
|
+
totalPendingVectors: embeddingStats.totalPendingVectors + indexingStats.totalPendingVectors,
|
|
330
|
+
activeBatchJobs: embeddingStats.activeBatchJobs + indexingStats.activeBatchJobs + walrusStats.activeBatchJobs,
|
|
331
|
+
cacheHitRate: cacheStats.performance.hitRateMemories,
|
|
332
|
+
averageBatchSize: (embeddingStats.averageBatchSize + indexingStats.averageBatchSize + walrusStats.averageBatchSize) / 3,
|
|
333
|
+
averageProcessingTime: this.metrics.averageMemoryProcessingTime,
|
|
334
|
+
|
|
335
|
+
// Individual managers
|
|
336
|
+
managers: {
|
|
337
|
+
embedding: embeddingStats,
|
|
338
|
+
indexing: indexingStats,
|
|
339
|
+
walrus: walrusStats
|
|
340
|
+
},
|
|
341
|
+
|
|
342
|
+
// Cache stats
|
|
343
|
+
cache: cacheStats,
|
|
344
|
+
|
|
345
|
+
// Performance metrics
|
|
346
|
+
performance: {
|
|
347
|
+
totalProcessingTime: this.metrics.totalProcessingTime,
|
|
348
|
+
averageMemoryProcessingTime: this.metrics.averageMemoryProcessingTime,
|
|
349
|
+
successfulBatches: this.metrics.successfulBatches,
|
|
350
|
+
failedBatches: this.metrics.failedBatches
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Cleanup and destroy batch manager
|
|
357
|
+
*/
|
|
358
|
+
destroy(): void {
|
|
359
|
+
// Destroy all batching services
|
|
360
|
+
this.embeddingBatcher.destroy();
|
|
361
|
+
this.indexingBatcher.destroy();
|
|
362
|
+
this.walrusBatcher.destroy();
|
|
363
|
+
|
|
364
|
+
// Destroy cache
|
|
365
|
+
this.cache.destroy();
|
|
366
|
+
|
|
367
|
+
// Clear job statuses
|
|
368
|
+
this.jobStatuses.clear();
|
|
369
|
+
|
|
370
|
+
// Remove all listeners
|
|
371
|
+
this.removeAllListeners();
|
|
372
|
+
|
|
373
|
+
console.log('BatchManager destroyed');
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// ==================== PRIVATE METHODS ====================
|
|
377
|
+
|
|
378
|
+
private initializeBatchingServices(): void {
|
|
379
|
+
// Initialize embedding batcher
|
|
380
|
+
this.embeddingBatcher = new BatchingService<Memory>(
|
|
381
|
+
{
|
|
382
|
+
maxBatchSize: this.config.embedding.batchSize,
|
|
383
|
+
batchDelayMs: this.config.embedding.delayMs
|
|
384
|
+
}
|
|
385
|
+
);
|
|
386
|
+
|
|
387
|
+
// Initialize indexing batcher
|
|
388
|
+
this.indexingBatcher = new BatchingService<ProcessedMemory>(
|
|
389
|
+
{
|
|
390
|
+
maxBatchSize: this.config.indexing.batchSize,
|
|
391
|
+
batchDelayMs: this.config.indexing.delayMs
|
|
392
|
+
}
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
// Initialize Walrus batcher
|
|
396
|
+
this.walrusBatcher = new BatchingService<ProcessedMemory>(
|
|
397
|
+
{
|
|
398
|
+
maxBatchSize: this.config.walrus.batchSize,
|
|
399
|
+
batchDelayMs: this.config.walrus.delayMs
|
|
400
|
+
}
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
// Register processors
|
|
404
|
+
this.registerBatchProcessors();
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
private registerBatchProcessors(): void {
|
|
408
|
+
// Embedding processor
|
|
409
|
+
this.embeddingBatcher.registerProcessor('memories', {
|
|
410
|
+
process: async (items: BatchItem<Memory>[]) => {
|
|
411
|
+
await this.processEmbeddingBatch(items.map(item => item.data));
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
// Indexing processor
|
|
416
|
+
this.indexingBatcher.registerProcessor('processed-memories', {
|
|
417
|
+
process: async (items: BatchItem<ProcessedMemory>[]) => {
|
|
418
|
+
await this.processIndexingBatch(items.map(item => item.data));
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
// Walrus processor
|
|
423
|
+
this.walrusBatcher.registerProcessor('walrus-uploads', {
|
|
424
|
+
process: async (items: BatchItem<ProcessedMemory>[]) => {
|
|
425
|
+
await this.processWalrusBatch(items.map(item => item.data));
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
private async processEmbeddingBatch(memories: Memory[]): Promise<void> {
|
|
431
|
+
if (!this.embeddingService) {
|
|
432
|
+
console.warn('EmbeddingService not initialized');
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
console.log(`Processing embedding batch of ${memories.length} memories`);
|
|
437
|
+
const startTime = Date.now();
|
|
438
|
+
|
|
439
|
+
try {
|
|
440
|
+
// Generate embeddings in batch
|
|
441
|
+
const results = await this.embeddingService.embedBatch(
|
|
442
|
+
memories.map(m => m.content)
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
// Process results
|
|
446
|
+
for (let i = 0; i < memories.length; i++) {
|
|
447
|
+
const memory = memories[i];
|
|
448
|
+
const embedding = results.vectors[i];
|
|
449
|
+
|
|
450
|
+
if (embedding && embedding.length > 0) {
|
|
451
|
+
const processedMemory: ProcessedMemory = {
|
|
452
|
+
...memory,
|
|
453
|
+
embedding: embedding,
|
|
454
|
+
embeddingModel: 'gemini-embedding',
|
|
455
|
+
processedAt: new Date()
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
// Update cache
|
|
459
|
+
this.cache.cacheMemory(memory, processedMemory);
|
|
460
|
+
this.cache.updateMemoryState(memory.id, 'completed');
|
|
461
|
+
|
|
462
|
+
// Add to indexing batch
|
|
463
|
+
this.indexingBatcher.addToBatch('processed-memories', {
|
|
464
|
+
id: memory.id,
|
|
465
|
+
data: processedMemory,
|
|
466
|
+
timestamp: new Date()
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
this.emit('memory:embedded', { memoryId: memory.id, embedding: embedding });
|
|
470
|
+
} else {
|
|
471
|
+
this.cache.updateMemoryState(memory.id, 'failed', 'Embedding generation failed');
|
|
472
|
+
this.emit('memory:embedding-failed', { memoryId: memory.id, error: 'Embedding generation failed' });
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
const processingTime = Date.now() - startTime;
|
|
477
|
+
this.updateMetrics(memories.length, processingTime, true);
|
|
478
|
+
|
|
479
|
+
} catch (error) {
|
|
480
|
+
console.error('Embedding batch processing failed:', error);
|
|
481
|
+
|
|
482
|
+
// Update all memories as failed
|
|
483
|
+
for (const memory of memories) {
|
|
484
|
+
this.cache.updateMemoryState(memory.id, 'failed', (error as Error).message);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
this.updateMetrics(memories.length, Date.now() - startTime, false);
|
|
488
|
+
throw error;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
private async processIndexingBatch(processedMemories: ProcessedMemory[]): Promise<void> {
|
|
493
|
+
if (!this.indexService) {
|
|
494
|
+
console.warn('IHnswService not initialized');
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
console.log(`Processing indexing batch of ${processedMemories.length} memories`);
|
|
499
|
+
|
|
500
|
+
try {
|
|
501
|
+
// Add to HNSW index
|
|
502
|
+
for (const memory of processedMemories) {
|
|
503
|
+
await this.indexService.addVector(
|
|
504
|
+
memory.userId || 'default-user',
|
|
505
|
+
parseInt(memory.id) || Date.now(),
|
|
506
|
+
memory.embedding || [],
|
|
507
|
+
{
|
|
508
|
+
content: memory.content,
|
|
509
|
+
category: memory.category,
|
|
510
|
+
timestamp: memory.createdAt
|
|
511
|
+
}
|
|
512
|
+
);
|
|
513
|
+
|
|
514
|
+
this.emit('memory:indexed', { memoryId: memory.id });
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// Flush pending vectors
|
|
518
|
+
if (this.indexService) {
|
|
519
|
+
await this.indexService.flushBatch('batch-operation');
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
} catch (error) {
|
|
523
|
+
console.error('Indexing batch processing failed:', error);
|
|
524
|
+
throw error;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
private async processWalrusBatch(processedMemories: ProcessedMemory[]): Promise<void> {
|
|
529
|
+
console.log(`Processing Walrus batch of ${processedMemories.length} memories`);
|
|
530
|
+
|
|
531
|
+
try {
|
|
532
|
+
// TODO: Implement Walrus batch operations
|
|
533
|
+
// This would involve:
|
|
534
|
+
// 1. Batch upload processed memories to Walrus
|
|
535
|
+
// 2. Update Sui blockchain records
|
|
536
|
+
// 3. Sync with knowledge graph
|
|
537
|
+
|
|
538
|
+
for (const memory of processedMemories) {
|
|
539
|
+
this.emit('memory:stored', { memoryId: memory.id });
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
} catch (error) {
|
|
543
|
+
console.error('Walrus batch processing failed:', error);
|
|
544
|
+
throw error;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
private async processMemoryImmediate(memory: Memory): Promise<void> {
|
|
549
|
+
// Process single memory immediately (bypass batching)
|
|
550
|
+
await this.processEmbeddingBatch([memory]);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
private updateJobStatus(jobId: string, status: BatchJobStatus['status'], error?: Error): void {
|
|
554
|
+
const job = this.jobStatuses.get(jobId);
|
|
555
|
+
if (job) {
|
|
556
|
+
job.status = status;
|
|
557
|
+
|
|
558
|
+
if (status === 'completed' || status === 'failed') {
|
|
559
|
+
job.endTime = new Date();
|
|
560
|
+
job.processingTimeMs = job.endTime.getTime() - (job.startTime?.getTime() || 0);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if (error) {
|
|
564
|
+
job.error = error.message;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
this.emit('job:status-changed', { jobId, status, error });
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
private updateMetrics(itemCount: number, processingTimeMs: number, success: boolean): void {
|
|
572
|
+
if (success) {
|
|
573
|
+
this.metrics.successfulBatches++;
|
|
574
|
+
} else {
|
|
575
|
+
this.metrics.failedBatches++;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
this.metrics.totalMemoriesProcessed += itemCount;
|
|
579
|
+
this.metrics.totalProcessingTime += processingTimeMs;
|
|
580
|
+
this.metrics.averageMemoryProcessingTime =
|
|
581
|
+
this.metrics.totalProcessingTime / this.metrics.totalMemoriesProcessed;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
private getPriorityValue(priority?: 'low' | 'normal' | 'high'): number {
|
|
585
|
+
const priorityMap = { low: 0, normal: 1, high: 2 };
|
|
586
|
+
return priorityMap[priority || 'normal'];
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
private generateJobId(): string {
|
|
590
|
+
return `job_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
private delay(ms: number): Promise<void> {
|
|
594
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
598
|
export default BatchManager;
|