"training/configs/model/gpt2model/gpt2-medium.yaml" did not exist on "9bc63d1e2dd3eee8cf0307036e077a752bd79fde"
+page.svelte 18.5 KB
Newer Older
1
2
3
4
5
6
7
8
<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';
9
	import { config, models, modelfiles, user, settings, db, chats, chatId } from '$lib/stores';
10
11
12
13
14
15

	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';
Timothy J. Baek's avatar
Timothy J. Baek committed
16
	import { createNewChat, getChatById, getChatList } from '$lib/apis/chats';
17
18
19
20
21
22
23

	let loaded = false;
	let stopResponseFlag = false;
	let autoScroll = true;

	// let chatId = $page.params.id;
	let selectedModels = [''];
24
25
26
27
28
29
	let selectedModelfile = null;
	$: selectedModelfile =
		selectedModels.length === 1 &&
		$modelfiles.filter((modelfile) => modelfile.tagName === selectedModels[0]).length > 0
			? $modelfiles.filter((modelfile) => modelfile.tagName === selectedModels[0])[0]
			: null;
30

31
32
	let chat = null;

33
34
	let title = '';
	let prompt = '';
Timothy J. Baek's avatar
Timothy J. Baek committed
35
	let files = [];
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

	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;
Timothy J. Baek's avatar
Timothy J. Baek committed
53
54
	} else {
		messages = [];
55
56
57
58
	}

	$: if ($page.params.id) {
		(async () => {
59
60
			if (await loadChat()) {
				await tick();
61
62
63
64
				loaded = true;
			} else {
				await goto('/');
			}
65
66
67
68
69
70
71
72
73
		})();
	}

	//////////////////////////
	// Web functions
	//////////////////////////

	const loadChat = async () => {
		await chatId.set($page.params.id);
Timothy J. Baek's avatar
Timothy J. Baek committed
74
		chat = await getChatById(localStorage.token, $chatId);
75

76
		const chatContent = chat.chat;
77

78
79
80
81
82
83
84
		if (chatContent) {
			console.log(chatContent);

			selectedModels =
				(chatContent?.models ?? undefined) !== undefined
					? chatContent.models
					: [chatContent.model ?? ''];
85
			history =
86
87
88
89
				(chatContent?.history ?? undefined) !== undefined
					? chatContent.history
					: convertMessagesToHistory(chatContent.messages);
			title = chatContent.title;
90

Timothy J. Baek's avatar
Timothy J. Baek committed
91
			let _settings = JSON.parse(localStorage.getItem('settings') ?? '{}');
92
			await settings.set({
Timothy J. Baek's avatar
Timothy J. Baek committed
93
				..._settings,
94
95
				system: chatContent.system ?? _settings.system,
				options: chatContent.options ?? _settings.options
96
97
98
			});
			autoScroll = true;
			await tick();
99

100
101
102
			if (messages.length > 0) {
				history.messages[messages.at(-1).id].done = true;
			}
Timothy J. Baek's avatar
Timothy J. Baek committed
103
104
			await tick();

105
			return true;
106
107
108
109
110
		} else {
			return null;
		}
	};

Timothy J. Baek's avatar
Timothy J. Baek committed
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
	const copyToClipboard = (text) => {
		if (!navigator.clipboard) {
			var textArea = document.createElement('textarea');
			textArea.value = text;

			// Avoid scrolling to bottom
			textArea.style.top = '0';
			textArea.style.left = '0';
			textArea.style.position = 'fixed';

			document.body.appendChild(textArea);
			textArea.focus();
			textArea.select();

			try {
				var successful = document.execCommand('copy');
				var msg = successful ? 'successful' : 'unsuccessful';
				console.log('Fallback: Copying text command was ' + msg);
			} catch (err) {
				console.error('Fallback: Oops, unable to copy', err);
			}

			document.body.removeChild(textArea);
			return;
		}
		navigator.clipboard.writeText(text).then(
			function () {
				console.log('Async: Copying to clipboard was successful!');
			},
			function (err) {
				console.error('Async: Could not copy text: ', err);
			}
		);
	};

