forked from open-webui/open-webui
		
	feat: custom chatId route support
This commit is contained in:
		
							parent
							
								
									99e8816e73
								
							
						
					
					
						commit
						2342c5036b
					
				
					 14 changed files with 2672 additions and 2042 deletions
				
			
		|  | @ -1,19 +1,162 @@ | |||
| <script> | ||||
| 	import { config, user } from '$lib/stores'; | ||||
| 	import { goto } from '$app/navigation'; | ||||
| <script lang="ts"> | ||||
| 	import { openDB, deleteDB } from 'idb'; | ||||
| 	import { onMount, tick } from 'svelte'; | ||||
| 	import { goto } from '$app/navigation'; | ||||
| 
 | ||||
| 	import { config, user, showSettings, settings, models, db } from '$lib/stores'; | ||||
| 
 | ||||
| 	import SettingsModal from '$lib/components/chat/SettingsModal.svelte'; | ||||
| 	import Sidebar from '$lib/components/layout/Sidebar.svelte'; | ||||
| 	import toast from 'svelte-french-toast'; | ||||
| 	import { OLLAMA_API_BASE_URL } from '$lib/constants'; | ||||
| 
 | ||||
| 	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 ?? [])); | ||||
| 
 | ||||
| 		// If OpenAI API Key exists | ||||
| 		if ($settings.OPENAI_API_KEY) { | ||||
| 			// Validate OPENAI_API_KEY | ||||
| 			const openaiModelRes = await fetch(`https://api.openai.com/v1/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 = openaiModelRes?.data ?? null; | ||||
| 
 | ||||
| 			models.push( | ||||
| 				...(openAIModels | ||||
| 					? [ | ||||
| 							{ name: 'hr' }, | ||||
| 							...openAIModels | ||||
| 								.map((model) => ({ name: model.id, label: 'OpenAI' })) | ||||
| 								.filter((model) => model.name.includes('gpt')) | ||||
| 					  ] | ||||
| 					: []) | ||||
| 			); | ||||
| 		} | ||||
| 
 | ||||
| 		return models; | ||||
| 	}; | ||||
| 
 | ||||
| 	const getDB = async () => { | ||||
| 		return await openDB('Chats', 1, { | ||||
| 			upgrade(db) { | ||||
| 				const store = db.createObjectStore('chats', { | ||||
| 					keyPath: 'id', | ||||
| 					autoIncrement: true | ||||
| 				}); | ||||
| 				store.createIndex('timestamp', 'timestamp'); | ||||
| 			} | ||||
| 		}); | ||||
| 	}; | ||||
| 
 | ||||
| 	onMount(async () => { | ||||
| 		if ($config && $config.auth && $user === undefined) { | ||||
| 			await goto('/auth'); | ||||
| 		} | ||||
| 
 | ||||
| 		let _models = await getModels(); | ||||
| 		await models.set(_models); | ||||
| 		let _db = await getDB(); | ||||
| 		await db.set(_db); | ||||
| 
 | ||||
| 		await tick(); | ||||
| 		loaded = true; | ||||
| 	}); | ||||
| </script> | ||||
| 
 | ||||
| {#if loaded} | ||||
| 	<slot /> | ||||
| 	<div class="app"> | ||||
| 		<div | ||||
| 			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> | ||||
| {/if} | ||||
| 
 | ||||
| <style> | ||||
| 	.loading { | ||||
| 		display: inline-block; | ||||
| 		clip-path: inset(0 1ch 0 0); | ||||
| 		animation: l 1s steps(3) infinite; | ||||
| 		letter-spacing: -0.5px; | ||||
| 	} | ||||
| 
 | ||||
| 	@keyframes l { | ||||
| 		to { | ||||
| 			clip-path: inset(0 -1ch 0 0); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	pre[class*='language-'] { | ||||
| 		position: relative; | ||||
| 		overflow: auto; | ||||
| 
 | ||||
| 		/* make space  */ | ||||
| 		margin: 5px 0; | ||||
| 		padding: 1.75rem 0 1.75rem 1rem; | ||||
| 		border-radius: 10px; | ||||
| 	} | ||||
| 
 | ||||
| 	pre[class*='language-'] button { | ||||
| 		position: absolute; | ||||
| 		top: 5px; | ||||
| 		right: 5px; | ||||
| 
 | ||||
| 		font-size: 0.9rem; | ||||
| 		padding: 0.15rem; | ||||
| 		background-color: #828282; | ||||
| 
 | ||||
| 		border: ridge 1px #7b7b7c; | ||||
| 		border-radius: 5px; | ||||
| 		text-shadow: #c4c4c4 0 0 2px; | ||||
| 	} | ||||
| 
 | ||||
| 	pre[class*='language-'] button:hover { | ||||
| 		cursor: pointer; | ||||
| 		background-color: #bcbabb; | ||||
| 	} | ||||
| </style> | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -0,0 +1,519 @@ | |||
| <script lang="ts"> | ||||
| 	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, splitStream } from '$lib/utils'; | ||||
| 	import { goto } from '$app/navigation'; | ||||
| 	import { config, user, settings, db, chats, chatId } from '$lib/stores'; | ||||
| 
 | ||||
| 	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 loaded = false; | ||||
| 	let stopResponseFlag = false; | ||||
| 	let autoScroll = true; | ||||
| 
 | ||||
| 	// let chatId = $page.params.id; | ||||
| 	let selectedModels = ['']; | ||||
| 
 | ||||
| 	let title = ''; | ||||
| 	let prompt = ''; | ||||
| 
 | ||||
| 	let messages = []; | ||||
| 	let history = { | ||||
| 		messages: {}, | ||||
| 		currentId: null | ||||
| 	}; | ||||
| 
 | ||||
| 	$: if (history.currentId !== null) { | ||||
| 		let _messages = []; | ||||
| 
 | ||||
| 		let currentMessage = history.messages[history.currentId]; | ||||
| 		while (currentMessage !== null) { | ||||
| 			_messages.unshift({ ...currentMessage }); | ||||
| 			currentMessage = | ||||
| 				currentMessage.parentId !== null ? history.messages[currentMessage.parentId] : null; | ||||
| 		} | ||||
| 		messages = _messages; | ||||
| 	} | ||||
| 
 | ||||
| 	onMount(async () => { | ||||
| 		let chat = await loadChat(); | ||||
| 
 | ||||
| 		await tick(); | ||||
| 		if (chat) { | ||||
| 			loaded = true; | ||||
| 		} else { | ||||
| 			await goto('/'); | ||||
| 		} | ||||
| 	}); | ||||
| 
 | ||||
| 	$: if ($page.params.id) { | ||||
| 		console.log($page.params.id); | ||||
| 		(async () => { | ||||
| 			await loadChat(); | ||||
| 		})(); | ||||
| 	} | ||||
| 
 | ||||
| 	////////////////////////// | ||||
| 	// Web functions | ||||
| 	////////////////////////// | ||||
| 
 | ||||
| 	const loadChat = async () => { | ||||
| 		await chatId.set($page.params.id); | ||||
| 		const chat = await $db.get('chats', $chatId); | ||||
| 
 | ||||
| 		if (chat) { | ||||
| 			console.log(chat); | ||||
| 
 | ||||
| 			selectedModels = (chat?.models ?? undefined) !== undefined ? chat.models : [chat.model ?? '']; | ||||
| 			history = | ||||
| 				(chat?.history ?? undefined) !== undefined | ||||
| 					? chat.history | ||||
| 					: convertMessagesToHistory(chat.messages); | ||||
| 
 | ||||
| 			console.log(history); | ||||
| 
 | ||||
| 			title = chat.title; | ||||
| 			await settings.set({ | ||||
| 				...$settings, | ||||
| 				system: chat.system ?? $settings.system, | ||||
| 				options: chat.options ?? $settings.options | ||||
| 			}); | ||||
| 			autoScroll = true; | ||||
| 
 | ||||
| 			await tick(); | ||||
| 			if (messages.length > 0) { | ||||
| 				history.messages[messages.at(-1).id].done = true; | ||||
| 			} | ||||
| 			return chat; | ||||
| 		} else { | ||||
| 			return null; | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
| 	////////////////////////// | ||||
| 	// Ollama functions | ||||
| 	////////////////////////// | ||||
| 
 | ||||
| 	const sendPrompt = async (userPrompt, parentId) => { | ||||
| 		await chats.set(await $db.getAllFromIndex('chats', 'timestamp')); | ||||
| 
 | ||||
| 		await Promise.all( | ||||
| 			selectedModels.map(async (model) => { | ||||
| 				if (model.includes('gpt-')) { | ||||
| 					await sendPromptOpenAI(model, userPrompt, parentId); | ||||
| 				} else { | ||||
| 					await sendPromptOllama(model, userPrompt, parentId); | ||||
| 				} | ||||
| 			}) | ||||
| 		); | ||||
| 
 | ||||
| 		await chats.set(await $db.getAllFromIndex('chats', 'timestamp')); | ||||
| 
 | ||||
| 		console.log(history); | ||||
| 	}; | ||||
| 
 | ||||
| 	const sendPromptOllama = async (model, userPrompt, parentId) => { | ||||
| 		let responseMessageId = uuidv4(); | ||||
| 
 | ||||
| 		let responseMessage = { | ||||
| 			parentId: parentId, | ||||
| 			id: responseMessageId, | ||||
| 			childrenIds: [], | ||||
| 			role: 'assistant', | ||||
| 			content: '', | ||||
| 			model: model | ||||
| 		}; | ||||
| 
 | ||||
| 		history.messages[responseMessageId] = responseMessage; | ||||
| 		history.currentId = responseMessageId; | ||||
| 		if (parentId !== null) { | ||||
| 			history.messages[parentId].childrenIds = [ | ||||
| 				...history.messages[parentId].childrenIds, | ||||
| 				responseMessageId | ||||
| 			]; | ||||
| 		} | ||||
| 
 | ||||
| 		window.scrollTo({ top: document.body.scrollHeight }); | ||||
| 
 | ||||
| 		const res = await fetch(`${$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL}/generate`, { | ||||
| 			method: 'POST', | ||||
| 			headers: { | ||||
| 				'Content-Type': 'text/event-stream', | ||||
| 				...($settings.authHeader && { Authorization: $settings.authHeader }), | ||||
| 				...($user && { Authorization: `Bearer ${localStorage.token}` }) | ||||
| 			}, | ||||
| 			body: JSON.stringify({ | ||||
| 				model: model, | ||||
| 				prompt: userPrompt, | ||||
| 				system: $settings.system ?? undefined, | ||||
| 				options: { | ||||
| 					seed: $settings.seed ?? undefined, | ||||
| 					temperature: $settings.temperature ?? undefined, | ||||
| 					repeat_penalty: $settings.repeat_penalty ?? undefined, | ||||
| 					top_k: $settings.top_k ?? undefined, | ||||
| 					top_p: $settings.top_p ?? undefined | ||||
| 				}, | ||||
| 				format: $settings.requestFormat ?? undefined, | ||||
| 				context: | ||||
| 					history.messages[parentId] !== null && | ||||
| 					history.messages[parentId].parentId in history.messages | ||||
| 						? history.messages[history.messages[parentId].parentId]?.context ?? undefined | ||||
| 						: undefined | ||||
| 			}) | ||||
| 		}); | ||||
| 
 | ||||
| 		const reader = res.body | ||||
| 			.pipeThrough(new TextDecoderStream()) | ||||
| 			.pipeThrough(splitStream('\n')) | ||||
| 			.getReader(); | ||||
| 
 | ||||
| 		while (true) { | ||||
| 			const { value, done } = await reader.read(); | ||||
| 			if (done || stopResponseFlag) { | ||||
| 				if (stopResponseFlag) { | ||||
| 					responseMessage.done = true; | ||||
| 					messages = messages; | ||||
| 				} | ||||
| 
 | ||||
| 				break; | ||||
| 			} | ||||
| 
 | ||||
| 			try { | ||||
| 				let lines = value.split('\n'); | ||||
| 
 | ||||
| 				for (const line of lines) { | ||||
| 					if (line !== '') { | ||||
| 						console.log(line); | ||||
| 						let data = JSON.parse(line); | ||||
| 						if (data.done == false) { | ||||
| 							if (responseMessage.content == '' && data.response == '\n') { | ||||
| 								continue; | ||||
| 							} else { | ||||
| 								responseMessage.content += data.response; | ||||
| 								messages = messages; | ||||
| 							} | ||||
| 						} else if ('detail' in data) { | ||||
| 							throw data; | ||||
| 						} else { | ||||
| 							responseMessage.done = true; | ||||
| 							responseMessage.context = data.context; | ||||
| 							messages = messages; | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} catch (error) { | ||||
| 				console.log(error); | ||||
| 				if ('detail' in error) { | ||||
| 					toast.error(error.detail); | ||||
| 				} | ||||
| 				break; | ||||
| 			} | ||||
| 
 | ||||
| 			if (autoScroll) { | ||||
| 				window.scrollTo({ top: document.body.scrollHeight }); | ||||
| 			} | ||||
| 
 | ||||
| 			await $db.put('chats', { | ||||
| 				id: $chatId, | ||||
| 				title: title === '' ? 'New Chat' : title, | ||||
| 				models: selectedModels, | ||||
| 				system: $settings.system ?? undefined, | ||||
| 				options: { | ||||
| 					seed: $settings.seed ?? undefined, | ||||
| 					temperature: $settings.temperature ?? undefined, | ||||
| 					repeat_penalty: $settings.repeat_penalty ?? undefined, | ||||
| 					top_k: $settings.top_k ?? undefined, | ||||
| 					top_p: $settings.top_p ?? undefined | ||||
| 				}, | ||||
| 				messages: messages, | ||||
| 				history: history, | ||||
| 				timestamp: Date.now() | ||||
| 			}); | ||||
| 		} | ||||
| 
 | ||||
| 		stopResponseFlag = false; | ||||
| 		await tick(); | ||||
| 		if (autoScroll) { | ||||
| 			window.scrollTo({ top: document.body.scrollHeight }); | ||||
| 		} | ||||
| 
 | ||||
| 		if (messages.length == 2 && messages.at(1).content !== '') { | ||||
| 			await generateChatTitle($chatId, userPrompt); | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
| 	const sendPromptOpenAI = async (model, userPrompt, parentId) => { | ||||
| 		if (settings.OPENAI_API_KEY) { | ||||
| 			if (models) { | ||||
| 				let responseMessageId = uuidv4(); | ||||
| 
 | ||||
| 				let responseMessage = { | ||||
| 					parentId: parentId, | ||||
| 					id: responseMessageId, | ||||
| 					childrenIds: [], | ||||
| 					role: 'assistant', | ||||
| 					content: '', | ||||
| 					model: model | ||||
| 				}; | ||||
| 
 | ||||
| 				history.messages[responseMessageId] = responseMessage; | ||||
| 				history.currentId = responseMessageId; | ||||
| 				if (parentId !== null) { | ||||
| 					history.messages[parentId].childrenIds = [ | ||||
| 						...history.messages[parentId].childrenIds, | ||||
| 						responseMessageId | ||||
| 					]; | ||||
| 				} | ||||
| 
 | ||||
| 				window.scrollTo({ top: document.body.scrollHeight }); | ||||
| 
 | ||||
| 				const res = await fetch(`https://api.openai.com/v1/chat/completions`, { | ||||
| 					method: 'POST', | ||||
| 					headers: { | ||||
| 						'Content-Type': 'application/json', | ||||
| 						Authorization: `Bearer ${settings.OPENAI_API_KEY}` | ||||
| 					}, | ||||
| 					body: JSON.stringify({ | ||||
| 						model: model, | ||||
| 						stream: true, | ||||
| 						messages: [ | ||||
| 							$settings.system | ||||
| 								? { | ||||
| 										role: 'system', | ||||
| 										content: settings.system | ||||
| 								  } | ||||
| 								: undefined, | ||||
| 							...messages | ||||
| 						] | ||||
| 							.filter((message) => message) | ||||
| 							.map((message) => ({ role: message.role, content: message.content })), | ||||
| 						temperature: $settings.temperature ?? undefined, | ||||
| 						top_p: $settings.top_p ?? undefined, | ||||
| 						frequency_penalty: $settings.repeat_penalty ?? undefined | ||||
| 					}) | ||||
| 				}); | ||||
| 
 | ||||
