Timothy J. Baek 11 months ago
parent
commit
74a8deb19f

+ 1 - 1
backend/apps/rag/main.py

@@ -655,7 +655,7 @@ def store_web_search(form_data: SearchForm, user=Depends(get_current_user)):
             )
         urls = [result.link for result in web_results]
         loader = get_web_loader(urls)
-        data = loader.aload()
+        data = loader.load()
 
         collection_name = form_data.collection_name
         if collection_name == "":

+ 2 - 0
backend/apps/rag/utils.py

@@ -550,6 +550,8 @@ def search_web(query: str) -> list[SearchResult]:
     Args:
         query (str): The query to search for
     """
+
+    # TODO: add playwright to search the web
     if SEARXNG_QUERY_URL:
         return search_searxng(SEARXNG_QUERY_URL, query)
     elif GOOGLE_PSE_API_KEY and GOOGLE_PSE_ENGINE_ID:

+ 9 - 43
src/lib/apis/openai/index.ts

@@ -431,59 +431,25 @@ export const generateSearchQuery = async (
 					role: 'assistant',
 					content: `You are tasked with generating web search queries. Give me an appropriate query to answer my question for google search. Answer with only the query. Today is ${currentDate}.`
 				},
-				{
-					role: 'user',
-					content: `Previous Questions:
-- Who is the president of France?
-
-Current Question: What about Mexico?`
-				},
-				{
-					role: 'assistant',
-					content: 'President of Mexico'
-				},
-				{
-					role: 'user',
-					content: `Previous questions: 
-- When is the next formula 1 grand prix?
-
-Current Question: Where is it being hosted?`
-				},
-				{
-					role: 'assistant',
-					content: 'location of next formula 1 grand prix'
-				},
-				{
-					role: 'user',
-					content: 'Current Question: What type of printhead does the Epson F2270 DTG printer use?'
-				},
-				{
-					role: 'assistant',
-					content: 'Epson F2270 DTG printer printhead'
-				},
-				{
-					role: 'user',
-					content: 'What were the news yesterday?'
-				},
-				{
-					role: 'assistant',
-					content: `news ${yesterdayDate}`
-				},
 				{
 					role: 'user',
 					content: 'What is the current weather in Paris?'
 				},
 				{
 					role: 'assistant',
-					content: `weather in Paris ${currentDate}`
+					content: `Weather in Paris ${currentDate}`
 				},
 				{
 					role: 'user',
-					content:
-						(previousMessages.length > 0
-							? `Previous Questions:\n${previousMessages.join('\n')}\n\n`
-							: '') + `Current Question: ${prompt}`
+					content: prompt
 				}
+				// {
+				// 	role: 'user',
+				// 	content:
+				// 		(previousMessages.length > 0
+				// 			? `Previous Questions:\n${previousMessages.join('\n')}\n\n`
+				// 			: '') + `Current Question: ${prompt}`
+				// }
 			],
 			stream: false,
 			// Restricting the max tokens to 30 to avoid long search queries

+ 47 - 24
src/lib/components/chat/Chat.svelte

@@ -407,7 +407,7 @@
 					responseMessage.userContext = userContext;
 
 					if (webSearchEnabled) {
-						await getWebSearchResultsAsFiles(model.id, parentId, responseMessageId);
+						await getWebSearchResults(model.id, parentId, responseMessageId);
 					}
 
 					if (model?.owned_by === 'openai') {
@@ -424,47 +424,65 @@
 		await chats.set(await getChatList(localStorage.token));
 	};
 
-	const getWebSearchResultsAsFiles = async (
-		model: string,
-		parentId: string,
-		responseId: string
-	) => {
+	const getWebSearchResults = async (model: string, parentId: string, responseId: string) => {
 		const responseMessage = history.messages[responseId];
-		responseMessage.progress = $i18n.t('Generating search query');
+
+		responseMessage.status = {
+			done: false,
+			action: 'web_search',
+			description: $i18n.t('Generating search query')
+		};
 		messages = messages;
 
 		const searchQuery = await generateChatSearchQuery(model, parentId);
 		if (!searchQuery) {
 			toast.warning($i18n.t('No search query generated'));
-			responseMessage.progress = undefined;
+			responseMessage.status = {
+				...responseMessage.status,
+				done: true,
+				error: true,
+				description: 'No search query generated'
+			};
 			messages = messages;
 			return;
 		}
 
-		responseMessage.progress = $i18n.t("Searching the web for '{{searchQuery}}'", { searchQuery });
+		responseMessage.status = {
+			...responseMessage.status,
+			description: $i18n.t("Searching the web for '{{searchQuery}}'", { searchQuery })
+		};
 		messages = messages;
 
-		const searchDocument = await runWebSearch(localStorage.token, searchQuery);
-		if (searchDocument === undefined) {
+		const results = await runWebSearch(localStorage.token, searchQuery);
+		if (results === undefined) {
 			toast.warning($i18n.t('No search results found'));
-			responseMessage.progress = undefined;
+			responseMessage.status = {
+				...responseMessage.status,
+				done: true,
+				error: true,
+				description: 'No search results found'
+			};
 			messages = messages;
 			return;
 		}
 
-		if (!responseMessage.files) {
+		responseMessage.status = {
+			...responseMessage.status,
+			done: true,
+			description: $i18n.t('Searched {{count}} sites', { count: results.filenames.length })
+		};
+
+		if (responseMessage?.files ?? undefined === undefined) {
 			responseMessage.files = [];
 		}
 
 		responseMessage.files.push({
-			collection_name: searchDocument.collection_name,
+			collection_name: results.collection_name,
 			name: searchQuery,
-			type: 'websearch',
-			upload_status: true,
-			error: '',
-			urls: searchDocument.filenames
+			type: 'web_search_results',
+			urls: results.filenames
 		});
-		responseMessage.progress = undefined;
+
 		messages = messages;
 	};
 
@@ -530,7 +548,9 @@
 		const docs = messages
 			.filter((message) => message?.files ?? null)
 			.map((message) =>
-				message.files.filter((item) => ['doc', 'collection', 'websearch'].includes(item.type))
+				message.files.filter((item) =>
+					['doc', 'collection', 'web_search_results'].includes(item.type)
+				)
 			)
 			.flat(1);
 
@@ -726,7 +746,9 @@
 		const docs = messages
 			.filter((message) => message?.files ?? null)
 			.map((message) =>
-				message.files.filter((item) => ['doc', 'collection', 'websearch'].includes(item.type))
+				message.files.filter((item) =>
+					['doc', 'collection', 'web_search_results'].includes(item.type)
+				)
 			)
 			.flat(1);
 
@@ -962,7 +984,7 @@
 			const model = $models.filter((m) => m.id === responseMessage.model).at(0);
 
 			if (model) {
-				if (model?.external) {
+				if (model?.owned_by === 'openai') {
 					await sendPromptOpenAI(
 						model,
 						history.messages[responseMessage.parentId].content,
@@ -987,7 +1009,7 @@
 			const model = $models.find((model) => model.id === selectedModels[0]);
 
 			const titleModelId =
-				model?.external ?? false
+				model?.owned_by === 'openai' ?? false
 					? $settings?.title?.modelExternal ?? selectedModels[0]
 					: $settings?.title?.model ?? selectedModels[0];
 			const titleModel = $models.find((model) => model.id === titleModelId);
@@ -1015,7 +1037,7 @@
 	const generateChatSearchQuery = async (modelId: string, messageId: string) => {
 		const model = $models.find((model) => model.id === modelId);
 		const taskModelId =
-			model?.external ?? false
+			model?.owned_by === 'openai' ?? false
 				? $settings?.title?.modelExternal ?? modelId
 				: $settings?.title?.model ?? modelId;
 		const taskModel = $models.find((model) => model.id === taskModelId);
@@ -1024,6 +1046,7 @@
 		const previousMessages = messages
 			.filter((message) => message.role === 'user')
 			.map((message) => message.content);
+
 		return await generateSearchQuery(
 			localStorage.token,
 			taskModelId,

+ 0 - 69
src/lib/components/chat/MessageInput.svelte

@@ -975,75 +975,6 @@
 
 							<div class="self-end mb-2 flex space-x-1 mr-1">
 								{#if messages.length == 0 || messages.at(-1).done == true}
-									<!-- {#if $config?.features.enable_web_search ?? false}
-										<Tooltip
-											content={webSearchEnabled
-												? $i18n.t('Web Search Enabled')
-												: $i18n.t('Web Search Disabled')}
-										>
-											{#if webSearchEnabled}
-												<button
-													id="toggle-websearch-button"
-													class=" text-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-850 transition rounded-full p-1.5 mr-0.5 self-center"
-													type="button"
-													on:click={() => {
-														webSearchEnabled = !webSearchEnabled;
-													}}
-												>
-													<svg
-														xmlns="http://www.w3.org/2000/svg"
-														viewBox="0 0 24 24"
-														fill="currentColor"
-														class="w-5 h-5 translate-y-[0.5px]"
-													>
-														<path
-															d="M21.721 12.752a9.711 9.711 0 0 0-.945-5.003 12.754 12.754 0 0 1-4.339 2.708 18.991 18.991 0 0 1-.214 4.772 17.165 17.165 0 0 0 5.498-2.477ZM14.634 15.55a17.324 17.324 0 0 0 .332-4.647c-.952.227-1.945.347-2.966.347-1.021 0-2.014-.12-2.966-.347a17.515 17.515 0 0 0 .332 4.647 17.385 17.385 0 0 0 5.268 0ZM9.772 17.119a18.963 18.963 0 0 0 4.456 0A17.182 17.182 0 0 1 12 21.724a17.18 17.18 0 0 1-2.228-4.605ZM7.777 15.23a18.87 18.87 0 0 1-.214-4.774 12.753 12.753 0 0 1-4.34-2.708 9.711 9.711 0 0 0-.944 5.004 17.165 17.165 0 0 0 5.498 2.477ZM21.356 14.752a9.765 9.765 0 0 1-7.478 6.817 18.64 18.64 0 0 0 1.988-4.718 18.627 18.627 0 0 0 5.49-2.098ZM2.644 14.752c1.682.971 3.53 1.688 5.49 2.099a18.64 18.64 0 0 0 1.988 4.718 9.765 9.765 0 0 1-7.478-6.816ZM13.878 2.43a9.755 9.755 0 0 1 6.116 3.986 11.267 11.267 0 0 1-3.746 2.504 18.63 18.63 0 0 0-2.37-6.49ZM12 2.276a17.152 17.152 0 0 1 2.805 7.121c-.897.23-1.837.353-2.805.353-.968 0-1.908-.122-2.805-.353A17.151 17.151 0 0 1 12 2.276ZM10.122 2.43a18.629 18.629 0 0 0-2.37 6.49 11.266 11.266 0 0 1-3.746-2.504 9.754 9.754 0 0 1 6.116-3.985Z"
-														/>
-													</svg>
-												</button>
-											{:else}
-												<button
-													id="toggle-websearch-button"
-													class=" {webSearchEnabled
-														? 'text-gray-600 dark:text-gray-300'
-														: 'text-gray-300 dark:text-gray-600 disabled'} hover:bg-gray-50 dark:hover:bg-gray-850 transition rounded-full p-1.5 mr-0.5 self-center"
-													type="button"
-													on:click={() => {
-														webSearchEnabled = !webSearchEnabled;
-													}}
-												>
-													{#if webSearchEnabled}
-														<svg
-															xmlns="http://www.w3.org/2000/svg"
-															viewBox="0 0 24 24"
-															fill="currentColor"
-															class="w-5 h-5 translate-y-[0.5px]"
-														>
-															<path
-																d="M21.721 12.752a9.711 9.711 0 0 0-.945-5.003 12.754 12.754 0 0 1-4.339 2.708 18.991 18.991 0 0 1-.214 4.772 17.165 17.165 0 0 0 5.498-2.477ZM14.634 15.55a17.324 17.324 0 0 0 .332-4.647c-.952.227-1.945.347-2.966.347-1.021 0-2.014-.12-2.966-.347a17.515 17.515 0 0 0 .332 4.647 17.385 17.385 0 0 0 5.268 0ZM9.772 17.119a18.963 18.963 0 0 0 4.456 0A17.182 17.182 0 0 1 12 21.724a17.18 17.18 0 0 1-2.228-4.605ZM7.777 15.23a18.87 18.87 0 0 1-.214-4.774 12.753 12.753 0 0 1-4.34-2.708 9.711 9.711 0 0 0-.944 5.004 17.165 17.165 0 0 0 5.498 2.477ZM21.356 14.752a9.765 9.765 0 0 1-7.478 6.817 18.64 18.64 0 0 0 1.988-4.718 18.627 18.627 0 0 0 5.49-2.098ZM2.644 14.752c1.682.971 3.53 1.688 5.49 2.099a18.64 18.64 0 0 0 1.988 4.718 9.765 9.765 0 0 1-7.478-6.816ZM13.878 2.43a9.755 9.755 0 0 1 6.116 3.986 11.267 11.267 0 0 1-3.746 2.504 18.63 18.63 0 0 0-2.37-6.49ZM12 2.276a17.152 17.152 0 0 1 2.805 7.121c-.897.23-1.837.353-2.805.353-.968 0-1.908-.122-2.805-.353A17.151 17.151 0 0 1 12 2.276ZM10.122 2.43a18.629 18.629 0 0 0-2.37 6.49 11.266 11.266 0 0 1-3.746-2.504 9.754 9.754 0 0 1 6.116-3.985Z"
-															/>
-														</svg>
-													{:else}
-														<svg
-															xmlns="http://www.w3.org/2000/svg"
-															fill="none"
-															viewBox="0 0 24 24"
-															stroke-width="1.5"
-															stroke="currentColor"
-															class="w-5 h-5 translate-y-[0.5px]"
-														>
-															<path
-																stroke-linecap="round"
-																stroke-linejoin="round"
-																d="M12 21a9.004 9.004 0 0 0 8.716-6.747M12 21a9.004 9.004 0 0 1-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 0 1 7.843 4.582M12 3a8.997 8.997 0 0 0-7.843 4.582m15.686 0A11.953 11.953 0 0 1 12 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0 1 21 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0 1 12 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 0 1 3 12c0-1.605.42-3.113 1.157-4.418"
-															/>
-														</svg>
-													{/if}
-												</button>
-											{/if}
-										</Tooltip>
-									{/if} -->
-
 									<Tooltip content={$i18n.t('Record voice')}>
 										{#if speechRecognitionEnabled}
 											<button

+ 13 - 18
src/lib/components/chat/MessageInput/InputMenu.svelte

@@ -13,6 +13,7 @@
 	import DocumentArrowUpSolid from '$lib/components/icons/DocumentArrowUpSolid.svelte';
 	import Switch from '$lib/components/common/Switch.svelte';
 	import GlobeAltSolid from '$lib/components/icons/GlobeAltSolid.svelte';
+	import { config } from '$lib/stores';
 
 	const i18n = getContext('i18n');
 
@@ -45,16 +46,20 @@
 			align="start"
 			transition={flyAndScale}
 		>
-			<div class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer rounded-xl">
-				<div class="flex-1 flex items-center gap-2">
-					<GlobeAltSolid />
-					<div class="flex items-center">{$i18n.t('Web Search')}</div>
-				</div>
+			{#if $config?.features?.enable_web_search}
+				<div
+					class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer rounded-xl"
+				>
+					<div class="flex-1 flex items-center gap-2">
+						<GlobeAltSolid />
+						<div class="flex items-center">{$i18n.t('Web Search')}</div>
+					</div>
 
-				<Switch bind:state={webSearchEnabled} />
-			</div>
+					<Switch bind:state={webSearchEnabled} />
+				</div>
 
-			<hr class="border-gray-100 dark:border-gray-800 my-1" />
+				<hr class="border-gray-100 dark:border-gray-800 my-1" />
+			{/if}
 
 			<DropdownMenu.Item
 				class="flex gap-2 items-center px-3 py-2 text-sm  font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800  rounded-xl"
@@ -65,16 +70,6 @@
 				<DocumentArrowUpSolid />
 				<div class="flex items-center">{$i18n.t('Upload Files')}</div>
 			</DropdownMenu.Item>
-
-			<!-- <DropdownMenu.Item
-				class="flex gap-2 items-center px-3 py-2 text-sm  font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800  rounded-xl"
-				on:click={() => {
-					shareHandler();
-				}}
-			>
-				<DocumentArrowUpSolid />
-				<div class="flex items-center">{$i18n.t('Upload Files')}</div>
-			</DropdownMenu.Item> -->
 		</DropdownMenu.Content>
 	</div>
 </Dropdown>

+ 12 - 94
src/lib/components/chat/Messages/ResponseMessage.svelte

@@ -33,6 +33,7 @@
 	import Tooltip from '$lib/components/common/Tooltip.svelte';
 	import RateComment from './RateComment.svelte';
 	import CitationsModal from '$lib/components/chat/Messages/CitationsModal.svelte';
+	import Spinner from '$lib/components/common/Spinner.svelte';
 
 	export let message;
 	export let siblings;
@@ -364,7 +365,7 @@
 				{/if}
 			</Name>
 
-			{#if message.files}
+			{#if (message?.files ?? []).filter((f) => f.type === 'image').length > 0}
 				<div class="my-2.5 w-full flex overflow-x-auto gap-2 flex-wrap">
 					{#each message.files as file}
 						<div>
@@ -380,102 +381,19 @@
 				class="prose chat-{message.role} w-full max-w-full dark:prose-invert prose-headings:my-0 prose-p:m-0 prose-p:-mb-6 prose-pre:my-0 prose-table:my-0 prose-blockquote:my-0 prose-img:my-0 prose-ul:-my-4 prose-ol:-my-4 prose-li:-my-3 prose-ul:-mb-6 prose-ol:-mb-8 prose-ol:p-0 prose-li:-mb-4 whitespace-pre-line"
 			>
 				<div>
-					{#if message.progress || message.files}
-						<div class="my-2.5 w-full flex overflow-x-auto gap-2 flex-wrap">
-							{#if message.progress}
-								<div>
-									<button
-										class="h-16 flex items-center space-x-3 px-2.5 dark:bg-gray-600 rounded-xl border border-gray-200 dark:border-none text-left"
-										type="button"
-									>
-										<div class="p-2.5 bg-red-400 text-white rounded-lg">
-											<svg
-												class=" w-6 h-6 translate-y-[0.5px]"
-												fill="currentColor"
-												viewBox="0 0 24 24"
-												xmlns="http://www.w3.org/2000/svg"
-												><style>
-													.spinner_qM83 {
-														animation: spinner_8HQG 1.05s infinite;
-													}
-													.spinner_oXPr {
-														animation-delay: 0.1s;
-													}
-													.spinner_ZTLf {
-														animation-delay: 0.2s;
-													}
-													@keyframes spinner_8HQG {
-														0%,
-														57.14% {
-															animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1);
-															transform: translate(0);
-														}
-														28.57% {
-															animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33);
-															transform: translateY(-6px);
-														}
-														100% {
-															transform: translate(0);
-														}
-													}
-												</style><circle class="spinner_qM83" cx="4" cy="12" r="2.5" /><circle
-													class="spinner_qM83 spinner_oXPr"
-													cx="12"
-													cy="12"
-													r="2.5"
-												/><circle class="spinner_qM83 spinner_ZTLf" cx="20" cy="12" r="2.5" /></svg
-											>
-										</div>
-
-										<div class="flex flex-col justify-center -space-y-0.5">
-											<div class=" dark:text-gray-100 text-sm font-medium line-clamp-2 text-wrap">
-												{message.progress}
-											</div>
-										</div>
-									</button>
+					{#if message?.status}
+						<div class="flex items-center gap-2 py-1.5">
+							{#if message?.status?.done === false}
+								<div class="">
+									<Spinner className="size-4" />
 								</div>
 							{/if}
-							{#if message.files}
-								{#each message.files as file}
-									<div>
-										{#if file.type === 'websearch'}
-											<button
-												class="h-16 w-[15rem] flex items-center space-x-3 px-2.5 dark:bg-gray-600 rounded-xl border border-gray-200 dark:border-none text-left"
-												type="button"
-											>
-												<div class="p-2.5 bg-red-400 text-white rounded-lg">
-													<svg
-														xmlns="http://www.w3.org/2000/svg"
-														viewBox="0 0 24 24"
-														fill="currentColor"
-														class="w-6 h-6"
-													>
-														<path
-															d="M11.625 16.5a1.875 1.875 0 1 0 0-3.75 1.875 1.875 0 0 0 0 3.75Z"
-														/>
-														<path
-															fill-rule="evenodd"
-															d="M5.625 1.5H9a3.75 3.75 0 0 1 3.75 3.75v1.875c0 1.036.84 1.875 1.875 1.875H16.5a3.75 3.75 0 0 1 3.75 3.75v7.875c0 1.035-.84 1.875-1.875 1.875H5.625a1.875 1.875 0 0 1-1.875-1.875V3.375c0-1.036.84-1.875 1.875-1.875Zm6 16.5c.66 0 1.277-.19 1.797-.518l1.048 1.048a.75.75 0 0 0 1.06-1.06l-1.047-1.048A3.375 3.375 0 1 0 11.625 18Z"
-															clip-rule="evenodd"
-														/>
-														<path
-															d="M14.25 5.25a5.23 5.23 0 0 0-1.279-3.434 9.768 9.768 0 0 1 6.963 6.963A5.23 5.23 0 0 0 16.5 7.5h-1.875a.375.375 0 0 1-.375-.375V5.25Z"
-														/>
-													</svg>
-												</div>
 
-												<div class="flex flex-col justify-center -space-y-0.5">
-													<div class=" dark:text-gray-100 text-sm font-medium line-clamp-1">
-														{file.name}
-													</div>
-
-													<div class=" text-gray-500 text-sm">{$i18n.t('Search Results')}</div>
-												</div>
-											</button>
-										{/if}
-									</div>
-								{/each}
-							{/if}
+							<div class="flex flex-col justify-center -space-y-0.5">
+								<div class=" text-gray-500 dark:text-gray-500 text-base line-clamp-1 text-wrap">
+									{message.status.description}
+								</div>
+							</div>
 						</div>
 					{/if}