Przeglądaj źródła

feat: openAI model support added

Timothy J. Baek 1 rok temu
rodzic
commit
d8d14fbfba
2 zmienionych plików z 344 dodań i 155 usunięć
  1. 110 44
      src/lib/components/chat/SettingsModal.svelte
  2. 234 111
      src/routes/+page.svelte

+ 110 - 44
src/lib/components/chat/SettingsModal.svelte

@@ -8,16 +8,21 @@
 	export let saveSettings: Function;
 	export let getModelTags: Function;
 
+	let selectedTab = 'general';
+
+	// General
 	let API_BASE_URL = BUILD_TIME_API_BASE_URL;
+	let OPENAI_API_KEY = '';
 	let system = '';
-	let temperature = 0.8;
 
-	let selectedMenu = 'general';
+	// Models
 	let modelTag = '';
 	let deleteModelTag = '';
-
 	let digest = '';
-	let pullProgress = '';
+	let pullProgress = null;
+
+	// Advanced
+	let temperature = 0.8;
 
 	const splitStream = (splitOn) => {
 		let buffer = '';
@@ -42,11 +47,9 @@
 
 		if (res) {
 			toast.success('Server connection verified');
-			saveSettings(
-				API_BASE_URL,
-				system != '' ? system : null,
-				temperature != 0.8 ? temperature : null
-			);
+			saveSettings({
+				API_BASE_URL: API_BASE_URL
+			});
 		}
 	};
 
@@ -156,7 +159,10 @@
 
 	$: if (show) {
 		let settings = JSON.parse(localStorage.getItem('settings') ?? '{}');
+		console.log(settings);
+
 		API_BASE_URL = settings.API_BASE_URL ?? BUILD_TIME_API_BASE_URL;
+		OPENAI_API_KEY = settings.OPENAI_API_KEY ?? '';
 		system = settings.system ?? '';
 		temperature = settings.temperature ?? 0.8;
 	}
@@ -191,12 +197,12 @@
 				class="flex flex-row space-x-1 md:space-x-0 md:space-y-1 md:flex-col flex-1 md:flex-none md:w-40 text-gray-200 text-xs text-left mb-3 md:mb-0"
 			>
 				<button
-					class="px-2 py-2 rounded flex-1 md:flex-none flex text-right transition {selectedMenu ===
+					class="px-2.5 py-2.5 rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
 					'general'
 						? 'bg-gray-700'
 						: 'hover:bg-gray-800'}"
 					on:click={() => {
-						selectedMenu = 'general';
+						selectedTab = 'general';
 					}}
 				>
 					<div class=" self-center mr-2">
@@ -217,12 +223,12 @@
 				</button>
 
 				<button
-					class="px-2 py-2 rounded flex-1 md:flex-none flex text-right transition {selectedMenu ===
+					class="px-2.5 py-2.5 rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
 					'models'
 						? 'bg-gray-700'
 						: 'hover:bg-gray-800'}"
 					on:click={() => {
-						selectedMenu = 'models';
+						selectedTab = 'models';
 					}}
 				>
 					<div class=" self-center mr-2">
@@ -241,9 +247,33 @@
 					</div>
 					<div class=" self-center">Models</div>
 				</button>
