forked from open-webui/open-webui
		
	feat: chat import to backend added
This commit is contained in:
		
							parent
							
								
									cc9e299814
								
							
						
					
					
						commit
						a696698ac8
					
				
					 6 changed files with 248 additions and 176 deletions
				
			
		|  | @ -66,8 +66,6 @@ def proxy(path): | |||
|     headers.pop("Origin", None) | ||||
|     headers.pop("Referer", None) | ||||
| 
 | ||||
|     print(headers) | ||||
| 
 | ||||
|     try: | ||||
|         # Make a request to the target server | ||||
|         r = requests.request( | ||||
|  |  | |||
|  | @ -29,14 +29,25 @@ | |||
| 	$: if (messages && messages.length > 0 && (messages.at(-1).done ?? false)) { | ||||
| 		(async () => { | ||||
| 			await tick(); | ||||
| 
 | ||||
| 			[...document.querySelectorAll('*')].forEach((node) => { | ||||
| 				if (node._tippy) { | ||||
| 					node._tippy.destroy(); | ||||
| 				} | ||||
| 			}); | ||||
| 
 | ||||
| 			console.log('rendering message'); | ||||
| 
 | ||||
| 			renderLatex(); | ||||
| 			hljs.highlightAll(); | ||||
| 			createCopyCodeBlockButton(); | ||||
| 
 | ||||
| 			for (const message of messages) { | ||||
| 				if (message.info) { | ||||
| 					console.log(message); | ||||
| 
 | ||||
| 					tippy(`#info-${message.id}`, { | ||||
| 						content: `<span class="text-xs">token/s: ${ | ||||
| 						content: `<span class="text-xs" id="tooltip-${message.id}">token/s: ${ | ||||
| 							`${ | ||||
| 								Math.round( | ||||
| 									((message.info.eval_count ?? 0) / (message.info.eval_duration / 1000000000)) * 100 | ||||
|  |  | |||
|  | @ -9,11 +9,12 @@ | |||
| 	} from '$lib/constants'; | ||||
| 	import toast from 'svelte-french-toast'; | ||||
| 	import { onMount } from 'svelte'; | ||||
| 	import { config, models, settings, user } from '$lib/stores'; | ||||
| 	import { config, models, settings, user, chats } from '$lib/stores'; | ||||
| 	import { splitStream, getGravatarURL } from '$lib/utils'; | ||||
| 	import Advanced from './Settings/Advanced.svelte'; | ||||
| 	import { stringify } from 'postcss'; | ||||
| 	import { getOllamaVersion } from '$lib/apis/ollama'; | ||||
| 	import { createNewChat, getChatList } from '$lib/apis/chats'; | ||||
| 
 | ||||
| 	export let show = false; | ||||
| 
 | ||||
|  | @ -75,6 +76,37 @@ | |||
| 	let OPENAI_API_KEY = ''; | ||||
| 	let OPENAI_API_BASE_URL = ''; | ||||
| 
 | ||||
| 	// Chats | ||||
| 
 | ||||
| 	let importFiles; | ||||
| 	let showDeleteHistoryConfirm = false; | ||||
| 
 | ||||
| 	const importChats = async (_chats) => { | ||||
| 		for (const chat of _chats) { | ||||
| 			console.log(chat); | ||||
| 			await createNewChat(localStorage.token, chat); | ||||
| 		} | ||||
| 
 | ||||
| 		await chats.set(await getChatList(localStorage.token)); | ||||
| 	}; | ||||
| 
 | ||||
| 	const exportChats = async () => { | ||||
| 		console.log('TODO: export all chats'); | ||||
| 	}; | ||||
| 
 | ||||
| 	$: if (importFiles) { | ||||
| 		console.log(importFiles); | ||||
| 
 | ||||
| 		let reader = new FileReader(); | ||||
| 		reader.onload = (event) => { | ||||
| 			let chats = JSON.parse(event.target.result); | ||||
| 			console.log(chats); | ||||
| 			importChats(chats); | ||||
| 		}; | ||||
| 
 | ||||
| 		reader.readAsText(importFiles[0]); | ||||
| 	} | ||||
| 
 | ||||
| 	// Auth | ||||
| 	let authEnabled = false; | ||||
| 	let authType = 'Basic'; | ||||
|  | @ -752,6 +784,32 @@ | |||
| 					<div class=" self-center">Add-ons</div> | ||||
| 				</button> | ||||
| 
 | ||||
| 				<button | ||||
| 					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab === | ||||
| 					'chats' | ||||
| 						? 'bg-gray-200 dark:bg-gray-700' | ||||
| 						: ' hover:bg-gray-300 dark:hover:bg-gray-800'}" | ||||
| 					on:click={() => { | ||||
| 						selectedTab = 'chats'; | ||||
| 					}} | ||||
| 				> | ||||
| 					<div class=" self-center mr-2"> | ||||
| 						<svg | ||||
| 							xmlns="http://www.w3.org/2000/svg" | ||||
| 							viewBox="0 0 16 16" | ||||
| 							fill="currentColor" | ||||
| 							class="w-4 h-4" | ||||
| 						> | ||||
| 							<path | ||||
| 								fill-rule="evenodd" | ||||
| 								d="M8 2C4.262 2 1 4.57 1 8c0 1.86.98 3.486 2.455 4.566a3.472 3.472 0 0 1-.469 1.26.75.75 0 0 0 .713 1.14 6.961 6.961 0 0 0 3.06-1.06c.403.062.818.094 1.241.094 3.738 0 7-2.57 7-6s-3.262-6-7-6ZM5 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2Zm7-1a1 1 0 1 1-2 0 1 1 0 0 1 2 0ZM8 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z" | ||||
| 								clip-rule="evenodd" | ||||
| 							/> | ||||
| 						</svg> | ||||
| 					</div> | ||||
| 					<div class=" self-center">Chats</div> | ||||
| 				</button> | ||||
| 
 | ||||
| 				{#if !$config || ($config && !$config.auth)} | ||||
| 					<button | ||||
| 						class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab === | ||||
|  | @ -1483,6 +1541,150 @@ | |||
| 							</button> | ||||
| 						</div> | ||||
| 					</form> | ||||
| 				{:else if selectedTab === 'chats'} | ||||
| 					<div class="flex flex-col h-full justify-between space-y-3 text-sm"> | ||||
| 						<div class="flex flex-col"> | ||||
| 							<input | ||||
| 								id="chat-import-input" | ||||
| 								bind:files={importFiles} | ||||
| 								type="file" | ||||
| 								accept=".json" | ||||
| 								hidden | ||||
| 							/> | ||||
| 							<button | ||||
| 								class=" flex rounded-md py-2 px-3.5 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition" | ||||
| 								on:click={() => { | ||||
| 									document.getElementById('chat-import-input').click(); | ||||
| 								}} | ||||
| 							> | ||||
| 								<div class=" self-center mr-3"> | ||||
| 									<svg | ||||
| 										xmlns="http://www.w3.org/2000/svg" | ||||
| 										viewBox="0 0 16 16" | ||||
| 										fill="currentColor" | ||||
| 										class="w-4 h-4" | ||||
| 									> | ||||
| 										<path | ||||
| 											fill-rule="evenodd" | ||||
| 											d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm4 9.5a.75.75 0 0 1-.75-.75V8.06l-.72.72a.75.75 0 0 1-1.06-1.06l2-2a.75.75 0 0 1 1.06 0l2 2a.75.75 0 1 1-1.06 1.06l-.72-.72v2.69a.75.75 0 0 1-.75.75Z" | ||||
| 											clip-rule="evenodd" | ||||
| 										/> | ||||
| 									</svg> | ||||
| 								</div> | ||||
| 								<div class=" self-center text-sm font-medium">Import Chats</div> | ||||
| 							</button> | ||||
| 							<button | ||||
| 								class=" flex rounded-md py-2 px-3.5 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition" | ||||
| 								on:click={() => { | ||||
| 									exportChats(); | ||||
| 								}} | ||||
| 							> | ||||
| 								<div class=" self-center mr-3"> | ||||
| 									<svg | ||||
| 										xmlns="http://www.w3.org/2000/svg" | ||||
| 										viewBox="0 0 16 16" | ||||
| 										fill="currentColor" | ||||
| 										class="w-4 h-4" | ||||
| 									> | ||||
| 										<path | ||||
| 											fill-rule="evenodd" | ||||
| 											d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm4 3.5a.75.75 0 0 1 .75.75v2.69l.72-.72a.75.75 0 1 1 1.06 1.06l-2 2a.75.75 0 0 1-1.06 0l-2-2a.75.75 0 0 1 1.06-1.06l.72.72V6.25A.75.75 0 0 1 8 5.5Z" | ||||
| 											clip-rule="evenodd" | ||||
| 										/> | ||||
| 									</svg> | ||||
| 								</div> | ||||
| 								<div class=" self-center text-sm font-medium">Export Chats</div> | ||||
| 							</button> | ||||
| 						</div> | ||||
| 						<!-- {#if showDeleteHistoryConfirm} | ||||
| 							<div | ||||
| 								class="flex justify-between rounded-md items-center py-3 px-3.5 w-full transition" | ||||
| 							> | ||||
| 								<div class="flex items-center"> | ||||
| 									<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 mr-3" | ||||
| 									> | ||||
| 										<path | ||||
| 											stroke-linecap="round" | ||||
| 											stroke-linejoin="round" | ||||
| 											d="M14.74 9l-.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 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" | ||||
| 										/> | ||||
| 									</svg> | ||||
| 									<span>Are you sure?</span> | ||||
| 								</div> | ||||
| 
 | ||||
| 								<div class="flex space-x-1.5 items-center"> | ||||
| 									<button | ||||
| 										class="hover:text-white transition" | ||||
| 										on:click={() => { | ||||
| 											deleteChatHistory(); | ||||
| 											showDeleteHistoryConfirm = false; | ||||
| 										}} | ||||
| 									> | ||||
| 										<svg | ||||
| 											xmlns="http://www.w3.org/2000/svg" | ||||
| 											viewBox="0 0 20 20" | ||||
| 											fill="currentColor" | ||||
| 											class="w-4 h-4" | ||||
| 										> | ||||
| 											<path | ||||
| 												fill-rule="evenodd" | ||||
| 												d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z" | ||||
| 												clip-rule="evenodd" | ||||
| 											/> | ||||
| 										</svg> | ||||
| 									</button> | ||||
| 									<button | ||||
| 										class="hover:text-white transition" | ||||
| 										on:click={() => { | ||||
| 											showDeleteHistoryConfirm = false; | ||||
| 										}} | ||||
| 									> | ||||
| 										<svg | ||||
| 											xmlns="http://www.w3.org/2000/svg" | ||||
| 											viewBox="0 0 20 20" | ||||
| 											fill="currentColor" | ||||
| 											class="w-4 h-4" | ||||
| 										> | ||||
| 											<path | ||||
| 												d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" | ||||
| 											/> | ||||
| 										</svg> | ||||
| 									</button> | ||||
| 								</div> | ||||
| 							</div> | ||||
| 						{:else} | ||||
| 							<button | ||||
| 								class=" flex rounded-md py-3 px-3.5 w-full hover:bg-gray-900 transition" | ||||
| 								on:click={() => { | ||||
| 									showDeleteHistoryConfirm = true; | ||||
| 								}} | ||||
| 							> | ||||
| 								<div class="mr-3"> | ||||
| 									<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" | ||||
| 									> | ||||
| 										<path | ||||
| 											stroke-linecap="round" | ||||
| 											stroke-linejoin="round" | ||||
| 											d="M14.74 9l-.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 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" | ||||
| 										/> | ||||
| 									</svg> | ||||
| 								</div> | ||||
| 								<span>Clear conversations</span> | ||||
| 							</button> | ||||
| 						{/if} --> | ||||
| 					</div> | ||||
| 				{:else if selectedTab === 'auth'} | ||||
| 					<form | ||||
| 						class="flex flex-col h-full justify-between space-y-3 text-sm" | ||||
|  |  | |||
|  | @ -6,27 +6,22 @@ | |||
| 
 | ||||
| 	import { goto, invalidateAll } from '$app/navigation'; | ||||
| 	import { page } from '$app/stores'; | ||||
| 	import { user, db, chats, showSettings, chatId } from '$lib/stores'; | ||||
| 	import { user, chats, showSettings, chatId } from '$lib/stores'; | ||||
| 	import { onMount } from 'svelte'; | ||||
| 	import { deleteChatById, getChatList, updateChatById } from '$lib/apis/chats'; | ||||
| 
 | ||||
| 	let show = false; | ||||
| 	let navElement; | ||||
| 	let importFileInputElement; | ||||
| 	let importFiles; | ||||
| 
 | ||||
| 	let title: string = 'Ollama Web UI'; | ||||
| 	let search = ''; | ||||
| 
 | ||||
| 	let chatDeleteId = null; | ||||
| 
 | ||||
| 	let chatTitleEditId = null; | ||||
| 	let chatTitle = ''; | ||||
| 
 | ||||
| 	let showDropdown = false; | ||||
| 
 | ||||
| 	let showDeleteHistoryConfirm = false; | ||||
| 
 | ||||
| 	onMount(async () => { | ||||
| 		if (window.innerWidth > 1280) { | ||||
| 			show = true; | ||||
|  | @ -57,29 +52,12 @@ | |||
| 
 | ||||
| 	// const deleteChatHistory = async () => { | ||||
| 	// 	await $db.deleteAllChat(); | ||||
| 
 | ||||
| 	// const tx = this.db.transaction('chats', 'readwrite'); | ||||
| 	// 			await Promise.all([tx.store.clear(), tx.done]); | ||||
| 
 | ||||
| 	// 			await chats.set(await this.getChats()); | ||||
| 	// }; | ||||
| 
 | ||||
| 	// const importChats = async (chatHistory) => { | ||||
| 	// 	await $db.importChats(chatHistory); | ||||
| 	// }; | ||||
| 
 | ||||
| 	// const exportChats = async () => { | ||||
| 	// 	let blob = new Blob([JSON.stringify(await $db.exportChats())], { type: 'application/json' }); | ||||
| 	// 	saveAs(blob, `chat-export-${Date.now()}.json`); | ||||
| 	// }; | ||||
| 
 | ||||
| 	// $: if (importFiles) { | ||||
| 	// 	console.log(importFiles); | ||||
| 
 | ||||
| 	// 	let reader = new FileReader(); | ||||
| 	// 	reader.onload = (event) => { | ||||
| 	// 		let chats = JSON.parse(event.target.result); | ||||
| 	// 		console.log(chats); | ||||
| 	// 		importChats(chats); | ||||
| 	// 	}; | ||||
| 
 | ||||
| 	// 	reader.readAsText(importFiles[0]); | ||||
| 	// } | ||||
| </script> | ||||
| 
 | ||||
| <div | ||||
|  | @ -405,145 +383,6 @@ | |||
| 			<hr class=" border-gray-900 mb-1 w-full" /> | ||||
| 
 | ||||
| 			<div class="flex flex-col"> | ||||
| 				<!-- <div class="flex"> | ||||
| 					<input bind:this={importFileInputElement} bind:files={importFiles} type="file" hidden /> | ||||
| 					<button | ||||
| 						class=" flex rounded-md py-3 px-3.5 w-full hover:bg-gray-900 transition" | ||||
| 						on:click={() => { | ||||
| 							importFileInputElement.click(); | ||||
| 							// importChats(); | ||||
| 						}} | ||||
| 					> | ||||
| 						<div class=" self-center mr-3"> | ||||
| 							<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" | ||||
| 							> | ||||
| 								<path | ||||
| 									stroke-linecap="round" | ||||
| 									stroke-linejoin="round" | ||||
| 									d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m6.75 12l-3-3m0 0l-3 3m3-3v6m-1.5-15H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" | ||||
| 								/> | ||||
| 							</svg> | ||||
| 						</div> | ||||
| 						<div class=" self-center">Import</div> | ||||
| 					</button> | ||||
| 					<button | ||||
| 						class=" flex rounded-md py-3 px-3.5 w-full hover:bg-gray-900 transition" | ||||
| 						on:click={() => { | ||||
| 							exportChats(); | ||||
| 						}} | ||||
| 					> | ||||
| 						<div class=" self-center mr-3"> | ||||
| 							<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" | ||||
| 							> | ||||
| 								<path | ||||
| 									stroke-linecap="round" | ||||
| 									stroke-linejoin="round" | ||||
| 									d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m.75 12l3 3m0 0l3-3m-3 3v-6m-1.5-9H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" | ||||
| 								/> | ||||
| 							</svg> | ||||
| 						</div> | ||||
| 						<div class=" self-center">Export</div> | ||||
| 					</button> | ||||
| 				</div> | ||||
| 				{#if showDeleteHistoryConfirm} | ||||
| 					<div class="flex justify-between rounded-md items-center py-3 px-3.5 w-full transition"> | ||||
| 						<div class="flex items-center"> | ||||
| 							<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 mr-3" | ||||
| 							> | ||||
| 								<path | ||||
| 									stroke-linecap="round" | ||||
| 									stroke-linejoin="round" | ||||
| 									d="M14.74 9l-.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 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" | ||||
| 								/> | ||||
| 							</svg> | ||||
| 							<span>Are you sure?</span> | ||||
| 						</div> | ||||
| 
 | ||||
| 						<div class="flex space-x-1.5 items-center"> | ||||
| 							<button | ||||
| 								class="hover:text-white transition" | ||||
| 								on:click={() => { | ||||
| 									deleteChatHistory(); | ||||
| 									showDeleteHistoryConfirm = false; | ||||
| 								}} | ||||
| 							> | ||||
| 								<svg | ||||
| 									xmlns="http://www.w3.org/2000/svg" | ||||
| 									viewBox="0 0 20 20" | ||||
| 									fill="currentColor" | ||||
| 									class="w-4 h-4" | ||||
| 								> | ||||
| 									<path | ||||
| 										fill-rule="evenodd" | ||||
| 										d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z" | ||||
| 										clip-rule="evenodd" | ||||
| 									/> | ||||
| 								</svg> | ||||
| 							</button> | ||||
| 							<button | ||||
| 								class="hover:text-white transition" | ||||
| 								on:click={() => { | ||||
| 									showDeleteHistoryConfirm = false; | ||||
| 								}} | ||||
| 							> | ||||
| 								<svg | ||||
| 									xmlns="http://www.w3.org/2000/svg" | ||||
| 									viewBox="0 0 20 20" | ||||
| 									fill="currentColor" | ||||
| 									class="w-4 h-4" | ||||
| 								> | ||||
| 									<path | ||||
| 										d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" | ||||
| 									/> | ||||
| 								</svg> | ||||
| 							</button> | ||||
| 						</div> | ||||
| 					</div> | ||||
| 				{:else} | ||||
| 					<button | ||||
| 						class=" flex rounded-md py-3 px-3.5 w-full hover:bg-gray-900 transition" | ||||
| 						on:click={() => { | ||||
| 							showDeleteHistoryConfirm = true; | ||||
| 						}} | ||||
| 					> | ||||
| 						<div class="mr-3"> | ||||
| 							<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" | ||||
| 							> | ||||
| 								<path | ||||
| 									stroke-linecap="round" | ||||
| 									stroke-linejoin="round" | ||||
| 									d="M14.74 9l-.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 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" | ||||
| 								/> | ||||
| 							</svg> | ||||
| 						</div> | ||||
| 						<span>Clear conversations</span> | ||||
| 					</button> | ||||
| 				{/if} --> | ||||
| 
 | ||||
| 				{#if $user !== undefined} | ||||
| 					<button | ||||
| 						class=" flex rounded-md py-3 px-3.5 w-full hover:bg-gray-900 transition" | ||||
|  |  | |||
|  | @ -1,8 +1,12 @@ | |||
| <script lang="ts"> | ||||
| 	import toast from 'svelte-french-toast'; | ||||
| 	import { openDB } from 'idb'; | ||||
| 	import { onMount, tick } from 'svelte'; | ||||
| 	import { goto } from '$app/navigation'; | ||||
| 
 | ||||
| 	import fileSaver from 'file-saver'; | ||||
| 	const { saveAs } = fileSaver; | ||||
| 
 | ||||
| 	import { getOllamaModels, getOllamaVersion } from '$lib/apis/ollama'; | ||||
| 	import { getOpenAIModels } from '$lib/apis/openai'; | ||||
| 
 | ||||
|  | @ -65,6 +69,21 @@ | |||
| 		if ($user === undefined) { | ||||
| 			await goto('/auth'); | ||||
| 		} else if (['user', 'admin'].includes($user.role)) { | ||||
| 			const DB = await openDB('Chats', 1); | ||||
| 
 | ||||
| 			if (DB) { | ||||
| 				let chats = await DB.getAllFromIndex('chats', 'timestamp'); | ||||
| 				chats = chats.map((item, idx) => chats[chats.length - 1 - idx]); | ||||
| 
 | ||||
| 				if (chats.length > 0) { | ||||
| 					let blob = new Blob([JSON.stringify(chats)], { type: 'application/json' }); | ||||
| 					saveAs(blob, `chat-export-${Date.now()}.json`); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			console.log(DB); | ||||
| 
 | ||||
| 			console.log(); | ||||
| 			await settings.set(JSON.parse(localStorage.getItem('settings') ?? '{}')); | ||||
| 			await models.set(await getModels()); | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,18 +2,21 @@ | |||
| 	import { v4 as uuidv4 } from 'uuid'; | ||||
| 	import toast from 'svelte-french-toast'; | ||||
| 
 | ||||
| 	import { OLLAMA_API_BASE_URL } from '$lib/constants'; | ||||
| 	import { onMount, tick } from 'svelte'; | ||||
| 	import { convertMessagesToHistory, copyToClipboard, splitStream } from '$lib/utils'; | ||||
| 	import { goto } from '$app/navigation'; | ||||
| 	import { config, models, modelfiles, user, settings, db, chats, chatId } from '$lib/stores'; | ||||
| 	import { page } from '$app/stores'; | ||||
| 
 | ||||
| 	import { models, modelfiles, user, settings, db, chats, chatId } from '$lib/stores'; | ||||
| 	import { OLLAMA_API_BASE_URL } from '$lib/constants'; | ||||
| 
 | ||||
| 	import { generateChatCompletion, generateTitle } from '$lib/apis/ollama'; | ||||
| 	import { copyToClipboard, splitStream } from '$lib/utils'; | ||||
| 
 | ||||
| 	import MessageInput from '$lib/components/chat/MessageInput.svelte'; | ||||
| 	import Messages from '$lib/components/chat/Messages.svelte'; | ||||
| 	import ModelSelector from '$lib/components/chat/ModelSelector.svelte'; | ||||
| 	import Navbar from '$lib/components/layout/Navbar.svelte'; | ||||
| 	import { page } from '$app/stores'; | ||||
| 	import { createNewChat, getChatById, getChatList } from '$lib/apis/chats'; | ||||
| 	import { createNewChat, getChatById, getChatList, updateChatById } from '$lib/apis/chats'; | ||||
| 
 | ||||
| 	let loaded = false; | ||||
| 	let stopResponseFlag = false; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Timothy J. Baek
						Timothy J. Baek