From 2503365c445d55fd75021a54980c40152d3f16e9 Mon Sep 17 00:00:00 2001 From: Daniel Lew Date: Tue, 28 Apr 2026 19:17:04 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=AB=20feat:=20Add=20Support=20for=20`n?= =?UTF-8?q?one`=20Reranker=20Type=20in=20Web=20Search=20Config=20(#12765)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most of the codebase already supports the concept of *not* using a reranker w/ web search, except there was no way to initially setup an absent reranker component. Now there's a special path for skipping the reranker auth when loading web search config, which allows for skipping the reranker when using web search. --- packages/api/src/web/web.spec.ts | 40 +++++++++++++++++++++++ packages/api/src/web/web.ts | 6 ++++ packages/data-provider/src/config.ts | 1 + packages/data-schemas/src/app/web.spec.ts | 1 + packages/data-schemas/src/app/web.ts | 4 ++- 5 files changed, 51 insertions(+), 1 deletion(-) diff --git a/packages/api/src/web/web.spec.ts b/packages/api/src/web/web.spec.ts index 74e02b20ef..5a7bf4a53a 100644 --- a/packages/api/src/web/web.spec.ts +++ b/packages/api/src/web/web.spec.ts @@ -818,6 +818,46 @@ describe('web.ts', () => { expect(cohereCalls.length).toBe(0); }); + it('should handle a rerankerType of `none`', async () => { + // Initialize a webSearchConfig with a specific rerankerType + const webSearchConfig: TCustomConfig['webSearch'] = { + serperApiKey: '${SERPER_API_KEY}', + searxngInstanceUrl: '${SEARXNG_INSTANCE_URL}', + searxngApiKey: '${SEARXNG_API_KEY}', + firecrawlApiKey: '${FIRECRAWL_API_KEY}', + firecrawlApiUrl: '${FIRECRAWL_API_URL}', + safeSearch: SafeSearchTypes.MODERATE, + rerankerType: 'none' as RerankerTypes, + }; + + // Mock successful authentication + mockLoadAuthValues.mockImplementation(({ authFields }) => { + const result: Record = {}; + authFields.forEach((field: string) => { + result[field] = + field === 'FIRECRAWL_API_URL' ? 'https://api.firecrawl.dev' : 'test-api-key'; + }); + return Promise.resolve(result); + }); + + const result = await loadWebSearchAuth({ + userId, + webSearchConfig, + loadAuthValues: mockLoadAuthValues, + }); + + expect(result.authenticated).toBe(true); + expect(result.authResult.rerankerType).toBe('none'); + + // Verify that we didn't request any reranker keys + const cohereCalls = mockLoadAuthValues.mock.calls.filter( + (call) => + call[0].authFields.includes('COHERE_API_KEY') || + call[0].authFields.includes('JINA_API_KEY'), + ); + expect(cohereCalls.length).toBe(0); + }); + it('should handle invalid specified service gracefully', async () => { // Initialize a webSearchConfig with an invalid searchProvider const webSearchConfig: TCustomConfig['webSearch'] = { diff --git a/packages/api/src/web/web.ts b/packages/api/src/web/web.ts index cc0d8688ca..0b66a49510 100644 --- a/packages/api/src/web/web.ts +++ b/packages/api/src/web/web.ts @@ -123,6 +123,12 @@ export async function loadWebSearchAuth({ specificService = webSearchConfig.scraperProvider as unknown as ServiceType; } else if (category === SearchCategories.RERANKERS && webSearchConfig?.rerankerType) { specificService = webSearchConfig.rerankerType as unknown as ServiceType; + + // Special case: skipping the reranker means skipping auth as well + if (specificService === 'none') { + authResult.rerankerType = specificService as RerankerTypes; + return [true, false]; + } } // If a specific service is specified, only check that one diff --git a/packages/data-provider/src/config.ts b/packages/data-provider/src/config.ts index f783391323..c3043765b6 100644 --- a/packages/data-provider/src/config.ts +++ b/packages/data-provider/src/config.ts @@ -937,6 +937,7 @@ export enum ScraperProviders { export enum RerankerTypes { JINA = 'jina', COHERE = 'cohere', + NONE = 'none', } export enum SafeSearchTypes { diff --git a/packages/data-schemas/src/app/web.spec.ts b/packages/data-schemas/src/app/web.spec.ts index 787d7809a3..2f188778ff 100644 --- a/packages/data-schemas/src/app/web.spec.ts +++ b/packages/data-schemas/src/app/web.spec.ts @@ -56,6 +56,7 @@ describe('loadWebSearchConfig', () => { jinaApiUrl: '${JINA_API_URL}', cohereApiKey: '${COHERE_API_KEY}', safeSearch: SafeSearchTypes.MODERATE, + rerankerType: undefined, }); }); diff --git a/packages/data-schemas/src/app/web.ts b/packages/data-schemas/src/app/web.ts index a61e1f1611..f416e67e2d 100644 --- a/packages/data-schemas/src/app/web.ts +++ b/packages/data-schemas/src/app/web.ts @@ -1,4 +1,4 @@ -import { SafeSearchTypes } from 'librechat-data-provider'; +import { RerankerTypes, SafeSearchTypes } from 'librechat-data-provider'; import type { TCustomConfig } from 'librechat-data-provider'; import type { TWebSearchKeys, TWebSearchCategories } from '~/types/web'; @@ -73,6 +73,7 @@ export function loadWebSearchConfig( const jinaApiUrl = config?.jinaApiUrl ?? '${JINA_API_URL}'; const cohereApiKey = config?.cohereApiKey ?? '${COHERE_API_KEY}'; const safeSearch = config?.safeSearch ?? SafeSearchTypes.MODERATE; + const rerankerType = config?.rerankerType; return { ...config, @@ -86,5 +87,6 @@ export function loadWebSearchConfig( firecrawlApiUrl, firecrawlVersion, searxngInstanceUrl, + rerankerType, }; }