+
+				<button
+					class="px-2.5 py-2.5 rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
+					'advanced'
+						? 'bg-gray-700'
+						: 'hover:bg-gray-800'}"
+					on:click={() => {
+						selectedTab = 'advanced';
+					}}
+				>
+					<div class=" self-center mr-2">
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							viewBox="0 0 20 20"
+							fill="currentColor"
+							class="w-4 h-4"
+						>
+							<path
+								d="M12 4.467c0-.405.262-.75.559-1.027.276-.257.441-.584.441-.94 0-.828-.895-1.5-2-1.5s-2 .672-2 1.5c0 .362.171.694.456.953.29.265.544.6.544.994a.968.968 0 01-1.024.974 39.655 39.655 0 01-3.014-.306.75.75 0 00-.847.847c.14.993.242 1.999.306 3.014A.968.968 0 014.447 10c-.393 0-.729-.253-.994-.544C3.194 9.17 2.862 9 2.5 9 1.672 9 1 9.895 1 11s.672 2 1.5 2c.356 0 .683-.165.94-.441.276-.297.622-.559 1.027-.559a.997.997 0 011.004 1.03 39.747 39.747 0 01-.319 3.734.75.75 0 00.64.842c1.05.146 2.111.252 3.184.318A.97.97 0 0010 16.948c0-.394-.254-.73-.545-.995C9.171 15.693 9 15.362 9 15c0-.828.895-1.5 2-1.5s2 .672 2 1.5c0 .356-.165.683-.441.94-.297.276-.559.622-.559 1.027a.998.998 0 001.03 1.005c1.337-.05 2.659-.162 3.961-.337a.75.75 0 00.644-.644c.175-1.302.288-2.624.337-3.961A.998.998 0 0016.967 12c-.405 0-.75.262-1.027.559-.257.276-.584.441-.94.441-.828 0-1.5-.895-1.5-2s.672-2 1.5-2c.362 0 .694.17.953.455.265.291.601.545.995.545a.97.97 0 00.976-1.024 41.159 41.159 0 00-.318-3.184.75.75 0 00-.842-.64c-1.228.164-2.473.271-3.734.319A.997.997 0 0112 4.467z"
+							/>
+						</svg>
+					</div>
+					<div class=" self-center">Advanced</div>
+				</button>
 			</div>
 			<div class="flex-1 md:min-h-[300px]">