146
147
148
149
	//////////////////////////
	// Ollama functions
	//////////////////////////

150
151
	const sendPrompt = async (prompt, parentId) => {
		const _chatId = JSON.parse(JSON.stringify($chatId));
152
153
		await Promise.all(
			selectedModels.map(async (model) => {
154
155
				console.log(model);
				if ($models.filter((m) => m.name === model)[0].external) {
156
					await sendPromptOpenAI(model, prompt, parentId, _chatId);
157
				} else {
158
					await sendPromptOllama(model, prompt, parentId, _chatId);
159
160
161
162
				}
			})
		);

Timothy J. Baek's avatar
Timothy J. Baek committed
163
		await chats.set(await getChatList(localStorage.token));
164
165
	};

Timothy J. Baek's avatar
Timothy J. Baek committed
166
	const sendPromptOllama = async (model, userPrompt, parentId, _chatId) => {
Timothy J. Baek's avatar
Timothy J. Baek committed
167
		// Create response message
168
169
170
171
172
173
174
175
176
177
		let responseMessageId = uuidv4();
		let responseMessage = {
			parentId: parentId,
			id: responseMessageId,
			childrenIds: [],
			role: 'assistant',
			content: '',
			model: model
		};

Timothy J. Baek's avatar
Timothy J. Baek committed
178
		// Add message to history and Set currentId to messageId
179
180
		history.messages[responseMessageId] = responseMessage;
		history.currentId = responseMessageId;
Timothy J. Baek's avatar
Timothy J. Baek committed
181
182

		// Append messageId to childrenIds of parent message
183
184
185
186
187
188
189
		if (parentId !== null) {
			history.messages[parentId].childrenIds = [
				...history.messages[parentId].childrenIds,
				responseMessageId
			];
		}

Timothy J. Baek's avatar
Timothy J. Baek committed
190
		// Wait until history/message have been updated
Timothy J. Baek's avatar
Timothy J. Baek committed
191
		await tick();
Timothy J. Baek's avatar
Timothy J. Baek committed
192
193

		// Scroll down
Timothy J. Baek's avatar
Timothy J. Baek committed
194
		window.scrollTo({ top: document.body.scrollHeight });
195

Timothy J. Baek's avatar
Timothy J. Baek committed
196
197
198
199
		const res = await generateChatCompletion(
			$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL,
			localStorage.token,
			{
Timothy J. Baek's avatar
Timothy J. Baek committed
200
201
202
203
204
205
206
207
208
209
210
				model: model,
				messages: [
					$settings.system
						? {
								role: 'system',
								content: $settings.system
						  }
						: undefined,
					...messages
				]
					.filter((message) => message)
Timothy J. Baek's avatar
Timothy J. Baek committed
211
212
213
214
215
216
217
218
219
					.map((message) => ({
						role: message.role,
						content: message.content,
						...(message.files && {
							images: message.files
								.filter((file) => file.type === 'image')
								.map((file) => file.url.slice(file.url.indexOf(',') + 1))
						})
					})),
Timothy J. Baek's avatar
Timothy J. Baek committed
220
221
222
223
				options: {
					...($settings.options ?? {})
				},
				format: $settings.requestFormat ?? undefined
Timothy J. Baek's avatar
Timothy J. Baek committed
224
225
			}
		);
Timothy J. Baek's avatar
Timothy J. Baek committed
226

227
		if (res && res.ok) {
Rohit Das's avatar
Rohit Das committed
228
229
230
231
232
233
234
235
236
237
238
239
			const reader = res.body
				.pipeThrough(new TextDecoderStream())
				.pipeThrough(splitStream('\n'))
				.getReader();

			while (true) {
				const { value, done } = await reader.read();
				if (done || stopResponseFlag || _chatId !== $chatId) {
					responseMessage.done = true;
					messages = messages;
					break;
				}
240

Rohit Das's avatar
Rohit Das committed
241
242
				try {
					let lines = value.split('\n');
243

Rohit Das's avatar
Rohit Das committed
244
245
246
247
					for (const line of lines) {
						if (line !== '') {
							console.log(line);
							let data = JSON.parse(line);
Timothy J. Baek's avatar
Timothy J. Baek committed
248

Rohit Das's avatar
Rohit Das committed
249
250
251
							if ('detail' in data) {
								throw data;
							}
Timothy J. Baek's avatar
Timothy J. Baek committed
252

Rohit Das's avatar
Rohit Das committed
253
254
255
256
257
258
259
							if (data.done == false) {
								if (responseMessage.content == '' && data.message.content == '\n') {
									continue;
								} else {
									responseMessage.content += data.message.content;
									messages = messages;
								}
260
							} else {
Rohit Das's avatar
Rohit Das committed
261
								responseMessage.done = true;
262
263
264
265
266
267
268

								if (responseMessage.content == '') {
									responseMessage.error = true;
									responseMessage.content =
										'Oops! No text generated from Ollama, Please try again.';
								}

Rohit Das's avatar
Rohit Das committed
269
270
271
								responseMessage.context = data.context ?? null;
								responseMessage.info = {
									total_duration: data.total_duration,
Timothy J. Baek's avatar
Timothy J. Baek committed
272
273
274
									load_duration: data.load_duration,
									sample_count: data.sample_count,
									sample_duration: data.sample_duration,
Rohit Das's avatar
Rohit Das committed
275
276
277
278
279
									prompt_eval_count: data.prompt_eval_count,
									prompt_eval_duration: data.prompt_eval_duration,
									eval_count: data.eval_count,
									eval_duration: data.eval_duration
								};
280
								messages = messages;
Timothy J. Baek's avatar
Timothy J. Baek committed
281

Timothy J. Baek's avatar
Timothy J. Baek committed
282
								if ($settings.notificationEnabled && !document.hasFocus()) {
Timothy J. Baek's avatar
Timothy J. Baek committed
283
284
285
286
287
288
289
290
291
292
293
294
295
									const notification = new Notification(
										selectedModelfile
											? `${
													selectedModelfile.title.charAt(0).toUpperCase() +
													selectedModelfile.title.slice(1)
											  }`
											: `Ollama - ${model}`,
										{
											body: responseMessage.content,
											icon: selectedModelfile?.imageUrl ?? '/favicon.png'
										}
									);
								}
Timothy J. Baek's avatar
Timothy J. Baek committed
296
297
298
299

								if ($settings.responseAutoCopy) {
									copyToClipboard(responseMessage.content);
								}
300
301
302
							}
						}
					}
Rohit Das's avatar
Rohit Das committed
303
304
305
306
307
308
				} catch (error) {
					console.log(error);
					if ('detail' in error) {
						toast.error(error.detail);
					}
					break;
309
				}
Rohit Das's avatar
Rohit Das committed
310
311
312

				if (autoScroll) {
					window.scrollTo({ top: document.body.scrollHeight });
313
				}
314
			}
315

316
			if ($chatId == _chatId) {
Timothy J. Baek's avatar
Timothy J. Baek committed
317
				chat = await updateChatById(localStorage.token, _chatId, {
Rohit Das's avatar
Rohit Das committed
318
319
320
					messages: messages,
					history: history
				});
Timothy J. Baek's avatar
Timothy J. Baek committed
321
				await chats.set(await getChatList(localStorage.token));
322
			}
323
324
325
		} else {
			if (res !== null) {
				const error = await res.json();
326
327
328
				console.log(error);
				if ('detail' in error) {
					toast.error(error.detail);
329
					responseMessage.content = error.detail;
330
331
				} else {
					toast.error(error.error);
332
					responseMessage.content = error.error;
333
				}
334
335
			} else {
				toast.error(`Uh-oh! There was an issue connecting to Ollama.`);
336
				responseMessage.content = `Uh-oh! There was an issue connecting to Ollama.`;
337
338
			}

339
340
341
342
			responseMessage.error = true;
			responseMessage.content = `Uh-oh! There was an issue connecting to Ollama.`;
			responseMessage.done = true;
			messages = messages;
343
344
345
346
		}

		stopResponseFlag = false;
		await tick();
Timothy J. Baek's avatar
Timothy J. Baek committed
347

348
349
350
351
352
		if (autoScroll) {
			window.scrollTo({ top: document.body.scrollHeight });
		}

		if (messages.length == 2 && messages.at(1).content !== '') {
Timothy J. Baek's avatar
Timothy J. Baek committed
353
354
			window.history.replaceState(history.state, '', `/c/${_chatId}`);
			await generateChatTitle(_chatId, userPrompt);
355
356
357
		}
	};