| 				const reader = res.body | ||||
| 					.pipeThrough(new TextDecoderStream()) | ||||
| 					.pipeThrough(splitStream('\n')) | ||||
| 					.getReader(); | ||||
| 
 | ||||
| 				while (true) { | ||||
| 					const { value, done } = await reader.read(); | ||||
| 					if (done || stopResponseFlag) { | ||||
| 						if (stopResponseFlag) { | ||||
| 							responseMessage.done = true; | ||||
| 							messages = messages; | ||||
| 						} | ||||
| 
 | ||||
| 						break; | ||||
| 					} | ||||
| 
 | ||||
| 					try { | ||||
| 						let lines = value.split('\n'); | ||||
| 
 | ||||
| 						for (const line of lines) { | ||||
| 							if (line !== '') { | ||||
| 								console.log(line); | ||||
| 								if (line === 'data: [DONE]') { | ||||
| 									responseMessage.done = true; | ||||
| 									messages = messages; | ||||
| 								} else { | ||||
| 									let data = JSON.parse(line.replace(/^data: /, '')); | ||||
| 									console.log(data); | ||||
| 
 | ||||
| 									if (responseMessage.content == '' && data.choices[0].delta.content == '\n') { | ||||
| 										continue; | ||||
| 									} else { | ||||
| 										responseMessage.content += data.choices[0].delta.content ?? ''; | ||||
| 										messages = messages; | ||||
| 									} | ||||
| 								} | ||||
| 							} | ||||
| 						} | ||||
| 					} catch (error) { | ||||
| 						console.log(error); | ||||
| 					} | ||||
| 
 | ||||
| 					if (autoScroll) { | ||||
| 						window.scrollTo({ top: document.body.scrollHeight }); | ||||
| 					} | ||||
| 
 | ||||
| 					await $db.put('chats', { | ||||
| 						id: $chatId, | ||||
| 						title: title === '' ? 'New Chat' : title, | ||||
| 						models: selectedModels, | ||||
| 
 | ||||
| 						system: $settings.system ?? undefined, | ||||
| 						options: { | ||||
| 							seed: $settings.seed ?? undefined, | ||||
| 							temperature: $settings.temperature ?? undefined, | ||||
| 							repeat_penalty: $settings.repeat_penalty ?? undefined, | ||||
| 							top_k: $settings.top_k ?? undefined, | ||||
| 							top_p: $settings.top_p ?? undefined | ||||
| 						}, | ||||
| 						messages: messages, | ||||
| 						history: history, | ||||
| 						timestamp: Date.now() | ||||
| 					}); | ||||
| 				} | ||||
| 
 | ||||