-				{#if selectedMenu === 'general'}
+				{#if selectedTab === 'general'}
 					<div class="flex flex-col space-y-3">
 						<div>
 							<div class=" mb-2.5 text-sm font-medium">Ollama Server URL</div>
@@ -290,42 +320,44 @@
 						<hr class=" border-gray-700" />
 
 						<div>
-							<div class=" mb-2.5 text-sm font-medium">System Prompt</div>
-							<textarea
-								bind:value={system}
-								class="w-full rounded p-4 text-sm text-gray-300 bg-gray-800 outline-none"
-								rows="4"
-							/>
+							<div class=" mb-2.5 text-sm font-medium">
+								OpenAI API Key <span class=" text-gray-400 text-sm">(optional)</span>
+							</div>
+							<div class="flex w-full">
+								<div class="flex-1 mr-2">
+									<input
+										class="w-full rounded py-2 px-4 text-sm text-gray-300 bg-gray-800 outline-none"
+										placeholder="Enter OpenAI API Key"
+										bind:value={OPENAI_API_KEY}
+										autocomplete="off"
+									/>
+								</div>
+							</div>
+							<div class="mt-2 text-xs text-gray-500">
+								Adds optional support for 'gpt-3.5-turbo'.
+							</div>
 						</div>
 
 						<hr class=" border-gray-700" />
 
 						<div>
-							<label for="steps-range" class=" mb-2 text-sm font-medium flex justify-between">
-								<div>Temperature</div>
-								<div>
-									{temperature}
-								</div></label
-							>
-							<input
-								id="steps-range"
-								type="range"
-								min="0"
-								max="1"
-								bind:value={temperature}
-								step="0.05"
-								class="w-full h-2 rounded-lg appearance-none cursor-pointer bg-gray-700"
+							<div class=" mb-2.5 text-sm font-medium">System Prompt</div>
+							<textarea
+								bind:value={system}
+								class="w-full rounded p-4 text-sm text-gray-300 bg-gray-800 outline-none resize-none"
+								rows="4"
 							/>
 						</div>
+
 						<div class="flex justify-end pt-3 text-sm font-medium">
 							<button
 								class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 transition rounded"
 								on:click={() => {
-									saveSettings(
-										API_BASE_URL === '' ? BUILD_TIME_API_BASE_URL : API_BASE_URL,
-										system != '' ? system : null,
-										temperature != 0.8 ? temperature : null
-									);
+									saveSettings({
+										API_BASE_URL: API_BASE_URL === '' ? BUILD_TIME_API_BASE_URL : API_BASE_URL,
+										OPENAI_API_KEY: OPENAI_API_KEY !== '' ? OPENAI_API_KEY : undefined,
+										system: system !== '' ? system : undefined
+									});
 									show = false;
 								}}
 							>
@@ -333,8 +365,8 @@
 							</button>
 						</div>
 					</div>
-				{:else if selectedMenu === 'models'}
-					<div class="flex flex-col space-y-3 text-sm">
+				{:else if selectedTab === 'models'}
+					<div class="flex flex-col space-y-3 text-sm mb-10">
 						<div>
 							<div class=" mb-2.5 text-sm font-medium">Pull a model</div>
 							<div class="flex w-full">
@@ -375,15 +407,15 @@
 								>
 							</div>
 
-							{#if pullProgress !== ''}
+							{#if pullProgress !== null}
 								<div class="mt-2">
 									<div class=" mb-2 text-xs">Pull Progress</div>
 									<div class="w-full rounded-full bg-gray-800">
 										<div
 											class="bg-gray-600 text-xs font-medium text-blue-100 text-center p-0.5 leading-none rounded-full"
-											style="width: {Math.max(15, pullProgress)}%"
+											style="width: {Math.max(15, pullProgress ?? 0)}%"
 										>
-											{pullProgress}%
+											{pullProgress ?? 0}%
 										</div>
 									</div>
 									<div class="mt-1 text-xs text-gray-700" style="font-size: 0.5rem;">
@@ -426,6 +458,40 @@
 							</div>
 						</div>
 					</div>
+				{:else if selectedTab === 'advanced'}
+					<div class="flex flex-col h-full justify-between space-y-3 text-sm">
+						<div>
+							<label for="steps-range" class=" mb-2 text-sm font-medium flex justify-between">
+								<div>Temperature</div>
+								<div>
+									{temperature}
+								</div></label
+							>
+							<input
+								id="steps-range"
+								type="range"
+								min="0"
+								max="1"
+								bind:value={temperature}
+								step="0.05"
+								class="w-full h-2 rounded-lg appearance-none cursor-pointer bg-gray-700"
+							/>
+						</div>
+
+						<div class="flex justify-end pt-3 text-sm font-medium">
+							<button
+								class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 transition rounded"
+								on:click={() => {
+									saveSettings({
+										temperature: temperature !== 0.8 ? temperature : undefined
+									});
+									show = false;
+								}}
+							>
+								Save
+							</button>
+						</div>
+					</div>
 				{/if}
 			</div>
 		</div>

+ 234 - 111
src/routes/+page.svelte

@@ -18,55 +18,30 @@
 	import Suggestions from '$lib/components/chat/Suggestions.svelte';
 
 	let API_BASE_URL = BUILD_TIME_API_BASE_URL;
-	let suggestions = ''; // $page.url.searchParams.get('suggestions');
-
-	let models = [];
-	let textareaElement;
-
-	let showSettings = false;
 	let db;
 
 	let selectedModel = '';
-	let system = null;
-	let temperature = null;
+	let settings = {
+		system: null,
+		temperature: null
+	};
 
+	let models = [];
 	let chats = [];
+
 	let chatId = uuidv4();
 	let title = '';
 	let prompt = '';
 	let messages = [];
 
+	let showSettings = false;
 	let stopResponseFlag = false;
 	let autoScroll = true;
+	let suggestions = ''; // $page.url.searchParams.get('suggestions');
 
 	onMount(async () => {
-		let settings = JSON.parse(localStorage.getItem('settings') ?? '{}');
-
-		API_BASE_URL = settings.API_BASE_URL ?? BUILD_TIME_API_BASE_URL;
-		console.log(API_BASE_URL);
-		system = settings.system ?? null;
-		temperature = settings.temperature ?? null;
-
-		await getModelTags();
-
-		selectedModel =
-			settings.model && models.map((model) => model.name).includes(settings.model)
-				? settings.model
-				: '';
-
-		db = await openDB('Chats', 1, {
-			upgrade(db) {
-				const store = db.createObjectStore('chats', {
-					keyPath: 'id',
-					autoIncrement: true
-				});
-				store.createIndex('timestamp', 'timestamp');
-			}
-		});
-
-		chats = await db.getAllFromIndex('chats', 'timestamp');
-		console.log(chats);
-		console.log(chatId);
+		await createNewChat(true);
+		await setDBandLoadChats();
 	});
 
 	//////////////////////////
@@ -184,58 +159,54 @@
 	// Web functions
 	//////////////////////////
 
-	const saveDefaultModel = () => {
-		let settings = localStorage.getItem('settings') ?? '{}';
-		if (settings) {
-			settings = JSON.parse(settings);
-			settings.model = selectedModel;
-			localStorage.setItem('settings', JSON.stringify(settings));
-		}
+	const createNewChat = async (init = false) => {
+		if (init || messages.length > 0) {
+			chatId = uuidv4();
+			messages = [];
+			title = '';
 
-		console.log('saved');
-		toast.success('Default model updated');
-	};
+			settings = JSON.parse(localStorage.getItem('settings') ?? JSON.stringify(settings));
+
+			API_BASE_URL = settings?.API_BASE_URL ?? BUILD_TIME_API_BASE_URL;
+			console.log(API_BASE_URL);
 
-	const saveSettings = async (_api_base_url, _system, _temperature) => {
-		API_BASE_URL = _api_base_url;
-		system = _system;
-		temperature = _temperature;
+			if (models.length === 0) {
+				await getModelTags();
+			}
 
-		let settings = localStorage.getItem('settings') ?? '{}';
-		if (settings) {
-			settings = JSON.parse(settings);
+			selectedModel =
+				settings.model && models.map((model) => model.name).includes(settings.model)
+					? settings.model
+					: '';
 
-			settings.API_BASE_URL = API_BASE_URL;
-			settings.system = system;
-			settings.temperature = temperature;
-			localStorage.setItem('settings', JSON.stringify(settings));
+			console.log(chatId);
 		}
+	};
 
-		console.log(settings);
-		await getModelTags();
+	const setDBandLoadChats = async () => {
+		db = await openDB('Chats', 1, {
+			upgrade(db) {
+				const store = db.createObjectStore('chats', {
+					keyPath: 'id',
+					autoIncrement: true
+				});
+				store.createIndex('timestamp', 'timestamp');
+			}
+		});
+
+		chats = await db.getAllFromIndex('chats', 'timestamp');
 	};
 
-	const createNewChat = () => {
-		if (messages.length > 0) {
-			chatId = uuidv4();
+	const saveDefaultModel = () => {
+		settings.model = selectedModel;
+		localStorage.setItem('settings', JSON.stringify(settings));
+		toast.success('Default model updated');
+	};
 
-			messages = [];
-			title = '';
-			console.log(localStorage.settings.model);
-
-			let settings = localStorage.getItem('settings');
-			if (settings) {
-				settings = JSON.parse(settings);
-				console.log(settings);
-
-				selectedModel =
-					settings.model && models.map((model) => model.name).includes(settings.model)
-						? settings.model
-						: '';
-				system = settings.system ?? system;
-				temperature = settings.temperature ?? temperature;
-			}
-		}
+	const saveSettings = async (updated) => {
+		settings = { ...settings, ...updated };
+		localStorage.setItem('settings', JSON.stringify(settings));
+		await getModelTags();
 	};
 
 	const loadChat = async (id) => {
@@ -248,8 +219,8 @@
 			title = chat.title;
 			chatId = chat.id;
 			selectedModel = chat.model ?? selectedModel;
-			system = chat.system ?? system;
-			temperature = chat.temperature ?? temperature;
+			settings.system = chat.system ?? settings.system;
+			settings.temperature = chat.temperature ?? settings.temperature;
 
 			await tick();
 			hljs.highlightAll();
@@ -285,8 +256,8 @@
 		chats = await db.getAllFromIndex('chats', 'timestamp');
 	};
 
-	const importChatHistory = async (results) => {
-		for (const chat of results) {
+	const importChatHistory = async (chatHistory) => {
+		for (const chat of chatHistory) {
 			console.log(chat);
 
 			await db.put('chats', {
@@ -357,9 +328,9 @@
 			id: chatId,
 			title: title === '' ? 'New Chat' : title,
 			model: selectedModel,
-			system: system,
+			system: settings.system,
 			options: {
-				temperature: temperature
+				temperature: settings.temperature
 			},
 			timestamp: Date.now(),
 			messages: messages
@@ -391,11 +362,53 @@
 			});
 
 		console.log(res);
-		models = res?.models ?? [];
-		return res;
+
+		if (settings.OPENAI_API_KEY) {
+			// Validate OPENAI_API_KEY
+			const openaiModels = 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}`);
+					return null;
+				});
+
+			console.log(openaiModels);
+
+			if (openaiModels) {
+				models = [
+					...(res?.models ?? []),
+					{ name: 'hr' },
+					{ name: 'gpt-3.5-turbo', label: '(OpenAI)' }
+				];
+			} else {
+				models = res?.models ?? [];
+			}
+		} else {
+			models = res?.models ?? [];
+		}
+
+		return models;
 	};
 
 	const sendPrompt = async (userPrompt) => {
+		if (selectedModel === 'gpt-3.5-turbo') {
+			await sendPromptOpenAI(userPrompt);
+		} else {
+			await sendPromptOllama(userPrompt);
+		}
+	};
+
+	const sendPromptOllama = async (userPrompt) => {
 		let responseMessage = {
 			role: 'assistant',
 			content: ''
@@ -412,11 +425,11 @@
 			body: JSON.stringify({
 				model: selectedModel,
 				prompt: userPrompt,
-				system: system ?? undefined,
+				system: settings.system ?? undefined,
 				options:
-					temperature != null
+					settings.temperature != null
 						? {
-								temperature: temperature
+								temperature: settings.temperature
 						  }
 						: undefined,
 				context:
@@ -481,9 +494,9 @@
 				id: chatId,
 				title: title === '' ? 'New Chat' : title,
 				model: selectedModel,
-				system: system,
+				system: settings.system,
 				options: {
-					temperature: temperature
+					temperature: settings.temperature
 				},
 				timestamp: Date.now(),
 				messages: messages
@@ -495,6 +508,111 @@
 		if (autoScroll) {
 			window.scrollTo({ top: document.body.scrollHeight });
 		}
+
+		if (messages.length == 2) {
+			await generateChatTitle(chatId, userPrompt);
+		}
+	};
+
+	const sendPromptOpenAI = async (userPrompt) => {
+		if (settings.OPENAI_API_KEY) {
+			if (models) {
+				let responseMessage = {
+					role: 'assistant',
+					content: ''
+				};
+
+				messages = [...messages, responseMessage];
+				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: 'gpt-3.5-turbo',
+						stream: true,
+						messages: messages.map((message) => ({ ...message, done: 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,
+						model: selectedModel,
+						system: settings.system,
+						options: {
+							temperature: settings.temperature
+						},
+						timestamp: Date.now(),
+						messages: messages
+					});
+				}
+
+				stopResponseFlag = false;
+
+				hljs.highlightAll();
+				createCopyCodeBlockButton();
+				renderLatex();
+
+				await tick();
+				if (autoScroll) {
+					window.scrollTo({ top: document.body.scrollHeight });
+				}
+
+				if (messages.length == 2) {
+					await setChatTitle(chatId, userPrompt);
+				}
+			}
+		}
 	};
 
 	const submitPrompt = async (userPrompt) => {
@@ -509,9 +627,9 @@
 				await db.put('chats', {
 					id: chatId,
 					model: selectedModel,
-					system: system,
+					system: settings.system,
 					options: {
-						temperature: temperature
+						temperature: settings.temperature
 					},
 					title: 'New Chat',
 					timestamp: Date.now(),
@@ -529,7 +647,6 @@
 			];
 
 			prompt = '';
-			textareaElement.style.height = '';
 
 			setTimeout(() => {
 				window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
@@ -537,9 +654,6 @@
 
 			await sendPrompt(userPrompt);
 
-			if (messages.length == 2) {
-				await generateTitle(chatId, userPrompt);
-			}
 			chats = await db.getAllFromIndex('chats', 'timestamp');
 		}
 	};
@@ -552,7 +666,9 @@
 
 			let userMessage = messages.at(-1);
 			let userPrompt = userMessage.content;
+
 			await sendPrompt(userPrompt);
+
 			chats = await db.getAllFromIndex('chats', 'timestamp');
 		}
 	};
@@ -562,8 +678,8 @@
 		console.log('stopResponse');
 	};
 
-	const generateTitle = async (_chatId, userPrompt) => {
-		console.log('generateTitle');
+	const generateChatTitle = async (_chatId, userPrompt) => {
+		console.log('generateChatTitle');
 
 		const res = await fetch(`${API_BASE_URL}/generate`, {
 			method: 'POST',
@@ -586,12 +702,15 @@
 			});
 
 		if (res) {
-			console.log(res);
-			const chat = await db.get('chats', _chatId);
-			await db.put('chats', { ...chat, title: res.response === '' ? 'New Chat' : res.response });
-			if (chat.id === chatId) {
-				title = res.response;
-			}
+			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>
@@ -669,7 +788,11 @@
 									<option value="" selected>Select a model</option>
 
 									{#each models as model}
-										<option value={model.name}>{model.name}</option>
+										{#if model.name === 'hr'}
+											<hr />
+										{:else}
+											<option value={model.name}>{model.name}</option>
+										{/if}
 									{/each}
 								</select>
 								<div class="text-right mt-1.5 text-xs text-gray-500">
@@ -728,7 +851,7 @@
 											</div>
 										{:else}
 											<div
-												class="prose chat-{message.role} w-full max-w-full prose-invert prose-headings:my-0 prose-p:my-0 prose-pre:my-0 prose-table:my-0 prose-blockquote:my-0 prose-img:my-0 prose-ul:-my-4 prose-ol:-my-4 prose-li:-my-3 prose-ul:-mb-8 prose-ol:-mb-8 prose-li:-mb-4 whitespace-pre-line"
+												class="prose chat-{message.role} w-full max-w-full prose-invert prose-headings:my-0 prose-p:my-0 prose-p:-mb-4 prose-pre:my-0 prose-table:my-0 prose-blockquote:my-0 prose-img:my-0 prose-ul:-my-4 prose-ol:-my-4 prose-li:-my-3 prose-ul:-mb-8 prose-ol:-mb-8 prose-li:-mb-4 whitespace-pre-line"
 											>
 												{#if message.role == 'user'}
 													{#if message?.edit === true}
@@ -948,7 +1071,6 @@
 							<textarea
 								class="rounded-xl bg-gray-700 outline-none w-full py-3 px-5 pr-12 resize-none"
 								placeholder="Send a message"
-								bind:this={textareaElement}
 								bind:value={prompt}
 								on:keypress={(e) => {
 									if (e.keyCode == 13 && !e.shiftKey) {
@@ -956,12 +1078,13 @@
 									}
 									if (prompt !== '' && e.keyCode == 13 && !e.shiftKey) {
 										submitPrompt(prompt);
+										e.target.style.height = '';
 									}
 								}}
 								rows="1"
-								on:input={() => {
-									textareaElement.style.height = '';
-									textareaElement.style.height = Math.min(textareaElement.scrollHeight, 200) + 'px';
+								on:input={(e) => {
+									e.target.style.height = '';
+									e.target.style.height = Math.min(e.target.scrollHeight, 200) + 'px';
 								}}
 							/>
 							<div class=" absolute right-0 bottom-0">