Timothy J. Baek's avatar
Timothy J. Baek committed
358
	const sendPromptOpenAI = async (model, userPrompt, parentId, _chatId) => {
Timothy J. Baek's avatar
Timothy J. Baek committed
359
		if ($settings.OPENAI_API_KEY) {
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
			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 });

383
384
385
386
387
388
				const res = await fetch(
					`${$settings.OPENAI_API_BASE_URL ?? 'https://api.openai.com/v1'}/chat/completions`,
					{
						method: 'POST',
						headers: {
							Authorization: `Bearer ${$settings.OPENAI_API_KEY}`,
Timothy J. Baek's avatar
Timothy J. Baek committed
389
							'Content-Type': 'application/json'
390
391
392
393
394
395
						},
						body: JSON.stringify({
							model: model,
							stream: true,
							messages: [
								$settings.system
396
									? {
397
398
											role: 'system',
											content: $settings.system
399
									  }
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
									: undefined,
								...messages
							]
								.filter((message) => message)
								.map((message) => ({
									role: message.role,
									...(message.files
										? {
												content: [
													{
														type: 'text',
														text: message.content
													},
													...message.files
														.filter((file) => file.type === 'image')
														.map((file) => ({
															type: 'image_url',
															image_url: {
																url: file.url
															}
														}))
												]
										  }
										: { content: message.content })
								})),
							temperature: $settings.temperature ?? undefined,
							top_p: $settings.top_p ?? undefined,
							num_ctx: $settings.num_ctx ?? undefined,
							frequency_penalty: $settings.repeat_penalty ?? undefined
						})
430
					}
431
432
433
434
				).catch((err) => {
					console.log(err);
					return null;
				});