| 				stopResponseFlag = false; | ||||
| 
 | ||||
| 				await tick(); | ||||
| 				if (autoScroll) { | ||||
| 					window.scrollTo({ top: document.body.scrollHeight }); | ||||
| 				} | ||||
| 
 | ||||
| 				if (messages.length == 2) { | ||||
| 					await setChatTitle($chatId, userPrompt); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
| 	const submitPrompt = async (userPrompt) => { | ||||
| 		console.log('submitPrompt'); | ||||
| 
 | ||||
| 		if (selectedModels.includes('')) { | ||||
| 			toast.error('Model not selected'); | ||||
| 		} else if (messages.length != 0 && messages.at(-1).done != true) { | ||||
| 			console.log('wait'); | ||||
| 		} else { | ||||
| 			document.getElementById('chat-textarea').style.height = ''; | ||||
| 
 | ||||
| 			let userMessageId = uuidv4(); | ||||
| 
 | ||||
| 			let userMessage = { | ||||
| 				id: userMessageId, | ||||
| 				parentId: messages.length !== 0 ? messages.at(-1).id : null, | ||||
| 				childrenIds: [], | ||||
| 				role: 'user', | ||||
| 				content: userPrompt | ||||
| 			}; | ||||
| 
 | ||||
| 			if (messages.length !== 0) { | ||||
| 				history.messages[messages.at(-1).id].childrenIds.push(userMessageId); | ||||
| 			} | ||||
| 
 | ||||
| 			history.messages[userMessageId] = userMessage; | ||||
| 			history.currentId = userMessageId; | ||||
| 
 | ||||
| 			prompt = ''; | ||||
| 
 | ||||
| 			if (messages.length == 0) { | ||||
| 				await $db.put('chats', { | ||||
| 					id: $chatId, | ||||
| 					title: 'New Chat', | ||||
| 					models: selectedModels, | ||||
| 					system: $settings.system ?? undefined, | ||||
| 					options: { | ||||
| 						seed: $settings.seed ?? undefined, | ||||
| 						temperature: $settings.temperature ?? undefined, | ||||
| 						repeat_penalty: $settings.repeat_penalty ?? undefined, | ||||
| 						top_k: $settings.top_k ?? undefined, | ||||
| 						top_p: $settings.top_p ?? undefined | ||||
| 					}, | ||||
| 					messages: messages, | ||||
| 					history: history, | ||||
| 					timestamp: Date.now() | ||||
| 				}); | ||||
| 			} | ||||
| 
 | ||||
| 			setTimeout(() => { | ||||
| 				window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' }); | ||||
| 			}, 50); | ||||
| 
 | ||||
| 			await sendPrompt(userPrompt, userMessageId); | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
| 	const stopResponse = () => { | ||||
| 		stopResponseFlag = true; | ||||
| 		console.log('stopResponse'); | ||||
| 	}; | ||||
| 
 | ||||
| 	const regenerateResponse = async () => { | ||||
| 		console.log('regenerateResponse'); | ||||
| 		if (messages.length != 0 && messages.at(-1).done == true) { | ||||
| 			messages.splice(messages.length - 1, 1); | ||||
| 			messages = messages; | ||||
| 
 | ||||
| 			let userMessage = messages.at(-1); | ||||
| 			let userPrompt = userMessage.content; | ||||
| 
 | ||||
| 			await sendPrompt(userPrompt, userMessage.id); | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
| 	const generateChatTitle = async (_chatId, userPrompt) => { | ||||
| 		console.log('generateChatTitle'); | ||||
| 
 | ||||
| 		const res = await fetch(`${$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL}/generate`, { | ||||
| 			method: 'POST', | ||||
| 			headers: { | ||||
| 				'Content-Type': 'text/event-stream', | ||||
| 				...($settings.authHeader && { Authorization: $settings.authHeader }), | ||||
| 				...($user && { Authorization: `Bearer ${localStorage.token}` }) | ||||
| 			}, | ||||
| 			body: JSON.stringify({ | ||||
| 				model: selectedModels[0], | ||||
| 				prompt: `Generate a brief 3-5 word title for this question, excluding the term 'title.' Then, please reply with only the title: ${userPrompt}`, | ||||
| 				stream: false | ||||
| 			}) | ||||
| 		}) | ||||
| 			.then(async (res) => { | ||||
| 				if (!res.ok) throw await res.json(); | ||||
| 				return res.json(); | ||||
| 			}) | ||||
| 			.catch((error) => { | ||||
| 				if ('detail' in error) { | ||||
| 					toast.error(error.detail); | ||||
| 				} | ||||
| 				console.log(error); | ||||
| 				return null; | ||||
| 			}); | ||||
| 
 | ||||
| 		if (res) { | ||||
| 			await setChatTitle(_chatId, res.response === '' ? 'New Chat' : res.response); | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
| 	const setChatTitle = async (_chatId, _title) => { | ||||
| 		const chat = await $db.get('chats', _chatId); | ||||
| 		await $db.put('chats', { ...chat, title: _title }); | ||||
| 		if (chat.id === $chatId) { | ||||
| 			title = _title; | ||||
| 		} | ||||
| 	}; | ||||
| </script> | ||||
| 
 | ||||
| <svelte:window | ||||
| 	on:scroll={(e) => { | ||||
| 		console.log(e); | ||||
| 
 | ||||
| 		autoScroll = window.innerHeight + window.scrollY >= document.body.offsetHeight - 40; | ||||
| 	}} | ||||
| /> | ||||
| 
 | ||||
| <Navbar {title} /> | ||||
| <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"> | ||||
| 			<ModelSelector bind:selectedModels disabled={messages.length > 0} /> | ||||
| 		</div> | ||||
| 
 | ||||
| 		<div class=" h-full mt-10 mb-32 w-full flex flex-col"> | ||||
| 			<Messages bind:history bind:messages bind:autoScroll {sendPrompt} {regenerateResponse} /> | ||||
| 		</div> | ||||
| 	</div> | ||||
| 
 | ||||
| 	<MessageInput bind:prompt bind:autoScroll {messages} {submitPrompt} {stopResponse} /> | ||||
| </div> | ||||
|  | @ -35,7 +35,7 @@ | |||
| 
 | ||||
| 		if (res) { | ||||
| 			console.log(res); | ||||
| 			toast.success(`You're now logged in. Redirecting you to the main page.`); | ||||
| 			toast.success(`You're now logged in.`); | ||||
| 			localStorage.token = res.token; | ||||
| 			await user.set(res); | ||||
| 			goto('/'); | ||||
|  | @ -66,7 +66,7 @@ | |||
| 
 | ||||
| 		if (res) { | ||||
| 			console.log(res); | ||||
| 			toast.success(`Account creation successful. Redirecting you to the main page."`); | ||||
| 			toast.success(`Account creation successful."`); | ||||
| 			localStorage.token = res.token; | ||||
| 			await user.set(res); | ||||
| 			goto('/'); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Timothy J. Baek
						Timothy J. Baek