Browse Source

feat: modelfile content linked to chat page

Timothy J. Baek 1 year ago
parent
commit
587101da88

+ 3 - 3
src/lib/components/chat/MessageInput.svelte

@@ -6,7 +6,7 @@
 	export let submitPrompt: Function;
 	export let submitPrompt: Function;
 	export let stopResponse: Function;
 	export let stopResponse: Function;
 
 
-	export let suggestions = 'true';
+	export let suggestionPrompts = [];
 	export let autoScroll = true;
 	export let autoScroll = true;
 
 
 	let filesInputElement;
 	let filesInputElement;
@@ -87,8 +87,8 @@
 <div class="fixed bottom-0 w-full bg-white dark:bg-gray-800">
 <div class="fixed bottom-0 w-full bg-white dark:bg-gray-800">
 	<div class=" absolute right-0 left-0 bottom-0 mb-20">
 	<div class=" absolute right-0 left-0 bottom-0 mb-20">
 		<div class="max-w-3xl px-2.5 pt-2.5 -mb-0.5 mx-auto inset-x-0">
 		<div class="max-w-3xl px-2.5 pt-2.5 -mb-0.5 mx-auto inset-x-0">
-			{#if messages.length == 0 && suggestions !== 'false'}
-				<Suggestions {submitPrompt} />
+			{#if messages.length == 0 && suggestionPrompts.length !== 0}
+				<Suggestions {suggestionPrompts} {submitPrompt} />
 			{/if}
 			{/if}
 
 
 			{#if autoScroll === false && messages.length > 0}
 			{#if autoScroll === false && messages.length > 0}

+ 36 - 115
src/lib/components/chat/MessageInput/Suggestions.svelte

@@ -1,122 +1,43 @@
 <script lang="ts">
 <script lang="ts">
 	export let submitPrompt: Function;
 	export let submitPrompt: Function;
+	export let suggestionPrompts = [];
 </script>
 </script>
 
 
-<div class=" grid sm:grid-cols-2 gap-2.5 mb-4 md:p-2 text-left">
-	<button
-		class=" flex justify-between w-full px-4 py-2.5 bg-white hover:bg-gray-50 dark:bg-gray-800 dark:hover:bg-gray-700 outline outline-1 outline-gray-200 dark:outline-gray-600 rounded-lg transition group"
-		on:click={() => {
-			submitPrompt(`Tell me a random fun fact about the Roman Empire`);
-		}}
-	>
-		<div class="flex flex-col text-left">
-			<div class="text-sm font-medium dark:text-gray-300">Tell me a fun fact</div>
-			<div class="text-sm text-gray-500">about the Roman Empire</div>
-		</div>
-
-		<div
-			class="self-center p-1 rounded-lg text-white group-hover:bg-gray-100 group-hover:text-gray-800 dark:group-hover:bg-gray-800 dark:group-hover:text-gray-300 dark:text-gray-800 transition"
-		>
-			<svg
-				xmlns="http://www.w3.org/2000/svg"
-				viewBox="0 0 20 20"
-				fill="currentColor"
-				class="w-4 h-4"
-			>
-				<path
-					fill-rule="evenodd"
-					d="M10 17a.75.75 0 01-.75-.75V5.612L5.29 9.77a.75.75 0 01-1.08-1.04l5.25-5.5a.75.75 0 011.08 0l5.25 5.5a.75.75 0 11-1.08 1.04l-3.96-4.158V16.25A.75.75 0 0110 17z"
-					clip-rule="evenodd"
-				/>
-			</svg>
-		</div>
-	</button>
-
-	<button
-		class=" flex justify-between w-full px-4 py-2.5 bg-white hover:bg-gray-50 dark:bg-gray-800 dark:hover:bg-gray-700 outline outline-1 outline-gray-200 dark:outline-gray-600 rounded-lg transition group"
-		on:click={() => {
-			submitPrompt(`Show me a code snippet of a website's sticky header in CSS and JavaScript.`);
-		}}
-	>
-		<div class="flex flex-col text-left">
-			<div class="text-sm font-medium dark:text-gray-300">Show me a code snippet</div>
-			<div class="text-sm text-gray-500">of a website's sticky header</div>
-		</div>
-		<div
-			class="self-center p-1 rounded-lg text-white group-hover:bg-gray-100 group-hover:text-gray-800 dark:group-hover:bg-gray-800 dark:group-hover:text-gray-300 dark:text-gray-800 transition"
-		>
-			<svg
-				xmlns="http://www.w3.org/2000/svg"
-				viewBox="0 0 20 20"
-				fill="currentColor"
-				class="w-4 h-4"
-			>
-				<path
-					fill-rule="evenodd"
-					d="M10 17a.75.75 0 01-.75-.75V5.612L5.29 9.77a.75.75 0 01-1.08-1.04l5.25-5.5a.75.75 0 011.08 0l5.25 5.5a.75.75 0 11-1.08 1.04l-3.96-4.158V16.25A.75.75 0 0110 17z"
-					clip-rule="evenodd"
-				/>
-			</svg>
-		</div>
-	</button>
-
-	<button
-		class="  hidden sm:flex justify-between w-full px-4 py-2.5 bg-white hover:bg-gray-50 dark:bg-gray-800 dark:hover:bg-gray-700 outline outline-1 outline-gray-200 dark:outline-gray-600 rounded-lg transition group"
-		on:click={() => {
-			submitPrompt(
-				`Help me study vocabulary: write a sentence for me to fill in the blank, and I'll try to pick the correct option.`
-			);
-		}}
-	>
-		<div class="flex flex-col text-left">
-			<div class="text-sm font-medium dark:text-gray-300">Help me study</div>
-			<div class="text-sm text-gray-500">vocabulary for a college entrance exam</div>
-		</div>
-		<div
-			class="self-center p-1 rounded-lg text-white group-hover:bg-gray-100 group-hover:text-gray-800 dark:group-hover:bg-gray-800 dark:group-hover:text-gray-300 dark:text-gray-800 transition"
-		>
-			<svg
-				xmlns="http://www.w3.org/2000/svg"
-				viewBox="0 0 20 20"
-				fill="currentColor"
-				class="w-4 h-4"
+<div class=" flex flex-wrap-reverse mb-3 md:p-1 text-left">
+	{#each suggestionPrompts as prompt, promptIdx}
+		<div class="{promptIdx > 1 ? 'hidden sm:inline-flex' : ''} basis-full sm:basis-1/2 p-[5px]">
+			<button
+				class=" flex-1 flex justify-between w-full px-4 py-2.5 bg-white hover:bg-gray-50 dark:bg-gray-800 dark:hover:bg-gray-700 outline outline-1 outline-gray-200 dark:outline-gray-600 rounded-lg transition group"
+				on:click={() => {
+					submitPrompt(prompt.content);
+				}}
 			>
 			>
-				<path
-					fill-rule="evenodd"
-					d="M10 17a.75.75 0 01-.75-.75V5.612L5.29 9.77a.75.75 0 01-1.08-1.04l5.25-5.5a.75.75 0 011.08 0l5.25 5.5a.75.75 0 11-1.08 1.04l-3.96-4.158V16.25A.75.75 0 0110 17z"
-					clip-rule="evenodd"
-				/>
-			</svg>
-		</div>
-	</button>
+				<div class="flex flex-col text-left self-center">
+					{#if prompt.title}
+						<div class="text-sm font-medium dark:text-gray-300">{prompt.title[0]}</div>
+						<div class="text-sm text-gray-500">{prompt.title[1]}</div>
+					{:else}
+						<div class=" self-center text-sm font-medium dark:text-gray-300">{prompt.content}</div>
+					{/if}
+				</div>
 
 
-	<button
-		class="  hidden sm:flex justify-between w-full px-4 py-2.5 bg-white hover:bg-gray-50 dark:bg-gray-800 dark:hover:bg-gray-700 outline outline-1 outline-gray-200 dark:outline-gray-600 rounded-lg transition group"
-		on:click={() => {
-			submitPrompt(
-				`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.`
-			);
-		}}
-	>
-		<div class="flex flex-col text-left">
-			<div class="text-sm font-medium dark:text-gray-300">Give me ideas</div>
-			<div class="text-sm text-gray-500">for what to do with my kids' art</div>
-		</div>
-		<div
-			class="self-center p-1 rounded-lg text-white group-hover:bg-gray-100 group-hover:text-gray-800 dark:group-hover:bg-gray-800 dark:group-hover:text-gray-300 dark:text-gray-800 transition"
-		>
-			<svg
-				xmlns="http://www.w3.org/2000/svg"
-				viewBox="0 0 20 20"
-				fill="currentColor"
-				class="w-4 h-4"
-			>
-				<path
-					fill-rule="evenodd"
-					d="M10 17a.75.75 0 01-.75-.75V5.612L5.29 9.77a.75.75 0 01-1.08-1.04l5.25-5.5a.75.75 0 011.08 0l5.25 5.5a.75.75 0 11-1.08 1.04l-3.96-4.158V16.25A.75.75 0 0110 17z"
-					clip-rule="evenodd"
-				/>
-			</svg>
-		</div>
-	</button>
+				<div
+					class="self-center p-1 rounded-lg text-white group-hover:bg-gray-100 group-hover:text-gray-800 dark:group-hover:bg-gray-800 dark:group-hover:text-gray-300 dark:text-gray-800 transition"
+				>
+					<svg
+						xmlns="http://www.w3.org/2000/svg"
+						viewBox="0 0 20 20"
+						fill="currentColor"
+						class="w-4 h-4"
+					>
+						<path
+							fill-rule="evenodd"
+							d="M10 17a.75.75 0 01-.75-.75V5.612L5.29 9.77a.75.75 0 01-1.08-1.04l5.25-5.5a.75.75 0 011.08 0l5.25 5.5a.75.75 0 11-1.08 1.04l-3.96-4.158V16.25A.75.75 0 0110 17z"
+							clip-rule="evenodd"
+						/>
+					</svg>
+				</div>
+			</button>
+		</div>
+	{/each}
 </div>
 </div>

+ 23 - 4
src/lib/components/chat/Messages.svelte

@@ -7,7 +7,7 @@
 	import auto_render from 'katex/dist/contrib/auto-render.mjs';
 	import auto_render from 'katex/dist/contrib/auto-render.mjs';
 	import 'katex/dist/katex.min.css';
 	import 'katex/dist/katex.min.css';
 
 
-	import { config, db, settings, user } from '$lib/stores';
+	import { config, db, modelfiles, settings, user } from '$lib/stores';
 	import { tick } from 'svelte';
 	import { tick } from 'svelte';
 
 
 	import toast from 'svelte-french-toast';
 	import toast from 'svelte-french-toast';
@@ -16,9 +16,12 @@
 	export let regenerateResponse: Function;
 	export let regenerateResponse: Function;
 
 
 	export let autoScroll;
 	export let autoScroll;
+	export let selectedModels;
 	export let history = {};
 	export let history = {};
 	export let messages = [];
 	export let messages = [];
 
 
+	export let selectedModelfile = null;
+
 	$: if (messages && messages.length > 0 && (messages.at(-1).done ?? false)) {
 	$: if (messages && messages.length > 0 && (messages.at(-1).done ?? false)) {
 		(async () => {
 		(async () => {
 			await tick();
 			await tick();
@@ -306,10 +309,18 @@
 {#if messages.length == 0}
 {#if messages.length == 0}
 	<div class="m-auto text-center max-w-md pb-56 px-2">
 	<div class="m-auto text-center max-w-md pb-56 px-2">
 		<div class="flex justify-center mt-8">
 		<div class="flex justify-center mt-8">
-			<img src="/ollama.png" class=" w-16 invert-[10%] dark:invert-[100%] rounded-full" />
+			{#if selectedModelfile && selectedModelfile.imageUrl}
+				<img src={selectedModelfile?.imageUrl} class=" w-20 mb-2 rounded-full" />
+			{:else}
+				<img src="/ollama.png" class=" w-16 invert-[10%] dark:invert-[100%] rounded-full" />
+			{/if}
 		</div>
 		</div>
-		<div class=" mt-1 text-2xl text-gray-800 dark:text-gray-100 font-semibold">
-			How can I help you today?
+		<div class=" mt-2 text-2xl text-gray-800 dark:text-gray-100 font-semibold">
+			{#if selectedModelfile}
+				{selectedModelfile.desc}
+			{:else}
+				How can I help you today?
+			{/if}
 		</div>
 		</div>
 	</div>
 	</div>
 {:else}
 {:else}
@@ -332,6 +343,12 @@
 									alt="User profile"
 									alt="User profile"
 								/>
 								/>
 							{/if}
 							{/if}
+						{:else if selectedModelfile}
+							<img
+								src={selectedModelfile?.imageUrl ?? '/favicon.png'}
+								class=" max-w-[28px] object-cover rounded-full"
+								alt="Ollama profile"
+							/>
 						{:else}
 						{:else}
 							<img
 							<img
 								src="/favicon.png"
 								src="/favicon.png"
@@ -345,6 +362,8 @@
 						<div class=" self-center font-bold mb-0.5">
 						<div class=" self-center font-bold mb-0.5">
 							{#if message.role === 'user'}
 							{#if message.role === 'user'}
 								You
 								You
+							{:else if selectedModelfile}
+								{selectedModelfile.title}
 							{:else}
 							{:else}
 								Ollama <span class=" text-gray-500 text-sm font-medium"
 								Ollama <span class=" text-gray-500 text-sm font-medium"
 									>{message.model ? ` ${message.model}` : ''}</span
 									>{message.model ? ` ${message.model}` : ''}</span

+ 46 - 4
src/routes/(app)/+page.svelte

@@ -7,17 +7,24 @@
 	import { splitStream } from '$lib/utils';
 	import { splitStream } from '$lib/utils';
 	import { goto } from '$app/navigation';
 	import { goto } from '$app/navigation';
 
 
-	import { config, user, settings, db, chats, chatId } from '$lib/stores';
+	import { config, modelfiles, user, settings, db, chats, chatId } from '$lib/stores';
 
 
 	import MessageInput from '$lib/components/chat/MessageInput.svelte';
 	import MessageInput from '$lib/components/chat/MessageInput.svelte';
 	import Messages from '$lib/components/chat/Messages.svelte';
 	import Messages from '$lib/components/chat/Messages.svelte';
 	import ModelSelector from '$lib/components/chat/ModelSelector.svelte';
 	import ModelSelector from '$lib/components/chat/ModelSelector.svelte';
 	import Navbar from '$lib/components/layout/Navbar.svelte';
 	import Navbar from '$lib/components/layout/Navbar.svelte';
+	import { page } from '$app/stores';
 
 
 	let stopResponseFlag = false;
 	let stopResponseFlag = false;
 	let autoScroll = true;
 	let autoScroll = true;
 
 
 	let selectedModels = [''];
 	let selectedModels = [''];
+	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;
 
 
 	let title = '';
 	let title = '';
 	let prompt = '';
 	let prompt = '';
@@ -64,7 +71,9 @@
 			messages: {},
 			messages: {},
 			currentId: null
 			currentId: null
 		};
 		};
-		selectedModels = $settings.models ?? [''];
+		selectedModels = $page.url.searchParams.get('models')
+			? $page.url.searchParams.get('models')?.split(',')
+			: $settings.models ?? [''];
 	};
 	};
 
 
 	//////////////////////////
 	//////////////////////////
@@ -487,9 +496,42 @@
 		</div>
 		</div>
 
 
 		<div class=" h-full mt-10 mb-32 w-full flex flex-col">
 		<div class=" h-full mt-10 mb-32 w-full flex flex-col">
-			<Messages bind:history bind:messages bind:autoScroll {sendPrompt} {regenerateResponse} />
+			<Messages
+				{selectedModels}
+				{selectedModelfile}
+				bind:history
+				bind:messages
+				bind:autoScroll
+				{sendPrompt}
+				{regenerateResponse}
+			/>
 		</div>
 		</div>
 	</div>
 	</div>
 
 
-	<MessageInput bind:prompt bind:files bind:autoScroll {messages} {submitPrompt} {stopResponse} />
+	<MessageInput
+		bind:prompt
+		bind:files
+		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}
+	/>
 </div>
 </div>

+ 41 - 3
src/routes/(app)/c/[id]/+page.svelte

@@ -6,7 +6,7 @@
 	import { onMount, tick } from 'svelte';
 	import { onMount, tick } from 'svelte';
 	import { convertMessagesToHistory, splitStream } from '$lib/utils';
 	import { convertMessagesToHistory, splitStream } from '$lib/utils';
 	import { goto } from '$app/navigation';
 	import { goto } from '$app/navigation';
-	import { config, user, settings, db, chats, chatId } from '$lib/stores';
+	import { config, modelfiles, user, settings, db, chats, chatId } from '$lib/stores';
 
 
 	import MessageInput from '$lib/components/chat/MessageInput.svelte';
 	import MessageInput from '$lib/components/chat/MessageInput.svelte';
 	import Messages from '$lib/components/chat/Messages.svelte';
 	import Messages from '$lib/components/chat/Messages.svelte';
@@ -20,6 +20,12 @@
 
 
 	// let chatId = $page.params.id;
 	// let chatId = $page.params.id;
 	let selectedModels = [''];
 	let selectedModels = [''];
+	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;
 
 
 	let title = '';
 	let title = '';
 	let prompt = '';
 	let prompt = '';
@@ -521,10 +527,42 @@
 			</div>
 			</div>
 
 
 			<div class=" h-full mt-10 mb-32 w-full flex flex-col">
 			<div class=" h-full mt-10 mb-32 w-full flex flex-col">
-				<Messages bind:history bind:messages bind:autoScroll {sendPrompt} {regenerateResponse} />
+				<Messages
+					{selectedModels}
+					{selectedModelfile}
+					bind:history
+					bind:messages
+					bind:autoScroll
+					{sendPrompt}
+					{regenerateResponse}
+				/>
 			</div>
 			</div>
 		</div>
 		</div>
 
 
-		<MessageInput bind:prompt bind:autoScroll {messages} {submitPrompt} {stopResponse} />
+		<MessageInput
+			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}
+		/>
 	</div>
 	</div>
 {/if}
 {/if}