435

436
437
438
439
440
441
442
443
444
445
446
447
448
				if (res && res.ok) {
					const reader = res.body
						.pipeThrough(new TextDecoderStream())
						.pipeThrough(splitStream('\n'))
						.getReader();

					while (true) {
						const { value, done } = await reader.read();
						if (done || stopResponseFlag || _chatId !== $chatId) {
							responseMessage.done = true;
							messages = messages;
							break;
						}
449

450
451
						try {
							let lines = value.split('\n');
452

453
454
455
456
457
							for (const line of lines) {
								if (line !== '') {
									console.log(line);
									if (line === 'data: [DONE]') {
										responseMessage.done = true;
458
										messages = messages;
459
460
461
462
463
464
465
466
467
468
									} 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;
										}
469
470
471
									}
								}
							}
472
473
						} catch (error) {
							console.log(error);
474
475
						}

476
477
478
479
480
481
						if ($settings.notificationEnabled && !document.hasFocus()) {
							const notification = new Notification(`OpenAI ${model}`, {
								body: responseMessage.content,
								icon: '/favicon.png'
							});
						}
482

483
484
485
						if ($settings.responseAutoCopy) {
							copyToClipboard(responseMessage.content);
						}
486

487
488
489
						if (autoScroll) {
							window.scrollTo({ top: document.body.scrollHeight });
						}
490
					}
491

