@@ -21,34 +21,42 @@ function cosineSimilarity(a: Float32Array, b: Float32Array): number {
2121 return dotProduct / ( Math . sqrt ( normA ) * Math . sqrt ( normB ) ) ;
2222}
2323
24+ export interface RankedResult {
25+ id : Hash ;
26+ score : number ;
27+ /** Child IDs from the ranked entity (volumeIds / bookIds / pageIds). */
28+ childIds : Hash [ ] ;
29+ }
30+
2431function pickTopK (
25- scored : Array < { id : Hash ; score : number } > ,
32+ scored : RankedResult [ ] ,
2633 k : number ,
27- ) : Array < { id : Hash ; score : number } > {
34+ ) : RankedResult [ ] {
2835 scored . sort ( ( a , b ) => b . score - a . score || a . id . localeCompare ( b . id ) ) ;
2936 return scored . slice ( 0 , k ) ;
3037}
3138
3239/**
3340 * Ranks shelves by cosine similarity of their routing prototype to the query.
3441 * Uses routingPrototypeOffsets[0] as the representative vector.
42+ * Returns child volumeIds alongside each scored shelf.
3543 */
3644export async function rankShelves (
3745 queryEmbedding : Float32Array ,
3846 residentShelfIds : Hash [ ] ,
3947 topK : number ,
4048 options : RankingOptions ,
41- ) : Promise < Array < { id : Hash ; score : number } > > {
49+ ) : Promise < RankedResult [ ] > {
4250 if ( residentShelfIds . length === 0 ) return [ ] ;
4351
4452 const { vectorStore, metadataStore } = options ;
45- const scored : Array < { id : Hash ; score : number } > = [ ] ;
53+ const scored : RankedResult [ ] = [ ] ;
4654
4755 for ( const shelfId of residentShelfIds ) {
4856 const shelf = await metadataStore . getShelf ( shelfId ) ;
4957 if ( ! shelf || shelf . routingPrototypeOffsets . length === 0 ) continue ;
5058 const vec = await vectorStore . readVector ( shelf . routingPrototypeOffsets [ 0 ] , shelf . routingDim ) ;
51- scored . push ( { id : shelfId , score : cosineSimilarity ( queryEmbedding , vec ) } ) ;
59+ scored . push ( { id : shelfId , score : cosineSimilarity ( queryEmbedding , vec ) , childIds : shelf . volumeIds } ) ;
5260 }
5361
5462 return pickTopK ( scored , topK ) ;
@@ -57,49 +65,51 @@ export async function rankShelves(
5765/**
5866 * Ranks volumes by cosine similarity of their first prototype to the query.
5967 * Uses prototypeOffsets[0] as the representative vector.
68+ * Returns child bookIds alongside each scored volume.
6069 */
6170export async function rankVolumes (
6271 queryEmbedding : Float32Array ,
6372 residentVolumeIds : Hash [ ] ,
6473 topK : number ,
6574 options : RankingOptions ,
66- ) : Promise < Array < { id : Hash ; score : number } > > {
75+ ) : Promise < RankedResult [ ] > {
6776 if ( residentVolumeIds . length === 0 ) return [ ] ;
6877
6978 const { vectorStore, metadataStore } = options ;
70- const scored : Array < { id : Hash ; score : number } > = [ ] ;
79+ const scored : RankedResult [ ] = [ ] ;
7180
7281 for ( const volumeId of residentVolumeIds ) {
7382 const volume = await metadataStore . getVolume ( volumeId ) ;
7483 if ( ! volume || volume . prototypeOffsets . length === 0 ) continue ;
7584 const vec = await vectorStore . readVector ( volume . prototypeOffsets [ 0 ] , volume . prototypeDim ) ;
76- scored . push ( { id : volumeId , score : cosineSimilarity ( queryEmbedding , vec ) } ) ;
85+ scored . push ( { id : volumeId , score : cosineSimilarity ( queryEmbedding , vec ) , childIds : volume . bookIds } ) ;
7786 }
7887
7988 return pickTopK ( scored , topK ) ;
8089}
8190
8291/**
8392 * Ranks books by cosine similarity of their medoid page embedding to the query.
93+ * Returns child pageIds alongside each scored book.
8494 */
8595export async function rankBooks (
8696 queryEmbedding : Float32Array ,
8797 residentBookIds : Hash [ ] ,
8898 topK : number ,
8999 options : RankingOptions ,
90- ) : Promise < Array < { id : Hash ; score : number } > > {
100+ ) : Promise < RankedResult [ ] > {
91101 if ( residentBookIds . length === 0 ) return [ ] ;
92102
93103 const { vectorStore, metadataStore } = options ;
94- const scored : Array < { id : Hash ; score : number } > = [ ] ;
104+ const scored : RankedResult [ ] = [ ] ;
95105
96106 for ( const bookId of residentBookIds ) {
97107 const book = await metadataStore . getBook ( bookId ) ;
98108 if ( ! book ) continue ;
99109 const medoidPage = await metadataStore . getPage ( book . medoidPageId ) ;
100110 if ( ! medoidPage ) continue ;
101111 const vec = await vectorStore . readVector ( medoidPage . embeddingOffset , medoidPage . embeddingDim ) ;
102- scored . push ( { id : bookId , score : cosineSimilarity ( queryEmbedding , vec ) } ) ;
112+ scored . push ( { id : bookId , score : cosineSimilarity ( queryEmbedding , vec ) , childIds : book . pageIds } ) ;
103113 }
104114
105115 return pickTopK ( scored , topK ) ;
@@ -113,17 +123,17 @@ export async function rankPages(
113123 residentPageIds : Hash [ ] ,
114124 topK : number ,
115125 options : RankingOptions ,
116- ) : Promise < Array < { id : Hash ; score : number } > > {
126+ ) : Promise < RankedResult [ ] > {
117127 if ( residentPageIds . length === 0 ) return [ ] ;
118128
119129 const { vectorStore, metadataStore } = options ;
120- const scored : Array < { id : Hash ; score : number } > = [ ] ;
130+ const scored : RankedResult [ ] = [ ] ;
121131
122132 for ( const pageId of residentPageIds ) {
123133 const page = await metadataStore . getPage ( pageId ) ;
124134 if ( ! page ) continue ;
125135 const vec = await vectorStore . readVector ( page . embeddingOffset , page . embeddingDim ) ;
126- scored . push ( { id : pageId , score : cosineSimilarity ( queryEmbedding , vec ) } ) ;
136+ scored . push ( { id : pageId , score : cosineSimilarity ( queryEmbedding , vec ) , childIds : [ ] } ) ;
127137 }
128138
129139 return pickTopK ( scored , topK ) ;
@@ -139,17 +149,17 @@ export async function spillToWarm(
139149 queryEmbedding : Float32Array ,
140150 topK : number ,
141151 options : RankingOptions ,
142- ) : Promise < Array < { id : Hash ; score : number } > > {
152+ ) : Promise < RankedResult [ ] > {
143153 if ( tier !== "page" ) return [ ] ;
144154
145155 const { vectorStore, metadataStore } = options ;
146156 const allPages = await metadataStore . getAllPages ( ) ;
147157 if ( allPages . length === 0 ) return [ ] ;
148158
149- const scored : Array < { id : Hash ; score : number } > = [ ] ;
159+ const scored : RankedResult [ ] = [ ] ;
150160 for ( const page of allPages ) {
151161 const vec = await vectorStore . readVector ( page . embeddingOffset , page . embeddingDim ) ;
152- scored . push ( { id : page . pageId , score : cosineSimilarity ( queryEmbedding , vec ) } ) ;
162+ scored . push ( { id : page . pageId , score : cosineSimilarity ( queryEmbedding , vec ) , childIds : [ ] } ) ;
153163 }
154164
155165 return pickTopK ( scored , topK ) ;
0 commit comments