forked from open-webui/open-webui
		
	feat: multi-user chat history support
This commit is contained in:
		
							parent
							
								
									1274bd986b
								
							
						
					
					
						commit
						0810a2648f
					
				
					 15 changed files with 495 additions and 216 deletions
				
			
		|  | @ -21,77 +21,37 @@ | |||
| 	import Sidebar from '$lib/components/layout/Sidebar.svelte'; | ||||
| 	import toast from 'svelte-french-toast'; | ||||
| 	import { OLLAMA_API_BASE_URL, WEBUI_API_BASE_URL } from '$lib/constants'; | ||||
| 	import { getOllamaModels, getOllamaVersion } from '$lib/apis/ollama'; | ||||
| 	import { getOpenAIModels } from '$lib/apis'; | ||||
| 	import { | ||||
| 		createNewChat, | ||||
| 		deleteChatById, | ||||
| 		getChatById, | ||||
| 		getChatlist, | ||||
| 		updateChatById | ||||
| 	} from '$lib/apis/chats'; | ||||
| 
 | ||||
| 	let requiredOllamaVersion = '0.1.16'; | ||||
| 	let loaded = false; | ||||
| 
 | ||||
| 	const getModels = async () => { | ||||
| 		let models = []; | ||||
| 		const res = await fetch(`${$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL}/tags`, { | ||||
| 			method: 'GET', | ||||
| 			headers: { | ||||
| 				Accept: 'application/json', | ||||
| 				'Content-Type': 'application/json', | ||||
| 				...($settings.authHeader && { Authorization: $settings.authHeader }), | ||||
| 				...($user && { Authorization: `Bearer ${localStorage.token}` }) | ||||
| 			} | ||||
| 		}) | ||||
| 			.then(async (res) => { | ||||
| 				if (!res.ok) throw await res.json(); | ||||
| 				return res.json(); | ||||
| 			}) | ||||
| 			.catch((error) => { | ||||
| 				console.log(error); | ||||
| 				if ('detail' in error) { | ||||
| 					toast.error(error.detail); | ||||
| 				} else { | ||||
| 					toast.error('Server connection failed'); | ||||
| 				} | ||||
| 				return null; | ||||
| 			}); | ||||
| 		console.log(res); | ||||
| 		models.push(...(res?.models ?? [])); | ||||
| 
 | ||||
| 		models.push( | ||||
| 			...(await getOllamaModels($settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL, localStorage.token)) | ||||
| 		); | ||||
| 		// If OpenAI API Key exists | ||||
| 		if ($settings.OPENAI_API_KEY) { | ||||
| 			// Validate OPENAI_API_KEY | ||||
| 			const openAIModels = await getOpenAIModels( | ||||
| 				$settings.OPENAI_API_BASE_URL ?? 'https://api.openai.com/v1', | ||||
| 				$settings.OPENAI_API_KEY | ||||
| 			).catch((error) => { | ||||
| 				console.log(error); | ||||
| 				toast.error(error); | ||||
| 				return null; | ||||
| 			}); | ||||
| 
 | ||||
| 			const API_BASE_URL = $settings.OPENAI_API_BASE_URL ?? 'https://api.openai.com/v1'; | ||||
| 			const openaiModelRes = await fetch(`${API_BASE_URL}/models`, { | ||||
| 				method: 'GET', | ||||
| 				headers: { | ||||
| 					'Content-Type': 'application/json', | ||||
| 					Authorization: `Bearer ${$settings.OPENAI_API_KEY}` | ||||
| 				} | ||||
| 			}) | ||||
| 				.then(async (res) => { | ||||
| 					if (!res.ok) throw await res.json(); | ||||
| 					return res.json(); | ||||
| 				}) | ||||
| 				.catch((error) => { | ||||
| 					console.log(error); | ||||
| 					toast.error(`OpenAI: ${error?.error?.message ?? 'Network Problem'}`); | ||||
| 					return null; | ||||
| 				}); | ||||
| 
 | ||||
| 			const openAIModels = Array.isArray(openaiModelRes) | ||||
| 				? openaiModelRes | ||||
| 				: openaiModelRes?.data ?? null; | ||||
| 
 | ||||
| 			models.push( | ||||
| 				...(openAIModels | ||||
| 					? [ | ||||
| 							{ name: 'hr' }, | ||||
| 							...openAIModels | ||||
| 								.map((model) => ({ name: model.id, external: true })) | ||||
| 								.filter((model) => | ||||
| 									API_BASE_URL.includes('openai') ? model.name.includes('gpt') : true | ||||
| 								) | ||||
| 					  ] | ||||
| 					: []) | ||||
| 			); | ||||
| 			models.push(...(openAIModels ? [{ name: 'hr' }, ...openAIModels] : [])); | ||||
| 		} | ||||
| 
 | ||||
| 		return models; | ||||
| 	}; | ||||
| 
 | ||||
