feat: add SQ8 vector quantization, lazy loading, and two-phase rerank to HNSW
- SQ8 scalar quantization (8-bit) for 4x vector storage reduction - Lazy vector loading: evict float32 vectors after graph construction, load on-demand from storage via UnifiedCache - Two-phase search: over-retrieve with SQ8 approximate distances, rerank top candidates with exact float32 distances - Configuration surface: hnsw.quantization and hnsw.vectorStorage in BrainyConfig - All features disabled by default (zero behavior change for existing users) - 27 new tests covering quantization accuracy, lazy loading, reranking - Remove GitHub Actions CI (build locally, cortex CI handles native builds)
This commit is contained in:
parent
e384afcdac
commit
0f3a88429d
9 changed files with 1138 additions and 222 deletions
|
|
@ -256,21 +256,22 @@ export class TypeAwareHNSWIndex {
|
|||
queryVector: Vector,
|
||||
k: number = 10,
|
||||
type?: NounType | NounType[],
|
||||
filter?: (id: string) => Promise<boolean>
|
||||
filter?: (id: string) => Promise<boolean>,
|
||||
options?: { rerank?: { multiplier: number } }
|
||||
): Promise<Array<[string, number]>> {
|
||||
// Single-type search (fast path)
|
||||
if (type && typeof type === 'string') {
|
||||
const index = this.getIndexForType(type)
|
||||
return await index.search(queryVector, k, filter)
|
||||
return await index.search(queryVector, k, filter, options)
|
||||
}
|
||||
|
||||
// Multi-type search (handle empty array edge case)
|
||||
if (type && Array.isArray(type) && type.length > 0) {
|
||||
return await this.searchMultipleTypes(queryVector, k, type, filter)
|
||||
return await this.searchMultipleTypes(queryVector, k, type, filter, options)
|
||||
}
|
||||
|
||||
// All-types search (slowest path + empty array fallback)
|
||||
return await this.searchAllTypes(queryVector, k, filter)
|
||||
return await this.searchAllTypes(queryVector, k, filter, options)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -286,7 +287,8 @@ export class TypeAwareHNSWIndex {
|
|||
queryVector: Vector,
|
||||
k: number,
|
||||
types: NounType[],
|
||||
filter?: (id: string) => Promise<boolean>
|
||||
filter?: (id: string) => Promise<boolean>,
|
||||
options?: { rerank?: { multiplier: number } }
|
||||
): Promise<Array<[string, number]>> {
|
||||
const allResults: Array<[string, number]> = []
|
||||
|
||||
|
|
@ -294,7 +296,7 @@ export class TypeAwareHNSWIndex {
|
|||
for (const type of types) {
|
||||
if (this.indexes.has(type)) {
|
||||
const index = this.indexes.get(type)!
|
||||
const results = await index.search(queryVector, k, filter)
|
||||
const results = await index.search(queryVector, k, filter, options)
|
||||
allResults.push(...results)
|
||||
}
|
||||
}
|
||||
|
|
@ -320,13 +322,14 @@ export class TypeAwareHNSWIndex {
|
|||
private async searchAllTypes(
|
||||
queryVector: Vector,
|
||||
k: number,
|
||||
filter?: (id: string) => Promise<boolean>
|
||||
filter?: (id: string) => Promise<boolean>,
|
||||
options?: { rerank?: { multiplier: number } }
|
||||
): Promise<Array<[string, number]>> {
|
||||
const allResults: Array<[string, number]> = []
|
||||
|
||||
// Search each type's graph
|
||||
for (const [type, index] of this.indexes.entries()) {
|
||||
const results = await index.search(queryVector, k, filter)
|
||||
const results = await index.search(queryVector, k, filter, options)
|
||||
allResults.push(...results)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue