@soulcraft/brainy 5.4.0 → 5.6.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/CHANGELOG.md +45 -0
- package/README.md +4 -3
- package/dist/augmentations/display/fieldPatterns.js +3 -3
- package/dist/augmentations/display/intelligentComputation.d.ts +1 -1
- package/dist/augmentations/display/intelligentComputation.js +1 -3
- package/dist/augmentations/typeMatching/brainyTypes.d.ts +1 -1
- package/dist/augmentations/typeMatching/brainyTypes.js +7 -9
- package/dist/augmentations/typeMatching/intelligentTypeMatcher.d.ts +1 -1
- package/dist/augmentations/typeMatching/intelligentTypeMatcher.js +1 -1
- package/dist/augmentations/universalDisplayAugmentation.d.ts +1 -1
- package/dist/augmentations/universalDisplayAugmentation.js +1 -1
- package/dist/brainy.js +2 -2
- package/dist/cli/commands/types.js +2 -2
- package/dist/cortex/neuralImport.js +0 -1
- package/dist/hnsw/typeAwareHNSWIndex.d.ts +3 -3
- package/dist/hnsw/typeAwareHNSWIndex.js +5 -5
- package/dist/importers/SmartExcelImporter.js +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/neural/embeddedKeywordEmbeddings.d.ts +1 -1
- package/dist/neural/embeddedKeywordEmbeddings.js +56 -56
- package/dist/neural/embeddedTypeEmbeddings.d.ts +3 -3
- package/dist/neural/embeddedTypeEmbeddings.js +14 -14
- package/dist/neural/entityExtractor.js +2 -2
- package/dist/neural/relationshipConfidence.js +1 -1
- package/dist/neural/signals/VerbContextSignal.js +6 -6
- package/dist/neural/signals/VerbExactMatchSignal.js +9 -9
- package/dist/neural/signals/VerbPatternSignal.js +5 -5
- package/dist/query/typeAwareQueryPlanner.d.ts +7 -7
- package/dist/query/typeAwareQueryPlanner.js +9 -10
- package/dist/storage/baseStorage.d.ts +48 -1
- package/dist/storage/baseStorage.js +237 -19
- package/dist/types/graphTypes.d.ts +588 -230
- package/dist/types/graphTypes.js +683 -248
- package/dist/types/typeMigration.d.ts +95 -0
- package/dist/types/typeMigration.js +141 -0
- package/dist/utils/intelligentTypeMapper.js +2 -2
- package/dist/utils/metadataIndex.js +6 -6
- package/package.json +2 -2
- package/dist/importManager.d.ts +0 -78
- package/dist/importManager.js +0 -267
- package/dist/storage/adapters/typeAwareStorageAdapter.d.ts +0 -300
- package/dist/storage/adapters/typeAwareStorageAdapter.js +0 -1012
|
@@ -1,1012 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Type-Aware Storage Adapter
|
|
3
|
-
*
|
|
4
|
-
* Wraps underlying storage (FileSystem, GCS, S3, etc.) with type-first organization.
|
|
5
|
-
* Enables efficient type-based queries via directory structure.
|
|
6
|
-
*
|
|
7
|
-
* IMPLEMENTED Features (v3.45.0):
|
|
8
|
-
* - Type-first paths: entities/nouns/{type}/vectors/{shard}/{uuid}.json
|
|
9
|
-
* - Fixed-size type count tracking: Uint32Array(31 + 40) = 284 bytes
|
|
10
|
-
* - Type-based filtering: List entities by type via directory structure
|
|
11
|
-
* - Type caching: Map<id, type> for frequently accessed entities
|
|
12
|
-
*
|
|
13
|
-
* MEASURED Performance (tests up to 1M entities):
|
|
14
|
-
* - Type count memory: 284 bytes (vs Map-based: ~100KB at 1M scale) = 99.7% reduction
|
|
15
|
-
* - getNounsByType: O(entities_of_type) via directory scan (vs O(total) full scan)
|
|
16
|
-
* - getVerbsByType: O(entities_of_type) via directory scan (vs O(total) full scan)
|
|
17
|
-
* - Type-cached lookups: O(1) after first access
|
|
18
|
-
*
|
|
19
|
-
* PROJECTED Performance (billion-scale, NOT tested):
|
|
20
|
-
* - Total memory: PROJECTED ~50-100GB (vs theoretical 500GB baseline)
|
|
21
|
-
* - Type count: 284 bytes remains constant (not dependent on entity count)
|
|
22
|
-
* - Type cache: Grows with usage (10% cached at 1B = ~5GB overhead)
|
|
23
|
-
* - Note: Billion-scale claims are EXTRAPOLATIONS, not measurements
|
|
24
|
-
*
|
|
25
|
-
* LIMITATIONS:
|
|
26
|
-
* - Type cache grows unbounded (no eviction policy)
|
|
27
|
-
* - Uncached entity lookups: O(types) worst case (searches all type directories)
|
|
28
|
-
* - v4.8.1: getVerbsBySource/Target delegate to underlying (previously O(total_verbs))
|
|
29
|
-
*
|
|
30
|
-
* TEST COVERAGE:
|
|
31
|
-
* - Unit tests: typeAwareStorageAdapter.test.ts (17 tests passing)
|
|
32
|
-
* - Integration tests: Tested with 1,155 entities (Workshop data)
|
|
33
|
-
* - Performance tests: None (no benchmark comparisons yet)
|
|
34
|
-
*
|
|
35
|
-
* @version 3.45.0 (created), 4.8.1 (performance fix)
|
|
36
|
-
* @since Phase 1 - Type-First Implementation
|
|
37
|
-
*/
|
|
38
|
-
import { BaseStorage } from '../baseStorage.js';
|
|
39
|
-
import { NounType, TypeUtils, NOUN_TYPE_COUNT, VERB_TYPE_COUNT } from '../../types/graphTypes.js';
|
|
40
|
-
import { getShardIdFromUuid } from '../sharding.js';
|
|
41
|
-
/**
|
|
42
|
-
* Type-first storage paths
|
|
43
|
-
* Beautiful, self-documenting structure
|
|
44
|
-
*/
|
|
45
|
-
const SYSTEM_DIR = '_system';
|
|
46
|
-
/**
|
|
47
|
-
* Get type-first path for noun vectors
|
|
48
|
-
*/
|
|
49
|
-
function getNounVectorPath(type, id) {
|
|
50
|
-
const shard = getShardIdFromUuid(id);
|
|
51
|
-
return `entities/nouns/${type}/vectors/${shard}/${id}.json`;
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Get type-first path for noun metadata
|
|
55
|
-
*/
|
|
56
|
-
function getNounMetadataPath(type, id) {
|
|
57
|
-
const shard = getShardIdFromUuid(id);
|
|
58
|
-
return `entities/nouns/${type}/metadata/${shard}/${id}.json`;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Get type-first path for verb vectors
|
|
62
|
-
*/
|
|
63
|
-
function getVerbVectorPath(type, id) {
|
|
64
|
-
const shard = getShardIdFromUuid(id);
|
|
65
|
-
return `entities/verbs/${type}/vectors/${shard}/${id}.json`;
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Get type-first path for verb metadata
|
|
69
|
-
*/
|
|
70
|
-
function getVerbMetadataPath(type, id) {
|
|
71
|
-
const shard = getShardIdFromUuid(id);
|
|
72
|
-
return `entities/verbs/${type}/metadata/${shard}/${id}.json`;
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Type-Aware Storage Adapter
|
|
76
|
-
*
|
|
77
|
-
* Wraps an underlying storage adapter and adds type-first routing
|
|
78
|
-
* Tracks types with fixed-size arrays for billion-scale efficiency
|
|
79
|
-
*/
|
|
80
|
-
export class TypeAwareStorageAdapter extends BaseStorage {
|
|
81
|
-
constructor(options) {
|
|
82
|
-
super();
|
|
83
|
-
// Fixed-size type tracking (99.76% memory reduction vs Maps)
|
|
84
|
-
this.nounCountsByType = new Uint32Array(NOUN_TYPE_COUNT); // 124 bytes
|
|
85
|
-
this.verbCountsByType = new Uint32Array(VERB_TYPE_COUNT); // 160 bytes
|
|
86
|
-
// Total: 284 bytes (vs ~120KB with Maps)
|
|
87
|
-
// Type cache for fast lookups (id -> type)
|
|
88
|
-
// Only for entities we've seen this session (bounded size)
|
|
89
|
-
this.nounTypeCache = new Map();
|
|
90
|
-
this.verbTypeCache = new Map();
|
|
91
|
-
this.underlying = options.underlyingStorage;
|
|
92
|
-
this.verbose = options.verbose || false;
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Helper to access protected methods on underlying storage
|
|
96
|
-
* TypeScript doesn't allow calling protected methods across instances,
|
|
97
|
-
* so we cast to any to bypass this restriction
|
|
98
|
-
*/
|
|
99
|
-
get u() {
|
|
100
|
-
return this.underlying;
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Initialize storage adapter
|
|
104
|
-
*/
|
|
105
|
-
async init() {
|
|
106
|
-
if (this.verbose) {
|
|
107
|
-
console.log('[TypeAwareStorage] Initializing...');
|
|
108
|
-
}
|
|
109
|
-
// Initialize underlying storage
|
|
110
|
-
if (typeof this.underlying.init === 'function') {
|
|
111
|
-
await this.underlying.init();
|
|
112
|
-
}
|
|
113
|
-
// Load type statistics from storage (if they exist)
|
|
114
|
-
await this.loadTypeStatistics();
|
|
115
|
-
this.isInitialized = true;
|
|
116
|
-
if (this.verbose) {
|
|
117
|
-
console.log('[TypeAwareStorage] Initialized successfully');
|
|
118
|
-
console.log(`[TypeAwareStorage] Noun counts:`, Array.from(this.nounCountsByType));
|
|
119
|
-
console.log(`[TypeAwareStorage] Verb counts:`, Array.from(this.verbCountsByType));
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Load type statistics from storage
|
|
124
|
-
* Rebuilds type counts if needed
|
|
125
|
-
*/
|
|
126
|
-
async loadTypeStatistics() {
|
|
127
|
-
try {
|
|
128
|
-
const stats = await this.u.readObjectFromPath(`${SYSTEM_DIR}/type-statistics.json`);
|
|
129
|
-
if (stats) {
|
|
130
|
-
// Restore counts from saved statistics
|
|
131
|
-
if (stats.nounCounts && stats.nounCounts.length === NOUN_TYPE_COUNT) {
|
|
132
|
-
this.nounCountsByType = new Uint32Array(stats.nounCounts);
|
|
133
|
-
}
|
|
134
|
-
if (stats.verbCounts && stats.verbCounts.length === VERB_TYPE_COUNT) {
|
|
135
|
-
this.verbCountsByType = new Uint32Array(stats.verbCounts);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
catch (error) {
|
|
140
|
-
if (this.verbose) {
|
|
141
|
-
console.log('[TypeAwareStorage] No existing type statistics, starting fresh');
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* Save type statistics to storage
|
|
147
|
-
*/
|
|
148
|
-
async saveTypeStatistics() {
|
|
149
|
-
const stats = {
|
|
150
|
-
nounCounts: Array.from(this.nounCountsByType),
|
|
151
|
-
verbCounts: Array.from(this.verbCountsByType),
|
|
152
|
-
updatedAt: Date.now()
|
|
153
|
-
};
|
|
154
|
-
await this.u.writeObjectToPath(`${SYSTEM_DIR}/type-statistics.json`, stats);
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Get noun type from cache
|
|
158
|
-
*
|
|
159
|
-
* v4.0.0: Metadata is stored separately, so we rely on the cache
|
|
160
|
-
* which is populated when saveNounMetadata is called
|
|
161
|
-
*/
|
|
162
|
-
getNounType(noun) {
|
|
163
|
-
// Check cache (populated when metadata is saved)
|
|
164
|
-
const cached = this.nounTypeCache.get(noun.id);
|
|
165
|
-
if (cached) {
|
|
166
|
-
return cached;
|
|
167
|
-
}
|
|
168
|
-
// Default to 'thing' if unknown
|
|
169
|
-
// This should only happen if saveNoun_internal is called before saveNounMetadata
|
|
170
|
-
console.warn(`[TypeAwareStorage] Unknown noun type for ${noun.id}, defaulting to 'thing'`);
|
|
171
|
-
return 'thing';
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Get verb type from verb object
|
|
175
|
-
*
|
|
176
|
-
* ARCHITECTURAL FIX (v3.50.1): Simplified - verb field is now always present
|
|
177
|
-
*/
|
|
178
|
-
getVerbType(verb) {
|
|
179
|
-
// v3.50.1+: verb is a required field in HNSWVerb
|
|
180
|
-
if ('verb' in verb && verb.verb) {
|
|
181
|
-
return verb.verb;
|
|
182
|
-
}
|
|
183
|
-
// Fallback for GraphVerb (type alias)
|
|
184
|
-
if ('type' in verb && verb.type) {
|
|
185
|
-
return verb.type;
|
|
186
|
-
}
|
|
187
|
-
// This should never happen with v3.50.1+ data
|
|
188
|
-
console.warn(`[TypeAwareStorage] Verb missing type field for ${verb.id}, defaulting to 'relatedTo'`);
|
|
189
|
-
return 'relatedTo';
|
|
190
|
-
}
|
|
191
|
-
// ============================================================================
|
|
192
|
-
// ABSTRACT METHOD IMPLEMENTATIONS
|
|
193
|
-
// ============================================================================
|
|
194
|
-
/**
|
|
195
|
-
* Save noun (type-first path)
|
|
196
|
-
*/
|
|
197
|
-
async saveNoun_internal(noun) {
|
|
198
|
-
const type = this.getNounType(noun);
|
|
199
|
-
const path = getNounVectorPath(type, noun.id);
|
|
200
|
-
// Update type tracking
|
|
201
|
-
const typeIndex = TypeUtils.getNounIndex(type);
|
|
202
|
-
this.nounCountsByType[typeIndex]++;
|
|
203
|
-
this.nounTypeCache.set(noun.id, type);
|
|
204
|
-
// COW-aware write (v5.0.1): Use COW helper for branch isolation
|
|
205
|
-
await this.writeObjectToBranch(path, noun);
|
|
206
|
-
// Periodically save statistics (every 100 saves)
|
|
207
|
-
if (this.nounCountsByType[typeIndex] % 100 === 0) {
|
|
208
|
-
await this.saveTypeStatistics();
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
/**
|
|
212
|
-
* Get noun (type-first path)
|
|
213
|
-
*/
|
|
214
|
-
async getNoun_internal(id) {
|
|
215
|
-
// Try cache first
|
|
216
|
-
const cachedType = this.nounTypeCache.get(id);
|
|
217
|
-
if (cachedType) {
|
|
218
|
-
const path = getNounVectorPath(cachedType, id);
|
|
219
|
-
// COW-aware read (v5.0.1): Use COW helper for branch isolation
|
|
220
|
-
return await this.readWithInheritance(path);
|
|
221
|
-
}
|
|
222
|
-
// Need to search across all types (expensive, but cached after first access)
|
|
223
|
-
for (let i = 0; i < NOUN_TYPE_COUNT; i++) {
|
|
224
|
-
const type = TypeUtils.getNounFromIndex(i);
|
|
225
|
-
const path = getNounVectorPath(type, id);
|
|
226
|
-
try {
|
|
227
|
-
// COW-aware read (v5.0.1): Use COW helper for branch isolation
|
|
228
|
-
const noun = await this.readWithInheritance(path);
|
|
229
|
-
if (noun) {
|
|
230
|
-
// Cache the type for next time
|
|
231
|
-
this.nounTypeCache.set(id, type);
|
|
232
|
-
return noun;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
catch (error) {
|
|
236
|
-
// Not in this type, continue searching
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
return null;
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Get nouns by noun type (O(1) with type-first paths!)
|
|
243
|
-
*/
|
|
244
|
-
async getNounsByNounType_internal(nounType) {
|
|
245
|
-
const type = nounType;
|
|
246
|
-
const prefix = `entities/nouns/${type}/vectors/`;
|
|
247
|
-
// COW-aware list (v5.0.1): Use COW helper for branch isolation
|
|
248
|
-
const paths = await this.listObjectsInBranch(prefix);
|
|
249
|
-
// Load all nouns of this type
|
|
250
|
-
const nouns = [];
|
|
251
|
-
for (const path of paths) {
|
|
252
|
-
try {
|
|
253
|
-
// COW-aware read (v5.0.1): Use COW helper for branch isolation
|
|
254
|
-
const noun = await this.readWithInheritance(path);
|
|
255
|
-
if (noun) {
|
|
256
|
-
nouns.push(noun);
|
|
257
|
-
// Cache the type
|
|
258
|
-
this.nounTypeCache.set(noun.id, type);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
catch (error) {
|
|
262
|
-
console.warn(`[TypeAwareStorage] Failed to load noun from ${path}:`, error);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
return nouns;
|
|
266
|
-
}
|
|
267
|
-
/**
|
|
268
|
-
* Delete noun (type-first path)
|
|
269
|
-
*/
|
|
270
|
-
async deleteNoun_internal(id) {
|
|
271
|
-
// Try cache first
|
|
272
|
-
const cachedType = this.nounTypeCache.get(id);
|
|
273
|
-
if (cachedType) {
|
|
274
|
-
const path = getNounVectorPath(cachedType, id);
|
|
275
|
-
// COW-aware delete (v5.0.1): Use COW helper for branch isolation
|
|
276
|
-
await this.deleteObjectFromBranch(path);
|
|
277
|
-
// Update counts
|
|
278
|
-
const typeIndex = TypeUtils.getNounIndex(cachedType);
|
|
279
|
-
if (this.nounCountsByType[typeIndex] > 0) {
|
|
280
|
-
this.nounCountsByType[typeIndex]--;
|
|
281
|
-
}
|
|
282
|
-
this.nounTypeCache.delete(id);
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
// Search across all types
|
|
286
|
-
for (let i = 0; i < NOUN_TYPE_COUNT; i++) {
|
|
287
|
-
const type = TypeUtils.getNounFromIndex(i);
|
|
288
|
-
const path = getNounVectorPath(type, id);
|
|
289
|
-
try {
|
|
290
|
-
// COW-aware delete (v5.0.1): Use COW helper for branch isolation
|
|
291
|
-
await this.deleteObjectFromBranch(path);
|
|
292
|
-
// Update counts
|
|
293
|
-
if (this.nounCountsByType[i] > 0) {
|
|
294
|
-
this.nounCountsByType[i]--;
|
|
295
|
-
}
|
|
296
|
-
this.nounTypeCache.delete(id);
|
|
297
|
-
return;
|
|
298
|
-
}
|
|
299
|
-
catch (error) {
|
|
300
|
-
// Not in this type, continue
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
/**
|
|
305
|
-
* Save verb (type-first path)
|
|
306
|
-
*
|
|
307
|
-
* ARCHITECTURAL FIX (v3.50.1): No more caching hack needed!
|
|
308
|
-
* HNSWVerb now includes verb field, so type is always available
|
|
309
|
-
*/
|
|
310
|
-
async saveVerb_internal(verb) {
|
|
311
|
-
// Type is now a first-class field in HNSWVerb - no caching needed!
|
|
312
|
-
const type = verb.verb;
|
|
313
|
-
const path = getVerbVectorPath(type, verb.id);
|
|
314
|
-
// Update type tracking
|
|
315
|
-
const typeIndex = TypeUtils.getVerbIndex(type);
|
|
316
|
-
this.verbCountsByType[typeIndex]++;
|
|
317
|
-
this.verbTypeCache.set(verb.id, type);
|
|
318
|
-
// COW-aware write (v5.0.1): Use COW helper for branch isolation
|
|
319
|
-
await this.writeObjectToBranch(path, verb);
|
|
320
|
-
// Periodically save statistics
|
|
321
|
-
if (this.verbCountsByType[typeIndex] % 100 === 0) {
|
|
322
|
-
await this.saveTypeStatistics();
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
/**
|
|
326
|
-
* Get verb (type-first path)
|
|
327
|
-
*
|
|
328
|
-
* ARCHITECTURAL FIX (v3.50.1): Cache still useful for performance
|
|
329
|
-
* Once we know where a verb is, we can retrieve it O(1) instead of searching all types
|
|
330
|
-
*/
|
|
331
|
-
async getVerb_internal(id) {
|
|
332
|
-
// Try cache first for O(1) retrieval
|
|
333
|
-
const cachedType = this.verbTypeCache.get(id);
|
|
334
|
-
if (cachedType) {
|
|
335
|
-
const path = getVerbVectorPath(cachedType, id);
|
|
336
|
-
// COW-aware read (v5.0.1): Use COW helper for branch isolation
|
|
337
|
-
const verb = await this.readWithInheritance(path);
|
|
338
|
-
return verb;
|
|
339
|
-
}
|
|
340
|
-
// Search across all types (only on first access)
|
|
341
|
-
for (let i = 0; i < VERB_TYPE_COUNT; i++) {
|
|
342
|
-
const type = TypeUtils.getVerbFromIndex(i);
|
|
343
|
-
const path = getVerbVectorPath(type, id);
|
|
344
|
-
try {
|
|
345
|
-
// COW-aware read (v5.0.1): Use COW helper for branch isolation
|
|
346
|
-
const verb = await this.readWithInheritance(path);
|
|
347
|
-
if (verb) {
|
|
348
|
-
// Cache the type for next time (read from verb.verb field)
|
|
349
|
-
this.verbTypeCache.set(id, verb.verb);
|
|
350
|
-
return verb;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
catch (error) {
|
|
354
|
-
// Not in this type, continue
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
return null;
|
|
358
|
-
}
|
|
359
|
-
/**
|
|
360
|
-
* Get verbs by source
|
|
361
|
-
*/
|
|
362
|
-
async getVerbsBySource_internal(sourceId) {
|
|
363
|
-
// v5.0.1 COW FIX: Use getVerbsWithPagination which is COW-aware
|
|
364
|
-
// Previous v4.8.1 implementation delegated to underlying storage, which bypasses COW!
|
|
365
|
-
// The underlying storage delegates to GraphAdjacencyIndex, which is shared between forks.
|
|
366
|
-
// This caused getRelations() to return 0 results for fork-created relationships.
|
|
367
|
-
//
|
|
368
|
-
// Now we use getVerbsWithPagination with sourceId filter, which:
|
|
369
|
-
// - Searches across all verb types using COW-aware listObjectsInBranch()
|
|
370
|
-
// - Reads verbs using COW-aware readWithInheritance()
|
|
371
|
-
// - Properly isolates fork data from parent
|
|
372
|
-
//
|
|
373
|
-
// Performance: Still efficient because sourceId filter reduces iteration
|
|
374
|
-
const result = await this.getVerbsWithPagination({
|
|
375
|
-
limit: 10000, // High limit to get all verbs for this source
|
|
376
|
-
offset: 0,
|
|
377
|
-
filter: { sourceId }
|
|
378
|
-
});
|
|
379
|
-
return result.items;
|
|
380
|
-
}
|
|
381
|
-
/**
|
|
382
|
-
* Get verbs by target
|
|
383
|
-
*/
|
|
384
|
-
async getVerbsByTarget_internal(targetId) {
|
|
385
|
-
// v5.0.1 COW FIX: Use getVerbsWithPagination which is COW-aware
|
|
386
|
-
// Same fix as getVerbsBySource_internal - delegating to underlying bypasses COW
|
|
387
|
-
const result = await this.getVerbsWithPagination({
|
|
388
|
-
limit: 10000, // High limit to get all verbs for this target
|
|
389
|
-
offset: 0,
|
|
390
|
-
filter: { targetId }
|
|
391
|
-
});
|
|
392
|
-
return result.items;
|
|
393
|
-
}
|
|
394
|
-
/**
|
|
395
|
-
* Get verbs by type (O(1) with type-first paths!)
|
|
396
|
-
*
|
|
397
|
-
* v4.0.0: Load verbs and combine with metadata
|
|
398
|
-
*/
|
|
399
|
-
async getVerbsByType_internal(verbType) {
|
|
400
|
-
const type = verbType;
|
|
401
|
-
const prefix = `entities/verbs/${type}/vectors/`;
|
|
402
|
-
// COW-aware list (v5.0.1): Use COW helper for branch isolation
|
|
403
|
-
const paths = await this.listObjectsInBranch(prefix);
|
|
404
|
-
const verbs = [];
|
|
405
|
-
for (const path of paths) {
|
|
406
|
-
try {
|
|
407
|
-
// COW-aware read (v5.0.1): Use COW helper for branch isolation
|
|
408
|
-
const hnswVerb = await this.readWithInheritance(path);
|
|
409
|
-
if (!hnswVerb)
|
|
410
|
-
continue;
|
|
411
|
-
// Cache type from HNSWVerb for future O(1) retrievals
|
|
412
|
-
this.verbTypeCache.set(hnswVerb.id, hnswVerb.verb);
|
|
413
|
-
// Load metadata separately (optional in v4.0.0!)
|
|
414
|
-
// FIX: Don't skip verbs without metadata - metadata is optional!
|
|
415
|
-
const metadata = await this.getVerbMetadata(hnswVerb.id);
|
|
416
|
-
// Create HNSWVerbWithMetadata (verbs don't have level field)
|
|
417
|
-
// Convert connections from plain object to Map<number, Set<string>>
|
|
418
|
-
const connectionsMap = new Map();
|
|
419
|
-
if (hnswVerb.connections && typeof hnswVerb.connections === 'object') {
|
|
420
|
-
for (const [level, ids] of Object.entries(hnswVerb.connections)) {
|
|
421
|
-
connectionsMap.set(Number(level), new Set(ids));
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
// v4.8.0: Extract standard fields from metadata to top-level
|
|
425
|
-
const metadataObj = (metadata || {});
|
|
426
|
-
const { createdAt, updatedAt, confidence, weight, service, data, createdBy, ...customMetadata } = metadataObj;
|
|
427
|
-
const verbWithMetadata = {
|
|
428
|
-
id: hnswVerb.id,
|
|
429
|
-
vector: [...hnswVerb.vector],
|
|
430
|
-
connections: connectionsMap,
|
|
431
|
-
verb: hnswVerb.verb,
|
|
432
|
-
sourceId: hnswVerb.sourceId,
|
|
433
|
-
targetId: hnswVerb.targetId,
|
|
434
|
-
createdAt: createdAt || Date.now(),
|
|
435
|
-
updatedAt: updatedAt || Date.now(),
|
|
436
|
-
confidence: confidence,
|
|
437
|
-
weight: weight,
|
|
438
|
-
service: service,
|
|
439
|
-
data: data,
|
|
440
|
-
createdBy,
|
|
441
|
-
metadata: customMetadata
|
|
442
|
-
};
|
|
443
|
-
verbs.push(verbWithMetadata);
|
|
444
|
-
}
|
|
445
|
-
catch (error) {
|
|
446
|
-
console.warn(`[TypeAwareStorage] Failed to load verb from ${path}:`, error);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
return verbs;
|
|
450
|
-
}
|
|
451
|
-
/**
|
|
452
|
-
* Delete verb (type-first path)
|
|
453
|
-
*/
|
|
454
|
-
async deleteVerb_internal(id) {
|
|
455
|
-
// Try cache first
|
|
456
|
-
const cachedType = this.verbTypeCache.get(id);
|
|
457
|
-
if (cachedType) {
|
|
458
|
-
const path = getVerbVectorPath(cachedType, id);
|
|
459
|
-
// COW-aware delete (v5.0.1): Use COW helper for branch isolation
|
|
460
|
-
await this.deleteObjectFromBranch(path);
|
|
461
|
-
const typeIndex = TypeUtils.getVerbIndex(cachedType);
|
|
462
|
-
if (this.verbCountsByType[typeIndex] > 0) {
|
|
463
|
-
this.verbCountsByType[typeIndex]--;
|
|
464
|
-
}
|
|
465
|
-
this.verbTypeCache.delete(id);
|
|
466
|
-
return;
|
|
467
|
-
}
|
|
468
|
-
// Search across all types
|
|
469
|
-
for (let i = 0; i < VERB_TYPE_COUNT; i++) {
|
|
470
|
-
const type = TypeUtils.getVerbFromIndex(i);
|
|
471
|
-
const path = getVerbVectorPath(type, id);
|
|
472
|
-
try {
|
|
473
|
-
// COW-aware delete (v5.0.1): Use COW helper for branch isolation
|
|
474
|
-
await this.deleteObjectFromBranch(path);
|
|
475
|
-
if (this.verbCountsByType[i] > 0) {
|
|
476
|
-
this.verbCountsByType[i]--;
|
|
477
|
-
}
|
|
478
|
-
this.verbTypeCache.delete(id);
|
|
479
|
-
return;
|
|
480
|
-
}
|
|
481
|
-
catch (error) {
|
|
482
|
-
// Continue
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
/**
|
|
487
|
-
* Save noun metadata (override to cache type for type-aware routing)
|
|
488
|
-
*
|
|
489
|
-
* v4.0.0: Extract and cache noun type when metadata is saved
|
|
490
|
-
*/
|
|
491
|
-
async saveNounMetadata(id, metadata) {
|
|
492
|
-
// Extract and cache the type
|
|
493
|
-
const type = (metadata.noun || 'thing');
|
|
494
|
-
this.nounTypeCache.set(id, type);
|
|
495
|
-
// COW-aware write (v5.0.1): Use COW helper for branch isolation
|
|
496
|
-
const path = getNounMetadataPath(type, id);
|
|
497
|
-
await this.writeObjectToBranch(path, metadata);
|
|
498
|
-
}
|
|
499
|
-
/**
|
|
500
|
-
* Get noun metadata (override to use type-aware paths)
|
|
501
|
-
*/
|
|
502
|
-
async getNounMetadata(id) {
|
|
503
|
-
// Try cache first
|
|
504
|
-
const cachedType = this.nounTypeCache.get(id);
|
|
505
|
-
if (cachedType) {
|
|
506
|
-
const path = getNounMetadataPath(cachedType, id);
|
|
507
|
-
// COW-aware read (v5.0.1): Use COW helper for branch isolation
|
|
508
|
-
return await this.readWithInheritance(path);
|
|
509
|
-
}
|
|
510
|
-
// Search across all types
|
|
511
|
-
for (let i = 0; i < NOUN_TYPE_COUNT; i++) {
|
|
512
|
-
const type = TypeUtils.getNounFromIndex(i);
|
|
513
|
-
const path = getNounMetadataPath(type, id);
|
|
514
|
-
try {
|
|
515
|
-
// COW-aware read (v5.0.1): Use COW helper for branch isolation
|
|
516
|
-
const metadata = await this.readWithInheritance(path);
|
|
517
|
-
if (metadata) {
|
|
518
|
-
// Cache the type for next time
|
|
519
|
-
const metadataType = (metadata.noun || 'thing');
|
|
520
|
-
this.nounTypeCache.set(id, metadataType);
|
|
521
|
-
return metadata;
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
catch (error) {
|
|
525
|
-
// Not in this type, continue searching
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
return null;
|
|
529
|
-
}
|
|
530
|
-
/**
|
|
531
|
-
* Delete noun metadata (override to use type-aware paths)
|
|
532
|
-
*/
|
|
533
|
-
async deleteNounMetadata(id) {
|
|
534
|
-
const cachedType = this.nounTypeCache.get(id);
|
|
535
|
-
if (cachedType) {
|
|
536
|
-
const path = getNounMetadataPath(cachedType, id);
|
|
537
|
-
// COW-aware delete (v5.0.1): Use COW helper for branch isolation
|
|
538
|
-
await this.deleteObjectFromBranch(path);
|
|
539
|
-
return;
|
|
540
|
-
}
|
|
541
|
-
// Search across all types
|
|
542
|
-
for (let i = 0; i < NOUN_TYPE_COUNT; i++) {
|
|
543
|
-
const type = TypeUtils.getNounFromIndex(i);
|
|
544
|
-
const path = getNounMetadataPath(type, id);
|
|
545
|
-
try {
|
|
546
|
-
// COW-aware delete (v5.0.1): Use COW helper for branch isolation
|
|
547
|
-
await this.deleteObjectFromBranch(path);
|
|
548
|
-
return;
|
|
549
|
-
}
|
|
550
|
-
catch (error) {
|
|
551
|
-
// Not in this type, continue
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
/**
|
|
556
|
-
* Save verb metadata (override to use type-aware paths)
|
|
557
|
-
*
|
|
558
|
-
* Note: Verb type comes from HNSWVerb.verb field, not metadata
|
|
559
|
-
* We need to read the verb to get the type for path routing
|
|
560
|
-
*/
|
|
561
|
-
async saveVerbMetadata(id, metadata) {
|
|
562
|
-
// Get verb type from cache or by reading the verb
|
|
563
|
-
let type = this.verbTypeCache.get(id);
|
|
564
|
-
if (!type) {
|
|
565
|
-
// Need to read the verb to get its type
|
|
566
|
-
const verb = await this.getVerb_internal(id);
|
|
567
|
-
if (verb) {
|
|
568
|
-
type = verb.verb;
|
|
569
|
-
this.verbTypeCache.set(id, type);
|
|
570
|
-
}
|
|
571
|
-
else {
|
|
572
|
-
type = 'relatedTo';
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
// Save to type-aware path
|
|
576
|
-
const path = getVerbMetadataPath(type, id);
|
|
577
|
-
// COW-aware write (v5.0.1): Use COW helper for branch isolation
|
|
578
|
-
await this.writeObjectToBranch(path, metadata);
|
|
579
|
-
}
|
|
580
|
-
/**
|
|
581
|
-
* Get verb metadata (override to use type-aware paths)
|
|
582
|
-
*/
|
|
583
|
-
async getVerbMetadata(id) {
|
|
584
|
-
// Try cache first
|
|
585
|
-
const cachedType = this.verbTypeCache.get(id);
|
|
586
|
-
if (cachedType) {
|
|
587
|
-
const path = getVerbMetadataPath(cachedType, id);
|
|
588
|
-
// COW-aware read (v5.0.1): Use COW helper for branch isolation
|
|
589
|
-
return await this.readWithInheritance(path);
|
|
590
|
-
}
|
|
591
|
-
// Search across all types
|
|
592
|
-
for (let i = 0; i < VERB_TYPE_COUNT; i++) {
|
|
593
|
-
const type = TypeUtils.getVerbFromIndex(i);
|
|
594
|
-
const path = getVerbMetadataPath(type, id);
|
|
595
|
-
try {
|
|
596
|
-
// COW-aware read (v5.0.1): Use COW helper for branch isolation
|
|
597
|
-
const metadata = await this.readWithInheritance(path);
|
|
598
|
-
if (metadata) {
|
|
599
|
-
// Cache the type for next time
|
|
600
|
-
this.verbTypeCache.set(id, type);
|
|
601
|
-
return metadata;
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
catch (error) {
|
|
605
|
-
// Not in this type, continue
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
return null;
|
|
609
|
-
}
|
|
610
|
-
/**
|
|
611
|
-
* Delete verb metadata (override to use type-aware paths)
|
|
612
|
-
*/
|
|
613
|
-
async deleteVerbMetadata(id) {
|
|
614
|
-
const cachedType = this.verbTypeCache.get(id);
|
|
615
|
-
if (cachedType) {
|
|
616
|
-
const path = getVerbMetadataPath(cachedType, id);
|
|
617
|
-
// COW-aware delete (v5.0.1): Use COW helper for branch isolation
|
|
618
|
-
await this.deleteObjectFromBranch(path);
|
|
619
|
-
return;
|
|
620
|
-
}
|
|
621
|
-
// Search across all types
|
|
622
|
-
for (let i = 0; i < VERB_TYPE_COUNT; i++) {
|
|
623
|
-
const type = TypeUtils.getVerbFromIndex(i);
|
|
624
|
-
const path = getVerbMetadataPath(type, id);
|
|
625
|
-
try {
|
|
626
|
-
// COW-aware delete (v5.0.1): Use COW helper for branch isolation
|
|
627
|
-
await this.deleteObjectFromBranch(path);
|
|
628
|
-
return;
|
|
629
|
-
}
|
|
630
|
-
catch (error) {
|
|
631
|
-
// Not in this type, continue
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
/**
|
|
636
|
-
* Write object to path (delegate to underlying storage)
|
|
637
|
-
*/
|
|
638
|
-
async writeObjectToPath(path, data) {
|
|
639
|
-
return this.u.writeObjectToPath(path, data);
|
|
640
|
-
}
|
|
641
|
-
/**
|
|
642
|
-
* Read object from path (delegate to underlying storage)
|
|
643
|
-
*/
|
|
644
|
-
async readObjectFromPath(path) {
|
|
645
|
-
return this.u.readObjectFromPath(path);
|
|
646
|
-
}
|
|
647
|
-
/**
|
|
648
|
-
* Delete object from path (delegate to underlying storage)
|
|
649
|
-
*/
|
|
650
|
-
async deleteObjectFromPath(path) {
|
|
651
|
-
return this.u.deleteObjectFromPath(path);
|
|
652
|
-
}
|
|
653
|
-
/**
|
|
654
|
-
* List objects under path (delegate to underlying storage)
|
|
655
|
-
*/
|
|
656
|
-
async listObjectsUnderPath(prefix) {
|
|
657
|
-
return this.u.listObjectsUnderPath(prefix);
|
|
658
|
-
}
|
|
659
|
-
/**
|
|
660
|
-
* Save statistics data
|
|
661
|
-
*/
|
|
662
|
-
async saveStatisticsData(statistics) {
|
|
663
|
-
return this.u.writeObjectToPath(`${SYSTEM_DIR}/statistics.json`, statistics);
|
|
664
|
-
}
|
|
665
|
-
/**
|
|
666
|
-
* Get statistics data
|
|
667
|
-
*/
|
|
668
|
-
async getStatisticsData() {
|
|
669
|
-
return this.u.readObjectFromPath(`${SYSTEM_DIR}/statistics.json`);
|
|
670
|
-
}
|
|
671
|
-
/**
|
|
672
|
-
* Clear all data
|
|
673
|
-
*/
|
|
674
|
-
async clear() {
|
|
675
|
-
// Clear type tracking
|
|
676
|
-
this.nounCountsByType.fill(0);
|
|
677
|
-
this.verbCountsByType.fill(0);
|
|
678
|
-
this.nounTypeCache.clear();
|
|
679
|
-
this.verbTypeCache.clear();
|
|
680
|
-
// Delegate to underlying storage
|
|
681
|
-
if (typeof this.underlying.clear === 'function') {
|
|
682
|
-
await this.underlying.clear();
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
/**
|
|
686
|
-
* Get storage status
|
|
687
|
-
*/
|
|
688
|
-
async getStorageStatus() {
|
|
689
|
-
const underlyingStatus = await this.underlying.getStorageStatus();
|
|
690
|
-
return {
|
|
691
|
-
...underlyingStatus,
|
|
692
|
-
type: 'type-aware',
|
|
693
|
-
details: {
|
|
694
|
-
...underlyingStatus.details,
|
|
695
|
-
typeTracking: {
|
|
696
|
-
nounTypes: NOUN_TYPE_COUNT,
|
|
697
|
-
verbTypes: VERB_TYPE_COUNT,
|
|
698
|
-
memoryBytes: 284, // 124 + 160
|
|
699
|
-
nounCounts: Array.from(this.nounCountsByType),
|
|
700
|
-
verbCounts: Array.from(this.verbCountsByType),
|
|
701
|
-
cacheSize: this.nounTypeCache.size + this.verbTypeCache.size
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
};
|
|
705
|
-
}
|
|
706
|
-
/**
|
|
707
|
-
* Initialize counts from storage
|
|
708
|
-
*/
|
|
709
|
-
async initializeCounts() {
|
|
710
|
-
// TypeAwareStorageAdapter maintains its own type-based counts
|
|
711
|
-
// which are loaded in loadTypeStatistics()
|
|
712
|
-
// But we should also initialize the underlying storage's counts
|
|
713
|
-
if (this.u.initializeCounts) {
|
|
714
|
-
await this.u.initializeCounts();
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
/**
|
|
718
|
-
* Persist counts to storage
|
|
719
|
-
*/
|
|
720
|
-
async persistCounts() {
|
|
721
|
-
// Persist our type statistics
|
|
722
|
-
await this.saveTypeStatistics();
|
|
723
|
-
// Also persist underlying storage counts
|
|
724
|
-
if (this.u.persistCounts) {
|
|
725
|
-
await this.u.persistCounts();
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
/**
|
|
729
|
-
* Get noun vector (delegate to underlying storage)
|
|
730
|
-
*/
|
|
731
|
-
async getNounVector(id) {
|
|
732
|
-
const noun = await this.getNoun_internal(id);
|
|
733
|
-
return noun?.vector || null;
|
|
734
|
-
}
|
|
735
|
-
/**
|
|
736
|
-
* Save HNSW data for a noun
|
|
737
|
-
*/
|
|
738
|
-
async saveHNSWData(nounId, hnswData) {
|
|
739
|
-
// Get noun type for type-first path
|
|
740
|
-
const cachedType = this.nounTypeCache.get(nounId);
|
|
741
|
-
const type = cachedType || 'thing'; // Default if not cached
|
|
742
|
-
const shard = getShardIdFromUuid(nounId);
|
|
743
|
-
const path = `entities/nouns/${type}/hnsw/${shard}/${nounId}.json`;
|
|
744
|
-
await this.u.writeObjectToPath(path, hnswData);
|
|
745
|
-
}
|
|
746
|
-
/**
|
|
747
|
-
* Get HNSW data for a noun
|
|
748
|
-
*/
|
|
749
|
-
async getHNSWData(nounId) {
|
|
750
|
-
// Try cache first
|
|
751
|
-
const cachedType = this.nounTypeCache.get(nounId);
|
|
752
|
-
if (cachedType) {
|
|
753
|
-
const shard = getShardIdFromUuid(nounId);
|
|
754
|
-
const path = `entities/nouns/${cachedType}/hnsw/${shard}/${nounId}.json`;
|
|
755
|
-
return await this.u.readObjectFromPath(path);
|
|
756
|
-
}
|
|
757
|
-
// Search across all types
|
|
758
|
-
for (let i = 0; i < NOUN_TYPE_COUNT; i++) {
|
|
759
|
-
const type = TypeUtils.getNounFromIndex(i);
|
|
760
|
-
const shard = getShardIdFromUuid(nounId);
|
|
761
|
-
const path = `entities/nouns/${type}/hnsw/${shard}/${nounId}.json`;
|
|
762
|
-
try {
|
|
763
|
-
const data = await this.u.readObjectFromPath(path);
|
|
764
|
-
if (data) {
|
|
765
|
-
return data;
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
catch (error) {
|
|
769
|
-
// Not in this type, continue
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
return null;
|
|
773
|
-
}
|
|
774
|
-
/**
|
|
775
|
-
* Get nouns with pagination (v5.0.1: COW-aware)
|
|
776
|
-
* Required for find() to work with TypeAwareStorage
|
|
777
|
-
*/
|
|
778
|
-
async getNounsWithPagination(options) {
|
|
779
|
-
const limit = options.limit || 100;
|
|
780
|
-
const offset = options.offset || 0;
|
|
781
|
-
const filter = options.filter || {};
|
|
782
|
-
// Determine which types to search
|
|
783
|
-
let typesToSearch;
|
|
784
|
-
if (filter.nounType) {
|
|
785
|
-
typesToSearch = Array.isArray(filter.nounType) ? filter.nounType : [filter.nounType];
|
|
786
|
-
}
|
|
787
|
-
else {
|
|
788
|
-
// Search all 31 types
|
|
789
|
-
typesToSearch = [];
|
|
790
|
-
for (let i = 0; i < NOUN_TYPE_COUNT; i++) {
|
|
791
|
-
const type = TypeUtils.getNounFromIndex(i);
|
|
792
|
-
typesToSearch.push(type);
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
// Collect all matching nouns across types (COW-aware!)
|
|
796
|
-
const allNouns = [];
|
|
797
|
-
for (const type of typesToSearch) {
|
|
798
|
-
const prefix = `entities/nouns/${type}/vectors/`;
|
|
799
|
-
// COW-aware list with inheritance (v5.0.1): Fork sees parent's nouns too!
|
|
800
|
-
const paths = await this.listObjectsWithInheritance(prefix);
|
|
801
|
-
for (const path of paths) {
|
|
802
|
-
try {
|
|
803
|
-
// COW-aware read with inheritance
|
|
804
|
-
const noun = await this.readWithInheritance(path);
|
|
805
|
-
if (!noun)
|
|
806
|
-
continue;
|
|
807
|
-
// Get metadata separately
|
|
808
|
-
const metadata = await this.getNounMetadata(noun.id);
|
|
809
|
-
if (!metadata)
|
|
810
|
-
continue;
|
|
811
|
-
// Filter by service if specified
|
|
812
|
-
if (filter.service && metadata.service !== filter.service)
|
|
813
|
-
continue;
|
|
814
|
-
// Filter by custom metadata if specified
|
|
815
|
-
if (filter.metadata) {
|
|
816
|
-
let matches = true;
|
|
817
|
-
for (const [key, value] of Object.entries(filter.metadata)) {
|
|
818
|
-
if (metadata[key] !== value) {
|
|
819
|
-
matches = false;
|
|
820
|
-
break;
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
if (!matches)
|
|
824
|
-
continue;
|
|
825
|
-
}
|
|
826
|
-
// Extract standard fields from metadata
|
|
827
|
-
const { noun: nounType, createdAt, updatedAt, confidence, weight, service, data, createdBy, ...customMetadata } = metadata;
|
|
828
|
-
// Create HNSWNounWithMetadata (v4.8.0 format)
|
|
829
|
-
const nounWithMetadata = {
|
|
830
|
-
id: noun.id,
|
|
831
|
-
vector: noun.vector,
|
|
832
|
-
connections: noun.connections,
|
|
833
|
-
level: noun.level || 0,
|
|
834
|
-
type: nounType || NounType.Thing,
|
|
835
|
-
createdAt: createdAt || Date.now(),
|
|
836
|
-
updatedAt: updatedAt || Date.now(),
|
|
837
|
-
confidence,
|
|
838
|
-
weight,
|
|
839
|
-
service,
|
|
840
|
-
data,
|
|
841
|
-
createdBy,
|
|
842
|
-
metadata: customMetadata
|
|
843
|
-
};
|
|
844
|
-
allNouns.push(nounWithMetadata);
|
|
845
|
-
}
|
|
846
|
-
catch (error) {
|
|
847
|
-
// Skip entities with errors
|
|
848
|
-
continue;
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
// Apply pagination
|
|
853
|
-
const totalCount = allNouns.length;
|
|
854
|
-
const paginatedNouns = allNouns.slice(offset, offset + limit);
|
|
855
|
-
const hasMore = offset + limit < totalCount;
|
|
856
|
-
// Generate cursor if more results exist
|
|
857
|
-
let nextCursor;
|
|
858
|
-
if (hasMore && paginatedNouns.length > 0) {
|
|
859
|
-
nextCursor = paginatedNouns[paginatedNouns.length - 1].id;
|
|
860
|
-
}
|
|
861
|
-
return {
|
|
862
|
-
items: paginatedNouns,
|
|
863
|
-
totalCount,
|
|
864
|
-
hasMore,
|
|
865
|
-
nextCursor
|
|
866
|
-
};
|
|
867
|
-
}
|
|
868
|
-
/**
|
|
869
|
-
* Get verbs with pagination (v5.0.1: COW-aware)
|
|
870
|
-
* Required for GraphAdjacencyIndex rebuild and find() to work
|
|
871
|
-
*/
|
|
872
|
-
async getVerbsWithPagination(options) {
|
|
873
|
-
const limit = options.limit || 100;
|
|
874
|
-
const offset = options.offset || 0;
|
|
875
|
-
const filter = options.filter || {};
|
|
876
|
-
// Determine which types to search
|
|
877
|
-
let typesToSearch;
|
|
878
|
-
if (filter.verbType) {
|
|
879
|
-
typesToSearch = Array.isArray(filter.verbType) ? filter.verbType : [filter.verbType];
|
|
880
|
-
}
|
|
881
|
-
else {
|
|
882
|
-
// Search all 40 verb types
|
|
883
|
-
typesToSearch = [];
|
|
884
|
-
for (let i = 0; i < VERB_TYPE_COUNT; i++) {
|
|
885
|
-
const type = TypeUtils.getVerbFromIndex(i);
|
|
886
|
-
typesToSearch.push(type);
|
|
887
|
-
}
|
|
888
|
-
}
|
|
889
|
-
// Collect all matching verbs across types (COW-aware!)
|
|
890
|
-
const allVerbs = [];
|
|
891
|
-
for (const type of typesToSearch) {
|
|
892
|
-
const prefix = `entities/verbs/${type}/vectors/`;
|
|
893
|
-
// COW-aware list with inheritance (v5.0.1): Fork sees parent's verbs too!
|
|
894
|
-
const paths = await this.listObjectsWithInheritance(prefix);
|
|
895
|
-
for (const path of paths) {
|
|
896
|
-
try {
|
|
897
|
-
// COW-aware read with inheritance
|
|
898
|
-
const verb = await this.readWithInheritance(path);
|
|
899
|
-
if (!verb)
|
|
900
|
-
continue;
|
|
901
|
-
// Filter by sourceId if specified
|
|
902
|
-
if (filter.sourceId) {
|
|
903
|
-
const sourceIds = Array.isArray(filter.sourceId) ? filter.sourceId : [filter.sourceId];
|
|
904
|
-
if (!sourceIds.includes(verb.sourceId))
|
|
905
|
-
continue;
|
|
906
|
-
}
|
|
907
|
-
// Filter by targetId if specified
|
|
908
|
-
if (filter.targetId) {
|
|
909
|
-
const targetIds = Array.isArray(filter.targetId) ? filter.targetId : [filter.targetId];
|
|
910
|
-
if (!targetIds.includes(verb.targetId))
|
|
911
|
-
continue;
|
|
912
|
-
}
|
|
913
|
-
// Get metadata separately
|
|
914
|
-
const metadata = await this.getVerbMetadata(verb.id);
|
|
915
|
-
// Filter by service if specified
|
|
916
|
-
if (filter.service && metadata && metadata.service !== filter.service)
|
|
917
|
-
continue;
|
|
918
|
-
// Filter by custom metadata if specified
|
|
919
|
-
if (filter.metadata && metadata) {
|
|
920
|
-
let matches = true;
|
|
921
|
-
for (const [key, value] of Object.entries(filter.metadata)) {
|
|
922
|
-
if (metadata[key] !== value) {
|
|
923
|
-
matches = false;
|
|
924
|
-
break;
|
|
925
|
-
}
|
|
926
|
-
}
|
|
927
|
-
if (!matches)
|
|
928
|
-
continue;
|
|
929
|
-
}
|
|
930
|
-
// Extract standard fields from metadata
|
|
931
|
-
const metadataObj = metadata || {};
|
|
932
|
-
const { createdAt, updatedAt, confidence, weight, service, data, createdBy, ...customMetadata } = metadataObj;
|
|
933
|
-
// Create HNSWVerbWithMetadata (v4.8.0 format)
|
|
934
|
-
const verbWithMetadata = {
|
|
935
|
-
id: verb.id,
|
|
936
|
-
vector: verb.vector,
|
|
937
|
-
connections: verb.connections,
|
|
938
|
-
verb: verb.verb,
|
|
939
|
-
sourceId: verb.sourceId,
|
|
940
|
-
targetId: verb.targetId,
|
|
941
|
-
createdAt: createdAt || Date.now(),
|
|
942
|
-
updatedAt: updatedAt || Date.now(),
|
|
943
|
-
confidence,
|
|
944
|
-
weight,
|
|
945
|
-
service,
|
|
946
|
-
data,
|
|
947
|
-
createdBy,
|
|
948
|
-
metadata: customMetadata
|
|
949
|
-
};
|
|
950
|
-
allVerbs.push(verbWithMetadata);
|
|
951
|
-
}
|
|
952
|
-
catch (error) {
|
|
953
|
-
// Skip verbs with errors
|
|
954
|
-
continue;
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
// Apply pagination
|
|
959
|
-
const totalCount = allVerbs.length;
|
|
960
|
-
const paginatedVerbs = allVerbs.slice(offset, offset + limit);
|
|
961
|
-
const hasMore = offset + limit < totalCount;
|
|
962
|
-
// Generate cursor if more results exist
|
|
963
|
-
let nextCursor;
|
|
964
|
-
if (hasMore && paginatedVerbs.length > 0) {
|
|
965
|
-
nextCursor = paginatedVerbs[paginatedVerbs.length - 1].id;
|
|
966
|
-
}
|
|
967
|
-
return {
|
|
968
|
-
items: paginatedVerbs,
|
|
969
|
-
totalCount,
|
|
970
|
-
hasMore,
|
|
971
|
-
nextCursor
|
|
972
|
-
};
|
|
973
|
-
}
|
|
974
|
-
/**
|
|
975
|
-
* Save HNSW system data (entry point, max level)
|
|
976
|
-
*/
|
|
977
|
-
async saveHNSWSystem(systemData) {
|
|
978
|
-
await this.u.writeObjectToPath(`${SYSTEM_DIR}/hnsw-system.json`, systemData);
|
|
979
|
-
}
|
|
980
|
-
/**
|
|
981
|
-
* Get HNSW system data
|
|
982
|
-
*/
|
|
983
|
-
async getHNSWSystem() {
|
|
984
|
-
return await this.u.readObjectFromPath(`${SYSTEM_DIR}/hnsw-system.json`);
|
|
985
|
-
}
|
|
986
|
-
/**
|
|
987
|
-
* Get type statistics
|
|
988
|
-
* Useful for analytics and optimization
|
|
989
|
-
*/
|
|
990
|
-
getTypeStatistics() {
|
|
991
|
-
const nouns = [];
|
|
992
|
-
for (let i = 0; i < NOUN_TYPE_COUNT; i++) {
|
|
993
|
-
const count = this.nounCountsByType[i];
|
|
994
|
-
if (count > 0) {
|
|
995
|
-
nouns.push({ type: TypeUtils.getNounFromIndex(i), count });
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
const verbs = [];
|
|
999
|
-
for (let i = 0; i < VERB_TYPE_COUNT; i++) {
|
|
1000
|
-
const count = this.verbCountsByType[i];
|
|
1001
|
-
if (count > 0) {
|
|
1002
|
-
verbs.push({ type: TypeUtils.getVerbFromIndex(i), count });
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
return {
|
|
1006
|
-
nouns: nouns.sort((a, b) => b.count - a.count),
|
|
1007
|
-
verbs: verbs.sort((a, b) => b.count - a.count),
|
|
1008
|
-
totalMemory: 284 // bytes
|
|
1009
|
-
};
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
//# sourceMappingURL=typeAwareStorageAdapter.js.map
|