|  | @ -109,135 +69,152 @@ | |||
| 		return { | ||||
| 			db: DB, | ||||
| 			getChatById: async function (id) { | ||||
| 				return await this.db.get('chats', id); | ||||
| 				const chat = await getChatById(localStorage.token, id); | ||||
| 				return chat; | ||||
| 			}, | ||||
| 			getChats: async function () { | ||||
| 				let chats = await this.db.getAllFromIndex('chats', 'timestamp'); | ||||
| 				chats = chats.map((item, idx) => ({ | ||||
| 					title: chats[chats.length - 1 - idx].title, | ||||
| 					id: chats[chats.length - 1 - idx].id | ||||
| 				})); | ||||
| 				const chats = await getChatlist(localStorage.token); | ||||
| 				return chats; | ||||
| 			}, | ||||
| 			exportChats: async function () { | ||||
| 				let chats = await this.db.getAllFromIndex('chats', 'timestamp'); | ||||
| 				chats = chats.map((item, idx) => chats[chats.length - 1 - idx]); | ||||
| 				return chats; | ||||
| 			}, | ||||
| 			addChats: async function (_chats) { | ||||
| 				for (const chat of _chats) { | ||||
| 					console.log(chat); | ||||
| 					await this.addChat(chat); | ||||
| 				} | ||||
| 			createNewChat: async function (_chat) { | ||||
| 				const chat = await createNewChat(localStorage.token, { ..._chat, timestamp: Date.now() }); | ||||
| 				console.log(chat); | ||||
| 				await chats.set(await this.getChats()); | ||||
| 
 | ||||
| 				return chat; | ||||
| 			}, | ||||
| 
 | ||||
| 			addChat: async function (chat) { | ||||
| 				await this.db.put('chats', { | ||||
| 					...chat | ||||
| 				}); | ||||
| 			}, | ||||
| 			createNewChat: async function (chat) { | ||||
| 				await this.addChat({ ...chat, timestamp: Date.now() }); | ||||
| 				await chats.set(await this.getChats()); | ||||
| 			}, | ||||
| 			updateChatById: async function (id, updated) { | ||||
| 				const chat = await this.getChatById(id); | ||||
| 
 | ||||
| 				await this.db.put('chats', { | ||||
| 					...chat, | ||||
| 			updateChatById: async function (id, updated) { | ||||
| 				const chat = await updateChatById(localStorage.token, id, { | ||||
| 					...updated, | ||||
| 					timestamp: Date.now() | ||||
| 				}); | ||||
| 
 | ||||
| 				await chats.set(await this.getChats()); | ||||
| 				return chat; | ||||
| 			}, | ||||
| 			deleteChatById: async function (id) { | ||||
| 				if ($chatId === id) { | ||||
| 					goto('/'); | ||||
| 					await chatId.set(uuidv4()); | ||||
| 				} | ||||
| 				await this.db.delete('chats', id); | ||||
| 
 | ||||
| 				await deleteChatById(localStorage.token, id); | ||||
| 				await chats.set(await this.getChats()); | ||||
| 			}, | ||||
| 
 | ||||
| 			deleteAllChat: async function () { | ||||
| 				const tx = this.db.transaction('chats', 'readwrite'); | ||||
| 				await Promise.all([tx.store.clear(), tx.done]); | ||||
| 
 | ||||
| 				await chats.set(await this.getChats()); | ||||
| 			}, | ||||
| 			exportChats: async function () { | ||||
| 				let chats = await this.db.getAllFromIndex('chats', 'timestamp'); | ||||
| 				chats = chats.map((item, idx) => chats[chats.length - 1 - idx]); | ||||
| 				return chats; | ||||
| 			}, | ||||
| 			importChats: async function (_chats) { | ||||
| 				for (const chat of _chats) { | ||||
| 					console.log(chat); | ||||
| 					await this.addChat(chat); | ||||
| 				} | ||||
| 				await chats.set(await this.getChats()); | ||||
| 			} | ||||
| 		}; | ||||
| 	}; | ||||
| 
 | ||||
| 	const getOllamaVersion = async () => { | ||||
| 		const res = await fetch(`${$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL}/version`, { | ||||
| 			method: 'GET', | ||||
| 			headers: { | ||||
| 				Accept: 'application/json', | ||||
| 				'Content-Type': 'application/json', | ||||
| 				...($settings.authHeader && { Authorization: $settings.authHeader }), | ||||
| 				...($user && { Authorization: `Bearer ${localStorage.token}` }) | ||||
| 			} | ||||
| 		}) | ||||
| 			.then(async (res) => { | ||||
| 				if (!res.ok) throw await res.json(); | ||||
| 				return res.json(); | ||||
| 			}) | ||||
| 			.catch((error) => { | ||||
| 				console.log(error); | ||||
| 				if ('detail' in error) { | ||||
| 					toast.error(error.detail); | ||||
| 				} else { | ||||
| 					toast.error('Server connection failed'); | ||||
| 				} | ||||
| 				return null; | ||||
| 			}); | ||||
| 	const setOllamaVersion = async () => { | ||||
| 		const version = await getOllamaVersion( | ||||
| 			$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL, | ||||
| 			localStorage.token | ||||
| 		).catch((error) => { | ||||
| 			toast.error(error); | ||||
| 			return '0'; | ||||
| 		}); | ||||
| 
 | ||||
| 		console.log(res); | ||||
| 
 | ||||
| 		return res?.version ?? '0'; | ||||
| 	}; | ||||
| 
 | ||||
| 	const setOllamaVersion = async (ollamaVersion) => { | ||||
| 		await info.set({ ...$info, ollama: { version: ollamaVersion } }); | ||||
| 		await info.set({ ...$info, ollama: { version: version } }); | ||||
| 
 | ||||
| 		if ( | ||||
| 			ollamaVersion.localeCompare(requiredOllamaVersion, undefined, { | ||||
| 			version.localeCompare(requiredOllamaVersion, undefined, { | ||||
| 				numeric: true, | ||||
| 				sensitivity: 'case', | ||||
| 				caseFirst: 'upper' | ||||
| 			}) < 0 | ||||
| 		) { | ||||
| 			toast.error(`Ollama Version: ${ollamaVersion}`); | ||||
| 			toast.error(`Ollama Version: ${version}`); | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
| 	onMount(async () => { | ||||
| 		if ($config && $config.auth && $user === undefined) { | ||||
| 		if ($config && $user === undefined) { | ||||
| 			await goto('/auth'); | ||||
| 		} | ||||
| 
 | ||||
| 		await settings.set(JSON.parse(localStorage.getItem('settings') ?? '{}')); | ||||
| 
 | ||||
| 		await models.set(await getModels()); | ||||
| 
 | ||||
| 		await modelfiles.set(JSON.parse(localStorage.getItem('modelfiles') ?? '[]')); | ||||
| 
 | ||||
| 		modelfiles.subscribe(async () => { | ||||
| 			await models.set(await getModels()); | ||||
| 		}); | ||||
| 		modelfiles.subscribe(async () => {}); | ||||
| 
 | ||||
| 		let _db = await getDB(); | ||||
| 		await db.set(_db); | ||||
| 
 | ||||
| 		await setOllamaVersion(await getOllamaVersion()); | ||||
| 		await setOllamaVersion(); | ||||
| 
 | ||||
| 		await tick(); | ||||
| 		loaded = true; | ||||
| 	}); | ||||
| 
 | ||||
| 	let child; | ||||
| </script> | ||||
| 
 | ||||
| {#if loaded} | ||||
| 	<div class="app relative"> | ||||
| 		{#if ($info?.ollama?.version ?? '0').localeCompare( requiredOllamaVersion, undefined, { numeric: true, sensitivity: 'case', caseFirst: 'upper' } ) < 0} | ||||
| 		{#if !['user', 'admin'].includes($user.role)} | ||||
| 			<div class="absolute w-full h-full flex z-50"> | ||||
| 				<div | ||||
| 					class="absolute rounded-xl w-full h-full backdrop-blur bg-gray-900/60 flex justify-center" | ||||
| 				> | ||||
| 					<div class="m-auto pb-44 flex flex-col justify-center"> | ||||
| 						<div class="max-w-md"> | ||||
| 							<div class="text-center dark:text-white text-2xl font-medium z-50"> | ||||
| 								Account Activation Pending<br /> Contact Admin for WebUI Access | ||||
| 							</div> | ||||
| 
 | ||||
| 							<div class=" mt-4 text-center text-sm dark:text-gray-200 w-full"> | ||||
| 								Your account status is currently pending activation. To access the WebUI, please | ||||
| 								reach out to the administrator. Admins can manage user statuses from the Admin | ||||
| 								Panel. | ||||
| 							</div> | ||||
| 
 | ||||
| 							<div class=" mt-6 mx-auto relative group w-fit"> | ||||
| 								<button | ||||
| 									class="relative z-20 flex px-5 py-2 rounded-full bg-gray-100 hover:bg-gray-200 transition font-medium text-sm" | ||||
| 									on:click={async () => { | ||||
| 										location.href = '/'; | ||||
| 									}} | ||||
| 								> | ||||
| 									Check Again | ||||
| 								</button> | ||||
| 
 | ||||
| 								<button | ||||
| 									class="text-xs text-center w-full mt-2 text-gray-400 underline" | ||||
| 									on:click={async () => { | ||||
| 										localStorage.removeItem('token'); | ||||
| 										location.href = '/auth'; | ||||
| 									}}>Sign Out</button | ||||
| 								> | ||||
| 							</div> | ||||
| 						</div> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 		{:else if ($info?.ollama?.version ?? '0').localeCompare( requiredOllamaVersion, undefined, { numeric: true, sensitivity: 'case', caseFirst: 'upper' } ) < 0} | ||||
| 			<div class="absolute w-full h-full flex z-50"> | ||||
| 				<div | ||||
| 					class="absolute rounded-xl w-full h-full backdrop-blur bg-gray-900/60 flex justify-center" | ||||
|  | @ -285,9 +262,7 @@ | |||
| 			class=" text-gray-700 dark:text-gray-100 bg-white dark:bg-gray-800 min-h-screen overflow-auto flex flex-row" | ||||
| 		> | ||||
| 			<Sidebar /> | ||||
| 
 | ||||
| 			<SettingsModal bind:show={$showSettings} /> | ||||
| 
 | ||||
| 			<slot /> | ||||
| 		</div> | ||||
| 	</div> | ||||
|  |  | |||
|  | @ -2,18 +2,18 @@ | |||
| 	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 { splitStream } from '$lib/utils'; | ||||
| 	import { onDestroy, onMount, tick } from 'svelte'; | ||||
| 	import { goto } from '$app/navigation'; | ||||
| 	import { page } from '$app/stores'; | ||||
| 
 | ||||
| 	import { config, models, modelfiles, user, settings, db, chats, chatId } from '$lib/stores'; | ||||
| 	import { OLLAMA_API_BASE_URL } from '$lib/constants'; | ||||
| 	import { 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'; | ||||
| 
 | ||||
| 	let stopResponseFlag = false; | ||||
| 	let autoScroll = true; | ||||
|  | @ -26,10 +26,11 @@ | |||
| 			? $modelfiles.filter((modelfile) => modelfile.tagName === selectedModels[0])[0] | ||||
| 			: null; | ||||
| 
 | ||||
| 	let chat = null; | ||||
| 
 | ||||
| 	let title = ''; | ||||
| 	let prompt = ''; | ||||
| 	let files = []; | ||||
| 
 | ||||
| 	let messages = []; | ||||
| 	let history = { | ||||
| 		messages: {}, | ||||
|  | @ -50,16 +51,8 @@ | |||
| 		messages = []; | ||||
| 	} | ||||
| 
 | ||||
| 	$: if (files) { | ||||
| 		console.log(files); | ||||
| 	} | ||||
| 
 | ||||
| 	onMount(async () => { | ||||
| 		await chatId.set(uuidv4()); | ||||
| 
 | ||||
| 		chatId.subscribe(async () => { | ||||
| 			await initNewChat(); | ||||
| 		}); | ||||
| 		await initNewChat(); | ||||
| 	}); | ||||
| 
 | ||||
| 	////////////////////////// | ||||
|  | @ -67,6 +60,9 @@ | |||
| 	////////////////////////// | ||||
| 
 | ||||
| 	const initNewChat = async () => { | ||||
| 		console.log('initNewChat'); | ||||
| 
 | ||||
| 		await chatId.set(''); | ||||
| 		console.log($chatId); | ||||
| 
 | ||||
| 		autoScroll = true; | ||||
|  | @ -82,7 +78,6 @@ | |||
| 			: $settings.models ?? ['']; | ||||
| 
 | ||||
| 		let _settings = JSON.parse(localStorage.getItem('settings') ?? '{}'); | ||||
| 		console.log(_settings); | ||||
| 		settings.set({ | ||||
| 			..._settings | ||||
| 		}); | ||||
|  | @ -127,14 +122,15 @@ | |||
| 	// Ollama functions | ||||
| 	////////////////////////// | ||||
| 
 | ||||
| 	const sendPrompt = async (userPrompt, parentId, _chatId) => { | ||||
| 	const sendPrompt = async (prompt, parentId) => { | ||||
| 		const _chatId = JSON.parse(JSON.stringify($chatId)); | ||||
| 		await Promise.all( | ||||
| 			selectedModels.map(async (model) => { | ||||
| 				console.log(model); | ||||
| 				if ($models.filter((m) => m.name === model)[0].external) { | ||||
| 					await sendPromptOpenAI(model, userPrompt, parentId, _chatId); | ||||
| 					await sendPromptOpenAI(model, prompt, parentId, _chatId); | ||||
| 				} else { | ||||
| 					await sendPromptOllama(model, userPrompt, parentId, _chatId); | ||||
| 					await sendPromptOllama(model, prompt, parentId, _chatId); | ||||
| 				} | ||||
| 			}) | ||||
| 		); | ||||
|  | @ -297,8 +293,11 @@ | |||
| 				if (autoScroll) { | ||||
| 					window.scrollTo({ top: document.body.scrollHeight }); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 				await $db.updateChatById(_chatId, { | ||||
| 			if ($chatId == _chatId) { | ||||
| 				chat = await $db.updateChatById(_chatId, { | ||||
| 					...chat.chat, | ||||
| 					title: title === '' ? 'New Chat' : title, | ||||
| 					models: selectedModels, | ||||
| 					system: $settings.system ?? undefined, | ||||
|  | @ -481,8 +480,11 @@ | |||
| 						if (autoScroll) { | ||||
| 							window.scrollTo({ top: document.body.scrollHeight }); | ||||
| 						} | ||||
| 					} | ||||
| 
 | ||||
| 						await $db.updateChatById(_chatId, { | ||||
| 					if ($chatId == _chatId) { | ||||
| 						chat = await $db.updateChatById(_chatId, { | ||||
| 							...chat.chat, | ||||
| 							title: title === '' ? 'New Chat' : title, | ||||
| 							models: selectedModels, | ||||
| 							system: $settings.system ?? undefined, | ||||
|  | @ -542,8 +544,7 @@ | |||
| 	}; | ||||
| 
 | ||||
| 	const submitPrompt = async (userPrompt) => { | ||||
| 		const _chatId = JSON.parse(JSON.stringify($chatId)); | ||||
| 		console.log('submitPrompt', _chatId); | ||||
| 		console.log('submitPrompt', $chatId); | ||||
| 
 | ||||
| 		if (selectedModels.includes('')) { | ||||
| 			toast.error('Model not selected'); | ||||
|  | @ -570,9 +571,10 @@ | |||
| 			history.currentId = userMessageId; | ||||
| 
 | ||||
| 			await tick(); | ||||
| 
 | ||||
| 			if (messages.length == 1) { | ||||
| 				await $db.createNewChat({ | ||||
| 					id: _chatId, | ||||
| 				chat = await $db.createNewChat({ | ||||
| 					id: $chatId, | ||||
| 					title: 'New Chat', | ||||
| 					models: selectedModels, | ||||
| 					system: $settings.system ?? undefined, | ||||
|  | @ -588,6 +590,11 @@ | |||
| 					messages: messages, | ||||
| 					history: history | ||||
| 				}); | ||||
| 
 | ||||
| 				console.log(chat); | ||||
| 
 | ||||
| 				await chatId.set(chat.id); | ||||
| 				await tick(); | ||||
| 			} | ||||
| 
 | ||||
| 			prompt = ''; | ||||
|  | @ -597,7 +604,7 @@ | |||
| 				window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' }); | ||||
| 			}, 50); | ||||
| 
 | ||||
| 			await sendPrompt(userPrompt, userMessageId, _chatId); | ||||
| 			await sendPrompt(userPrompt, userMessageId); | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
|  | @ -629,7 +636,6 @@ | |||
| 				method: 'POST', | ||||
| 				headers: { | ||||
| 					'Content-Type': 'text/event-stream', | ||||
| 					...($settings.authHeader && { Authorization: $settings.authHeader }), | ||||
| 					...($user && { Authorization: `Bearer ${localStorage.token}` }) | ||||
| 				}, | ||||
| 				body: JSON.stringify({ | ||||
|  | @ -659,7 +665,7 @@ | |||
| 	}; | ||||
| 
 | ||||
| 	const setChatTitle = async (_chatId, _title) => { | ||||
| 		await $db.updateChatById(_chatId, { title: _title }); | ||||
| 		chat = await $db.updateChatById(_chatId, { ...chat.chat, title: _title }); | ||||
| 		if (_chatId === $chatId) { | ||||
| 			title = _title; | ||||
| 		} | ||||
|  | @ -672,7 +678,7 @@ | |||
| 	}} | ||||
| /> | ||||
| 
 | ||||
| <Navbar {title} shareEnabled={messages.length > 0} /> | ||||
| <Navbar {title} shareEnabled={messages.length > 0} {initNewChat} /> | ||||
| <div class="min-h-screen w-full flex justify-center"> | ||||
| 	<div class=" py-2.5 flex flex-col justify-between w-full"> | ||||
| 		<div class="max-w-2xl mx-auto w-full px-3 md:px-0 mt-10"> | ||||
|  | @ -681,6 +687,7 @@ | |||
| 
 | ||||
| 		<div class=" h-full mt-10 mb-32 w-full flex flex-col"> | ||||
| 			<Messages | ||||
| 				chatId={$chatId} | ||||
| 				{selectedModels} | ||||
| 				{selectedModelfile} | ||||
| 				bind:history | ||||
|  |  | |||
|  | @ -27,6 +27,8 @@ | |||
| 			? $modelfiles.filter((modelfile) => modelfile.tagName === selectedModels[0])[0] | ||||
| 			: null; | ||||
| 
 | ||||
| 	let chat = null; | ||||
| 
 | ||||
| 	let title = ''; | ||||
| 	let prompt = ''; | ||||
| 	let files = []; | ||||
|  | @ -53,10 +55,8 @@ | |||
| 
 | ||||
| 	$: if ($page.params.id) { | ||||
| 		(async () => { | ||||
| 			let chat = await loadChat(); | ||||
| 
 | ||||
| 			await tick(); | ||||
| 			if (chat) { | ||||
| 			if (await loadChat()) { | ||||
| 				await tick(); | ||||
| 				loaded = true; | ||||
| 			} else { | ||||
| 				await goto('/'); | ||||
|  | @ -70,33 +70,38 @@ | |||
| 
 | ||||
| 	const loadChat = async () => { | ||||
| 		await chatId.set($page.params.id); | ||||
| 		const chat = await $db.getChatById($chatId); | ||||
| 		chat = await $db.getChatById($chatId); | ||||
| 
 | ||||
| 		if (chat) { | ||||
| 			console.log(chat); | ||||
| 		const chatContent = chat.chat; | ||||
| 
 | ||||
| 			selectedModels = (chat?.models ?? undefined) !== undefined ? chat.models : [chat.model ?? '']; | ||||
| 		if (chatContent) { | ||||
| 			console.log(chatContent); | ||||
| 
 | ||||
| 			selectedModels = | ||||
| 				(chatContent?.models ?? undefined) !== undefined | ||||
| 					? chatContent.models | ||||
| 					: [chatContent.model ?? '']; | ||||
| 			history = | ||||
| 				(chat?.history ?? undefined) !== undefined | ||||
| 					? chat.history | ||||
| 					: convertMessagesToHistory(chat.messages); | ||||
| 			title = chat.title; | ||||
| 				(chatContent?.history ?? undefined) !== undefined | ||||
| 					? chatContent.history | ||||
| 					: convertMessagesToHistory(chatContent.messages); | ||||
| 			title = chatContent.title; | ||||
| 
 | ||||
| 			let _settings = JSON.parse(localStorage.getItem('settings') ?? '{}'); | ||||
| 			await settings.set({ | ||||
| 				..._settings, | ||||
| 				system: chat.system ?? _settings.system, | ||||
| 				options: chat.options ?? _settings.options | ||||
| 				system: chatContent.system ?? _settings.system, | ||||
| 				options: chatContent.options ?? _settings.options | ||||
| 			}); | ||||
| 			autoScroll = true; | ||||
| 
 | ||||
| 			await tick(); | ||||
| 
 | ||||
| 			if (messages.length > 0) { | ||||
| 				history.messages[messages.at(-1).id].done = true; | ||||
| 			} | ||||
| 			await tick(); | ||||
| 
 | ||||
| 			return chat; | ||||
| 			return true; | ||||
| 		} else { | ||||
| 			return null; | ||||
| 		} | ||||
|  | @ -141,14 +146,15 @@ | |||
| 	// Ollama functions | ||||
| 	////////////////////////// | ||||
| 
 | ||||
| 	const sendPrompt = async (userPrompt, parentId, _chatId) => { | ||||
| 	const sendPrompt = async (prompt, parentId) => { | ||||
| 		const _chatId = JSON.parse(JSON.stringify($chatId)); | ||||
| 		await Promise.all( | ||||
| 			selectedModels.map(async (model) => { | ||||
| 				console.log(model); | ||||
| 				if ($models.filter((m) => m.name === model)[0].external) { | ||||
| 					await sendPromptOpenAI(model, userPrompt, parentId, _chatId); | ||||
| 					await sendPromptOpenAI(model, prompt, parentId, _chatId); | ||||
| 				} else { | ||||
| 					await sendPromptOllama(model, userPrompt, parentId, _chatId); | ||||
| 					await sendPromptOllama(model, prompt, parentId, _chatId); | ||||
| 				} | ||||
| 			}) | ||||
| 		); | ||||
|  | @ -311,8 +317,11 @@ | |||
| 				if (autoScroll) { | ||||
| 					window.scrollTo({ top: document.body.scrollHeight }); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 				await $db.updateChatById(_chatId, { | ||||
| 			if ($chatId == _chatId) { | ||||
| 				chat = await $db.updateChatById(_chatId, { | ||||
| 					...chat.chat, | ||||
| 					title: title === '' ? 'New Chat' : title, | ||||
| 					models: selectedModels, | ||||
| 					system: $settings.system ?? undefined, | ||||
|  | @ -495,8 +504,11 @@ | |||
| 						if (autoScroll) { | ||||
| 							window.scrollTo({ top: document.body.scrollHeight }); | ||||
| 						} | ||||
| 					} | ||||
| 
 | ||||
| 						await $db.updateChatById(_chatId, { | ||||
| 					if ($chatId == _chatId) { | ||||
| 						chat = await $db.updateChatById(_chatId, { | ||||
| 							...chat.chat, | ||||
| 							title: title === '' ? 'New Chat' : title, | ||||
| 							models: selectedModels, | ||||
| 							system: $settings.system ?? undefined, | ||||
|  | @ -556,8 +568,7 @@ | |||
| 	}; | ||||
| 
 | ||||
| 	const submitPrompt = async (userPrompt) => { | ||||
| 		const _chatId = JSON.parse(JSON.stringify($chatId)); | ||||
| 		console.log('submitPrompt', _chatId); | ||||
| 		console.log('submitPrompt', $chatId); | ||||
| 
 | ||||
| 		if (selectedModels.includes('')) { | ||||
| 			toast.error('Model not selected'); | ||||
|  | @ -584,9 +595,10 @@ | |||
| 			history.currentId = userMessageId; | ||||
| 
 | ||||
| 			await tick(); | ||||
| 
 | ||||
| 			if (messages.length == 1) { | ||||
| 				await $db.createNewChat({ | ||||
| 					id: _chatId, | ||||
| 				chat = await $db.createNewChat({ | ||||
| 					id: $chatId, | ||||
| 					title: 'New Chat', | ||||
| 					models: selectedModels, | ||||
| 					system: $settings.system ?? undefined, | ||||
|  | @ -602,6 +614,11 @@ | |||
| 					messages: messages, | ||||
| 					history: history | ||||
| 				}); | ||||
| 
 | ||||
| 				console.log(chat); | ||||
| 
 | ||||
| 				await chatId.set(chat.id); | ||||
| 				await tick(); | ||||
| 			} | ||||
| 
 | ||||
| 			prompt = ''; | ||||
|  | @ -611,7 +628,7 @@ | |||
| 				window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' }); | ||||
| 			}, 50); | ||||
| 
 | ||||
| 			await sendPrompt(userPrompt, userMessageId, _chatId); | ||||
| 			await sendPrompt(userPrompt, userMessageId); | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
|  | @ -673,7 +690,10 @@ | |||
| 	}; | ||||
| 
 | ||||
| 	const setChatTitle = async (_chatId, _title) => { | ||||
| 		await $db.updateChatById(_chatId, { title: _title }); | ||||
| 		chat = await $db.updateChatById(_chatId, { | ||||
| 			...chat.chat, | ||||
| 			title: _title | ||||
| 		}); | ||||
| 		if (_chatId === $chatId) { | ||||
| 			title = _title; | ||||
| 		} | ||||
|  | @ -687,7 +707,13 @@ | |||
| /> | ||||
| 
 | ||||
| {#if loaded} | ||||
| 	<Navbar {title} shareEnabled={messages.length > 0} /> | ||||
| 	<Navbar | ||||
| 		{title} | ||||
| 		shareEnabled={messages.length > 0} | ||||
| 		initNewChat={() => { | ||||
| 			goto('/'); | ||||
| 		}} | ||||
| 	/> | ||||
| 	<div class="min-h-screen w-full flex justify-center"> | ||||
| 		<div class=" py-2.5 flex flex-col justify-between w-full"> | ||||
| 			<div class="max-w-2xl mx-auto w-full px-3 md:px-0 mt-10"> | ||||
|  | @ -696,6 +722,7 @@ | |||
| 
 | ||||
| 			<div class=" h-full mt-10 mb-32 w-full flex flex-col"> | ||||
| 				<Messages | ||||
| 					chatId={$chatId} | ||||
| 					{selectedModels} | ||||
| 					{selectedModelfile} | ||||
| 					bind:history | ||||
|  |  | |||
|  | @ -132,7 +132,6 @@ SYSTEM """${system}"""`.replace(/^\s*\n/gm, ''); | |||
| 				method: 'POST', | ||||
| 				headers: { | ||||
| 					'Content-Type': 'text/event-stream', | ||||
| 					...($settings.authHeader && { Authorization: $settings.authHeader }), | ||||
| 					...($user && { Authorization: `Bearer ${localStorage.token}` }) | ||||
| 				}, | ||||
| 				body: JSON.stringify({ | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Timothy J. Baek
						Timothy J. Baek