@@ -69,6 +69,8 @@ export default function LibraryDetail() {
6969 const [ searching , setSearching ] = useState ( false ) ;
7070 const [ searchResults , setSearchResults ] = useState ( null ) ;
7171 const [ embedder , setEmbedder ] = useState ( "cohere://" ) ;
72+ const [ indexAlgorithm , setIndexAlgorithm ] = useState ( "kd_tree" ) ;
73+ const [ lastBuiltAlgorithm , setLastBuiltAlgorithm ] = useState ( null ) ;
7274
7375 function load ( ) {
7476 setError ( null ) ;
@@ -82,6 +84,8 @@ export default function LibraryDetail() {
8284 }
8385
8486 useEffect ( ( ) => {
87+ setIndexBuilt ( false ) ;
88+ setLastBuiltAlgorithm ( null ) ;
8589 load ( ) ;
8690 } , [ libraryId ] ) ;
8791
@@ -105,14 +109,22 @@ export default function LibraryDetail() {
105109 async function handleBuildIndex ( ) {
106110 setBuilding ( true ) ;
107111 setError ( null ) ;
108- try {
109- await buildIndex ( libraryId , "brute_force" ) ;
110- setIndexBuilt ( true ) ;
111- } catch ( e ) {
112- setError ( e . message ) ;
113- } finally {
114- setBuilding ( false ) ;
112+ const order = indexAlgorithm === "auto" ? [ "kd_tree" , "ivf" , "brute_force" ] : [ indexAlgorithm ] ;
113+ let lastErr = null ;
114+ let used = null ;
115+ for ( const algo of order ) {
116+ try {
117+ await buildIndex ( libraryId , algo ) ;
118+ setIndexBuilt ( true ) ;
119+ setLastBuiltAlgorithm ( algo ) ;
120+ used = algo ;
121+ break ;
122+ } catch ( e ) {
123+ lastErr = e ;
124+ }
115125 }
126+ if ( ! used ) setError ( lastErr ?. message ?? "Index build failed" ) ;
127+ setBuilding ( false ) ;
116128 }
117129
118130 async function handleSearch ( e ) {
@@ -253,15 +265,30 @@ export default function LibraryDetail() {
253265 < div className = "card" >
254266 < h2 > Build index</ h2 >
255267 < p style = { { marginBottom : "0.75rem" , color : "var(--text-muted)" , fontSize : "0.875rem" } } >
256- Required before search. Build after ingesting documents.
268+ Required before search. Build after ingesting documents. < strong > Auto </ strong > tries KD-Tree → IVF → Brute-force until one succeeds. Brute-force and KD-Tree are exact; IVF is approximate.
257269 </ p >
270+ < div style = { { display : "flex" , gap : "0.75rem" , alignItems : "center" , flexWrap : "wrap" , marginBottom : "0.75rem" } } >
271+ < label className = "label" htmlFor = "index-algorithm" > Algorithm</ label >
272+ < select
273+ id = "index-algorithm"
274+ className = "input"
275+ value = { indexAlgorithm }
276+ onChange = { ( e ) => setIndexAlgorithm ( e . target . value ) }
277+ style = { { maxWidth : "320px" } }
278+ >
279+ < option value = "auto" > Auto (KD-Tree → IVF → Brute-force)</ option >
280+ < option value = "kd_tree" > KD-Tree (exact, O(log n) typical)</ option >
281+ < option value = "ivf" > IVF (approximate, cluster-based)</ option >
282+ < option value = "brute_force" > Brute-force (exact, linear scan)</ option >
283+ </ select >
284+ </ div >
258285 < button
259286 type = "button"
260287 className = "btn btn-primary"
261288 onClick = { handleBuildIndex }
262289 disabled = { building || totalChunks === 0 }
263290 >
264- { building ? "Building…" : indexBuilt ? " Index built" : " Build index (brute_force)" }
291+ { building ? "Building…" : indexBuilt ? ` Index built ( ${ lastBuiltAlgorithm ?? indexAlgorithm } )` : ` Build index (${ indexAlgorithm === "auto" ? "auto" : indexAlgorithm } )` }
265292 </ button >
266293 </ div >
267294
0 commit comments