492
					if ($chatId == _chatId) {
Timothy J. Baek's avatar
Timothy J. Baek committed
493
						chat = await updateChatById(localStorage.token, _chatId, {
494
495
496
							messages: messages,
							history: history
						});
Timothy J. Baek's avatar
Timothy J. Baek committed
497
						await chats.set(await getChatList(localStorage.token));
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
					}
				} else {
					if (res !== null) {
						const error = await res.json();
						console.log(error);
						if ('detail' in error) {
							toast.error(error.detail);
							responseMessage.content = error.detail;
						} else {
							if ('message' in error.error) {
								toast.error(error.error.message);
								responseMessage.content = error.error.message;
							} else {
								toast.error(error.error);
								responseMessage.content = error.error;
							}
						}
					} else {
						toast.error(`Uh-oh! There was an issue connecting to ${model}.`);
						responseMessage.content = `Uh-oh! There was an issue connecting to ${model}.`;
					}
Timothy J. Baek's avatar
Timothy J. Baek committed
519

520
521
522
523
					responseMessage.error = true;
					responseMessage.content = `Uh-oh! There was an issue connecting to ${model}.`;
					responseMessage.done = true;
					messages = messages;
Timothy J. Baek's avatar
Timothy J. Baek committed
524
525
				}

526
527
				stopResponseFlag = false;
				await tick();
Timothy J. Baek's avatar
Timothy J. Baek committed
528

529
530
531
532
533
				if (autoScroll) {
					window.scrollTo({ top: document.body.scrollHeight });
				}

				if (messages.length == 2) {
Timothy J. Baek's avatar
Timothy J. Baek committed
534
535
					window.history.replaceState(history.state, '', `/c/${_chatId}`);
					await setChatTitle(_chatId, userPrompt);
536
537
538
539
540
541
				}
			}
		}
	};

	const submitPrompt = async (userPrompt) => {
542
		console.log('submitPrompt', $chatId);
543
544
545
546

		if (selectedModels.includes('')) {
			toast.error('Model not selected');
		} else if (messages.length != 0 && messages.at(-1).done != true) {
Timothy J. Baek's avatar
Timothy J. Baek committed
547
			// Response not done
548
549
			console.log('wait');
		} else {
Timothy J. Baek's avatar
Timothy J. Baek committed
550
			// Reset chat message textarea height
551
552
			document.getElementById('chat-textarea').style.height = '';

Timothy J. Baek's avatar
Timothy J. Baek committed
553
			// Create user message
554
555
556
557
558
559
			let userMessageId = uuidv4();
			let userMessage = {
				id: userMessageId,
				parentId: messages.length !== 0 ? messages.at(-1).id : null,
				childrenIds: [],
				role: 'user',
Timothy J. Baek's avatar
Timothy J. Baek committed
560
561
				content: userPrompt,
				files: files.length > 0 ? files : undefined
562
563
			};

Timothy J. Baek's avatar
Timothy J. Baek committed
564
565
566
567
568
			// Add message to history and Set currentId to messageId
			history.messages[userMessageId] = userMessage;
			history.currentId = userMessageId;

			// Append messageId to childrenIds of parent message
569
570
571
572
			if (messages.length !== 0) {
				history.messages[messages.at(-1).id].childrenIds.push(userMessageId);
			}

Timothy J. Baek's avatar
Timothy J. Baek committed
573
			// Wait until history/message have been updated
Timothy J. Baek's avatar
Timothy J. Baek committed
574
			await tick();
575

Timothy J. Baek's avatar
Timothy J. Baek committed
576
			// Create new chat if only one message in messages
Timothy J. Baek's avatar
Timothy J. Baek committed
577
			if (messages.length == 1) {
Timothy J. Baek's avatar
Timothy J. Baek committed
578
				chat = await createNewChat(localStorage.token, {
579
					id: $chatId,
580
581
582
583
					title: 'New Chat',
					models: selectedModels,
					system: $settings.system ?? undefined,
					options: {
584
						...($settings.options ?? {})
585
586
					},
					messages: messages,
Timothy J. Baek's avatar
Timothy J. Baek committed
587
588
					history: history,
					timestamp: Date.now()
589
				});
Timothy J. Baek's avatar
Timothy J. Baek committed
590
				await chats.set(await getChatList(localStorage.token));
591
592
				await chatId.set(chat.id);
				await tick();
593
594
			}

Timothy J. Baek's avatar
Timothy J. Baek committed
595
			// Reset chat input textarea
Timothy J. Baek's avatar
Timothy J. Baek committed
596
597
598
			prompt = '';
			files = [];

Timothy J. Baek's avatar
Timothy J. Baek committed
599
			// Send prompt
600
			await sendPrompt(userPrompt, userMessageId);
601
602
603
604
605
606
607
608
609
		}
	};

	const stopResponse = () => {
		stopResponseFlag = true;
		console.log('stopResponse');
	};

	const regenerateResponse = async () => {
Timothy J. Baek's avatar
Timothy J. Baek committed
610
		console.log('regenerateResponse');
611
612
613
614
615
616
617
		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;

Timothy J. Baek's avatar
Timothy J. Baek committed
618
			await sendPrompt(userPrompt, userMessage.id);
619
620
621
622
		}
	};

	const generateChatTitle = async (_chatId, userPrompt) => {
623
		if ($settings.titleAutoGenerate ?? true) {
Timothy J. Baek's avatar
Timothy J. Baek committed
624
625
626
627
628
629
630
631
632
			const title = await generateTitle(
				$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL,
				localStorage.token,
				selectedModels[0],
				userPrompt
			);

			if (title) {
				await setChatTitle(_chatId, title);
633
634
635
			}
		} else {
			await setChatTitle(_chatId, `${userPrompt}`);
636
637
638
639
		}
	};

	const setChatTitle = async (_chatId, _title) => {
640
		if (_chatId === $chatId) {
641
642
			title = _title;
		}
Timothy J. Baek's avatar
Timothy J. Baek committed
643
644
645

		chat = await updateChatById(localStorage.token, _chatId, { title: _title });
		await chats.set(await getChatList(localStorage.token));
646
647
648
649
650
651
652
653
654
	};
