浏览代码

enh: bypass embedding and retrieval

Timothy Jaeryang Baek 2 月之前
父节点
当前提交
57010901e6

+ 16 - 5
backend/open_webui/config.py

@@ -1502,13 +1502,16 @@ VECTOR_DB = os.environ.get("VECTOR_DB", "chroma")
 # Chroma
 if VECTOR_DB == "chroma":
     import chromadb
+
     CHROMA_DATA_PATH = f"{DATA_DIR}/vector_db"
     CHROMA_TENANT = os.environ.get("CHROMA_TENANT", chromadb.DEFAULT_TENANT)
     CHROMA_DATABASE = os.environ.get("CHROMA_DATABASE", chromadb.DEFAULT_DATABASE)
     CHROMA_HTTP_HOST = os.environ.get("CHROMA_HTTP_HOST", "")
     CHROMA_HTTP_PORT = int(os.environ.get("CHROMA_HTTP_PORT", "8000"))
     CHROMA_CLIENT_AUTH_PROVIDER = os.environ.get("CHROMA_CLIENT_AUTH_PROVIDER", "")
-    CHROMA_CLIENT_AUTH_CREDENTIALS = os.environ.get("CHROMA_CLIENT_AUTH_CREDENTIALS", "")
+    CHROMA_CLIENT_AUTH_CREDENTIALS = os.environ.get(
+        "CHROMA_CLIENT_AUTH_CREDENTIALS", ""
+    )
     # Comma-separated list of header=value pairs
     CHROMA_HTTP_HEADERS = os.environ.get("CHROMA_HTTP_HEADERS", "")
     if CHROMA_HTTP_HEADERS:
@@ -1608,6 +1611,14 @@ DOCUMENT_INTELLIGENCE_KEY = PersistentConfig(
     os.getenv("DOCUMENT_INTELLIGENCE_KEY", ""),
 )
 
+
+BYPASS_EMBEDDING_AND_RETRIEVAL = PersistentConfig(
+    "BYPASS_EMBEDDING_AND_RETRIEVAL",
+    "rag.bypass_embedding_and_retrieval",
+    os.environ.get("BYPASS_EMBEDDING_AND_RETRIEVAL", "False").lower() == "true",
+)
+
+
 RAG_TOP_K = PersistentConfig(
     "RAG_TOP_K", "rag.top_k", int(os.environ.get("RAG_TOP_K", "3"))
 )
@@ -1824,10 +1835,10 @@ RAG_WEB_SEARCH_ENGINE = PersistentConfig(
     os.getenv("RAG_WEB_SEARCH_ENGINE", ""),
 )
 
