forked from open-webui/open-webui
feat: modelfile content linked to chat page
This commit is contained in:
parent
12d7ae96b9
commit
587101da88
5 changed files with 148 additions and 128 deletions
|
@ -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'}
|
{#if messages.length == 0 && suggestionPrompts.length !== 0}
|
||||||
<Suggestions {submitPrompt} />
|
<Suggestions {suggestionPrompts} {submitPrompt} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if autoScroll === false && messages.length > 0}
|
{#if autoScroll === false && messages.length > 0}
|
||||||
|
|
|
@ -1,17 +1,24 @@
|
||||||
<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">
|
<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
|
<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"
|
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={() => {
|
on:click={() => {
|
||||||
submitPrompt(`Tell me a random fun fact about the Roman Empire`);
|
submitPrompt(prompt.content);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="flex flex-col text-left">
|
<div class="flex flex-col text-left self-center">
|
||||||
<div class="text-sm font-medium dark:text-gray-300">Tell me a fun fact</div>
|
{#if prompt.title}
|
||||||
<div class="text-sm text-gray-500">about the Roman Empire</div>
|
<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>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
@ -31,92 +38,6 @@
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</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>
|
||||||
<div
|
{/each}
|
||||||
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"
|
|
||||||
>
|
|
||||||
<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(
|
|
||||||
`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>
|
</div>
|
||||||
|
|
|
@ -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">
|
||||||
|
{#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" />
|
<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">
|
<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?
|
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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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}
|
||||||
|
|
Loading…
Reference in a new issue