@@ -83,7 +83,11 @@ const reused = reuseFile ? readFixtureReport(reuseFile) : undefined
8383const ids = reused ?. fixture ?? createKnowledgeAclFixtureIds ( )
8484const unrelated = reused ?. unrelatedFixture ?? createKnowledgeAclFixtureIds ( )
8585const organizationChatId = generateId ( )
86- const queryVector = Array . from ( { length : dimensions } , ( _ , index ) => ( index === 0 ? 1 : 0 ) )
86+ const queryVector = Array . from ( { length : dimensions } , ( _ , index ) =>
87+ Math . sin ( ( index + 1 ) * 12.9898 )
88+ )
89+ const queryMagnitude = Math . hypot ( ...queryVector )
90+ for ( let index = 0 ; index < queryVector . length ; index ++ ) queryVector [ index ] /= queryMagnitude
8791const captured : CapturedQuery [ ] = [ ]
8892const report : Record < string , unknown > = {
8993 fixture : ids ,
@@ -133,6 +137,7 @@ const explainSchema = z.array(z.object({ Plan: explainNodeSchema }).passthrough(
133137
134138function usesVectorIndex ( node : ExplainNode ) : boolean {
135139 return (
140+ node [ 'Index Name' ] === 'embedding_binary_hnsw_idx' ||
136141 node [ 'Index Name' ] === 'embedding_vector_hnsw_idx' ||
137142 ( node . Plans ?. some ( usesVectorIndex ) ?? false )
138143 )
@@ -237,14 +242,21 @@ async function sample(label: string, run: () => ReturnType<typeof search>) {
237242 const plan = await db . $client . begin ( async ( tx ) => {
238243 await tx . unsafe ( "SET LOCAL hnsw.iterative_scan = 'relaxed_order'" )
239244 await tx . unsafe ( 'SET LOCAL hnsw.max_scan_tuples = 20000' )
245+ if ( query . query . includes ( 'binary_quantize' ) ) {
246+ await tx . unsafe ( 'SET LOCAL hnsw.max_scan_tuples = 100000' )
247+ await tx . unsafe ( 'SET LOCAL hnsw.ef_search = 200' )
248+ await tx . unsafe ( 'SET LOCAL hnsw.scan_mem_multiplier = 4' )
249+ }
240250 return tx . unsafe ( `EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON) ${ query . query } ` , query . parameters )
241251 } )
242252 plans . push ( {
243253 kind : query . query . includes ( 'keyword_rank' )
244254 ? 'keyword'
245- : query . query . includes ( 'order by ' )
255+ : query . query . includes ( 'binary_quantize ' )
246256 ? 'vector'
247- : 'probe' ,
257+ : query . query . includes ( 'order by' )
258+ ? 'rerank'
259+ : 'probe' ,
248260 query : query . query ,
249261 parameters : query . parameters ,
250262 plan : explainSchema . parse ( plan [ 0 ] [ 'QUERY PLAN' ] ) ,
@@ -378,8 +390,8 @@ describe.skipIf(!enabled)('Assistant search latency on a realistic indexed corpu
378390 CASE WHEN n % 8 = 0 THEN 'Orion deployment reference. ' ELSE 'Engineering operations reference. ' END ||
379391 (SELECT string_agg(md5(n::text || ':' || paragraph::text), ' ') FROM generate_series(1, 90) paragraph),
380392 3000, 750, 0, 3000,
381- l2_normalize(ARRAY(SELECT (CASE WHEN coordinate = n % 32 + 1 THEN 1 ELSE 0 END +
382- 0.025 * sin(n::double precision * coordinate * 12.9898 + coordinate * 78.233))::real
393+ l2_normalize(ARRAY(SELECT (sin( coordinate * ( n % 32 + 1) * 12.9898) +
394+ 0.25 * sin(n::double precision * coordinate * 12.9898 + coordinate * 78.233))::real
383395 FROM generate_series(1, ${ dimensions } ) coordinate)::vector(1536))
384396 FROM generate_series(${ first } ::int, ${ last } ::int) n` )
385397 } )
@@ -494,7 +506,13 @@ describe.skipIf(!enabled)('Assistant search latency on a realistic indexed corpu
494506 }
495507 )
496508 if ( delayedLegs === 'both' ) {
497- expect ( result ) . toMatchObject ( { success : false , retryable : true } )
509+ expect ( result ) . toMatchObject ( {
510+ success : true ,
511+ data : {
512+ retrieval : { status : 'partial' , timedOutLegs : [ 'vector' , 'keyword' ] } ,
513+ results : [ ] ,
514+ } ,
515+ } )
498516 return
499517 }
500518 expect ( result ) . toMatchObject ( {
@@ -526,6 +544,18 @@ describe.skipIf(!enabled)('Assistant search latency on a realistic indexed corpu
526544 const vectorPlans = plans . filter ( ( plan ) => plan . kind === 'vector' )
527545 expect ( vectorPlans ) . toHaveLength ( 1 )
528546 expect ( usesVectorIndex ( vectorPlans [ 0 ] . plan [ 0 ] . Plan ) ) . toBe ( true )
547+ expect ( plans . some ( ( plan ) => plan . kind === 'rerank' ) ) . toBe ( true )
548+ const rerank = plans . find ( ( plan ) => plan . kind === 'rerank' ) !
549+ const actual = await db . $client . unsafe ( rerank . query , rerank . parameters ) . values ( )
550+ const expected = await db . execute < { id : string } > ( sql `SELECT id FROM embedding
551+ WHERE knowledge_base_id = ${ ids . knowledgeBaseId } AND enabled
552+ ORDER BY (embedding <=> ${ JSON . stringify ( queryVector ) } ::vector) + 0, id
553+ LIMIT ${ actual . length } ` )
554+ const expectedIds = new Set ( expected . map ( ( { id } ) => id ) )
555+ const recall = actual . filter ( ( [ id ] ) => expectedIds . has ( id ) ) . length / expected . length
556+ expect ( recall ) . toBeGreaterThanOrEqual ( 0.95 )
557+ report [ `recall.${ iteration } ` ] = { neighbors : expected . length , recall }
558+ saveReport ( )
529559 }
530560 expect ( embeddingCalls - before ) . toBe ( 2 )
531561 } , 180_000 )
@@ -578,7 +608,7 @@ describe.skipIf(!enabled)('Assistant search latency on a realistic indexed corpu
578608 expect ( probe ) . toHaveLength ( 1 )
579609 expect ( probe [ 0 ] . query ) . not . toContain ( '<=>' )
580610 expect ( probe [ 0 ] . plan [ 0 ] . Plan [ 'Actual Rows' ] ) . toBe ( 12 )
581- const vector = plans . filter ( ( plan ) => plan . kind === 'vector ' )
611+ const vector = plans . filter ( ( plan ) => plan . kind === 'rerank ' )
582612 expect ( vector ) . toHaveLength ( 1 )
583613 expect ( vector [ 0 ] . query ) . toContain ( '"embedding"."id" in' )
584614 } finally {
@@ -672,7 +702,7 @@ describe.skipIf(!enabled)('Assistant search latency on a realistic indexed corpu
672702 } , 180_000 )
673703 /** Opt in with local Sim and Go URLs; uses the real configured provider, billing adapter, and async resume protocol. */
674704 it . skipIf ( ! process . env . KNOWLEDGE_SEARCH_ASSISTANT_URL ) (
675- 'answers through local Go Assistant with progressive reads and citations ' ,
705+ 'recovers quietly from incomplete search through local Go Assistant, then reads and cites evidence ' ,
676706 async ( ) => {
677707 const assistantUrl = new URL ( process . env . KNOWLEDGE_SEARCH_ASSISTANT_URL ! )
678708 const simUrl = new URL ( process . env . KNOWLEDGE_SEARCH_SIM_URL ! )
@@ -708,6 +738,18 @@ describe.skipIf(!enabled)('Assistant search latency on a realistic indexed corpu
708738 bytes : number
709739 } > = [ ]
710740 let answer = ''
741+ let incompleteSearch = true
742+ const query = SearchBudget . prototype . query
743+ const delayed = vi . spyOn ( SearchBudget . prototype , 'query' ) . mockImplementation ( function < T > (
744+ this : SearchBudget ,
745+ stage : SearchStage ,
746+ run : ( executor : SearchExecutor ) => PromiseLike < T >
747+ ) : Promise < T > {
748+ return query . call ( this , stage , async ( tx ) => {
749+ if ( incompleteSearch ) await tx . execute ( sql `SELECT pg_sleep(9)` )
750+ return run ( tx )
751+ } ) as Promise < T >
752+ } )
711753 const started = performance . now ( )
712754 try {
713755 await db
@@ -808,9 +850,20 @@ describe.skipIf(!enabled)('Assistant search latency on a realistic indexed corpu
808850 bytes : Buffer . byteLength ( JSON . stringify ( result ) ) ,
809851 } )
810852 const { success } = z . object ( { success : z . boolean ( ) } ) . parse ( result )
853+ expect ( success ) . toBe ( true )
854+ if ( incompleteSearch ) {
855+ expect ( call . toolName ) . toBe ( 'search_workspace' )
856+ expect ( result ) . toMatchObject ( {
857+ data : {
858+ retrieval : { status : 'partial' , timedOutLegs : [ 'vector' , 'keyword' ] } ,
859+ results : [ ] ,
860+ } ,
861+ } )
862+ }
811863 return { callId, name : call . toolName , success, data : result }
812864 } )
813865 )
866+ incompleteSearch = false
814867 path = '/api/tools/resume'
815868 body = {
816869 checkpointId : checkpoint . checkpointId ,
@@ -828,10 +881,12 @@ describe.skipIf(!enabled)('Assistant search latency on a realistic indexed corpu
828881 expect ( answer ) . toContain ( 'SILVER COMET' )
829882 expect ( answer ) . toContain ( 'K7M2-84' )
830883 expect ( answer ) . toContain ( '<source>' )
884+ expect ( answer ) . not . toMatch ( / t i m e d ? \s * o u t | t i m e o u t | i n t e r n a l r e t r (?: y | i e s ) / i)
831885 expect ( calls . some ( ( call ) => call . name === 'read_document' ) ) . toBe ( true )
832- expect ( calls . some ( ( call ) => call . name === 'search_workspace' ) ) . toBe ( true )
886+ expect ( calls . filter ( ( call ) => call . name === 'search_workspace' ) . length ) . toBeGreaterThan ( 1 )
833887 expect ( calls . every ( ( call ) => call . bytes < 40000 ) ) . toBe ( true )
834888 } finally {
889+ delayed . mockRestore ( )
835890 await db . update ( embedding ) . set ( original ) . where ( eq ( embedding . id , chunkId ) )
836891 }
837892 } ,
0 commit comments