-RAG_WEB_SEARCH_FULL_CONTEXT = PersistentConfig(
-    "RAG_WEB_SEARCH_FULL_CONTEXT",
-    "rag.web.search.full_context",
-    os.getenv("RAG_WEB_SEARCH_FULL_CONTEXT", "False").lower() == "true",
+BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL = PersistentConfig(
+    "BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL",
+    "rag.web.search.bypass_embedding_and_retrieval",
+    os.getenv("BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL", "False").lower() == "true",
 )
 
 # You can provide a list of your own websites to filter after performing a web search.

+ 6 - 2
backend/open_webui/main.py

@@ -162,6 +162,7 @@ from open_webui.config import (
     RAG_TEMPLATE,
     DEFAULT_RAG_TEMPLATE,
     RAG_FULL_CONTEXT,
+    BYPASS_EMBEDDING_AND_RETRIEVAL,
     RAG_EMBEDDING_MODEL,
     RAG_EMBEDDING_MODEL_AUTO_UPDATE,
     RAG_EMBEDDING_MODEL_TRUST_REMOTE_CODE,
@@ -191,7 +192,7 @@ from open_webui.config import (
     YOUTUBE_LOADER_PROXY_URL,
     # Retrieval (Web Search)
     RAG_WEB_SEARCH_ENGINE,
-    RAG_WEB_SEARCH_FULL_CONTEXT,
+    BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL,
     RAG_WEB_SEARCH_RESULT_COUNT,
     RAG_WEB_SEARCH_CONCURRENT_REQUESTS,
     RAG_WEB_SEARCH_TRUST_ENV,
@@ -531,6 +532,7 @@ app.state.config.FILE_MAX_COUNT = RAG_FILE_MAX_COUNT
 
 
 app.state.config.RAG_FULL_CONTEXT = RAG_FULL_CONTEXT
+app.state.config.BYPASS_EMBEDDING_AND_RETRIEVAL = BYPASS_EMBEDDING_AND_RETRIEVAL
 app.state.config.ENABLE_RAG_HYBRID_SEARCH = ENABLE_RAG_HYBRID_SEARCH
 app.state.config.ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION = (
     ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION
@@ -567,7 +569,9 @@ app.state.config.YOUTUBE_LOADER_PROXY_URL = YOUTUBE_LOADER_PROXY_URL
 
 app.state.config.ENABLE_RAG_WEB_SEARCH = ENABLE_RAG_WEB_SEARCH
 app.state.config.RAG_WEB_SEARCH_ENGINE = RAG_WEB_SEARCH_ENGINE
-app.state.config.RAG_WEB_SEARCH_FULL_CONTEXT = RAG_WEB_SEARCH_FULL_CONTEXT
+app.state.config.BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL = (
+    BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL
+)
 app.state.config.RAG_WEB_SEARCH_DOMAIN_FILTER_LIST = RAG_WEB_SEARCH_DOMAIN_FILTER_LIST
 
 app.state.config.ENABLE_GOOGLE_DRIVE_INTEGRATION = ENABLE_GOOGLE_DRIVE_INTEGRATION

+ 50 - 2
backend/open_webui/retrieval/utils.py

@@ -17,6 +17,7 @@ from open_webui.retrieval.vector.connector import VECTOR_DB_CLIENT
 from open_webui.utils.misc import get_last_user_message, calculate_sha256_string
 
 from open_webui.models.users import UserModel
+from open_webui.models.files import Files
 
 from open_webui.env import (
     SRC_LOG_LEVELS,
@@ -342,6 +343,7 @@ def get_embedding_function(
 
 
 def get_sources_from_files(
+    request,
     files,
     queries,
     embedding_function,
@@ -359,19 +361,64 @@ def get_sources_from_files(
     relevant_contexts = []
 
     for file in files:
+
+        context = None
         if file.get("docs"):
+            # BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL
             context = {
                 "documents": [[doc.get("content") for doc in file.get("docs")]],
                 "metadatas": [[doc.get("metadata") for doc in file.get("docs")]],
             }
         elif file.get("context") == "full":
+            # Manual Full Mode Toggle
             context = {
                 "documents": [[file.get("file").get("data", {}).get("content")]],
                 "metadatas": [[{"file_id": file.get("id"), "name": file.get("name")}]],
             }
-        else:
-            context = None
+        elif (
+            file.get("type") != "web_search"
+            and request.app.state.config.BYPASS_EMBEDDING_AND_RETRIEVAL
+        ):
+            # BYPASS_EMBEDDING_AND_RETRIEVAL
+            if file.get("type") == "collection":
+                file_ids = file.get("data", {}).get("file_ids", [])
+
+                documents = []
+                metadatas = []
+                for file_id in file_ids:
+                    file_object = Files.get_file_by_id(file_id)
+
+                    if file_object:
+                        documents.append(file_object.data.get("content", ""))
+                        metadatas.append(
+                            {
+                                "file_id": file_id,
+                                "name": file_object.filename,
+                                "source": file_object.filename,
+                            }
+                        )
+
+                context = {
+                    "documents": [documents],
+                    "metadatas": [metadatas],
+                }
 
+            elif file.get("id"):
+                file_object = Files.get_file_by_id(file.get("id"))
+                if file_object:
+                    context = {
+                        "documents": [[file_object.data.get("content", "")]],
+                        "metadatas": [
+                            [
+                                {
+                                    "file_id": file.get("id"),
+                                    "name": file_object.filename,
+                                    "source": file_object.filename,
+                                }
+                            ]
+                        ],
+                    }
+        else:
             collection_names = []
             if file.get("type") == "collection":
                 if file.get("legacy"):
@@ -434,6 +481,7 @@ def get_sources_from_files(
         if context:
             if "data" in file:
                 del file["data"]
+
             relevant_contexts.append({**context, "file": file})
 
     sources = []

+ 1 - 2
backend/open_webui/retrieval/vector/dbs/chroma.py

@@ -107,8 +107,7 @@ class ChromaClient:
                     }
                 )
             return None
-        except Exception as e:
-            log.exception(f"{e}")
+        except:
             return None
 
     def get(self, collection_name: str) -> Optional[GetResult]:

+ 53 - 34
backend/open_webui/routers/retrieval.py

@@ -352,6 +352,7 @@ async def get_rag_config(request: Request, user=Depends(get_admin_user)):
         "status": True,
         "pdf_extract_images": request.app.state.config.PDF_EXTRACT_IMAGES,
         "RAG_FULL_CONTEXT": request.app.state.config.RAG_FULL_CONTEXT,
+        "BYPASS_EMBEDDING_AND_RETRIEVAL": request.app.state.config.BYPASS_EMBEDDING_AND_RETRIEVAL,
         "enable_google_drive_integration": request.app.state.config.ENABLE_GOOGLE_DRIVE_INTEGRATION,
         "enable_onedrive_integration": request.app.state.config.ENABLE_ONEDRIVE_INTEGRATION,
         "content_extraction": {
@@ -378,7 +379,7 @@ async def get_rag_config(request: Request, user=Depends(get_admin_user)):
         },
         "web": {
             "ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION": request.app.state.config.ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION,
-            "RAG_WEB_SEARCH_FULL_CONTEXT": request.app.state.config.RAG_WEB_SEARCH_FULL_CONTEXT,
+            "BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL": request.app.state.config.BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL,
             "search": {
                 "enabled": request.app.state.config.ENABLE_RAG_WEB_SEARCH,
                 "drive": request.app.state.config.ENABLE_GOOGLE_DRIVE_INTEGRATION,
@@ -473,11 +474,12 @@ class WebSearchConfig(BaseModel):
 class WebConfig(BaseModel):
     search: WebSearchConfig
     ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION: Optional[bool] = None
-    RAG_WEB_SEARCH_FULL_CONTEXT: Optional[bool] = None
+    BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL: Optional[bool] = None
 
 
 class ConfigUpdateForm(BaseModel):
     RAG_FULL_CONTEXT: Optional[bool] = None
+    BYPASS_EMBEDDING_AND_RETRIEVAL: Optional[bool] = None
     pdf_extract_images: Optional[bool] = None
     enable_google_drive_integration: Optional[bool] = None
     enable_onedrive_integration: Optional[bool] = None
@@ -504,6 +506,12 @@ async def update_rag_config(
         else request.app.state.config.RAG_FULL_CONTEXT
     )
 
+    request.app.state.config.BYPASS_EMBEDDING_AND_RETRIEVAL = (
+        form_data.BYPASS_EMBEDDING_AND_RETRIEVAL
+        if form_data.BYPASS_EMBEDDING_AND_RETRIEVAL is not None
+        else request.app.state.config.BYPASS_EMBEDDING_AND_RETRIEVAL
+    )
+
     request.app.state.config.ENABLE_GOOGLE_DRIVE_INTEGRATION = (
         form_data.enable_google_drive_integration
         if form_data.enable_google_drive_integration is not None
@@ -557,8 +565,8 @@ async def update_rag_config(
         request.app.state.config.ENABLE_RAG_WEB_SEARCH = form_data.web.search.enabled
         request.app.state.config.RAG_WEB_SEARCH_ENGINE = form_data.web.search.engine
 
-        request.app.state.config.RAG_WEB_SEARCH_FULL_CONTEXT = (
-            form_data.web.RAG_WEB_SEARCH_FULL_CONTEXT
+        request.app.state.config.BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL = (
+            form_data.web.BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL
         )
 
         request.app.state.config.SEARXNG_QUERY_URL = (
@@ -626,6 +634,7 @@ async def update_rag_config(
         "status": True,
         "pdf_extract_images": request.app.state.config.PDF_EXTRACT_IMAGES,
         "RAG_FULL_CONTEXT": request.app.state.config.RAG_FULL_CONTEXT,
+        "BYPASS_EMBEDDING_AND_RETRIEVAL": request.app.state.config.BYPASS_EMBEDDING_AND_RETRIEVAL,
         "file": {
             "max_size": request.app.state.config.FILE_MAX_SIZE,
             "max_count": request.app.state.config.FILE_MAX_COUNT,
@@ -650,7 +659,7 @@ async def update_rag_config(
         },
         "web": {
             "ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION": request.app.state.config.ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION,
-            "RAG_WEB_SEARCH_FULL_CONTEXT": request.app.state.config.RAG_WEB_SEARCH_FULL_CONTEXT,
+            "BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL": request.app.state.config.BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL,
             "search": {
                 "enabled": request.app.state.config.ENABLE_RAG_WEB_SEARCH,
                 "engine": request.app.state.config.RAG_WEB_SEARCH_ENGINE,
@@ -1019,36 +1028,45 @@ def process_file(
         hash = calculate_sha256_string(text_content)
         Files.update_file_hash_by_id(file.id, hash)
 
-        try:
-            result = save_docs_to_vector_db(
-                request,
-                docs=docs,
-                collection_name=collection_name,
-                metadata={
-                    "file_id": file.id,
-                    "name": file.filename,
-                    "hash": hash,
-                },
-                add=(True if form_data.collection_name else False),
-                user=user,
-            )
-
-            if result:
-                Files.update_file_metadata_by_id(
-                    file.id,
-                    {
-                        "collection_name": collection_name,
+        if not request.app.state.config.BYPASS_EMBEDDING_AND_RETRIEVAL:
+            try:
+                result = save_docs_to_vector_db(
+                    request,
+                    docs=docs,
+                    collection_name=collection_name,
+                    metadata={
+                        "file_id": file.id,
+                        "name": file.filename,
+                        "hash": hash,
                     },
+                    add=(True if form_data.collection_name else False),
+                    user=user,
                 )
 
-                return {
-                    "status": True,
-                    "collection_name": collection_name,
-                    "filename": file.filename,
-                    "content": text_content,
-                }
-        except Exception as e:
-            raise e
+                if result:
+                    Files.update_file_metadata_by_id(
+                        file.id,
+                        {
+                            "collection_name": collection_name,
+                        },
+                    )
+
+                    return {
+                        "status": True,
+                        "collection_name": collection_name,
+                        "filename": file.filename,
+                        "content": text_content,
+                    }
+            except Exception as e:
+                raise e
+        else:
+            return {
+                "status": True,
+                "collection_name": None,
+                "filename": file.filename,
+                "content": text_content,
+            }
+
     except Exception as e:
         log.exception(e)
         if "No pandoc was found" in str(e):
@@ -1408,9 +1426,11 @@ async def process_web_search(
         )
         docs = await loader.aload()
 
-        if request.app.state.config.RAG_WEB_SEARCH_FULL_CONTEXT:
+        if request.app.state.config.BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL:
             return {
                 "status": True,
+                "collection_name": None,
+                "filenames": urls,
                 "docs": [
                     {
                         "content": doc.page_content,
@@ -1418,7 +1438,6 @@ async def process_web_search(
                     }
                     for doc in docs
                 ],
-                "filenames": urls,
                 "loaded_count": len(docs),
             }
         else:

+ 15 - 11
backend/open_webui/utils/middleware.py

@@ -351,24 +351,25 @@ async def chat_web_search_handler(
                 all_results.append(results)
                 files = form_data.get("files", [])
 
-                if request.app.state.config.RAG_WEB_SEARCH_FULL_CONTEXT:
+                if results.get("collection_name"):
                     files.append(
                         {
-                            "docs": results.get("docs", []),
+                            "collection_name": results["collection_name"],
                             "name": searchQuery,
-                            "type": "web_search_docs",
+                            "type": "web_search",
                             "urls": results["filenames"],
                         }
                     )
-                else:
+                elif results.get("docs"):
                     files.append(
                         {
-                            "collection_name": results["collection_name"],
+                            "docs": results.get("docs", []),
                             "name": searchQuery,
-                            "type": "web_search_results",
+                            "type": "web_search",
                             "urls": results["filenames"],
                         }
                     )
+
                 form_data["files"] = files
         except Exception as e:
             log.exception(e)
@@ -518,6 +519,7 @@ async def chat_completion_files_handler(
     sources = []
 
     if files := body.get("metadata", {}).get("files", None):
+        queries = []
         try:
             queries_response = await generate_queries(
                 request,
@@ -543,8 +545,8 @@ async def chat_completion_files_handler(
                 queries_response = {"queries": [queries_response]}
 
             queries = queries_response.get("queries", [])
-        except Exception as e:
-            queries = []
+        except:
+            pass
 
         if len(queries) == 0:
             queries = [get_last_user_message(body["messages"])]
@@ -556,6 +558,7 @@ async def chat_completion_files_handler(
                 sources = await loop.run_in_executor(
                     executor,
                     lambda: get_sources_from_files(
+                        request=request,
                         files=files,
                         queries=queries,
                         embedding_function=lambda query: request.app.state.EMBEDDING_FUNCTION(
@@ -738,6 +741,7 @@ async def process_chat_payload(request, form_data, metadata, user, model):
 
     tool_ids = form_data.pop("tool_ids", None)
     files = form_data.pop("files", None)
+
     # Remove files duplicates
     if files:
         files = list({json.dumps(f, sort_keys=True): f for f in files}.values())
@@ -795,8 +799,6 @@ async def process_chat_payload(request, form_data, metadata, user, model):
     if len(sources) > 0:
         context_string = ""
         for source_idx, source in enumerate(sources):
-            source_id = source.get("source", {}).get("name", "")
-
             if "document" in source:
                 for doc_idx, doc_context in enumerate(source["document"]):
                     context_string += f"<source><source_id>{source_idx}</source_id><source_context>{doc_context}</source_context></source>\n"
@@ -1913,7 +1915,9 @@ async def process_chat_response(
                         )
 
                         log.info(f"content_blocks={content_blocks}")
-                        log.info(f"serialize_content_blocks={serialize_content_blocks(content_blocks)}")
+                        log.info(
+                            f"serialize_content_blocks={serialize_content_blocks(content_blocks)}"
+                        )
 
                         try:
                             res = await generate_chat_completion(

+ 316 - 290
src/lib/components/admin/Settings/Documents.svelte

@@ -59,6 +59,7 @@
 	let pdfExtractImages = true;
 
 	let RAG_FULL_CONTEXT = false;
+	let BYPASS_EMBEDDING_AND_RETRIEVAL = false;
 
 	let enableGoogleDriveIntegration = false;
 	let enableOneDriveIntegration = false;
@@ -170,12 +171,6 @@
 	};
 
 	const submitHandler = async () => {
-		await embeddingModelUpdateHandler();
-
-		if (querySettings.hybrid) {
-			await rerankingModelUpdateHandler();
-		}
-
 		if (contentExtractionEngine === 'tika' && tikaServerUrl === '') {
 			toast.error($i18n.t('Tika Server URL required.'));
 			return;
@@ -187,6 +182,15 @@
 			toast.error($i18n.t('Document Intelligence endpoint and key required.'));
 			return;
 		}
+
+		if (!BYPASS_EMBEDDING_AND_RETRIEVAL) {
+			await embeddingModelUpdateHandler();
+
+			if (querySettings.hybrid) {
+				await rerankingModelUpdateHandler();
+			}
+		}
+
 		const res = await updateRAGConfig(localStorage.token, {
 			pdf_extract_images: pdfExtractImages,
 			enable_google_drive_integration: enableGoogleDriveIntegration,
@@ -196,6 +200,7 @@
 				max_count: fileMaxCount === '' ? null : fileMaxCount
 			},
 			RAG_FULL_CONTEXT: RAG_FULL_CONTEXT,
+			BYPASS_EMBEDDING_AND_RETRIEVAL: BYPASS_EMBEDDING_AND_RETRIEVAL,
 			chunk: {
 				text_splitter: textSplitter,
 				chunk_overlap: chunkOverlap,
@@ -260,6 +265,7 @@
 			chunkOverlap = res.chunk.chunk_overlap;
 
 			RAG_FULL_CONTEXT = res.RAG_FULL_CONTEXT;
+			BYPASS_EMBEDDING_AND_RETRIEVAL = res.BYPASS_EMBEDDING_AND_RETRIEVAL;
 
 			contentExtractionEngine = res.content_extraction.engine;
 			tikaServerUrl = res.content_extraction.tika_server_url;
@@ -328,9 +334,6 @@
 							<select
 								class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 text-xs bg-transparent outline-hidden text-right"
 								bind:value={contentExtractionEngine}
-								on:change={(e) => {
-									showDocumentIntelligenceConfig = e.target.value === 'document_intelligence';
-								}}
 							>
 								<option value="">{$i18n.t('Default')} </option>
 								<option value="tika">{$i18n.t('Tika')}</option>
@@ -376,151 +379,295 @@
 				{/if}
 
 				<div class="  mb-2.5 flex w-full justify-between">
-					<div class=" self-center text-xs font-medium">{$i18n.t('Text Splitter')}</div>
+					<div class=" self-center text-xs font-medium">
+						<Tooltip content={$i18n.t('Full Context Mode')} placement="top-start">
+							{$i18n.t('Bypass Embedding and Retrieval')}
+						</Tooltip>
+					</div>
 					<div class="flex items-center relative">
-						<select
-							class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 text-xs bg-transparent outline-hidden text-right"
-							bind:value={textSplitter}
+						<Tooltip
+							content={BYPASS_EMBEDDING_AND_RETRIEVAL
+								? 'Inject the entire content as context for comprehensive processing, this is recommended for complex queries.'
+								: 'Default to segmented retrieval for focused and relevant content extraction, this is recommended for most cases.'}
 						>
-							<option value="">{$i18n.t('Default')} ({$i18n.t('Character')})</option>
-							<option value="token">{$i18n.t('Token')} ({$i18n.t('Tiktoken')})</option>
-						</select>
+							<Switch bind:state={BYPASS_EMBEDDING_AND_RETRIEVAL} />
+						</Tooltip>
 					</div>
 				</div>
 
-				<div class="  mb-2.5 flex w-full justify-between">
-					<div class=" flex gap-1.5 w-full">
-						<div class="  w-full justify-between">
-							<div class="self-center text-xs font-medium min-w-fit mb-1">
-								{$i18n.t('Chunk Size')}
-							</div>
-							<div class="self-center">
-								<input
-									class=" w-full rounded-lg py-1.5 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
-									type="number"
-									placeholder={$i18n.t('Enter Chunk Size')}
-									bind:value={chunkSize}
-									autocomplete="off"
-									min="0"
-								/>
-							</div>
+				{#if !BYPASS_EMBEDDING_AND_RETRIEVAL}
+					<div class="  mb-2.5 flex w-full justify-between">
+						<div class=" self-center text-xs font-medium">{$i18n.t('Text Splitter')}</div>
+						<div class="flex items-center relative">
+							<select
+								class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 text-xs bg-transparent outline-hidden text-right"
+								bind:value={textSplitter}
+							>
+								<option value="">{$i18n.t('Default')} ({$i18n.t('Character')})</option>
+								<option value="token">{$i18n.t('Token')} ({$i18n.t('Tiktoken')})</option>
+							</select>
 						</div>
+					</div>
 
-						<div class="w-full">
-							<div class=" self-center text-xs font-medium min-w-fit mb-1">
-								{$i18n.t('Chunk Overlap')}
+					<div class="  mb-2.5 flex w-full justify-between">
+						<div class=" flex gap-1.5 w-full">
+							<div class="  w-full justify-between">
+								<div class="self-center text-xs font-medium min-w-fit mb-1">
+									{$i18n.t('Chunk Size')}
+								</div>
+								<div class="self-center">
+									<input
+										class=" w-full rounded-lg py-1.5 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
+										type="number"
+										placeholder={$i18n.t('Enter Chunk Size')}
+										bind:value={chunkSize}
+										autocomplete="off"
+										min="0"
+									/>
+								</div>
 							</div>
 
-							<div class="self-center">
-								<input
-									class="w-full rounded-lg py-1.5 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
-									type="number"
-									placeholder={$i18n.t('Enter Chunk Overlap')}
-									bind:value={chunkOverlap}
-									autocomplete="off"
-									min="0"
-								/>
+							<div class="w-full">
+								<div class=" self-center text-xs font-medium min-w-fit mb-1">
+									{$i18n.t('Chunk Overlap')}
+								</div>
+
+								<div class="self-center">
+									<input
+										class="w-full rounded-lg py-1.5 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
+										type="number"
+										placeholder={$i18n.t('Enter Chunk Overlap')}
+										bind:value={chunkOverlap}
+										autocomplete="off"
+										min="0"
+									/>
+								</div>
 							</div>
 						</div>
 					</div>
-				</div>
+				{/if}
 			</div>
 
-			<div class="mb-3">
-				<div class=" mb-2.5 text-base font-medium">{$i18n.t('Embedding')}</div>
+			{#if !BYPASS_EMBEDDING_AND_RETRIEVAL}
+				<div class="mb-3">
+					<div class=" mb-2.5 text-base font-medium">{$i18n.t('Embedding')}</div>
 
-				<hr class=" border-gray-100 dark:border-gray-850 my-2" />
+					<hr class=" border-gray-100 dark:border-gray-850 my-2" />
 
-				<div class="  mb-2.5 flex flex-col w-full justify-between">
-					<div class="flex w-full justify-between">
-						<div class=" self-center text-xs font-medium">{$i18n.t('Embedding Model Engine')}</div>
-						<div class="flex items-center relative">
-							<select
-								class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-right"
-								bind:value={embeddingEngine}
-								placeholder="Select an embedding model engine"
-								on:change={(e) => {
-									if (e.target.value === 'ollama') {
-										embeddingModel = '';
-									} else if (e.target.value === 'openai') {
-										embeddingModel = 'text-embedding-3-small';
-									} else if (e.target.value === '') {
-										embeddingModel = 'sentence-transformers/all-MiniLM-L6-v2';
-									}
-								}}
-							>
-								<option value="">{$i18n.t('Default (SentenceTransformers)')}</option>
-								<option value="ollama">{$i18n.t('Ollama')}</option>
-								<option value="openai">{$i18n.t('OpenAI')}</option>
-							</select>
+					<div class="  mb-2.5 flex flex-col w-full justify-between">
+						<div class="flex w-full justify-between">
+							<div class=" self-center text-xs font-medium">
+								{$i18n.t('Embedding Model Engine')}
+							</div>
+							<div class="flex items-center relative">
+								<select
+									class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-right"
+									bind:value={embeddingEngine}
+									placeholder="Select an embedding model engine"
+									on:change={(e) => {
+										if (e.target.value === 'ollama') {
+											embeddingModel = '';
+										} else if (e.target.value === 'openai') {
+											embeddingModel = 'text-embedding-3-small';
+										} else if (e.target.value === '') {
+											embeddingModel = 'sentence-transformers/all-MiniLM-L6-v2';
+										}
+									}}
+								>
+									<option value="">{$i18n.t('Default (SentenceTransformers)')}</option>
+									<option value="ollama">{$i18n.t('Ollama')}</option>
+									<option value="openai">{$i18n.t('OpenAI')}</option>
+								</select>
+							</div>
 						</div>
+
+						{#if embeddingEngine === 'openai'}
+							<div class="my-0.5 flex gap-2 pr-2">
+								<input
+									class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
+									placeholder={$i18n.t('API Base URL')}
+									bind:value={OpenAIUrl}
+									required
+								/>
+
+								<SensitiveInput placeholder={$i18n.t('API Key')} bind:value={OpenAIKey} />
+							</div>
+						{:else if embeddingEngine === 'ollama'}
+							<div class="my-0.5 flex gap-2 pr-2">
+								<input
+									class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
+									placeholder={$i18n.t('API Base URL')}
+									bind:value={OllamaUrl}
+									required
+								/>
+
+								<SensitiveInput
+									placeholder={$i18n.t('API Key')}
+									bind:value={OllamaKey}
+									required={false}
+								/>
+							</div>
+						{/if}
 					</div>
 
-					{#if embeddingEngine === 'openai'}
-						<div class="my-0.5 flex gap-2 pr-2">
-							<input
-								class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
-								placeholder={$i18n.t('API Base URL')}
-								bind:value={OpenAIUrl}
-								required
-							/>
+					<div class="  mb-2.5 flex flex-col w-full">
+						<div class=" mb-1 text-xs font-medium">{$i18n.t('Embedding Model')}</div>
+
+						<div class="">
+							{#if embeddingEngine === 'ollama'}
+								<div class="flex w-full">
+									<div class="flex-1 mr-2">
+										<input
+											class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
+											bind:value={embeddingModel}
+											placeholder={$i18n.t('Set embedding model')}
+											required
+										/>
+									</div>
+								</div>
+							{:else}
+								<div class="flex w-full">
+									<div class="flex-1 mr-2">
+										<input
+											class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
+											placeholder={$i18n.t('Set embedding model (e.g. {{model}})', {
+												model: embeddingModel.slice(-40)
+											})}
+											bind:value={embeddingModel}
+										/>
+									</div>
+
+									{#if embeddingEngine === ''}
+										<button
+											class="px-2.5 bg-transparent text-gray-800 dark:bg-transparent dark:text-gray-100 rounded-lg transition"
+											on:click={() => {
+												embeddingModelUpdateHandler();
+											}}
+											disabled={updateEmbeddingModelLoading}
+										>
+											{#if updateEmbeddingModelLoading}
+												<div class="self-center">
+													<svg
+														class=" w-4 h-4"
+														viewBox="0 0 24 24"
+														fill="currentColor"
+														xmlns="http://www.w3.org/2000/svg"
+													>
+														<style>
+															.spinner_ajPY {
+																transform-origin: center;
+																animation: spinner_AtaB 0.75s infinite linear;
+															}
 
-							<SensitiveInput placeholder={$i18n.t('API Key')} bind:value={OpenAIKey} />
+															@keyframes spinner_AtaB {
+																100% {
+																	transform: rotate(360deg);
+																}
+															}
+														</style>
+														<path
+															d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
+															opacity=".25"
+														/>
+														<path
+															d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
+															class="spinner_ajPY"
+														/>
+													</svg>
+												</div>
+											{:else}
+												<svg
+													xmlns="http://www.w3.org/2000/svg"
+													viewBox="0 0 16 16"
+													fill="currentColor"
+													class="w-4 h-4"
+												>
+													<path
+														d="M8.75 2.75a.75.75 0 0 0-1.5 0v5.69L5.03 6.22a.75.75 0 0 0-1.06 1.06l3.5 3.5a.75.75 0 0 0 1.06 0l3.5-3.5a.75.75 0 0 0-1.06-1.06L8.75 8.44V2.75Z"
+													/>
+													<path
+														d="M3.5 9.75a.75.75 0 0 0-1.5 0v1.5A2.75 2.75 0 0 0 4.75 14h6.5A2.75 2.75 0 0 0 14 11.25v-1.5a.75.75 0 0 0-1.5 0v1.5c0 .69-.56 1.25-1.25 1.25h-6.5c-.69 0-1.25-.56-1.25-1.25v-1.5Z"
+													/>
+												</svg>
+											{/if}
+										</button>
+									{/if}
+								</div>
+							{/if}
 						</div>
-					{:else if embeddingEngine === 'ollama'}
-						<div class="my-0.5 flex gap-2 pr-2">
-							<input
-								class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
-								placeholder={$i18n.t('API Base URL')}
-								bind:value={OllamaUrl}
-								required
-							/>
 
-							<SensitiveInput
-								placeholder={$i18n.t('API Key')}
-								bind:value={OllamaKey}
-								required={false}
-							/>
+						<div class="mt-1 mb-1 text-xs text-gray-400 dark:text-gray-500">
+							{$i18n.t(
+								'Warning: If you update or change your embedding model, you will need to re-import all documents.'
+							)}
 						</div>
-					{/if}
-				</div>
+					</div>
 
-				<div class="  mb-2.5 flex flex-col w-full">
-					<div class=" mb-1 text-xs font-medium">{$i18n.t('Embedding Model')}</div>
+					{#if embeddingEngine === 'ollama' || embeddingEngine === 'openai'}
+						<div class="  mb-2.5 flex w-full justify-between">
+							<div class=" self-center text-xs font-medium">{$i18n.t('Embedding Batch Size')}</div>
 
-					<div class="">
-						{#if embeddingEngine === 'ollama'}
-							<div class="flex w-full">
-								<div class="flex-1 mr-2">
-									<input
-										class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
-										bind:value={embeddingModel}
-										placeholder={$i18n.t('Set embedding model')}
-										required
-									/>
-								</div>
+							<div class="">
+								<input
+									bind:value={embeddingBatchSize}
+									type="number"
+									class=" bg-transparent text-center w-14 outline-none"
+									min="-2"
+									max="16000"
+									step="1"
+								/>
 							</div>
-						{:else}
-							<div class="flex w-full">
-								<div class="flex-1 mr-2">
-									<input
-										class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
-										placeholder={$i18n.t('Set embedding model (e.g. {{model}})', {
-											model: embeddingModel.slice(-40)
-										})}
-										bind:value={embeddingModel}
-									/>
-								</div>
+						</div>
+					{/if}
 
-								{#if embeddingEngine === ''}
+					<div class="  mb-2.5 flex w-full justify-between">
+						<div class=" self-center text-xs font-medium">{$i18n.t('Full Context Mode')}</div>
+						<div class="flex items-center relative">
+							<Tooltip
+								content={RAG_FULL_CONTEXT
+									? 'Inject entire contents as context for comprehensive processing, this is recommended for complex queries.'
+									: 'Default to segmented retrieval for focused and relevant content extraction, this is recommended for most cases.'}
+							>
+								<Switch bind:state={RAG_FULL_CONTEXT} />
+							</Tooltip>
+						</div>
+					</div>
+
+					<div class="  mb-2.5 flex w-full justify-between">
+						<div class=" self-center text-xs font-medium">{$i18n.t('Hybrid Search')}</div>
+						<div class="flex items-center relative">
+							<Switch
+								bind:state={querySettings.hybrid}
+								on:change={() => {
+									toggleHybridSearch();
+								}}
+							/>
+						</div>
+					</div>
+
+					{#if querySettings.hybrid === true}
+						<div class="  mb-2.5 flex flex-col w-full">
+							<div class=" mb-1 text-xs font-medium">{$i18n.t('Reranking Model')}</div>
+
+							<div class="">
+								<div class="flex w-full">
+									<div class="flex-1 mr-2">
+										<input
+											class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
+											placeholder={$i18n.t('Set reranking model (e.g. {{model}})', {
+												model: 'BAAI/bge-reranker-v2-m3'
+											})}
+											bind:value={rerankingModel}
+										/>
+									</div>
 									<button
 										class="px-2.5 bg-transparent text-gray-800 dark:bg-transparent dark:text-gray-100 rounded-lg transition"
 										on:click={() => {
-											embeddingModelUpdateHandler();
+											rerankingModelUpdateHandler();
 										}}
-										disabled={updateEmbeddingModelLoading}
+										disabled={updateRerankingModelLoading}
 									>
-										{#if updateEmbeddingModelLoading}
+										{#if updateRerankingModelLoading}
 											<div class="self-center">
 												<svg
 													class=" w-4 h-4"
@@ -566,196 +713,75 @@
 											</svg>
 										{/if}
 									</button>
-								{/if}
+								</div>
 							</div>
-						{/if}
-					</div>
-
-					<div class="mt-1 mb-1 text-xs text-gray-400 dark:text-gray-500">
-						{$i18n.t(
-							'Warning: If you update or change your embedding model, you will need to re-import all documents.'
-						)}
-					</div>
+						</div>
+					{/if}
 				</div>
 
-				{#if embeddingEngine === 'ollama' || embeddingEngine === 'openai'}
-					<div class="  mb-2.5 flex w-full justify-between">
-						<div class=" self-center text-xs font-medium">{$i18n.t('Embedding Batch Size')}</div>
+				<div class="mb-3">
+					<div class=" mb-2.5 text-base font-medium">{$i18n.t('Retrieval')}</div>
 
-						<div class="">
+					<hr class=" border-gray-100 dark:border-gray-850 my-2" />
+
+					<div class="  mb-2.5 flex w-full justify-between">
+						<div class=" self-center text-xs font-medium">{$i18n.t('Top K')}</div>
+						<div class="flex items-center relative">
 							<input
-								bind:value={embeddingBatchSize}
+								class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
 								type="number"
-								class=" bg-transparent text-center w-14 outline-none"
-								min="-2"
-								max="16000"
-								step="1"
+								placeholder={$i18n.t('Enter Top K')}
+								bind:value={querySettings.k}
+								autocomplete="off"
+								min="0"
 							/>
 						</div>
 					</div>
-				{/if}
-
-				<div class="  mb-2.5 flex w-full justify-between">
-					<div class=" self-center text-xs font-medium">{$i18n.t('Full Context Mode')}</div>
-					<div class="flex items-center relative">
-						<Tooltip
-							content={RAG_FULL_CONTEXT
-								? 'Inject entire contents as context for comprehensive processing, this is recommended for complex queries.'
-								: 'Default to segmented retrieval for focused and relevant content extraction, this is recommended for most cases.'}
-						>
-							<Switch bind:state={RAG_FULL_CONTEXT} />
-						</Tooltip>
-					</div>
-				</div>
-
-				<div class="  mb-2.5 flex w-full justify-between">
-					<div class=" self-center text-xs font-medium">{$i18n.t('Hybrid Search')}</div>
-					<div class="flex items-center relative">
-						<Switch
-							bind:state={querySettings.hybrid}
-							on:change={() => {
-								toggleHybridSearch();
-							}}
-						/>
-					</div>
-				</div>
-
-				{#if querySettings.hybrid === true}
-					<div class="  mb-2.5 flex flex-col w-full">
-						<div class=" mb-1 text-xs font-medium">{$i18n.t('Reranking Model')}</div>
 
-						<div class="">
-							<div class="flex w-full">
-								<div class="flex-1 mr-2">
+					{#if querySettings.hybrid === true}
+						<div class="  mb-2.5 flex flex-col w-full justify-between">
+							<div class=" flex w-full justify-between">
+								<div class=" self-center text-xs font-medium">{$i18n.t('Minimum Score')}</div>
+								<div class="flex items-center relative">
 									<input
 										class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
-										placeholder={$i18n.t('Set reranking model (e.g. {{model}})', {
-											model: 'BAAI/bge-reranker-v2-m3'
-										})}
-										bind:value={rerankingModel}
+										type="number"
+										step="0.01"
+										placeholder={$i18n.t('Enter Score')}
+										bind:value={querySettings.r}
+										autocomplete="off"
+										min="0.0"
+										title={$i18n.t('The score should be a value between 0.0 (0%) and 1.0 (100%).')}
 									/>
 								</div>
-								<button
-									class="px-2.5 bg-transparent text-gray-800 dark:bg-transparent dark:text-gray-100 rounded-lg transition"
-									on:click={() => {
-										rerankingModelUpdateHandler();
-									}}
-									disabled={updateRerankingModelLoading}
-								>
-									{#if updateRerankingModelLoading}
-										<div class="self-center">
-											<svg
-												class=" w-4 h-4"
-												viewBox="0 0 24 24"
-												fill="currentColor"
-												xmlns="http://www.w3.org/2000/svg"
-											>
-												<style>
-													.spinner_ajPY {
-														transform-origin: center;
-														animation: spinner_AtaB 0.75s infinite linear;
-													}
-
-													@keyframes spinner_AtaB {
-														100% {
-															transform: rotate(360deg);
-														}
-													}
-												</style>
-												<path
-													d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
-													opacity=".25"
-												/>
-												<path
-													d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
-													class="spinner_ajPY"
-												/>
-											</svg>
-										</div>
-									{:else}
-										<svg
-											xmlns="http://www.w3.org/2000/svg"
-											viewBox="0 0 16 16"
-											fill="currentColor"
-											class="w-4 h-4"
-										>
-											<path
-												d="M8.75 2.75a.75.75 0 0 0-1.5 0v5.69L5.03 6.22a.75.75 0 0 0-1.06 1.06l3.5 3.5a.75.75 0 0 0 1.06 0l3.5-3.5a.75.75 0 0 0-1.06-1.06L8.75 8.44V2.75Z"
-											/>
-											<path
-												d="M3.5 9.75a.75.75 0 0 0-1.5 0v1.5A2.75 2.75 0 0 0 4.75 14h6.5A2.75 2.75 0 0 0 14 11.25v-1.5a.75.75 0 0 0-1.5 0v1.5c0 .69-.56 1.25-1.25 1.25h-6.5c-.69 0-1.25-.56-1.25-1.25v-1.5Z"
-											/>
-										</svg>
-									{/if}
-								</button>
+							</div>
+							<div class="mt-1 text-xs text-gray-400 dark:text-gray-500">
+								{$i18n.t(
+									'Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.'
+								)}
 							</div>
 						</div>
-					</div>
-				{/if}
-			</div>
-
-			<div class="mb-3">
-				<div class=" mb-2.5 text-base font-medium">{$i18n.t('Query')}</div>
-
-				<hr class=" border-gray-100 dark:border-gray-850 my-2" />
-
-				<div class="  mb-2.5 flex w-full justify-between">
-					<div class=" self-center text-xs font-medium">{$i18n.t('Top K')}</div>
-					<div class="flex items-center relative">
-						<input
-							class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
-							type="number"
-							placeholder={$i18n.t('Enter Top K')}
-							bind:value={querySettings.k}
-							autocomplete="off"
-							min="0"
-						/>
-					</div>
-				</div>
+					{/if}
 
-				{#if querySettings.hybrid === true}
 					<div class="  mb-2.5 flex flex-col w-full justify-between">
-						<div class=" flex w-full justify-between">
-							<div class=" self-center text-xs font-medium">{$i18n.t('Minimum Score')}</div>
-							<div class="flex items-center relative">
-								<input
-									class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
-									type="number"
-									step="0.01"
-									placeholder={$i18n.t('Enter Score')}
-									bind:value={querySettings.r}
-									autocomplete="off"
-									min="0.0"
-									title={$i18n.t('The score should be a value between 0.0 (0%) and 1.0 (100%).')}
+						<div class=" mb-1 text-xs font-medium">{$i18n.t('RAG Template')}</div>
+						<div class="flex w-full items-center relative">
+							<Tooltip
+								content={$i18n.t('Leave empty to use the default prompt, or enter a custom prompt')}
+								placement="top-start"
+								className="w-full"
+							>
+								<Textarea
+									bind:value={querySettings.template}
+									placeholder={$i18n.t(
+										'Leave empty to use the default prompt, or enter a custom prompt'
+									)}
 								/>
-							</div>
-						</div>
-						<div class="mt-1 text-xs text-gray-400 dark:text-gray-500">
-							{$i18n.t(
-								'Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.'
-							)}
+							</Tooltip>
 						</div>
 					</div>
-				{/if}
-
-				<div class="  mb-2.5 flex flex-col w-full justify-between">
-					<div class=" mb-1 text-xs font-medium">{$i18n.t('RAG Template')}</div>
-					<div class="flex w-full items-center relative">
-						<Tooltip
-							content={$i18n.t('Leave empty to use the default prompt, or enter a custom prompt')}
-							placement="top-start"
-							className="w-full"
-						>
-							<Textarea
-								bind:value={querySettings.template}
-								placeholder={$i18n.t(
-									'Leave empty to use the default prompt, or enter a custom prompt'
-								)}
-							/>
-						</Tooltip>
-					</div>
 				</div>
-			</div>
+			{/if}
 
 			<div class="mb-3">
 				<div class=" mb-2.5 text-base font-medium">{$i18n.t('Files')}</div>

+ 8 - 4
src/lib/components/admin/Settings/WebSearch.svelte

@@ -118,14 +118,18 @@
 				</div>
 
 				<div class=" py-0.5 flex w-full justify-between">
-					<div class=" self-center text-xs font-medium">{$i18n.t('Full Context Mode')}</div>
+					<div class=" self-center text-xs font-medium">
+						<Tooltip content={$i18n.t('Full Context Mode')} placement="top-start">
+							{$i18n.t('Bypass Embedding and Retrieval')}
+						</Tooltip>
+					</div>
 					<div class="flex items-center relative">
 						<Tooltip
-							content={webConfig.RAG_WEB_SEARCH_FULL_CONTEXT
-								? 'Inject the entire web results as context for comprehensive processing, this is recommended for complex queries.'
+							content={webConfig.BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL
+								? 'Inject the entire content as context for comprehensive processing, this is recommended for complex queries.'
 								: 'Default to segmented retrieval for focused and relevant content extraction, this is recommended for most cases.'}
 						>
-							<Switch bind:state={webConfig.RAG_WEB_SEARCH_FULL_CONTEXT} />
+							<Switch bind:state={webConfig.BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL} />
 						</Tooltip>
 					</div>
 				</div>

+ 2 - 1
src/lib/components/chat/Messages/Citations.svelte

@@ -43,6 +43,7 @@
 	}
 
 	$: {
+		console.log('sources', sources);
 		citations = sources.reduce((acc, source) => {
 			if (Object.keys(source).length === 0) {
 				return acc;
@@ -53,7 +54,7 @@
 				const distance = source.distances?.[index];
 
 				// Within the same citation there could be multiple documents
-				const id = metadata?.source ?? 'N/A';
+				const id = metadata?.source ?? source?.source?.id ?? 'N/A';
 				let _source = source?.source;
 
 				if (metadata?.name) {

+ 1 - 1
src/lib/components/common/FileItemModal.svelte

@@ -87,7 +87,7 @@
 						<div>
 							<Tooltip
 								content={enableFullContent
-									? 'Inject the entire document as context for comprehensive processing, this is recommended for complex queries.'
+									? 'Inject the entire content as context for comprehensive processing, this is recommended for complex queries.'
 									: 'Default to segmented retrieval for focused and relevant content extraction, this is recommended for most cases.'}
 							>
 								<div class="flex items-center gap-1.5 text-xs">