forked from open-webui/open-webui
feat:'@' model support
This commit is contained in:
parent
bf35297e4a
commit
d680d52b85
5 changed files with 97 additions and 30 deletions
|
@ -1,23 +1,25 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import { onMount, tick, getContext } from 'svelte';
|
import { onMount, tick, getContext } from 'svelte';
|
||||||
import { settings, showSidebar } from '$lib/stores';
|
import { modelfiles, settings, showSidebar } from '$lib/stores';
|
||||||
import { blobToFile, calculateSHA256, findWordIndices } from '$lib/utils';
|
import { blobToFile, calculateSHA256, findWordIndices } from '$lib/utils';
|
||||||
|
|
||||||
import Prompts from './MessageInput/PromptCommands.svelte';
|
|
||||||
import Suggestions from './MessageInput/Suggestions.svelte';
|
|
||||||
import {
|
import {
|
||||||
uploadDocToVectorDB,
|
uploadDocToVectorDB,
|
||||||
uploadWebToVectorDB,
|
uploadWebToVectorDB,
|
||||||
uploadYoutubeTranscriptionToVectorDB
|
uploadYoutubeTranscriptionToVectorDB
|
||||||
} from '$lib/apis/rag';
|
} from '$lib/apis/rag';
|
||||||
|
import { SUPPORTED_FILE_TYPE, SUPPORTED_FILE_EXTENSIONS, WEBUI_BASE_URL } from '$lib/constants';
|
||||||
|
|
||||||
|
import { transcribeAudio } from '$lib/apis/audio';
|
||||||
|
|
||||||
|
import Prompts from './MessageInput/PromptCommands.svelte';
|
||||||
|
import Suggestions from './MessageInput/Suggestions.svelte';
|
||||||
import AddFilesPlaceholder from '../AddFilesPlaceholder.svelte';
|
import AddFilesPlaceholder from '../AddFilesPlaceholder.svelte';
|
||||||
import { SUPPORTED_FILE_TYPE, SUPPORTED_FILE_EXTENSIONS } from '$lib/constants';
|
|
||||||
import Documents from './MessageInput/Documents.svelte';
|
import Documents from './MessageInput/Documents.svelte';
|
||||||
import Models from './MessageInput/Models.svelte';
|
import Models from './MessageInput/Models.svelte';
|
||||||
import { transcribeAudio } from '$lib/apis/audio';
|
|
||||||
import Tooltip from '../common/Tooltip.svelte';
|
import Tooltip from '../common/Tooltip.svelte';
|
||||||
import Page from '../../../routes/(app)/+page.svelte';
|
import XMark from '$lib/components/icons/XMark.svelte';
|
||||||
|
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
|
|
||||||
|
@ -25,6 +27,8 @@
|
||||||
export let stopResponse: Function;
|
export let stopResponse: Function;
|
||||||
|
|
||||||
export let autoScroll = true;
|
export let autoScroll = true;
|
||||||
|
export let selectedModel = '';
|
||||||
|
|
||||||
let chatTextAreaElement: HTMLTextAreaElement;
|
let chatTextAreaElement: HTMLTextAreaElement;
|
||||||
let filesInputElement;
|
let filesInputElement;
|
||||||
|
|
||||||
|
@ -424,11 +428,11 @@
|
||||||
|
|
||||||
<div class="fixed bottom-0 {$showSidebar ? 'left-0 lg:left-[260px]' : 'left-0'} right-0">
|
<div class="fixed bottom-0 {$showSidebar ? 'left-0 lg:left-[260px]' : 'left-0'} right-0">
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<div class=" px-2.5 -mb-0.5 mx-auto inset-x-0 bg-transparent flex justify-center">
|
<div class="px-2.5 lg:px-16 -mb-0.5 mx-auto inset-x-0 bg-transparent flex justify-center">
|
||||||
<div class="flex flex-col max-w-5xl w-full">
|
<div class="flex flex-col max-w-5xl w-full">
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
{#if autoScroll === false && messages.length > 0}
|
{#if autoScroll === false && messages.length > 0}
|
||||||
<div class=" absolute -top-12 left-0 right-0 flex justify-center">
|
<div class=" absolute -top-12 left-0 right-0 flex justify-center z-30">
|
||||||
<button
|
<button
|
||||||
class=" bg-white border border-gray-100 dark:border-none dark:bg-white/20 p-1.5 rounded-full"
|
class=" bg-white border border-gray-100 dark:border-none dark:bg-white/20 p-1.5 rounded-full"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
|
@ -488,14 +492,46 @@
|
||||||
bind:user
|
bind:user
|
||||||
bind:chatInputPlaceholder
|
bind:chatInputPlaceholder
|
||||||
{messages}
|
{messages}
|
||||||
|
on:select={(e) => {
|
||||||
|
selectedModel = e.detail;
|
||||||
|
chatTextAreaElement?.focus();
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- {#if messages.length == 0 && suggestionPrompts.length !== 0}
|
{#if selectedModel !== ''}
|
||||||
<Suggestions {suggestionPrompts} {submitPrompt} />
|
<div
|
||||||
{/if} -->
|
class="md:px-3 py-2.5 text-left w-full flex justify-between items-center absolute bottom-0 left-0 right-0 bg-gradient-to-t from-50% from-white dark:from-gray-900"
|
||||||
|
>
|
||||||
|
<div class="flex items-center gap-2 text-sm dark:text-gray-500">
|
||||||
|
<img
|
||||||
|
alt="model profile"
|
||||||
|
class="size-5 max-w-[28px] object-cover rounded-full"
|
||||||
|
src={$modelfiles.find((modelfile) => modelfile.tagName === selectedModel.id)
|
||||||
|
?.imageUrl ??
|
||||||
|
($i18n.language === 'dg-DG'
|
||||||
|
? `/doge.png`
|
||||||
|
: `${WEBUI_BASE_URL}/static/favicon.png`)}
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
Talking to <span class=" font-medium">{selectedModel.name} </span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
class="flex items-center"
|
||||||
|
on:click={() => {
|
||||||
|
selectedModel = '';
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<XMark />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bg-white dark:bg-gray-900">
|
<div class="bg-white dark:bg-gray-900">
|
||||||
<div class="max-w-6xl px-2.5 lg:px-16 mx-auto inset-x-0">
|
<div class="max-w-6xl px-2.5 lg:px-16 mx-auto inset-x-0">
|
||||||
<div class=" pb-2">
|
<div class=" pb-2">
|
||||||
|
@ -832,6 +868,11 @@
|
||||||
e.target.style.height = '';
|
e.target.style.height = '';
|
||||||
e.target.style.height = Math.min(e.target.scrollHeight, 200) + 'px';
|
e.target.style.height = Math.min(e.target.scrollHeight, 200) + 'px';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
console.log('Escape');
|
||||||
|
selectedModel = '';
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
rows="1"
|
rows="1"
|
||||||
on:input={(e) => {
|
on:input={(e) => {
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
|
||||||
import { generatePrompt } from '$lib/apis/ollama';
|
import { generatePrompt } from '$lib/apis/ollama';
|
||||||
import { models } from '$lib/stores';
|
import { models } from '$lib/stores';
|
||||||
import { splitStream } from '$lib/utils';
|
import { splitStream } from '$lib/utils';
|
||||||
|
@ -7,14 +9,14 @@
|
||||||
|
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
export let prompt = '';
|
export let prompt = '';
|
||||||
export let user = null;
|
export let user = null;
|
||||||
|
|
||||||
export let chatInputPlaceholder = '';
|
export let chatInputPlaceholder = '';
|
||||||
export let messages = [];
|
export let messages = [];
|
||||||
|
|
||||||
let selectedModel = null;
|
|
||||||
|
|
||||||
let selectedIdx = 0;
|
let selectedIdx = 0;
|
||||||
let filteredModels = [];
|
let filteredModels = [];
|
||||||
|
|
||||||
|
@ -36,9 +38,7 @@
|
||||||
|
|
||||||
const confirmSelect = async (model) => {
|
const confirmSelect = async (model) => {
|
||||||
prompt = '';
|
prompt = '';
|
||||||
selectedModel = model;
|
dispatch('select', model);
|
||||||
|
|
||||||
console.log(selectedModel);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmSelectCollaborativeChat = async (model) => {
|
const confirmSelectCollaborativeChat = async (model) => {
|
||||||
|
@ -170,11 +170,3 @@
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if selectedModel !== null}
|
|
||||||
<div class="md:px-2 mb-3 text-left w-full absolute bottom-0 left-0 right-0">
|
|
||||||
<div>
|
|
||||||
{JSON.stringify(selectedModel)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
15
src/lib/components/icons/XMark.svelte
Normal file
15
src/lib/components/icons/XMark.svelte
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<script lang="ts">
|
||||||
|
export let className = 'size-3.5';
|
||||||
|
export let strokeWidth = '2';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width={strokeWidth}
|
||||||
|
stroke="currentColor"
|
||||||
|
class={className}
|
||||||
|
>
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
|
||||||
|
</svg>
|
|
@ -51,7 +51,9 @@
|
||||||
let currentRequestId = null;
|
let currentRequestId = null;
|
||||||
|
|
||||||
let showModelSelector = true;
|
let showModelSelector = true;
|
||||||
|
|
||||||
let selectedModels = [''];
|
let selectedModels = [''];
|
||||||
|
let atSelectedModel = '';
|
||||||
|
|
||||||
let selectedModelfile = null;
|
let selectedModelfile = null;
|
||||||
$: selectedModelfile =
|
$: selectedModelfile =
|
||||||
|
@ -145,7 +147,8 @@
|
||||||
setTimeout(() => chatInput?.focus(), 0);
|
setTimeout(() => chatInput?.focus(), 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
const scrollToBottom = () => {
|
const scrollToBottom = async () => {
|
||||||
|
await tick();
|
||||||
if (messagesContainerElement) {
|
if (messagesContainerElement) {
|
||||||
messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
|
messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
|
||||||
}
|
}
|
||||||
|
@ -243,7 +246,8 @@
|
||||||
const _chatId = JSON.parse(JSON.stringify($chatId));
|
const _chatId = JSON.parse(JSON.stringify($chatId));
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
selectedModels.map(async (modelId) => {
|
(atSelectedModel !== '' ? [atSelectedModel.id] : selectedModels).map(async (modelId) => {
|
||||||
|
console.log('modelId', modelId);
|
||||||
const model = $models.filter((m) => m.id === modelId).at(0);
|
const model = $models.filter((m) => m.id === modelId).at(0);
|
||||||
|
|
||||||
if (model) {
|
if (model) {
|
||||||
|
@ -537,7 +541,7 @@
|
||||||
|
|
||||||
console.log(docs);
|
console.log(docs);
|
||||||
|
|
||||||
console.log(model);
|
scrollToBottom();
|
||||||
|
|
||||||
const [res, controller] = await generateOpenAIChatCompletion(
|
const [res, controller] = await generateOpenAIChatCompletion(
|
||||||
localStorage.token,
|
localStorage.token,
|
||||||
|
@ -884,4 +888,13 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MessageInput bind:files bind:prompt bind:autoScroll {messages} {submitPrompt} {stopResponse} />
|
|
||||||
|
<MessageInput
|
||||||
|
bind:files
|
||||||
|
bind:prompt
|
||||||
|
bind:autoScroll
|
||||||
|
bind:selectedModel={atSelectedModel}
|
||||||
|
{messages}
|
||||||
|
{submitPrompt}
|
||||||
|
{stopResponse}
|
||||||
|
/>
|
||||||
|
|
|
@ -57,6 +57,8 @@
|
||||||
// let chatId = $page.params.id;
|
// let chatId = $page.params.id;
|
||||||
let showModelSelector = true;
|
let showModelSelector = true;
|
||||||
let selectedModels = [''];
|
let selectedModels = [''];
|
||||||
|
let atSelectedModel = '';
|
||||||
|
|
||||||
let selectedModelfile = null;
|
let selectedModelfile = null;
|
||||||
|
|
||||||
$: selectedModelfile =
|
$: selectedModelfile =
|
||||||
|
@ -167,7 +169,8 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const scrollToBottom = () => {
|
const scrollToBottom = async () => {
|
||||||
|
await tick();
|
||||||
if (messagesContainerElement) {
|
if (messagesContainerElement) {
|
||||||
messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
|
messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
|
||||||
}
|
}
|
||||||
|
@ -256,7 +259,7 @@
|
||||||
const _chatId = JSON.parse(JSON.stringify($chatId));
|
const _chatId = JSON.parse(JSON.stringify($chatId));
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
selectedModels.map(async (modelId) => {
|
(atSelectedModel !== '' ? [atSelectedModel.id] : selectedModels).map(async (modelId) => {
|
||||||
const model = $models.filter((m) => m.id === modelId).at(0);
|
const model = $models.filter((m) => m.id === modelId).at(0);
|
||||||
|
|
||||||
if (model) {
|
if (model) {
|
||||||
|
@ -550,6 +553,8 @@
|
||||||
|
|
||||||
console.log(docs);
|
console.log(docs);
|
||||||
|
|
||||||
|
scrollToBottom();
|
||||||
|
|
||||||
const [res, controller] = await generateOpenAIChatCompletion(
|
const [res, controller] = await generateOpenAIChatCompletion(
|
||||||
localStorage.token,
|
localStorage.token,
|
||||||
{
|
{
|
||||||
|
@ -911,6 +916,7 @@
|
||||||
bind:files
|
bind:files
|
||||||
bind:prompt
|
bind:prompt
|
||||||
bind:autoScroll
|
bind:autoScroll
|
||||||
|
bind:selectedModel={atSelectedModel}
|
||||||
suggestionPrompts={selectedModelfile?.suggestionPrompts ?? $config.default_prompt_suggestions}
|
suggestionPrompts={selectedModelfile?.suggestionPrompts ?? $config.default_prompt_suggestions}
|
||||||
{messages}
|
{messages}
|
||||||
{submitPrompt}
|
{submitPrompt}
|
||||||
|
|
Loading…
Reference in a new issue