</script>

<svelte:window
	on:scroll={(e) => {
		autoScroll = window.innerHeight + window.scrollY >= document.body.offsetHeight - 40;
	}}
/>

Timothy J. Baek's avatar
Timothy J. Baek committed
655
{#if loaded}
656
657
658
659
660
661
662
	<Navbar
		{title}
		shareEnabled={messages.length > 0}
		initNewChat={() => {
			goto('/');
		}}
	/>
Timothy J. Baek's avatar
Timothy J. Baek committed
663
664
665
666
667
668
669
	<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">
670
				<Messages
671
					chatId={$chatId}
672
673
674
675
676
					{selectedModels}
					{selectedModelfile}
					bind:history
					bind:messages
					bind:autoScroll
Timothy J. Baek's avatar
Timothy J. Baek committed
677
					bottomPadding={files.length > 0}
678
679
680
					{sendPrompt}
					{regenerateResponse}
				/>
Timothy J. Baek's avatar
Timothy J. Baek committed
681
			</div>
682
683
		</div>

684
		<MessageInput
Timothy J. Baek's avatar
Timothy J. Baek committed
685
			bind:files
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
			bind:prompt
			bind:autoScroll
			suggestionPrompts={selectedModelfile?.suggestionPrompts ?? [
				{
					title: ['Help me study', 'vocabulary for a college entrance exam'],
					content: `Help me study vocabulary: write a sentence for me to fill in the blank, and I'll try to pick the correct option.`
				},
				{
					title: ['Give me ideas', `for what to do with my kids' art`],
					content: `What are 5 creative things I could do with my kids' art? I don't want to throw them away, but it's also so much clutter.`
				},
				{
					title: ['Tell me a fun fact', 'about the Roman Empire'],
					content: 'Tell me a random fun fact about the Roman Empire'
				},
				{
					title: ['Show me a code snippet', `of a website's sticky header`],
					content: `Show me a code snippet of a website's sticky header in CSS and JavaScript.`
				}
			]}
			{messages}
			{submitPrompt}
			{stopResponse}
		/>
710
	</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
711
{/if}