@@ -21,17 +21,13 @@ import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vites
2121
2222const {
2323 mockGetDocumentTagDefinitions,
24- mockHandleTagOnlySearch,
25- mockHandleVectorOnlySearch,
26- mockHandleTagAndVectorSearch,
24+ mockExecuteKnowledgeSearch,
2725 mockGetQueryStrategy,
2826 mockGenerateSearchEmbedding,
2927 mockGetDocumentMetadataByIds,
3028} = vi . hoisted ( ( ) => ( {
3129 mockGetDocumentTagDefinitions : vi . fn ( ) ,
32- mockHandleTagOnlySearch : vi . fn ( ) ,
33- mockHandleVectorOnlySearch : vi . fn ( ) ,
34- mockHandleTagAndVectorSearch : vi . fn ( ) ,
30+ mockExecuteKnowledgeSearch : vi . fn ( ) ,
3531 mockGetQueryStrategy : vi . fn ( ) ,
3632 mockGenerateSearchEmbedding : vi . fn ( ) ,
3733 mockGetDocumentMetadataByIds : vi . fn ( ) ,
@@ -69,9 +65,7 @@ vi.mock('@/lib/knowledge/tags/service', () => ({
6965} ) )
7066
7167vi . mock ( './utils' , ( ) => ( {
72- handleTagOnlySearch : mockHandleTagOnlySearch ,
73- handleVectorOnlySearch : mockHandleVectorOnlySearch ,
74- handleTagAndVectorSearch : mockHandleTagAndVectorSearch ,
68+ executeKnowledgeSearch : mockExecuteKnowledgeSearch ,
7569 getQueryStrategy : mockGetQueryStrategy ,
7670 generateSearchEmbedding : mockGenerateSearchEmbedding ,
7771 getDocumentMetadataByIds : mockGetDocumentMetadataByIds ,
@@ -118,9 +112,7 @@ describe('Knowledge Search API Route', () => {
118112 resetDbChainMock ( )
119113 setEnv ( { OPENAI_API_KEY : 'test-api-key' } )
120114
121- mockHandleTagOnlySearch . mockClear ( )
122- mockHandleVectorOnlySearch . mockClear ( )
123- mockHandleTagAndVectorSearch . mockClear ( )
115+ mockExecuteKnowledgeSearch . mockClear ( )
124116 mockGetQueryStrategy . mockClear ( ) . mockReturnValue ( {
125117 useParallel : false ,
126118 distanceThreshold : 1.0 ,
@@ -192,7 +184,7 @@ describe('Knowledge Search API Route', () => {
192184
193185 dbChainMockFns . limit . mockResolvedValue ( [ ] )
194186
195- mockHandleVectorOnlySearch . mockResolvedValue ( mockSearchResults )
187+ mockExecuteKnowledgeSearch . mockResolvedValue ( mockSearchResults )
196188
197189 mockFetch . mockResolvedValue ( {
198190 ok : true ,
@@ -212,14 +204,50 @@ describe('Knowledge Search API Route', () => {
212204 expect ( data . data . results [ 0 ] . similarity ) . toBe ( 0.8 ) // 1 - 0.2
213205 expect ( data . data . query ) . toBe ( validSearchData . query )
214206 expect ( data . data . knowledgeBaseIds ) . toEqual ( [ 'kb-123' ] )
215- expect ( mockHandleVectorOnlySearch ) . toHaveBeenCalledWith ( {
207+ expect ( mockExecuteKnowledgeSearch ) . toHaveBeenCalledWith ( {
216208 knowledgeBaseIds : [ 'kb-123' ] ,
217209 topK : 10 ,
210+ searchMode : 'hybrid' ,
211+ query : validSearchData . query ,
218212 queryVector : JSON . stringify ( mockEmbedding ) ,
219- distanceThreshold : expect . any ( Number ) ,
213+ structuredFilters : undefined ,
220214 } )
221215 } )
222216
217+ it ( 'should forward the searchMode opt-out to the retrieval layer' , async ( ) => {
218+ mockGetUserId . mockResolvedValue ( 'user-123' )
219+
220+ mockCheckKnowledgeBaseAccess . mockResolvedValue ( {
221+ hasAccess : true ,
222+ knowledgeBase : {
223+ id : 'kb-123' ,
224+ userId : 'user-123' ,
225+ name : 'Test KB' ,
226+ deletedAt : null ,
227+ } ,
228+ } )
229+
230+ dbChainMockFns . limit . mockResolvedValue ( [ ] )
231+
232+ mockExecuteKnowledgeSearch . mockResolvedValue ( mockSearchResults )
233+
234+ mockFetch . mockResolvedValue ( {
235+ ok : true ,
236+ json : ( ) =>
237+ Promise . resolve ( {
238+ data : [ { embedding : mockEmbedding } ] ,
239+ } ) ,
240+ } )
241+
242+ const req = createMockRequest ( 'POST' , { ...validSearchData , searchMode : 'vector' } )
243+ const response = await POST ( req )
244+
245+ expect ( response . status ) . toBe ( 200 )
246+ expect ( mockExecuteKnowledgeSearch ) . toHaveBeenCalledWith (
247+ expect . objectContaining ( { searchMode : 'vector' } )
248+ )
249+ } )
250+
223251 it ( 'should perform search successfully with multiple knowledge bases' , async ( ) => {
224252 const multiKbData = {
225253 ...validSearchData ,
@@ -239,7 +267,7 @@ describe('Knowledge Search API Route', () => {
239267
240268 dbChainMockFns . limit . mockResolvedValue ( [ ] )
241269
242- mockHandleVectorOnlySearch . mockResolvedValue ( mockSearchResults )
270+ mockExecuteKnowledgeSearch . mockResolvedValue ( mockSearchResults )
243271
244272 mockFetch . mockResolvedValue ( {
245273 ok : true ,
@@ -256,11 +284,13 @@ describe('Knowledge Search API Route', () => {
256284 expect ( response . status ) . toBe ( 200 )
257285 expect ( data . success ) . toBe ( true )
258286 expect ( data . data . knowledgeBaseIds ) . toEqual ( [ 'kb-123' , 'kb-456' ] )
259- expect ( mockHandleVectorOnlySearch ) . toHaveBeenCalledWith ( {
287+ expect ( mockExecuteKnowledgeSearch ) . toHaveBeenCalledWith ( {
260288 knowledgeBaseIds : [ 'kb-123' , 'kb-456' ] ,
261289 topK : 10 ,
290+ searchMode : 'hybrid' ,
291+ query : multiKbData . query ,
262292 queryVector : JSON . stringify ( mockEmbedding ) ,
263- distanceThreshold : expect . any ( Number ) ,
293+ structuredFilters : undefined ,
264294 } )
265295 } )
266296
@@ -284,7 +314,7 @@ describe('Knowledge Search API Route', () => {
284314
285315 dbChainMockFns . limit . mockResolvedValue ( [ ] )
286316
287- mockHandleVectorOnlySearch . mockResolvedValue ( mockSearchResults )
317+ mockExecuteKnowledgeSearch . mockResolvedValue ( mockSearchResults )
288318
289319 mockFetch . mockResolvedValue ( {
290320 ok : true ,
@@ -348,7 +378,7 @@ describe('Knowledge Search API Route', () => {
348378 embeddingModel : 'text-embedding-3-small' ,
349379 } ,
350380 } )
351- mockHandleVectorOnlySearch . mockResolvedValue ( mockSearchResults )
381+ mockExecuteKnowledgeSearch . mockResolvedValue ( mockSearchResults )
352382 const attribution = encodeURIComponent (
353383 JSON . stringify ( {
354384 actorUserId : 'user-123' ,
@@ -532,7 +562,7 @@ describe('Knowledge Search API Route', () => {
532562 mockGetUserId . mockResolvedValue ( 'user-123' )
533563 dbChainMockFns . limit . mockResolvedValueOnce ( mockKnowledgeBases )
534564
535- mockHandleVectorOnlySearch . mockRejectedValueOnce ( new Error ( 'Database error' ) )
565+ mockExecuteKnowledgeSearch . mockRejectedValueOnce ( new Error ( 'Database error' ) )
536566
537567 const req = createMockRequest ( 'POST' , validSearchData )
538568 const response = await POST ( req )
@@ -750,7 +780,7 @@ describe('Knowledge Search API Route', () => {
750780
751781 dbChainMockFns . limit . mockResolvedValueOnce ( mockTagDefinitions )
752782
753- mockHandleTagOnlySearch . mockResolvedValue ( mockTaggedResults )
783+ mockExecuteKnowledgeSearch . mockResolvedValue ( mockTaggedResults )
754784
755785 const req = createMockRequest ( 'POST' , tagOnlyData )
756786 const response = await POST ( req )
@@ -763,9 +793,10 @@ describe('Knowledge Search API Route', () => {
763793 expect ( data . data . query ) . toBe ( '' ) // Empty query
764794 expect ( data . data . cost ) . toBeUndefined ( ) // No cost for tag-only search
765795 expect ( mockGenerateSearchEmbedding ) . not . toHaveBeenCalled ( ) // No embedding API call
766- expect ( mockHandleTagOnlySearch ) . toHaveBeenCalledWith ( {
796+ expect ( mockExecuteKnowledgeSearch ) . toHaveBeenCalledWith ( {
767797 knowledgeBaseIds : [ 'kb-123' ] ,
768798 topK : 10 ,
799+ searchMode : 'hybrid' ,
769800 structuredFilters : [
770801 { tagSlot : 'tag1' , fieldType : 'text' , operator : 'eq' , value : 'api' , valueTo : undefined } ,
771802 ] ,
@@ -796,7 +827,7 @@ describe('Knowledge Search API Route', () => {
796827
797828 dbChainMockFns . limit . mockResolvedValueOnce ( mockTagDefinitions )
798829
799- mockHandleTagAndVectorSearch . mockResolvedValue ( mockSearchResults )
830+ mockExecuteKnowledgeSearch . mockResolvedValue ( mockSearchResults )
800831
801832 mockFetch . mockResolvedValue ( {
802833 ok : true ,
@@ -816,14 +847,15 @@ describe('Knowledge Search API Route', () => {
816847 expect ( data . data . query ) . toBe ( 'test search' )
817848 expect ( data . data . cost ) . toBeDefined ( ) // Cost included for vector search
818849 expect ( mockGenerateSearchEmbedding ) . toHaveBeenCalled ( ) // Embedding API called
819- expect ( mockHandleTagAndVectorSearch ) . toHaveBeenCalledWith ( {
850+ expect ( mockExecuteKnowledgeSearch ) . toHaveBeenCalledWith ( {
820851 knowledgeBaseIds : [ 'kb-123' ] ,
821852 topK : 10 ,
853+ searchMode : 'hybrid' ,
854+ query : 'test search' ,
855+ queryVector : JSON . stringify ( mockEmbedding ) ,
822856 structuredFilters : [
823857 { tagSlot : 'tag1' , fieldType : 'text' , operator : 'eq' , value : 'api' , valueTo : undefined } ,
824858 ] ,
825- queryVector : JSON . stringify ( mockEmbedding ) ,
826- distanceThreshold : 1 , // Single KB uses threshold of 1.0
827859 } )
828860 } )
829861
@@ -987,7 +1019,7 @@ describe('Knowledge Search API Route', () => {
9871019
9881020 mockGetDocumentTagDefinitions . mockResolvedValue ( mockTagDefinitions )
9891021
990- mockHandleTagOnlySearch . mockResolvedValue ( mockTaggedResults )
1022+ mockExecuteKnowledgeSearch . mockResolvedValue ( mockTaggedResults )
9911023
9921024 dbChainMockFns . limit . mockResolvedValueOnce ( mockTagDefinitions )
9931025
@@ -1016,7 +1048,7 @@ describe('Knowledge Search API Route', () => {
10161048 } ,
10171049 } )
10181050
1019- mockHandleVectorOnlySearch . mockResolvedValue ( [
1051+ mockExecuteKnowledgeSearch . mockResolvedValue ( [
10201052 {
10211053 id : 'chunk-1' ,
10221054 content : 'Content from active document' ,
@@ -1092,7 +1124,7 @@ describe('Knowledge Search API Route', () => {
10921124 { tagSlot : 'tag1' , displayName : 'tag1' , fieldType : 'text' } ,
10931125 ] )
10941126
1095- mockHandleTagOnlySearch . mockResolvedValue ( [
1127+ mockExecuteKnowledgeSearch . mockResolvedValue ( [
10961128 {
10971129 id : 'chunk-2' ,
10981130 content : 'Content from active document with tag' ,
@@ -1164,7 +1196,7 @@ describe('Knowledge Search API Route', () => {
11641196 { tagSlot : 'tag1' , displayName : 'tag1' , fieldType : 'text' } ,
11651197 ] )
11661198
1167- mockHandleTagAndVectorSearch . mockResolvedValue ( [
1199+ mockExecuteKnowledgeSearch . mockResolvedValue ( [
11681200 {
11691201 id : 'chunk-3' ,
11701202 content : 'Relevant content from active document' ,
0 commit comments