forked from open-webui/open-webui
		
	merged conflicts
This commit is contained in:
		
						commit
						0d127ffef1
					
				
					 41 changed files with 1104 additions and 814 deletions
				
			
		| 
						 | 
				
			
			@ -225,33 +225,80 @@
 | 
			
		|||
		}, 100);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// TODO: change delete behaviour
 | 
			
		||||
	// const deleteMessageAndDescendants = async (messageId: string) => {
 | 
			
		||||
	// 	if (history.messages[messageId]) {
 | 
			
		||||
	// 		history.messages[messageId].deleted = true;
 | 
			
		||||
 | 
			
		||||
	// 		for (const childId of history.messages[messageId].childrenIds) {
 | 
			
		||||
	// 			await deleteMessageAndDescendants(childId);
 | 
			
		||||
	// 		}
 | 
			
		||||
	// 	}
 | 
			
		||||
	// };
 | 
			
		||||
 | 
			
		||||
	// const triggerDeleteMessageRecursive = async (messageId: string) => {
 | 
			
		||||
	// 	await deleteMessageAndDescendants(messageId);
 | 
			
		||||
	// 	await updateChatById(localStorage.token, chatId, { history });
 | 
			
		||||
	// 	await chats.set(await getChatList(localStorage.token));
 | 
			
		||||
	// };
 | 
			
		||||
 | 
			
		||||
	const messageDeleteHandler = async (messageId) => {
 | 
			
		||||
		if (history.messages[messageId]) {
 | 
			
		||||
			history.messages[messageId].deleted = true;
 | 
			
		||||
 | 
			
		||||
			for (const childId of history.messages[messageId].childrenIds) {
 | 
			
		||||
				history.messages[childId].deleted = true;
 | 
			
		||||
		const messageToDelete = history.messages[messageId];
 | 
			
		||||
		const messageParentId = messageToDelete.parentId;
 | 
			
		||||
		const messageChildrenIds = messageToDelete.childrenIds ?? [];
 | 
			
		||||
		const hasSibling = messageChildrenIds.some(
 | 
			
		||||
			(childId) => history.messages[childId]?.childrenIds?.length > 0
 | 
			
		||||
		);
 | 
			
		||||
		messageChildrenIds.forEach((childId) => {
 | 
			
		||||
			const child = history.messages[childId];
 | 
			
		||||
			if (child && child.childrenIds) {
 | 
			
		||||
				if (child.childrenIds.length === 0 && !hasSibling) {
 | 
			
		||||
					// if last prompt/response pair
 | 
			
		||||
					history.messages[messageParentId].childrenIds = [];
 | 
			
		||||
					history.currentId = messageParentId;
 | 
			
		||||
				} else {
 | 
			
		||||
					child.childrenIds.forEach((grandChildId) => {
 | 
			
		||||
						if (history.messages[grandChildId]) {
 | 
			
		||||
							history.messages[grandChildId].parentId = messageParentId;
 | 
			
		||||
							history.messages[messageParentId].childrenIds.push(grandChildId);
 | 
			
		||||
						}
 | 
			
		||||
					});
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		await updateChatById(localStorage.token, chatId, { history });
 | 
			
		||||
			// remove response
 | 
			
		||||
			history.messages[messageParentId].childrenIds = history.messages[
 | 
			
		||||
				messageParentId
 | 
			
		||||
			].childrenIds.filter((id) => id !== childId);
 | 
			
		||||
		});
 | 
			
		||||
		// remove prompt
 | 
			
		||||
		history.messages[messageParentId].childrenIds = history.messages[
 | 
			
		||||
			messageParentId
 | 
			
		||||
		].childrenIds.filter((id) => id !== messageId);
 | 
			
		||||
		await updateChatById(localStorage.token, chatId, {
 | 
			
		||||
			messages: messages,
 | 
			
		||||
			history: history
 | 
			
		||||
		});
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// const messageDeleteHandler = async (messageId) => {
 | 
			
		||||
	// 	const message = history.messages[messageId];
 | 
			
		||||
	// 	const parentId = message.parentId;
 | 
			
		||||
	// 	const childrenIds = message.childrenIds ?? [];
 | 
			
		||||
	// 	const grandchildrenIds = [];
 | 
			
		||||
 | 
			
		||||
	// 	// Iterate through childrenIds to find grandchildrenIds
 | 
			
		||||
	// 	for (const childId of childrenIds) {
 | 
			
		||||
	// 		const childMessage = history.messages[childId];
 | 
			
		||||
	// 		const grandChildrenIds = childMessage.childrenIds ?? [];
 | 
			
		||||
 | 
			
		||||
	// 		for (const grandchildId of grandchildrenIds) {
 | 
			
		||||
	// 			const childMessage = history.messages[grandchildId];
 | 
			
		||||
	// 			childMessage.parentId = parentId;
 | 
			
		||||
	// 		}
 | 
			
		||||
	// 		grandchildrenIds.push(...grandChildrenIds);
 | 
			
		||||
	// 	}
 | 
			
		||||
 | 
			
		||||
	// 	history.messages[parentId].childrenIds.push(...grandchildrenIds);
 | 
			
		||||
	// 	history.messages[parentId].childrenIds = history.messages[parentId].childrenIds.filter(
 | 
			
		||||
	// 		(id) => id !== messageId
 | 
			
		||||
	// 	);
 | 
			
		||||
 | 
			
		||||
	// 	// Select latest message
 | 
			
		||||
	// 	let currentMessageId = grandchildrenIds.at(-1);
 | 
			
		||||
	// 	if (currentMessageId) {
 | 
			
		||||
	// 		let messageChildrenIds = history.messages[currentMessageId].childrenIds;
 | 
			
		||||
	// 		while (messageChildrenIds.length !== 0) {
 | 
			
		||||
	// 			currentMessageId = messageChildrenIds.at(-1);
 | 
			
		||||
	// 			messageChildrenIds = history.messages[currentMessageId].childrenIds;
 | 
			
		||||
	// 		}
 | 
			
		||||
	// 		history.currentId = currentMessageId;
 | 
			
		||||
	// 	}
 | 
			
		||||
 | 
			
		||||
	// 	await updateChatById(localStorage.token, chatId, { messages, history });
 | 
			
		||||
	// };
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
{#if messages.length == 0}
 | 
			
		||||
| 
						 | 
				
			
			@ -260,57 +307,55 @@
 | 
			
		|||
	<div class=" pb-10">
 | 
			
		||||
		{#key chatId}
 | 
			
		||||
			{#each messages as message, messageIdx}
 | 
			
		||||
				{#if !message.deleted}
 | 
			
		||||
					<div class=" w-full">
 | 
			
		||||
						<div
 | 
			
		||||
							class="flex flex-col justify-between px-5 mb-3 {$settings?.fullScreenMode ?? null
 | 
			
		||||
								? 'max-w-full'
 | 
			
		||||
								: 'max-w-3xl'} mx-auto rounded-lg group"
 | 
			
		||||
						>
 | 
			
		||||
							{#if message.role === 'user'}
 | 
			
		||||
								<UserMessage
 | 
			
		||||
									on:delete={() => messageDeleteHandler(message.id)}
 | 
			
		||||
									user={$user}
 | 
			
		||||
									{message}
 | 
			
		||||
									isFirstMessage={messageIdx === 0}
 | 
			
		||||
									siblings={message.parentId !== null
 | 
			
		||||
										? history.messages[message.parentId]?.childrenIds ?? []
 | 
			
		||||
										: Object.values(history.messages)
 | 
			
		||||
												.filter((message) => message.parentId === null)
 | 
			
		||||
												.map((message) => message.id) ?? []}
 | 
			
		||||
									{confirmEditMessage}
 | 
			
		||||
									{showPreviousMessage}
 | 
			
		||||
									{showNextMessage}
 | 
			
		||||
									{copyToClipboard}
 | 
			
		||||
								/>
 | 
			
		||||
							{:else}
 | 
			
		||||
								<ResponseMessage
 | 
			
		||||
									{message}
 | 
			
		||||
									modelfiles={selectedModelfiles}
 | 
			
		||||
									siblings={history.messages[message.parentId]?.childrenIds ?? []}
 | 
			
		||||
									isLastMessage={messageIdx + 1 === messages.length}
 | 
			
		||||
									{confirmEditResponseMessage}
 | 
			
		||||
									{showPreviousMessage}
 | 
			
		||||
									{showNextMessage}
 | 
			
		||||
									{rateMessage}
 | 
			
		||||
									{copyToClipboard}
 | 
			
		||||
									{continueGeneration}
 | 
			
		||||
									{regenerateResponse}
 | 
			
		||||
									on:save={async (e) => {
 | 
			
		||||
										console.log('save', e);
 | 
			
		||||
				<div class=" w-full">
 | 
			
		||||
					<div
 | 
			
		||||
						class="flex flex-col justify-between px-5 mb-3 {$settings?.fullScreenMode ?? null
 | 
			
		||||
							? 'max-w-full'
 | 
			
		||||
							: 'max-w-3xl'} mx-auto rounded-lg group"
 | 
			
		||||
					>
 | 
			
		||||
						{#if message.role === 'user'}
 | 
			
		||||
							<UserMessage
 | 
			
		||||
								on:delete={() => messageDeleteHandler(message.id)}
 | 
			
		||||
								user={$user}
 | 
			
		||||
								{message}
 | 
			
		||||
								isFirstMessage={messageIdx === 0}
 | 
			
		||||
								siblings={message.parentId !== null
 | 
			
		||||
									? history.messages[message.parentId]?.childrenIds ?? []
 | 
			
		||||
									: Object.values(history.messages)
 | 
			
		||||
											.filter((message) => message.parentId === null)
 | 
			
		||||
											.map((message) => message.id) ?? []}
 | 
			
		||||
								{confirmEditMessage}
 | 
			
		||||
								{showPreviousMessage}
 | 
			
		||||
								{showNextMessage}
 | 
			
		||||
								{copyToClipboard}
 | 
			
		||||
							/>
 | 
			
		||||
						{:else}
 | 
			
		||||
							<ResponseMessage
 | 
			
		||||
								{message}
 | 
			
		||||
								modelfiles={selectedModelfiles}
 | 
			
		||||
								siblings={history.messages[message.parentId]?.childrenIds ?? []}
 | 
			
		||||
								isLastMessage={messageIdx + 1 === messages.length}
 | 
			
		||||
								{confirmEditResponseMessage}
 | 
			
		||||
								{showPreviousMessage}
 | 
			
		||||
								{showNextMessage}
 | 
			
		||||
								{rateMessage}
 | 
			
		||||
								{copyToClipboard}
 | 
			
		||||
								{continueGeneration}
 | 
			
		||||
								{regenerateResponse}
 | 
			
		||||
								on:save={async (e) => {
 | 
			
		||||
									console.log('save', e);
 | 
			
		||||
 | 
			
		||||
										const message = e.detail;
 | 
			
		||||
										history.messages[message.id] = message;
 | 
			
		||||
										await updateChatById(localStorage.token, chatId, {
 | 
			
		||||
											messages: messages,
 | 
			
		||||
											history: history
 | 
			
		||||
										});
 | 
			
		||||
									}}
 | 
			
		||||
								/>
 | 
			
		||||
							{/if}
 | 
			
		||||
						</div>
 | 
			
		||||
									const message = e.detail;
 | 
			
		||||
									history.messages[message.id] = message;
 | 
			
		||||
									await updateChatById(localStorage.token, chatId, {
 | 
			
		||||
										messages: messages,
 | 
			
		||||
										history: history
 | 
			
		||||
									});
 | 
			
		||||
								}}
 | 
			
		||||
							/>
 | 
			
		||||
						{/if}
 | 
			
		||||
					</div>
 | 
			
		||||
				{/if}
 | 
			
		||||
				</div>
 | 
			
		||||
			{/each}
 | 
			
		||||
 | 
			
		||||
			{#if bottomPadding}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,6 +24,7 @@
 | 
			
		|||
	import CodeBlock from './CodeBlock.svelte';
 | 
			
		||||
	import Image from '$lib/components/common/Image.svelte';
 | 
			
		||||
	import { WEBUI_BASE_URL } from '$lib/constants';
 | 
			
		||||
	import Tooltip from '$lib/components/common/Tooltip.svelte';
 | 
			
		||||
 | 
			
		||||
	export let modelfiles = [];
 | 
			
		||||
	export let message;
 | 
			
		||||
| 
						 | 
				
			
			@ -346,6 +347,7 @@
 | 
			
		|||
									class=" bg-transparent outline-none w-full resize-none"
 | 
			
		||||
									bind:value={editedContent}
 | 
			
		||||
									on:input={(e) => {
 | 
			
		||||
										e.target.style.height = '';
 | 
			
		||||
										e.target.style.height = `${e.target.scrollHeight}px`;
 | 
			
		||||
									}}
 | 
			
		||||
								/>
 | 
			
		||||
| 
						 | 
				
			
			@ -464,189 +466,125 @@
 | 
			
		|||
											</div>
 | 
			
		||||
										{/if}
 | 
			
		||||
 | 
			
		||||
										<button
 | 
			
		||||
											class="{isLastMessage
 | 
			
		||||
												? 'visible'
 | 
			
		||||
												: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition"
 | 
			
		||||
											on:click={() => {
 | 
			
		||||
												editMessageHandler();
 | 
			
		||||
											}}
 | 
			
		||||
										>
 | 
			
		||||
											<svg
 | 
			
		||||
												xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
												fill="none"
 | 
			
		||||
												viewBox="0 0 24 24"
 | 
			
		||||
												stroke-width="1.5"
 | 
			
		||||
												stroke="currentColor"
 | 
			
		||||
												class="w-4 h-4"
 | 
			
		||||
											>
 | 
			
		||||
												<path
 | 
			
		||||
													stroke-linecap="round"
 | 
			
		||||
													stroke-linejoin="round"
 | 
			
		||||
													d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
 | 
			
		||||
												/>
 | 
			
		||||
											</svg>
 | 
			
		||||
										</button>
 | 
			
		||||
 | 
			
		||||
										<button
 | 
			
		||||
											class="{isLastMessage
 | 
			
		||||
												? 'visible'
 | 
			
		||||
												: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition copy-response-button"
 | 
			
		||||
											on:click={() => {
 | 
			
		||||
												copyToClipboard(message.content);
 | 
			
		||||
											}}
 | 
			
		||||
										>
 | 
			
		||||
											<svg
 | 
			
		||||
												xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
												fill="none"
 | 
			
		||||
												viewBox="0 0 24 24"
 | 
			
		||||
												stroke-width="1.5"
 | 
			
		||||
												stroke="currentColor"
 | 
			
		||||
												class="w-4 h-4"
 | 
			
		||||
											>
 | 
			
		||||
												<path
 | 
			
		||||
													stroke-linecap="round"
 | 
			
		||||
													stroke-linejoin="round"
 | 
			
		||||
													d="M15.666 3.888A2.25 2.25 0 0013.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 01-.75.75H9a.75.75 0 01-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 01-2.25 2.25H6.75A2.25 2.25 0 014.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 011.927-.184"
 | 
			
		||||
												/>
 | 
			
		||||
											</svg>
 | 
			
		||||
										</button>
 | 
			
		||||
 | 
			
		||||
										<button
 | 
			
		||||
											class="{isLastMessage
 | 
			
		||||
												? 'visible'
 | 
			
		||||
												: 'invisible group-hover:visible'} p-1 rounded {message.rating === 1
 | 
			
		||||
												? 'bg-gray-100 dark:bg-gray-800'
 | 
			
		||||
												: ''} dark:hover:text-white hover:text-black transition"
 | 
			
		||||
											on:click={() => {
 | 
			
		||||
												rateMessage(message.id, 1);
 | 
			
		||||
											}}
 | 
			
		||||
										>
 | 
			
		||||
											<svg
 | 
			
		||||
												stroke="currentColor"
 | 
			
		||||
												fill="none"
 | 
			
		||||
												stroke-width="2"
 | 
			
		||||
												viewBox="0 0 24 24"
 | 
			
		||||
												stroke-linecap="round"
 | 
			
		||||
												stroke-linejoin="round"
 | 
			
		||||
												class="w-4 h-4"
 | 
			
		||||
												xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
												><path
 | 
			
		||||
													d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"
 | 
			
		||||
												/></svg
 | 
			
		||||
											>
 | 
			
		||||
										</button>
 | 
			
		||||
										<button
 | 
			
		||||
											class="{isLastMessage
 | 
			
		||||
												? 'visible'
 | 
			
		||||
												: 'invisible group-hover:visible'} p-1 rounded {message.rating === -1
 | 
			
		||||
												? 'bg-gray-100 dark:bg-gray-800'
 | 
			
		||||
												: ''} dark:hover:text-white hover:text-black transition"
 | 
			
		||||
											on:click={() => {
 | 
			
		||||
												rateMessage(message.id, -1);
 | 
			
		||||
											}}
 | 
			
		||||
										>
 | 
			
		||||
											<svg
 | 
			
		||||
												stroke="currentColor"
 | 
			
		||||
												fill="none"
 | 
			
		||||
												stroke-width="2"
 | 
			
		||||
												viewBox="0 0 24 24"
 | 
			
		||||
												stroke-linecap="round"
 | 
			
		||||
												stroke-linejoin="round"
 | 
			
		||||
												class="w-4 h-4"
 | 
			
		||||
												xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
												><path
 | 
			
		||||
													d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"
 | 
			
		||||
												/></svg
 | 
			
		||||
											>
 | 
			
		||||
										</button>
 | 
			
		||||
 | 
			
		||||
										<button
 | 
			
		||||
											id="speak-button-{message.id}"
 | 
			
		||||
											class="{isLastMessage
 | 
			
		||||
												? 'visible'
 | 
			
		||||
												: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition"
 | 
			
		||||
											on:click={() => {
 | 
			
		||||
												if (!loadingSpeech) {
 | 
			
		||||
													toggleSpeakMessage(message);
 | 
			
		||||
												}
 | 
			
		||||
											}}
 | 
			
		||||
										>
 | 
			
		||||
											{#if loadingSpeech}
 | 
			
		||||
												<svg
 | 
			
		||||
													class=" w-4 h-4"
 | 
			
		||||
													fill="currentColor"
 | 
			
		||||
													viewBox="0 0 24 24"
 | 
			
		||||
													xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
													><style>
 | 
			
		||||
														.spinner_S1WN {
 | 
			
		||||
															animation: spinner_MGfb 0.8s linear infinite;
 | 
			
		||||
															animation-delay: -0.8s;
 | 
			
		||||
														}
 | 
			
		||||
														.spinner_Km9P {
 | 
			
		||||
															animation-delay: -0.65s;
 | 
			
		||||
														}
 | 
			
		||||
														.spinner_JApP {
 | 
			
		||||
															animation-delay: -0.5s;
 | 
			
		||||
														}
 | 
			
		||||
														@keyframes spinner_MGfb {
 | 
			
		||||
															93.75%,
 | 
			
		||||
															100% {
 | 
			
		||||
																opacity: 0.2;
 | 
			
		||||
															}
 | 
			
		||||
														}
 | 
			
		||||
													</style><circle class="spinner_S1WN" cx="4" cy="12" r="3" /><circle
 | 
			
		||||
														class="spinner_S1WN spinner_Km9P"
 | 
			
		||||
														cx="12"
 | 
			
		||||
														cy="12"
 | 
			
		||||
														r="3"
 | 
			
		||||
													/><circle class="spinner_S1WN spinner_JApP" cx="20" cy="12" r="3" /></svg
 | 
			
		||||
												>
 | 
			
		||||
											{:else if speaking}
 | 
			
		||||
												<svg
 | 
			
		||||
													xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
													fill="none"
 | 
			
		||||
													viewBox="0 0 24 24"
 | 
			
		||||
													stroke-width="1.5"
 | 
			
		||||
													stroke="currentColor"
 | 
			
		||||
													class="w-4 h-4"
 | 
			
		||||
												>
 | 
			
		||||
													<path
 | 
			
		||||
														stroke-linecap="round"
 | 
			
		||||
														stroke-linejoin="round"
 | 
			
		||||
														d="M17.25 9.75 19.5 12m0 0 2.25 2.25M19.5 12l2.25-2.25M19.5 12l-2.25 2.25m-10.5-6 4.72-4.72a.75.75 0 0 1 1.28.53v15.88a.75.75 0 0 1-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.009 9.009 0 0 1 2.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25H6.75Z"
 | 
			
		||||
													/>
 | 
			
		||||
												</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-4 h-4"
 | 
			
		||||
												>
 | 
			
		||||
													<path
 | 
			
		||||
														stroke-linecap="round"
 | 
			
		||||
														stroke-linejoin="round"
 | 
			
		||||
														d="M19.114 5.636a9 9 0 010 12.728M16.463 8.288a5.25 5.25 0 010 7.424M6.75 8.25l4.72-4.72a.75.75 0 011.28.53v15.88a.75.75 0 01-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.01 9.01 0 012.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25H6.75z"
 | 
			
		||||
													/>
 | 
			
		||||
												</svg>
 | 
			
		||||
											{/if}
 | 
			
		||||
										</button>
 | 
			
		||||
 | 
			
		||||
										{#if $config.images}
 | 
			
		||||
										<Tooltip content="Edit" placement="bottom">
 | 
			
		||||
											<button
 | 
			
		||||
												class="{isLastMessage
 | 
			
		||||
													? 'visible'
 | 
			
		||||
													: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition"
 | 
			
		||||
												on:click={() => {
 | 
			
		||||
													if (!generatingImage) {
 | 
			
		||||
														generateImage(message);
 | 
			
		||||
													editMessageHandler();
 | 
			
		||||
												}}
 | 
			
		||||
											>
 | 
			
		||||
												<svg
 | 
			
		||||
													xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
													fill="none"
 | 
			
		||||
													viewBox="0 0 24 24"
 | 
			
		||||
													stroke-width="1.5"
 | 
			
		||||
													stroke="currentColor"
 | 
			
		||||
													class="w-4 h-4"
 | 
			
		||||
												>
 | 
			
		||||
													<path
 | 
			
		||||
														stroke-linecap="round"
 | 
			
		||||
														stroke-linejoin="round"
 | 
			
		||||
														d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
 | 
			
		||||
													/>
 | 
			
		||||
												</svg>
 | 
			
		||||
											</button>
 | 
			
		||||
										</Tooltip>
 | 
			
		||||
 | 
			
		||||
										<Tooltip content="Copy" placement="bottom">
 | 
			
		||||
											<button
 | 
			
		||||
												class="{isLastMessage
 | 
			
		||||
													? 'visible'
 | 
			
		||||
													: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition copy-response-button"
 | 
			
		||||
												on:click={() => {
 | 
			
		||||
													copyToClipboard(message.content);
 | 
			
		||||
												}}
 | 
			
		||||
											>
 | 
			
		||||
												<svg
 | 
			
		||||
													xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
													fill="none"
 | 
			
		||||
													viewBox="0 0 24 24"
 | 
			
		||||
													stroke-width="1.5"
 | 
			
		||||
													stroke="currentColor"
 | 
			
		||||
													class="w-4 h-4"
 | 
			
		||||
												>
 | 
			
		||||
													<path
 | 
			
		||||
														stroke-linecap="round"
 | 
			
		||||
														stroke-linejoin="round"
 | 
			
		||||
														d="M15.666 3.888A2.25 2.25 0 0013.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 01-.75.75H9a.75.75 0 01-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 01-2.25 2.25H6.75A2.25 2.25 0 014.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 011.927-.184"
 | 
			
		||||
													/>
 | 
			
		||||
												</svg>
 | 
			
		||||
											</button>
 | 
			
		||||
										</Tooltip>
 | 
			
		||||
 | 
			
		||||
										<Tooltip content="Good Response" placement="bottom">
 | 
			
		||||
											<button
 | 
			
		||||
												class="{isLastMessage
 | 
			
		||||
													? 'visible'
 | 
			
		||||
													: 'invisible group-hover:visible'} p-1 rounded {message.rating === 1
 | 
			
		||||
													? 'bg-gray-100 dark:bg-gray-800'
 | 
			
		||||
													: ''} dark:hover:text-white hover:text-black transition"
 | 
			
		||||
												on:click={() => {
 | 
			
		||||
													rateMessage(message.id, 1);
 | 
			
		||||
												}}
 | 
			
		||||
											>
 | 
			
		||||
												<svg
 | 
			
		||||
													stroke="currentColor"
 | 
			
		||||
													fill="none"
 | 
			
		||||
													stroke-width="2"
 | 
			
		||||
													viewBox="0 0 24 24"
 | 
			
		||||
													stroke-linecap="round"
 | 
			
		||||
													stroke-linejoin="round"
 | 
			
		||||
													class="w-4 h-4"
 | 
			
		||||
													xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
													><path
 | 
			
		||||
														d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"
 | 
			
		||||
													/></svg
 | 
			
		||||
												>
 | 
			
		||||
											</button>
 | 
			
		||||
										</Tooltip>
 | 
			
		||||
 | 
			
		||||
										<Tooltip content="Bad Response" placement="bottom">
 | 
			
		||||
											<button
 | 
			
		||||
												class="{isLastMessage
 | 
			
		||||
													? 'visible'
 | 
			
		||||
													: 'invisible group-hover:visible'} p-1 rounded {message.rating === -1
 | 
			
		||||
													? 'bg-gray-100 dark:bg-gray-800'
 | 
			
		||||
													: ''} dark:hover:text-white hover:text-black transition"
 | 
			
		||||
												on:click={() => {
 | 
			
		||||
													rateMessage(message.id, -1);
 | 
			
		||||
												}}
 | 
			
		||||
											>
 | 
			
		||||
												<svg
 | 
			
		||||
													stroke="currentColor"
 | 
			
		||||
													fill="none"
 | 
			
		||||
													stroke-width="2"
 | 
			
		||||
													viewBox="0 0 24 24"
 | 
			
		||||
													stroke-linecap="round"
 | 
			
		||||
													stroke-linejoin="round"
 | 
			
		||||
													class="w-4 h-4"
 | 
			
		||||
													xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
													><path
 | 
			
		||||
														d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"
 | 
			
		||||
													/></svg
 | 
			
		||||
												>
 | 
			
		||||
											</button>
 | 
			
		||||
										</Tooltip>
 | 
			
		||||
 | 
			
		||||
										<Tooltip content="Read Aloud" placement="bottom">
 | 
			
		||||
											<button
 | 
			
		||||
												id="speak-button-{message.id}"
 | 
			
		||||
												class="{isLastMessage
 | 
			
		||||
													? 'visible'
 | 
			
		||||
													: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition"
 | 
			
		||||
												on:click={() => {
 | 
			
		||||
													if (!loadingSpeech) {
 | 
			
		||||
														toggleSpeakMessage(message);
 | 
			
		||||
													}
 | 
			
		||||
												}}
 | 
			
		||||
											>
 | 
			
		||||
												{#if generatingImage}
 | 
			
		||||
												{#if loadingSpeech}
 | 
			
		||||
													<svg
 | 
			
		||||
														class=" w-4 h-4"
 | 
			
		||||
														fill="currentColor"
 | 
			
		||||
| 
						 | 
				
			
			@ -681,6 +619,21 @@
 | 
			
		|||
															r="3"
 | 
			
		||||
														/></svg
 | 
			
		||||
													>
 | 
			
		||||
												{:else if speaking}
 | 
			
		||||
													<svg
 | 
			
		||||
														xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
														fill="none"
 | 
			
		||||
														viewBox="0 0 24 24"
 | 
			
		||||
														stroke-width="1.5"
 | 
			
		||||
														stroke="currentColor"
 | 
			
		||||
														class="w-4 h-4"
 | 
			
		||||
													>
 | 
			
		||||
														<path
 | 
			
		||||
															stroke-linecap="round"
 | 
			
		||||
															stroke-linejoin="round"
 | 
			
		||||
															d="M17.25 9.75 19.5 12m0 0 2.25 2.25M19.5 12l2.25-2.25M19.5 12l-2.25 2.25m-10.5-6 4.72-4.72a.75.75 0 0 1 1.28.53v15.88a.75.75 0 0 1-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.009 9.009 0 0 1 2.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25H6.75Z"
 | 
			
		||||
														/>
 | 
			
		||||
													</svg>
 | 
			
		||||
												{:else}
 | 
			
		||||
													<svg
 | 
			
		||||
														xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
| 
						 | 
				
			
			@ -693,93 +646,166 @@
 | 
			
		|||
														<path
 | 
			
		||||
															stroke-linecap="round"
 | 
			
		||||
															stroke-linejoin="round"
 | 
			
		||||
															d="m2.25 15.75 5.159-5.159a2.25 2.25 0 0 1 3.182 0l5.159 5.159m-1.5-1.5 1.409-1.409a2.25 2.25 0 0 1 3.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 0 0 1.5-1.5V6a1.5 1.5 0 0 0-1.5-1.5H3.75A1.5 1.5 0 0 0 2.25 6v12a1.5 1.5 0 0 0 1.5 1.5Zm10.5-11.25h.008v.008h-.008V8.25Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z"
 | 
			
		||||
															d="M19.114 5.636a9 9 0 010 12.728M16.463 8.288a5.25 5.25 0 010 7.424M6.75 8.25l4.72-4.72a.75.75 0 011.28.53v15.88a.75.75 0 01-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.01 9.01 0 012.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25H6.75z"
 | 
			
		||||
														/>
 | 
			
		||||
													</svg>
 | 
			
		||||
												{/if}
 | 
			
		||||
											</button>
 | 
			
		||||
										</Tooltip>
 | 
			
		||||
 | 
			
		||||
										{#if $config.images}
 | 
			
		||||
											<Tooltip content="Generate Image" placement="bottom">
 | 
			
		||||
												<button
 | 
			
		||||
													class="{isLastMessage
 | 
			
		||||
														? 'visible'
 | 
			
		||||
														: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition"
 | 
			
		||||
													on:click={() => {
 | 
			
		||||
														if (!generatingImage) {
 | 
			
		||||
															generateImage(message);
 | 
			
		||||
														}
 | 
			
		||||
													}}
 | 
			
		||||
												>
 | 
			
		||||
													{#if generatingImage}
 | 
			
		||||
														<svg
 | 
			
		||||
															class=" w-4 h-4"
 | 
			
		||||
															fill="currentColor"
 | 
			
		||||
															viewBox="0 0 24 24"
 | 
			
		||||
															xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
															><style>
 | 
			
		||||
																.spinner_S1WN {
 | 
			
		||||
																	animation: spinner_MGfb 0.8s linear infinite;
 | 
			
		||||
																	animation-delay: -0.8s;
 | 
			
		||||
																}
 | 
			
		||||
																.spinner_Km9P {
 | 
			
		||||
																	animation-delay: -0.65s;
 | 
			
		||||
																}
 | 
			
		||||
																.spinner_JApP {
 | 
			
		||||
																	animation-delay: -0.5s;
 | 
			
		||||
																}
 | 
			
		||||
																@keyframes spinner_MGfb {
 | 
			
		||||
																	93.75%,
 | 
			
		||||
																	100% {
 | 
			
		||||
																		opacity: 0.2;
 | 
			
		||||
																	}
 | 
			
		||||
																}
 | 
			
		||||
															</style><circle class="spinner_S1WN" cx="4" cy="12" r="3" /><circle
 | 
			
		||||
																class="spinner_S1WN spinner_Km9P"
 | 
			
		||||
																cx="12"
 | 
			
		||||
																cy="12"
 | 
			
		||||
																r="3"
 | 
			
		||||
															/><circle
 | 
			
		||||
																class="spinner_S1WN spinner_JApP"
 | 
			
		||||
																cx="20"
 | 
			
		||||
																cy="12"
 | 
			
		||||
																r="3"
 | 
			
		||||
															/></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-4 h-4"
 | 
			
		||||
														>
 | 
			
		||||
															<path
 | 
			
		||||
																stroke-linecap="round"
 | 
			
		||||
																stroke-linejoin="round"
 | 
			
		||||
																d="m2.25 15.75 5.159-5.159a2.25 2.25 0 0 1 3.182 0l5.159 5.159m-1.5-1.5 1.409-1.409a2.25 2.25 0 0 1 3.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 0 0 1.5-1.5V6a1.5 1.5 0 0 0-1.5-1.5H3.75A1.5 1.5 0 0 0 2.25 6v12a1.5 1.5 0 0 0 1.5 1.5Zm10.5-11.25h.008v.008h-.008V8.25Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z"
 | 
			
		||||
															/>
 | 
			
		||||
														</svg>
 | 
			
		||||
													{/if}
 | 
			
		||||
												</button>
 | 
			
		||||
											</Tooltip>
 | 
			
		||||
										{/if}
 | 
			
		||||
 | 
			
		||||
										{#if message.info}
 | 
			
		||||
											<button
 | 
			
		||||
												class=" {isLastMessage
 | 
			
		||||
													? 'visible'
 | 
			
		||||
													: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition whitespace-pre-wrap"
 | 
			
		||||
												on:click={() => {
 | 
			
		||||
													console.log(message);
 | 
			
		||||
												}}
 | 
			
		||||
												id="info-{message.id}"
 | 
			
		||||
											>
 | 
			
		||||
												<svg
 | 
			
		||||
													xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
													fill="none"
 | 
			
		||||
													viewBox="0 0 24 24"
 | 
			
		||||
													stroke-width="1.5"
 | 
			
		||||
													stroke="currentColor"
 | 
			
		||||
													class="w-4 h-4"
 | 
			
		||||
											<Tooltip content="Generation Info" placement="bottom">
 | 
			
		||||
												<button
 | 
			
		||||
													class=" {isLastMessage
 | 
			
		||||
														? 'visible'
 | 
			
		||||
														: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition whitespace-pre-wrap"
 | 
			
		||||
													on:click={() => {
 | 
			
		||||
														console.log(message);
 | 
			
		||||
													}}
 | 
			
		||||
													id="info-{message.id}"
 | 
			
		||||
												>
 | 
			
		||||
													<path
 | 
			
		||||
														stroke-linecap="round"
 | 
			
		||||
														stroke-linejoin="round"
 | 
			
		||||
														d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z"
 | 
			
		||||
													/>
 | 
			
		||||
												</svg>
 | 
			
		||||
											</button>
 | 
			
		||||
													<svg
 | 
			
		||||
														xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
														fill="none"
 | 
			
		||||
														viewBox="0 0 24 24"
 | 
			
		||||
														stroke-width="1.5"
 | 
			
		||||
														stroke="currentColor"
 | 
			
		||||
														class="w-4 h-4"
 | 
			
		||||
													>
 | 
			
		||||
														<path
 | 
			
		||||
															stroke-linecap="round"
 | 
			
		||||
															stroke-linejoin="round"
 | 
			
		||||
															d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z"
 | 
			
		||||
														/>
 | 
			
		||||
													</svg>
 | 
			
		||||
												</button>
 | 
			
		||||
											</Tooltip>
 | 
			
		||||
										{/if}
 | 
			
		||||
 | 
			
		||||
										{#if isLastMessage}
 | 
			
		||||
											<button
 | 
			
		||||
												type="button"
 | 
			
		||||
												class="{isLastMessage
 | 
			
		||||
													? 'visible'
 | 
			
		||||
													: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition regenerate-response-button"
 | 
			
		||||
												on:click={() => {
 | 
			
		||||
													continueGeneration();
 | 
			
		||||
												}}
 | 
			
		||||
											>
 | 
			
		||||
												<svg
 | 
			
		||||
													xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
													fill="none"
 | 
			
		||||
													viewBox="0 0 24 24"
 | 
			
		||||
													stroke-width="1.5"
 | 
			
		||||
													stroke="currentColor"
 | 
			
		||||
													class="w-4 h-4"
 | 
			
		||||
											<Tooltip content="Continue Response" placement="bottom">
 | 
			
		||||
												<button
 | 
			
		||||
													type="button"
 | 
			
		||||
													class="{isLastMessage
 | 
			
		||||
														? 'visible'
 | 
			
		||||
														: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition regenerate-response-button"
 | 
			
		||||
													on:click={() => {
 | 
			
		||||
														continueGeneration();
 | 
			
		||||
													}}
 | 
			
		||||
												>
 | 
			
		||||
													<path
 | 
			
		||||
														stroke-linecap="round"
 | 
			
		||||
														stroke-linejoin="round"
 | 
			
		||||
														d="M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
 | 
			
		||||
													/>
 | 
			
		||||
													<path
 | 
			
		||||
														stroke-linecap="round"
 | 
			
		||||
														stroke-linejoin="round"
 | 
			
		||||
														d="M15.91 11.672a.375.375 0 0 1 0 .656l-5.603 3.113a.375.375 0 0 1-.557-.328V8.887c0-.286.307-.466.557-.327l5.603 3.112Z"
 | 
			
		||||
													/>
 | 
			
		||||
												</svg>
 | 
			
		||||
											</button>
 | 
			
		||||
													<svg
 | 
			
		||||
														xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
														fill="none"
 | 
			
		||||
														viewBox="0 0 24 24"
 | 
			
		||||
														stroke-width="1.5"
 | 
			
		||||
														stroke="currentColor"
 | 
			
		||||
														class="w-4 h-4"
 | 
			
		||||
													>
 | 
			
		||||
														<path
 | 
			
		||||
															stroke-linecap="round"
 | 
			
		||||
															stroke-linejoin="round"
 | 
			
		||||
															d="M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
 | 
			
		||||
														/>
 | 
			
		||||
														<path
 | 
			
		||||
															stroke-linecap="round"
 | 
			
		||||
															stroke-linejoin="round"
 | 
			
		||||
															d="M15.91 11.672a.375.375 0 0 1 0 .656l-5.603 3.113a.375.375 0 0 1-.557-.328V8.887c0-.286.307-.466.557-.327l5.603 3.112Z"
 | 
			
		||||
														/>
 | 
			
		||||
													</svg>
 | 
			
		||||
												</button>
 | 
			
		||||
											</Tooltip>
 | 
			
		||||
 | 
			
		||||
											<button
 | 
			
		||||
												type="button"
 | 
			
		||||
												class="{isLastMessage
 | 
			
		||||
													? 'visible'
 | 
			
		||||
													: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition regenerate-response-button"
 | 
			
		||||
												on:click={regenerateResponse}
 | 
			
		||||
											>
 | 
			
		||||
												<svg
 | 
			
		||||
													xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
													fill="none"
 | 
			
		||||
													viewBox="0 0 24 24"
 | 
			
		||||
													stroke-width="1.5"
 | 
			
		||||
													stroke="currentColor"
 | 
			
		||||
													class="w-4 h-4"
 | 
			
		||||
											<Tooltip content="Regenerate" placement="bottom">
 | 
			
		||||
												<button
 | 
			
		||||
													type="button"
 | 
			
		||||
													class="{isLastMessage
 | 
			
		||||
														? 'visible'
 | 
			
		||||
														: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition regenerate-response-button"
 | 
			
		||||
													on:click={regenerateResponse}
 | 
			
		||||
												>
 | 
			
		||||
													<path
 | 
			
		||||
														stroke-linecap="round"
 | 
			
		||||
														stroke-linejoin="round"
 | 
			
		||||
														d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99"
 | 
			
		||||
													/>
 | 
			
		||||
												</svg>
 | 
			
		||||
											</button>
 | 
			
		||||
													<svg
 | 
			
		||||
														xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
														fill="none"
 | 
			
		||||
														viewBox="0 0 24 24"
 | 
			
		||||
														stroke-width="1.5"
 | 
			
		||||
														stroke="currentColor"
 | 
			
		||||
														class="w-4 h-4"
 | 
			
		||||
													>
 | 
			
		||||
														<path
 | 
			
		||||
															stroke-linecap="round"
 | 
			
		||||
															stroke-linejoin="round"
 | 
			
		||||
															d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99"
 | 
			
		||||
														/>
 | 
			
		||||
													</svg>
 | 
			
		||||
												</button>
 | 
			
		||||
											</Tooltip>
 | 
			
		||||
										{/if}
 | 
			
		||||
									</div>
 | 
			
		||||
								{/if}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,6 +5,7 @@
 | 
			
		|||
	import Name from './Name.svelte';
 | 
			
		||||
	import ProfileImage from './ProfileImage.svelte';
 | 
			
		||||
	import { modelfiles, settings } from '$lib/stores';
 | 
			
		||||
	import Tooltip from '$lib/components/common/Tooltip.svelte';
 | 
			
		||||
 | 
			
		||||
	const i18n = getContext('i18n');
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -171,7 +172,8 @@
 | 
			
		|||
						class=" bg-transparent outline-none w-full resize-none"
 | 
			
		||||
						bind:value={editedContent}
 | 
			
		||||
						on:input={(e) => {
 | 
			
		||||
							messageEditTextAreaElement.style.height = `${messageEditTextAreaElement.scrollHeight}px`;
 | 
			
		||||
							e.target.style.height = '';
 | 
			
		||||
							e.target.style.height = `${e.target.scrollHeight}px`;
 | 
			
		||||
						}}
 | 
			
		||||
					/>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -248,55 +250,11 @@
 | 
			
		|||
							</div>
 | 
			
		||||
						{/if}
 | 
			
		||||
 | 
			
		||||
						<button
 | 
			
		||||
							class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition edit-user-message-button"
 | 
			
		||||
							on:click={() => {
 | 
			
		||||
								editMessageHandler();
 | 
			
		||||
							}}
 | 
			
		||||
						>
 | 
			
		||||
							<svg
 | 
			
		||||
								xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
								fill="none"
 | 
			
		||||
								viewBox="0 0 24 24"
 | 
			
		||||
								stroke-width="1.5"
 | 
			
		||||
								stroke="currentColor"
 | 
			
		||||
								class="w-4 h-4"
 | 
			
		||||
							>
 | 
			
		||||
								<path
 | 
			
		||||
									stroke-linecap="round"
 | 
			
		||||
									stroke-linejoin="round"
 | 
			
		||||
									d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
 | 
			
		||||
								/>
 | 
			
		||||
							</svg>
 | 
			
		||||
						</button>
 | 
			
		||||
 | 
			
		||||
						<button
 | 
			
		||||
							class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition"
 | 
			
		||||
							on:click={() => {
 | 
			
		||||
								copyToClipboard(message.content);
 | 
			
		||||
							}}
 | 
			
		||||
						>
 | 
			
		||||
							<svg
 | 
			
		||||
								xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
								fill="none"
 | 
			
		||||
								viewBox="0 0 24 24"
 | 
			
		||||
								stroke-width="1.5"
 | 
			
		||||
								stroke="currentColor"
 | 
			
		||||
								class="w-4 h-4"
 | 
			
		||||
							>
 | 
			
		||||
								<path
 | 
			
		||||
									stroke-linecap="round"
 | 
			
		||||
									stroke-linejoin="round"
 | 
			
		||||
									d="M15.666 3.888A2.25 2.25 0 0013.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 01-.75.75H9a.75.75 0 01-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 01-2.25 2.25H6.75A2.25 2.25 0 014.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 011.927-.184"
 | 
			
		||||
								/>
 | 
			
		||||
							</svg>
 | 
			
		||||
						</button>
 | 
			
		||||
 | 
			
		||||
						{#if !isFirstMessage}
 | 
			
		||||
						<Tooltip content="Edit" placement="bottom">
 | 
			
		||||
							<button
 | 
			
		||||
								class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition"
 | 
			
		||||
								class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition edit-user-message-button"
 | 
			
		||||
								on:click={() => {
 | 
			
		||||
									deleteMessageHandler();
 | 
			
		||||
									editMessageHandler();
 | 
			
		||||
								}}
 | 
			
		||||
							>
 | 
			
		||||
								<svg
 | 
			
		||||
| 
						 | 
				
			
			@ -310,10 +268,60 @@
 | 
			
		|||
									<path
 | 
			
		||||
										stroke-linecap="round"
 | 
			
		||||
										stroke-linejoin="round"
 | 
			
		||||
										d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
 | 
			
		||||
										d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
 | 
			
		||||
									/>
 | 
			
		||||
								</svg>
 | 
			
		||||
							</button>
 | 
			
		||||
						</Tooltip>
 | 
			
		||||
 | 
			
		||||
						<Tooltip content="Copy" placement="bottom">
 | 
			
		||||
							<button
 | 
			
		||||
								class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition"
 | 
			
		||||
								on:click={() => {
 | 
			
		||||
									copyToClipboard(message.content);
 | 
			
		||||
								}}
 | 
			
		||||
							>
 | 
			
		||||
								<svg
 | 
			
		||||
									xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
									fill="none"
 | 
			
		||||
									viewBox="0 0 24 24"
 | 
			
		||||
									stroke-width="1.5"
 | 
			
		||||
									stroke="currentColor"
 | 
			
		||||
									class="w-4 h-4"
 | 
			
		||||
								>
 | 
			
		||||
									<path
 | 
			
		||||
										stroke-linecap="round"
 | 
			
		||||
										stroke-linejoin="round"
 | 
			
		||||
										d="M15.666 3.888A2.25 2.25 0 0013.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 01-.75.75H9a.75.75 0 01-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 01-2.25 2.25H6.75A2.25 2.25 0 014.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 011.927-.184"
 | 
			
		||||
									/>
 | 
			
		||||
								</svg>
 | 
			
		||||
							</button>
 | 
			
		||||
						</Tooltip>
 | 
			
		||||
 | 
			
		||||
						{#if !isFirstMessage}
 | 
			
		||||
							<Tooltip content="Delete" placement="bottom">
 | 
			
		||||
								<button
 | 
			
		||||
									class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition"
 | 
			
		||||
									on:click={() => {
 | 
			
		||||
										deleteMessageHandler();
 | 
			
		||||
									}}
 | 
			
		||||
								>
 | 
			
		||||
									<svg
 | 
			
		||||
										xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
										fill="none"
 | 
			
		||||
										viewBox="0 0 24 24"
 | 
			
		||||
										stroke-width="1.5"
 | 
			
		||||
										stroke="currentColor"
 | 
			
		||||
										class="w-4 h-4"
 | 
			
		||||
									>
 | 
			
		||||
										<path
 | 
			
		||||
											stroke-linecap="round"
 | 
			
		||||
											stroke-linejoin="round"
 | 
			
		||||
											d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
 | 
			
		||||
										/>
 | 
			
		||||
									</svg>
 | 
			
		||||
								</button>
 | 
			
		||||
							</Tooltip>
 | 
			
		||||
						{/if}
 | 
			
		||||
					</div>
 | 
			
		||||
				</div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -116,7 +116,7 @@
 | 
			
		|||
                                d="M10 2a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 2zM10 15a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 15zM10 7a3 3 0 100 6 3 3 0 000-6zM15.657 5.404a.75.75 0 10-1.06-1.06l-1.061 1.06a.75.75 0 001.06 1.06l1.06-1.06zM6.464 14.596a.75.75 0 10-1.06-1.06l-1.06 1.06a.75.75 0 001.06 1.06l1.06-1.06zM18 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 0118 10zM5 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 015 10zM14.596 15.657a.75.75 0 001.06-1.06l-1.06-1.061a.75.75 0 10-1.06 1.06l1.06 1.06zM5.404 6.464a.75.75 0 001.06-1.06l-1.06-1.06a.75.75 0 10-1.061 1.06l1.06 1.06z"
 | 
			
		||||
                            />
 | 
			
		||||
                        </svg> -->
 | 
			
		||||
						<span class="ml-2 self-center">{$i18n.t(' JSON ')}</span>
 | 
			
		||||
						<span class="ml-2 self-center">{$i18n.t('JSON')}</span>
 | 
			
		||||
					{/if}
 | 
			
		||||
				</button>
 | 
			
		||||
			</div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,12 @@
 | 
			
		|||
	const dispatch = createEventDispatcher();
 | 
			
		||||
 | 
			
		||||
	import { getOllamaUrls, getOllamaVersion, updateOllamaUrls } from '$lib/apis/ollama';
 | 
			
		||||
	import { getOpenAIKey, getOpenAIUrl, updateOpenAIKey, updateOpenAIUrl } from '$lib/apis/openai';
 | 
			
		||||
	import {
 | 
			
		||||
		getOpenAIKeys,
 | 
			
		||||
		getOpenAIUrls,
 | 
			
		||||
		updateOpenAIKeys,
 | 
			
		||||
		updateOpenAIUrls
 | 
			
		||||
	} from '$lib/apis/openai';
 | 
			
		||||
	import { toast } from 'svelte-sonner';
 | 
			
		||||
 | 
			
		||||
	const i18n = getContext('i18n');
 | 
			
		||||
| 
						 | 
				
			
			@ -18,12 +23,14 @@
 | 
			
		|||
	let OPENAI_API_KEY = '';
 | 
			
		||||
	let OPENAI_API_BASE_URL = '';
 | 
			
		||||
 | 
			
		||||
	let OPENAI_API_KEYS = [''];
 | 
			
		||||
	let OPENAI_API_BASE_URLS = [''];
 | 
			
		||||
 | 
			
		||||
	let showOpenAI = false;
 | 
			
		||||
	let showLiteLLM = false;
 | 
			
		||||
 | 
			
		||||
	const updateOpenAIHandler = async () => {
 | 
			
		||||
		OPENAI_API_BASE_URL = await updateOpenAIUrl(localStorage.token, OPENAI_API_BASE_URL);
 | 
			
		||||
		OPENAI_API_KEY = await updateOpenAIKey(localStorage.token, OPENAI_API_KEY);
 | 
			
		||||
		OPENAI_API_BASE_URLS = await updateOpenAIUrls(localStorage.token, OPENAI_API_BASE_URLS);
 | 
			
		||||
		OPENAI_API_KEYS = await updateOpenAIKeys(localStorage.token, OPENAI_API_KEYS);
 | 
			
		||||
 | 
			
		||||
		await models.set(await getModels());
 | 
			
		||||
	};
 | 
			
		||||
| 
						 | 
				
			
			@ -45,8 +52,8 @@
 | 
			
		|||
	onMount(async () => {
 | 
			
		||||
		if ($user.role === 'admin') {
 | 
			
		||||
			OLLAMA_BASE_URLS = await getOllamaUrls(localStorage.token);
 | 
			
		||||
			OPENAI_API_BASE_URL = await getOpenAIUrl(localStorage.token);
 | 
			
		||||
			OPENAI_API_KEY = await getOpenAIKey(localStorage.token);
 | 
			
		||||
			OPENAI_API_BASE_URLS = await getOpenAIUrls(localStorage.token);
 | 
			
		||||
			OPENAI_API_KEYS = await getOpenAIKeys(localStorage.token);
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			@ -73,37 +80,74 @@
 | 
			
		|||
				</div>
 | 
			
		||||
 | 
			
		||||
				{#if showOpenAI}
 | 
			
		||||
					<div>
 | 
			
		||||
						<div class=" mb-2.5 text-sm font-medium">{$i18n.t('API Key')}</div>
 | 
			
		||||
						<div class="flex w-full">
 | 
			
		||||
							<div class="flex-1">
 | 
			
		||||
								<input
 | 
			
		||||
									class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
 | 
			
		||||
									placeholder={$i18n.t('Enter OpenAI API Key')}
 | 
			
		||||
									bind:value={OPENAI_API_KEY}
 | 
			
		||||
									autocomplete="off"
 | 
			
		||||
								/>
 | 
			
		||||
							</div>
 | 
			
		||||
						</div>
 | 
			
		||||
					</div>
 | 
			
		||||
					<div class="flex flex-col gap-1">
 | 
			
		||||
						{#each OPENAI_API_BASE_URLS as url, idx}
 | 
			
		||||
							<div class="flex w-full gap-2">
 | 
			
		||||
								<div class="flex-1">
 | 
			
		||||
									<input
 | 
			
		||||
										class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
 | 
			
		||||
										placeholder={$i18n.t('API Base URL')}
 | 
			
		||||
										bind:value={url}
 | 
			
		||||
										autocomplete="off"
 | 
			
		||||
									/>
 | 
			
		||||
								</div>
 | 
			
		||||
 | 
			
		||||
					<div>
 | 
			
		||||
						<div class=" mb-2.5 text-sm font-medium">{$i18n.t('API Base URL')}</div>
 | 
			
		||||
						<div class="flex w-full">
 | 
			
		||||
							<div class="flex-1">
 | 
			
		||||
								<input
 | 
			
		||||
									class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
 | 
			
		||||
									placeholder="Enter OpenAI API Base URL"
 | 
			
		||||
									bind:value={OPENAI_API_BASE_URL}
 | 
			
		||||
									autocomplete="off"
 | 
			
		||||
								/>
 | 
			
		||||
								<div class="flex-1">
 | 
			
		||||
									<input
 | 
			
		||||
										class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
 | 
			
		||||
										placeholder={$i18n.t('API Key')}
 | 
			
		||||
										bind:value={OPENAI_API_KEYS[idx]}
 | 
			
		||||
										autocomplete="off"
 | 
			
		||||
									/>
 | 
			
		||||
								</div>
 | 
			
		||||
								<div class="self-center flex items-center">
 | 
			
		||||
									{#if idx === 0}
 | 
			
		||||
										<button
 | 
			
		||||
											class="px-1"
 | 
			
		||||
											on:click={() => {
 | 
			
		||||
												OPENAI_API_BASE_URLS = [...OPENAI_API_BASE_URLS, ''];
 | 
			
		||||
												OPENAI_API_KEYS = [...OPENAI_API_KEYS, ''];
 | 
			
		||||
											}}
 | 
			
		||||
											type="button"
 | 
			
		||||
										>
 | 
			
		||||
											<svg
 | 
			
		||||
												xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
												viewBox="0 0 16 16"
 | 
			
		||||
												fill="currentColor"
 | 
			
		||||
												class="w-4 h-4"
 | 
			
		||||
											>
 | 
			
		||||
												<path
 | 
			
		||||
													d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z"
 | 
			
		||||
												/>
 | 
			
		||||
											</svg>
 | 
			
		||||
										</button>
 | 
			
		||||
									{:else}
 | 
			
		||||
										<button
 | 
			
		||||
											class="px-1"
 | 
			
		||||
											on:click={() => {
 | 
			
		||||
												OPENAI_API_BASE_URLS = OPENAI_API_BASE_URLS.filter(
 | 
			
		||||
													(url, urlIdx) => idx !== urlIdx
 | 
			
		||||
												);
 | 
			
		||||
												OPENAI_API_KEYS = OPENAI_API_KEYS.filter((key, keyIdx) => idx !== keyIdx);
 | 
			
		||||
											}}
 | 
			
		||||
											type="button"
 | 
			
		||||
										>
 | 
			
		||||
											<svg
 | 
			
		||||
												xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
												viewBox="0 0 16 16"
 | 
			
		||||
												fill="currentColor"
 | 
			
		||||
												class="w-4 h-4"
 | 
			
		||||
											>
 | 
			
		||||
												<path d="M3.75 7.25a.75.75 0 0 0 0 1.5h8.5a.75.75 0 0 0 0-1.5h-8.5Z" />
 | 
			
		||||
											</svg>
 | 
			
		||||
										</button>
 | 
			
		||||
									{/if}
 | 
			
		||||
								</div>
 | 
			
		||||
							</div>
 | 
			
		||||
						</div>
 | 
			
		||||
						<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
 | 
			
		||||
							WebUI will make requests to <span class=" text-gray-200"
 | 
			
		||||
								>'{OPENAI_API_BASE_URL}/chat'</span
 | 
			
		||||
							>
 | 
			
		||||
						</div>
 | 
			
		||||
							<div class=" mb-1 text-xs text-gray-400 dark:text-gray-500">
 | 
			
		||||
								WebUI will make requests to <span class=" text-gray-200">'{url}/models'</span>
 | 
			
		||||
							</div>
 | 
			
		||||
						{/each}
 | 
			
		||||
					</div>
 | 
			
		||||
				{/if}
 | 
			
		||||
			</div>
 | 
			
		||||
| 
						 | 
				
			
			@ -193,7 +237,7 @@
 | 
			
		|||
			<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
 | 
			
		||||
				{$i18n.t('Trouble accessing Ollama?')}
 | 
			
		||||
				<a
 | 
			
		||||
					class=" text-gray-300 font-medium"
 | 
			
		||||
					class=" text-gray-300 font-medium underline"
 | 
			
		||||
					href="https://github.com/open-webui/open-webui#troubleshooting"
 | 
			
		||||
					target="_blank"
 | 
			
		||||
				>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -149,7 +149,7 @@
 | 
			
		|||
				<br />
 | 
			
		||||
				{$i18n.t('You can help us translate the WebUI.')}
 | 
			
		||||
				<a
 | 
			
		||||
					class=" text-gray-300 font-medium"
 | 
			
		||||
					class=" text-gray-300 font-medium underline"
 | 
			
		||||
					href="https://github.com/open-webui/open-webui/blob/main/docs/CONTRIBUTING.md#-translations-and-internationalization"
 | 
			
		||||
					target="_blank"
 | 
			
		||||
				>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -56,7 +56,7 @@
 | 
			
		|||
	let modelUploadMode = 'file';
 | 
			
		||||
	let modelInputFile = '';
 | 
			
		||||
	let modelFileUrl = '';
 | 
			
		||||
	let modelFileContent = `TEMPLATE """{{ .System }}\nUSER: {{ .Prompt }}\nASSSISTANT: """\nPARAMETER num_ctx 4096\nPARAMETER stop "</s>"\nPARAMETER stop "USER:"\nPARAMETER stop "ASSSISTANT:"`;
 | 
			
		||||
	let modelFileContent = `TEMPLATE """{{ .System }}\nUSER: {{ .Prompt }}\nASSISTANT: """\nPARAMETER num_ctx 4096\nPARAMETER stop "</s>"\nPARAMETER stop "USER:"\nPARAMETER stop "ASSISTANT:"`;
 | 
			
		||||
	let modelFileDigest = '';
 | 
			
		||||
	let uploadProgress = null;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -517,7 +517,7 @@
 | 
			
		|||
									{#if !deleteModelTag}
 | 
			
		||||
										<option value="" disabled selected>Select a model</option>
 | 
			
		||||
									{/if}
 | 
			
		||||
									{#each $models.filter((m) => m.size != null) as model}
 | 
			
		||||
									{#each $models.filter((m) => m.size != null && (selectedOllamaUrlIdx === null ? true : (m?.urls ?? []).includes(selectedOllamaUrlIdx))) as model}
 | 
			
		||||
										<option value={model.name} class="bg-gray-100 dark:bg-gray-700"
 | 
			
		||||
											>{model.name + ' (' + (model.size / 1024 ** 3).toFixed(1) + ' GB)'}</option
 | 
			
		||||
										>
 | 
			
		||||
| 
						 | 
				
			
			@ -599,7 +599,7 @@
 | 
			
		|||
												on:change={() => {
 | 
			
		||||
													console.log(modelInputFile);
 | 
			
		||||
												}}
 | 
			
		||||
												accept=".gguf"
 | 
			
		||||
												accept=".gguf,.safetensors"
 | 
			
		||||
												required
 | 
			
		||||
												hidden
 | 
			
		||||
											/>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue