From b5ddaf6417349e6bb6cd1ed041b4a332911551ba Mon Sep 17 00:00:00 2001 From: Jan Kessler Date: Tue, 20 May 2025 10:39:31 +0200 Subject: [PATCH 1/4] make weight for bm25 retriever in hybrid search ui-configurable --- backend/open_webui/config.py | 5 +++++ backend/open_webui/main.py | 6 ++++-- backend/open_webui/retrieval/utils.py | 18 +++++++++++++++--- backend/open_webui/routers/retrieval.py | 8 ++++++++ .../components/admin/Settings/Documents.svelte | 18 ++++++++++++++++++ src/lib/i18n/locales/en-US/translation.json | 1 + 6 files changed, 51 insertions(+), 5 deletions(-) diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index b1955b056..eac03fd8a 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -1928,6 +1928,11 @@ RAG_RELEVANCE_THRESHOLD = PersistentConfig( "rag.relevance_threshold", float(os.environ.get("RAG_RELEVANCE_THRESHOLD", "0.0")), ) +RAG_BM25_WEIGHT = PersistentConfig( + "RAG_BM25_WEIGHT", + "rag.bm25_weight", + float(os.environ.get("RAG_BM25_WEIGHT", "0.5")), +) ENABLE_RAG_HYBRID_SEARCH = PersistentConfig( "ENABLE_RAG_HYBRID_SEARCH", diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index a5aee4bb8..01dd42cf3 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -196,7 +196,10 @@ from open_webui.config import ( RAG_RERANKING_MODEL_TRUST_REMOTE_CODE, RAG_EMBEDDING_ENGINE, RAG_EMBEDDING_BATCH_SIZE, + RAG_TOP_K, + RAG_TOP_K_RERANKER, RAG_RELEVANCE_THRESHOLD, + RAG_BM25_WEIGHT, RAG_ALLOWED_FILE_EXTENSIONS, RAG_FILE_MAX_COUNT, RAG_FILE_MAX_SIZE, @@ -217,8 +220,6 @@ from open_webui.config import ( DOCUMENT_INTELLIGENCE_ENDPOINT, DOCUMENT_INTELLIGENCE_KEY, MISTRAL_OCR_API_KEY, - RAG_TOP_K, - RAG_TOP_K_RERANKER, RAG_TEXT_SPLITTER, TIKTOKEN_ENCODING_NAME, PDF_EXTRACT_IMAGES, @@ -646,6 +647,7 @@ app.state.FUNCTIONS = {} app.state.config.TOP_K = RAG_TOP_K app.state.config.TOP_K_RERANKER = RAG_TOP_K_RERANKER app.state.config.RELEVANCE_THRESHOLD = RAG_RELEVANCE_THRESHOLD +app.state.config.BM25_WEIGHT = RAG_BM25_WEIGHT app.state.config.ALLOWED_FILE_EXTENSIONS = RAG_ALLOWED_FILE_EXTENSIONS app.state.config.FILE_MAX_SIZE = RAG_FILE_MAX_SIZE app.state.config.FILE_MAX_COUNT = RAG_FILE_MAX_COUNT diff --git a/backend/open_webui/retrieval/utils.py b/backend/open_webui/retrieval/utils.py index a132d7201..c083b9f67 100644 --- a/backend/open_webui/retrieval/utils.py +++ b/backend/open_webui/retrieval/utils.py @@ -29,6 +29,7 @@ from open_webui.config import ( RAG_EMBEDDING_QUERY_PREFIX, RAG_EMBEDDING_CONTENT_PREFIX, RAG_EMBEDDING_PREFIX_FIELD_NAME, + RAG_BM25_WEIGHT, ) log = logging.getLogger(__name__) @@ -131,9 +132,20 @@ def query_doc_with_hybrid_search( top_k=k, ) - ensemble_retriever = EnsembleRetriever( - retrievers=[bm25_retriever, vector_search_retriever], weights=[0.5, 0.5] - ) + if RAG_BM25_WEIGHT <= 0: + ensemble_retriever = EnsembleRetriever( + retrievers=[vector_search_retriever], weights=[1.] + ) + elif RAG_BM25_WEIGHT >= 1: + ensemble_retriever = EnsembleRetriever( + retrievers=[bm25_retriever], weights=[1.] + ) + else: + ensemble_retriever = EnsembleRetriever( + retrievers=[bm25_retriever, vector_search_retriever], + weights=[RAG_BM25_WEIGHT, 1. - RAG_BM25_WEIGHT] + ) + compressor = RerankCompressor( embedding_function=embedding_function, top_n=k_reranker, diff --git a/backend/open_webui/routers/retrieval.py b/backend/open_webui/routers/retrieval.py index 5cb47373f..cdd71196c 100644 --- a/backend/open_webui/routers/retrieval.py +++ b/backend/open_webui/routers/retrieval.py @@ -349,6 +349,7 @@ async def get_rag_config(request: Request, user=Depends(get_admin_user)): "ENABLE_RAG_HYBRID_SEARCH": request.app.state.config.ENABLE_RAG_HYBRID_SEARCH, "TOP_K_RERANKER": request.app.state.config.TOP_K_RERANKER, "RELEVANCE_THRESHOLD": request.app.state.config.RELEVANCE_THRESHOLD, + "BM25_WEIGHT": request.app.state.config.BM25_WEIGHT, # Content extraction settings "CONTENT_EXTRACTION_ENGINE": request.app.state.config.CONTENT_EXTRACTION_ENGINE, "PDF_EXTRACT_IMAGES": request.app.state.config.PDF_EXTRACT_IMAGES, @@ -492,6 +493,7 @@ class ConfigForm(BaseModel): ENABLE_RAG_HYBRID_SEARCH: Optional[bool] = None TOP_K_RERANKER: Optional[int] = None RELEVANCE_THRESHOLD: Optional[float] = None + BM25_WEIGHT: Optional[float] = None # Content extraction settings CONTENT_EXTRACTION_ENGINE: Optional[str] = None @@ -578,6 +580,11 @@ async def update_rag_config( if form_data.RELEVANCE_THRESHOLD is not None else request.app.state.config.RELEVANCE_THRESHOLD ) + request.app.state.config.BM25_WEIGHT = ( + form_data.BM25_WEIGHT + if form_data.BM25_WEIGHT is not None + else request.app.state.config.BM25_WEIGHT + ) # Content extraction settings request.app.state.config.CONTENT_EXTRACTION_ENGINE = ( @@ -837,6 +844,7 @@ async def update_rag_config( "ENABLE_RAG_HYBRID_SEARCH": request.app.state.config.ENABLE_RAG_HYBRID_SEARCH, "TOP_K_RERANKER": request.app.state.config.TOP_K_RERANKER, "RELEVANCE_THRESHOLD": request.app.state.config.RELEVANCE_THRESHOLD, + "BM25_WEIGHT": request.app.state.config.BM25_WEIGHT, # Content extraction settings "CONTENT_EXTRACTION_ENGINE": request.app.state.config.CONTENT_EXTRACTION_ENGINE, "PDF_EXTRACT_IMAGES": request.app.state.config.PDF_EXTRACT_IMAGES, diff --git a/src/lib/components/admin/Settings/Documents.svelte b/src/lib/components/admin/Settings/Documents.svelte index 32efd3b54..6dacb3257 100644 --- a/src/lib/components/admin/Settings/Documents.svelte +++ b/src/lib/components/admin/Settings/Documents.svelte @@ -770,6 +770,24 @@ {/if} + + {#if RAGConfig.ENABLE_RAG_HYBRID_SEARCH === true} +
+
{$i18n.t('BM25 Weight')}
+
+ +
+
+ {/if} {/if}
diff --git a/src/lib/i18n/locales/en-US/translation.json b/src/lib/i18n/locales/en-US/translation.json index 5516b6e1f..ea3308b6c 100644 --- a/src/lib/i18n/locales/en-US/translation.json +++ b/src/lib/i18n/locales/en-US/translation.json @@ -425,6 +425,7 @@ "Enter Application DN Password": "", "Enter Bing Search V7 Endpoint": "", "Enter Bing Search V7 Subscription Key": "", + "Enter BM25 Weight": "", "Enter Bocha Search API Key": "", "Enter Brave Search API Key": "", "Enter certificate path": "", From 308d8ac04a8e71485c4c89bc0da1acaab804b38d Mon Sep 17 00:00:00 2001 From: Jan Kessler Date: Tue, 20 May 2025 11:21:14 +0200 Subject: [PATCH 2/4] make bm25_weight a regular parameter of query_doc.. / get_sources_from_files functions --- backend/open_webui/retrieval/utils.py | 12 ++++++++---- backend/open_webui/routers/retrieval.py | 10 ++++++++++ backend/open_webui/utils/middleware.py | 1 + 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/backend/open_webui/retrieval/utils.py b/backend/open_webui/retrieval/utils.py index c083b9f67..70c3f4115 100644 --- a/backend/open_webui/retrieval/utils.py +++ b/backend/open_webui/retrieval/utils.py @@ -29,7 +29,6 @@ from open_webui.config import ( RAG_EMBEDDING_QUERY_PREFIX, RAG_EMBEDDING_CONTENT_PREFIX, RAG_EMBEDDING_PREFIX_FIELD_NAME, - RAG_BM25_WEIGHT, ) log = logging.getLogger(__name__) @@ -117,6 +116,7 @@ def query_doc_with_hybrid_search( reranking_function, k_reranker: int, r: float, + bm25_weight: float, ) -> dict: try: log.debug(f"query_doc_with_hybrid_search:doc {collection_name}") @@ -132,18 +132,18 @@ def query_doc_with_hybrid_search( top_k=k, ) - if RAG_BM25_WEIGHT <= 0: + if bm25_weight <= 0: ensemble_retriever = EnsembleRetriever( retrievers=[vector_search_retriever], weights=[1.] ) - elif RAG_BM25_WEIGHT >= 1: + elif bm25_weight >= 1: ensemble_retriever = EnsembleRetriever( retrievers=[bm25_retriever], weights=[1.] ) else: ensemble_retriever = EnsembleRetriever( retrievers=[bm25_retriever, vector_search_retriever], - weights=[RAG_BM25_WEIGHT, 1. - RAG_BM25_WEIGHT] + weights=[bm25_weight, 1. - bm25_weight] ) compressor = RerankCompressor( @@ -325,6 +325,7 @@ def query_collection_with_hybrid_search( reranking_function, k_reranker: int, r: float, + bm25_weight: float, ) -> dict: results = [] error = False @@ -358,6 +359,7 @@ def query_collection_with_hybrid_search( reranking_function=reranking_function, k_reranker=k_reranker, r=r, + bm25_weight=bm25_weight, ) return result, None except Exception as e: @@ -445,6 +447,7 @@ def get_sources_from_files( reranking_function, k_reranker, r, + bm25_weight, hybrid_search, full_context=False, ): @@ -562,6 +565,7 @@ def get_sources_from_files( reranking_function=reranking_function, k_reranker=k_reranker, r=r, + bm25_weight=bm25_weight, ) except Exception as e: log.debug( diff --git a/backend/open_webui/routers/retrieval.py b/backend/open_webui/routers/retrieval.py index cdd71196c..e31dba299 100644 --- a/backend/open_webui/routers/retrieval.py +++ b/backend/open_webui/routers/retrieval.py @@ -1782,6 +1782,11 @@ def query_doc_handler( if form_data.r else request.app.state.config.RELEVANCE_THRESHOLD ), + bm25_weight=( + form_data.bm25_weight + if form_data.bm25_weight + else request.app.state.config.BM25_WEIGHT + ), user=user, ) else: @@ -1833,6 +1838,11 @@ def query_collection_handler( if form_data.r else request.app.state.config.RELEVANCE_THRESHOLD ), + bm25_weight=( + form_data.bm25_weight + if form_data.bm25_weight + else request.app.state.config.BM25_WEIGHT + ), ) else: return query_collection( diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index c9095f931..c0ce2f063 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -603,6 +603,7 @@ async def chat_completion_files_handler( reranking_function=request.app.state.rf, k_reranker=request.app.state.config.TOP_K_RERANKER, r=request.app.state.config.RELEVANCE_THRESHOLD, + bm25_weight=request.app.state.config.BM25_WEIGHT, hybrid_search=request.app.state.config.ENABLE_RAG_HYBRID_SEARCH, full_context=request.app.state.config.RAG_FULL_CONTEXT, ), From e70dd3323390c9da5c9197efbbc50a07db7d79d2 Mon Sep 17 00:00:00 2001 From: Jan Kessler Date: Fri, 23 May 2025 22:06:44 +0200 Subject: [PATCH 3/4] rename BM25_WEIGHT -> HYBRID_BM25_WEIGHT --- backend/open_webui/config.py | 8 ++--- backend/open_webui/main.py | 4 +-- backend/open_webui/retrieval/utils.py | 16 +++++----- backend/open_webui/routers/retrieval.py | 30 +++++++++---------- backend/open_webui/utils/middleware.py | 2 +- .../admin/Settings/Documents.svelte | 4 +-- 6 files changed, 32 insertions(+), 32 deletions(-) diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index eac03fd8a..d21832e3c 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -1928,10 +1928,10 @@ RAG_RELEVANCE_THRESHOLD = PersistentConfig( "rag.relevance_threshold", float(os.environ.get("RAG_RELEVANCE_THRESHOLD", "0.0")), ) -RAG_BM25_WEIGHT = PersistentConfig( - "RAG_BM25_WEIGHT", - "rag.bm25_weight", - float(os.environ.get("RAG_BM25_WEIGHT", "0.5")), +RAG_HYBRID_BM25_WEIGHT = PersistentConfig( + "RAG_HYBRID_BM25_WEIGHT", + "rag.hybrid_bm25_weight", + float(os.environ.get("RAG_HYBRID_BM25_WEIGHT", "0.5")), ) ENABLE_RAG_HYBRID_SEARCH = PersistentConfig( diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index 01dd42cf3..c384f55e6 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -199,7 +199,7 @@ from open_webui.config import ( RAG_TOP_K, RAG_TOP_K_RERANKER, RAG_RELEVANCE_THRESHOLD, - RAG_BM25_WEIGHT, + RAG_HYBRID_BM25_WEIGHT, RAG_ALLOWED_FILE_EXTENSIONS, RAG_FILE_MAX_COUNT, RAG_FILE_MAX_SIZE, @@ -647,7 +647,7 @@ app.state.FUNCTIONS = {} app.state.config.TOP_K = RAG_TOP_K app.state.config.TOP_K_RERANKER = RAG_TOP_K_RERANKER app.state.config.RELEVANCE_THRESHOLD = RAG_RELEVANCE_THRESHOLD -app.state.config.BM25_WEIGHT = RAG_BM25_WEIGHT +app.state.config.HYBRID_BM25_WEIGHT = RAG_HYBRID_BM25_WEIGHT app.state.config.ALLOWED_FILE_EXTENSIONS = RAG_ALLOWED_FILE_EXTENSIONS app.state.config.FILE_MAX_SIZE = RAG_FILE_MAX_SIZE app.state.config.FILE_MAX_COUNT = RAG_FILE_MAX_COUNT diff --git a/backend/open_webui/retrieval/utils.py b/backend/open_webui/retrieval/utils.py index 70c3f4115..b7b4912ac 100644 --- a/backend/open_webui/retrieval/utils.py +++ b/backend/open_webui/retrieval/utils.py @@ -116,7 +116,7 @@ def query_doc_with_hybrid_search( reranking_function, k_reranker: int, r: float, - bm25_weight: float, + hybrid_bm25_weight: float, ) -> dict: try: log.debug(f"query_doc_with_hybrid_search:doc {collection_name}") @@ -132,18 +132,18 @@ def query_doc_with_hybrid_search( top_k=k, ) - if bm25_weight <= 0: + if hybrid_bm25_weight <= 0: ensemble_retriever = EnsembleRetriever( retrievers=[vector_search_retriever], weights=[1.] ) - elif bm25_weight >= 1: + elif hybrid_bm25_weight >= 1: ensemble_retriever = EnsembleRetriever( retrievers=[bm25_retriever], weights=[1.] ) else: ensemble_retriever = EnsembleRetriever( retrievers=[bm25_retriever, vector_search_retriever], - weights=[bm25_weight, 1. - bm25_weight] + weights=[hybrid_bm25_weight, 1. - hybrid_bm25_weight] ) compressor = RerankCompressor( @@ -325,7 +325,7 @@ def query_collection_with_hybrid_search( reranking_function, k_reranker: int, r: float, - bm25_weight: float, + hybrid_bm25_weight: float, ) -> dict: results = [] error = False @@ -359,7 +359,7 @@ def query_collection_with_hybrid_search( reranking_function=reranking_function, k_reranker=k_reranker, r=r, - bm25_weight=bm25_weight, + hybrid_bm25_weight=hybrid_bm25_weight, ) return result, None except Exception as e: @@ -447,7 +447,7 @@ def get_sources_from_files( reranking_function, k_reranker, r, - bm25_weight, + hybrid_bm25_weight, hybrid_search, full_context=False, ): @@ -565,7 +565,7 @@ def get_sources_from_files( reranking_function=reranking_function, k_reranker=k_reranker, r=r, - bm25_weight=bm25_weight, + hybrid_bm25_weight=hybrid_bm25_weight, ) except Exception as e: log.debug( diff --git a/backend/open_webui/routers/retrieval.py b/backend/open_webui/routers/retrieval.py index e31dba299..5076ff8a6 100644 --- a/backend/open_webui/routers/retrieval.py +++ b/backend/open_webui/routers/retrieval.py @@ -349,7 +349,7 @@ async def get_rag_config(request: Request, user=Depends(get_admin_user)): "ENABLE_RAG_HYBRID_SEARCH": request.app.state.config.ENABLE_RAG_HYBRID_SEARCH, "TOP_K_RERANKER": request.app.state.config.TOP_K_RERANKER, "RELEVANCE_THRESHOLD": request.app.state.config.RELEVANCE_THRESHOLD, - "BM25_WEIGHT": request.app.state.config.BM25_WEIGHT, + "HYBRID_BM25_WEIGHT": request.app.state.config.HYBRID_BM25_WEIGHT, # Content extraction settings "CONTENT_EXTRACTION_ENGINE": request.app.state.config.CONTENT_EXTRACTION_ENGINE, "PDF_EXTRACT_IMAGES": request.app.state.config.PDF_EXTRACT_IMAGES, @@ -493,7 +493,7 @@ class ConfigForm(BaseModel): ENABLE_RAG_HYBRID_SEARCH: Optional[bool] = None TOP_K_RERANKER: Optional[int] = None RELEVANCE_THRESHOLD: Optional[float] = None - BM25_WEIGHT: Optional[float] = None + HYBRID_BM25_WEIGHT: Optional[float] = None # Content extraction settings CONTENT_EXTRACTION_ENGINE: Optional[str] = None @@ -580,10 +580,10 @@ async def update_rag_config( if form_data.RELEVANCE_THRESHOLD is not None else request.app.state.config.RELEVANCE_THRESHOLD ) - request.app.state.config.BM25_WEIGHT = ( - form_data.BM25_WEIGHT - if form_data.BM25_WEIGHT is not None - else request.app.state.config.BM25_WEIGHT + request.app.state.config.HYBRID_BM25_WEIGHT = ( + form_data.HYBRID_BM25_WEIGHT + if form_data.HYBRID_BM25_WEIGHT is not None + else request.app.state.config.HYBRID_BM25_WEIGHT ) # Content extraction settings @@ -844,7 +844,7 @@ async def update_rag_config( "ENABLE_RAG_HYBRID_SEARCH": request.app.state.config.ENABLE_RAG_HYBRID_SEARCH, "TOP_K_RERANKER": request.app.state.config.TOP_K_RERANKER, "RELEVANCE_THRESHOLD": request.app.state.config.RELEVANCE_THRESHOLD, - "BM25_WEIGHT": request.app.state.config.BM25_WEIGHT, + "HYBRID_BM25_WEIGHT": request.app.state.config.HYBRID_BM25_WEIGHT, # Content extraction settings "CONTENT_EXTRACTION_ENGINE": request.app.state.config.CONTENT_EXTRACTION_ENGINE, "PDF_EXTRACT_IMAGES": request.app.state.config.PDF_EXTRACT_IMAGES, @@ -1782,10 +1782,10 @@ def query_doc_handler( if form_data.r else request.app.state.config.RELEVANCE_THRESHOLD ), - bm25_weight=( - form_data.bm25_weight - if form_data.bm25_weight - else request.app.state.config.BM25_WEIGHT + hybrid_bm25_weight=( + form_data.hybrid_bm25_weight + if form_data.hybrid_bm25_weight + else request.app.state.config.HYBRID_BM25_WEIGHT ), user=user, ) @@ -1838,10 +1838,10 @@ def query_collection_handler( if form_data.r else request.app.state.config.RELEVANCE_THRESHOLD ), - bm25_weight=( - form_data.bm25_weight - if form_data.bm25_weight - else request.app.state.config.BM25_WEIGHT + hybrid_bm25_weight=( + form_data.hybrid_bm25_weight + if form_data.hybrid_bm25_weight + else request.app.state.config.HYBRID_BM25_WEIGHT ), ) else: diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index c0ce2f063..9fa513bf9 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -603,7 +603,7 @@ async def chat_completion_files_handler( reranking_function=request.app.state.rf, k_reranker=request.app.state.config.TOP_K_RERANKER, r=request.app.state.config.RELEVANCE_THRESHOLD, - bm25_weight=request.app.state.config.BM25_WEIGHT, + hybrid_bm25_weight=request.app.state.config.HYBRID_BM25_WEIGHT, hybrid_search=request.app.state.config.ENABLE_RAG_HYBRID_SEARCH, full_context=request.app.state.config.RAG_FULL_CONTEXT, ), diff --git a/src/lib/components/admin/Settings/Documents.svelte b/src/lib/components/admin/Settings/Documents.svelte index 6dacb3257..7de9e8261 100644 --- a/src/lib/components/admin/Settings/Documents.svelte +++ b/src/lib/components/admin/Settings/Documents.svelte @@ -773,14 +773,14 @@ {#if RAGConfig.ENABLE_RAG_HYBRID_SEARCH === true}
-
{$i18n.t('BM25 Weight')}
+
{$i18n.t('Weight of BM25 Retrieval')}
Date: Fri, 23 May 2025 22:30:04 +0200 Subject: [PATCH 4/4] add missing locale string --- src/lib/i18n/locales/en-US/translation.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/i18n/locales/en-US/translation.json b/src/lib/i18n/locales/en-US/translation.json index ea3308b6c..d778adc87 100644 --- a/src/lib/i18n/locales/en-US/translation.json +++ b/src/lib/i18n/locales/en-US/translation.json @@ -1317,6 +1317,7 @@ "What’s New in": "", "When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "", "wherever you are": "", + "Weight of BM25 Retrieval": "", "Whisper (Local)": "", "Why?": "", "Widescreen Mode": "",