feat: unified models integration

This commit is contained in:
Timothy J. Baek 2024-05-24 03:02:56 -07:00
parent e80e4c304a
commit 468c6398cd
9 changed files with 94 additions and 92 deletions

View File

@ -207,7 +207,7 @@ def merge_models_lists(model_lists):
[ [
{ {
**model, **model,
"name": model["id"], "name": model.get("name", model["id"]),
"owned_by": "openai", "owned_by": "openai",
"openai": model, "openai": model,
"urlIdx": idx, "urlIdx": idx,
@ -319,6 +319,8 @@ async def proxy(path: str, request: Request, user=Depends(get_verified_user)):
body = body.decode("utf-8") body = body.decode("utf-8")
body = json.loads(body) body = json.loads(body)
print(app.state.MODELS)
model = app.state.MODELS[body.get("model")] model = app.state.MODELS[body.get("model")]
idx = model["urlIdx"] idx = model["urlIdx"]

View File

@ -276,13 +276,11 @@ async def get_models(user=Depends(get_verified_user)):
if app.state.config.ENABLE_OPENAI_API: if app.state.config.ENABLE_OPENAI_API:
openai_models = await get_openai_models() openai_models = await get_openai_models()
openai_app.state.MODELS = openai_models
openai_models = openai_models["data"] openai_models = openai_models["data"]
if app.state.config.ENABLE_OLLAMA_API: if app.state.config.ENABLE_OLLAMA_API:
ollama_models = await get_ollama_models() ollama_models = await get_ollama_models()
ollama_app.state.MODELS = ollama_models
print(ollama_models) print(ollama_models)

View File

@ -27,7 +27,7 @@ export const getModels = async (token: string = '') => {
let models = res?.data ?? []; let models = res?.data ?? [];
models = models.filter((models) => models).reduce((a, e, i, arr) => a.concat(e), []); models = models.filter((models) => models).sort((a, b) => (a.name > b.name ? 1 : -1));
console.log(models); console.log(models);
return models; return models;

View File

@ -11,7 +11,6 @@
chats, chats,
config, config,
type Model, type Model,
modelfiles,
models, models,
settings, settings,
showSidebar, showSidebar,
@ -63,24 +62,6 @@
let selectedModels = ['']; let selectedModels = [''];
let atSelectedModel: Model | undefined; let atSelectedModel: Model | undefined;
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 selectedModelfiles = {};
$: selectedModelfiles = selectedModels.reduce((a, tagName, i, arr) => {
const modelfile =
$modelfiles.filter((modelfile) => modelfile.tagName === tagName)?.at(0) ?? undefined;
return {
...a,
...(modelfile && { [tagName]: modelfile })
};
}, {});
let chat = null; let chat = null;
let tags = []; let tags = [];
@ -345,6 +326,7 @@
const hasImages = messages.some((message) => const hasImages = messages.some((message) =>
message.files?.some((file) => file.type === 'image') message.files?.some((file) => file.type === 'image')
); );
if (hasImages && !(model.custom_info?.meta.vision_capable ?? true)) { if (hasImages && !(model.custom_info?.meta.vision_capable ?? true)) {
toast.error( toast.error(
$i18n.t('Model {{modelName}} is not vision capable', { $i18n.t('Model {{modelName}} is not vision capable', {
@ -362,7 +344,7 @@
role: 'assistant', role: 'assistant',
content: '', content: '',
model: model.id, model: model.id,
modelName: model.custom_info?.name ?? model.name ?? model.id, modelName: model.name ?? model.id,
userContext: null, userContext: null,
timestamp: Math.floor(Date.now() / 1000) // Unix epoch timestamp: Math.floor(Date.now() / 1000) // Unix epoch
}; };
@ -407,7 +389,7 @@
} }
responseMessage.userContext = userContext; responseMessage.userContext = userContext;
if (model?.external) { if (model?.owned_by === 'openai') {
await sendPromptOpenAI(model, prompt, responseMessageId, _chatId); await sendPromptOpenAI(model, prompt, responseMessageId, _chatId);
} else if (model) { } else if (model) {
await sendPromptOllama(model, prompt, responseMessageId, _chatId); await sendPromptOllama(model, prompt, responseMessageId, _chatId);
@ -956,10 +938,8 @@
) + ' {{prompt}}', ) + ' {{prompt}}',
titleModelId, titleModelId,
userPrompt, userPrompt,
titleModel?.external ?? false titleModel?.owned_by === 'openai' ?? false
? titleModel?.source?.toLowerCase() === 'litellm' ? `${OPENAI_API_BASE_URL}`
? `${LITELLM_API_BASE_URL}/v1`
: `${OPENAI_API_BASE_URL}`
: `${OLLAMA_API_BASE_URL}/v1` : `${OLLAMA_API_BASE_URL}/v1`
); );
@ -1046,16 +1026,12 @@
<Messages <Messages
chatId={$chatId} chatId={$chatId}
{selectedModels} {selectedModels}
{selectedModelfiles}
{processing} {processing}
bind:history bind:history
bind:messages bind:messages
bind:autoScroll bind:autoScroll
bind:prompt bind:prompt
bottomPadding={files.length > 0} bottomPadding={files.length > 0}
suggestionPrompts={chatIdProp
? []
: selectedModelfile?.suggestionPrompts ?? $config.default_prompt_suggestions}
{sendPrompt} {sendPrompt}
{continueGeneration} {continueGeneration}
{regenerateResponse} {regenerateResponse}

View File

@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import { chats, config, modelfiles, settings, user as _user, mobile } from '$lib/stores'; import { chats, config, settings, user as _user, mobile } from '$lib/stores';
import { tick, getContext } from 'svelte'; import { tick, getContext } from 'svelte';
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
@ -26,7 +26,6 @@
export let user = $_user; export let user = $_user;
export let prompt; export let prompt;
export let suggestionPrompts = [];
export let processing = ''; export let processing = '';
export let bottomPadding = false; export let bottomPadding = false;
export let autoScroll; export let autoScroll;
@ -34,7 +33,6 @@
export let messages = []; export let messages = [];
export let selectedModels; export let selectedModels;
export let selectedModelfiles = [];
$: if (autoScroll && bottomPadding) { $: if (autoScroll && bottomPadding) {
(async () => { (async () => {
@ -247,9 +245,7 @@
<div class="h-full flex mb-16"> <div class="h-full flex mb-16">
{#if messages.length == 0} {#if messages.length == 0}
<Placeholder <Placeholder
models={selectedModels} modelIds={selectedModels}
modelfiles={selectedModelfiles}
{suggestionPrompts}
submitPrompt={async (p) => { submitPrompt={async (p) => {
let text = p; let text = p;
@ -316,7 +312,6 @@
{#key message.id} {#key message.id}
<ResponseMessage <ResponseMessage
{message} {message}
modelfiles={selectedModelfiles}
siblings={history.messages[message.parentId]?.childrenIds ?? []} siblings={history.messages[message.parentId]?.childrenIds ?? []}
isLastMessage={messageIdx + 1 === messages.length} isLastMessage={messageIdx + 1 === messages.length}
{readOnly} {readOnly}
@ -348,7 +343,6 @@
{chatId} {chatId}
parentMessage={history.messages[message.parentId]} parentMessage={history.messages[message.parentId]}
{messageIdx} {messageIdx}
{selectedModelfiles}
{updateChatMessages} {updateChatMessages}
{confirmEditResponseMessage} {confirmEditResponseMessage}
{rateMessage} {rateMessage}

View File

@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { WEBUI_BASE_URL } from '$lib/constants'; import { WEBUI_BASE_URL } from '$lib/constants';
import { user } from '$lib/stores'; import { config, user, models as _models } from '$lib/stores';
import { onMount, getContext } from 'svelte'; import { onMount, getContext } from 'svelte';
import { blur, fade } from 'svelte/transition'; import { blur, fade } from 'svelte/transition';
@ -9,23 +9,21 @@
const i18n = getContext('i18n'); const i18n = getContext('i18n');
export let modelIds = [];
export let models = []; export let models = [];
export let modelfiles = [];
export let submitPrompt; export let submitPrompt;
export let suggestionPrompts; export let suggestionPrompts;
let mounted = false; let mounted = false;
let modelfile = null;
let selectedModelIdx = 0; let selectedModelIdx = 0;
$: modelfile = $: if (modelIds.length > 0) {
models[selectedModelIdx] in modelfiles ? modelfiles[models[selectedModelIdx]] : null;
$: if (models.length > 0) {
selectedModelIdx = models.length - 1; selectedModelIdx = models.length - 1;
} }
$: models = modelIds.map((id) => $_models.find((m) => m.id === id));
onMount(() => { onMount(() => {
mounted = true; mounted = true;
}); });
@ -41,25 +39,14 @@
selectedModelIdx = modelIdx; selectedModelIdx = modelIdx;
}} }}
> >
{#if model in modelfiles} <img
<img crossorigin="anonymous"
crossorigin="anonymous" src={model?.info?.meta?.profile_image_url ??
src={modelfiles[model]?.imageUrl ?? `${WEBUI_BASE_URL}/static/favicon.png`} ($i18n.language === 'dg-DG' ? `/doge.png` : `${WEBUI_BASE_URL}/static/favicon.png`)}
alt="modelfile" class=" size-[2.7rem] rounded-full border-[1px] border-gray-200 dark:border-none"
class=" size-[2.7rem] rounded-full border-[1px] border-gray-200 dark:border-none" alt="logo"
draggable="false" draggable="false"
/> />
{:else}
<img
crossorigin="anonymous"
src={$i18n.language === 'dg-DG'
? `/doge.png`
: `${WEBUI_BASE_URL}/static/favicon.png`}
class=" size-[2.7rem] rounded-full border-[1px] border-gray-200 dark:border-none"
alt="logo"
draggable="false"
/>
{/if}
</button> </button>
{/each} {/each}
</div> </div>
@ -70,23 +57,32 @@
> >
<div> <div>
<div class=" capitalize line-clamp-1" in:fade={{ duration: 200 }}> <div class=" capitalize line-clamp-1" in:fade={{ duration: 200 }}>
{#if modelfile} {#if models[selectedModelIdx]?.info}
{modelfile.title} {models[selectedModelIdx]?.info?.name}
{:else} {:else}
{$i18n.t('Hello, {{name}}', { name: $user.name })} {$i18n.t('Hello, {{name}}', { name: $user.name })}
{/if} {/if}
</div> </div>
<div in:fade={{ duration: 200, delay: 200 }}> <div in:fade={{ duration: 200, delay: 200 }}>
{#if modelfile} {#if models[selectedModelIdx]?.info}
<div class="mt-0.5 text-base font-normal text-gray-500 dark:text-gray-400"> <div class="mt-0.5 text-base font-normal text-gray-500 dark:text-gray-400">
{modelfile.desc} {models[selectedModelIdx]?.info?.meta?.description}
</div> </div>
{#if modelfile.user} {#if models[selectedModelIdx]?.info?.meta?.user}
<div class="mt-0.5 text-sm font-normal text-gray-400 dark:text-gray-500"> <div class="mt-0.5 text-sm font-normal text-gray-400 dark:text-gray-500">
By <a href="https://openwebui.com/m/{modelfile.user.username}" By
>{modelfile.user.name ? modelfile.user.name : `@${modelfile.user.username}`}</a {#if models[selectedModelIdx]?.info?.meta?.user.community}
> <a
href="https://openwebui.com/m/{models[selectedModelIdx]?.info?.meta?.user
.username}"
>{models[selectedModelIdx]?.info?.meta?.user.name
? models[selectedModelIdx]?.info?.meta?.user.name
: `@${models[selectedModelIdx]?.info?.meta?.user.username}`}</a
>
{:else}
{models[selectedModelIdx]?.info?.meta?.user.name}
{/if}
</div> </div>
{/if} {/if}
{:else} {:else}
@ -99,7 +95,11 @@
</div> </div>
<div class=" w-full" in:fade={{ duration: 200, delay: 300 }}> <div class=" w-full" in:fade={{ duration: 200, delay: 300 }}>
<Suggestions {suggestionPrompts} {submitPrompt} /> <Suggestions
suggestionPrompts={models[selectedModelIdx]?.info?.meta?.suggestion_prompts ??
$config.default_prompt_suggestions}
{submitPrompt}
/>
</div> </div>
</div> </div>
{/key} {/key}

File diff suppressed because one or more lines are too long

View File

@ -45,13 +45,11 @@
<div class="mr-1 max-w-full"> <div class="mr-1 max-w-full">
<Selector <Selector
placeholder={$i18n.t('Select a model')} placeholder={$i18n.t('Select a model')}
items={$models items={$models.map((model) => ({
.filter((model) => model.name !== 'hr') value: model.id,
.map((model) => ({ label: model.name,
value: model.id, model: model
label: model.custom_info?.name ?? model.name, }))}
info: model
}))}
bind:value={selectedModel} bind:value={selectedModel}
/> />
</div> </div>

View File

@ -249,15 +249,17 @@
<div class="line-clamp-1"> <div class="line-clamp-1">
{item.label} {item.label}
<span class=" text-xs font-medium text-gray-600 dark:text-gray-400" {#if item.model.owned_by === 'ollama'}
>{item.info?.details?.parameter_size ?? ''}</span <span class=" text-xs font-medium text-gray-600 dark:text-gray-400"
> >{item.model.ollama?.details?.parameter_size ?? ''}</span
>
{/if}
</div> </div>
<!-- {JSON.stringify(item.info)} --> <!-- {JSON.stringify(item.info)} -->
{#if item.info.external} {#if item.model.owned_by === 'openai'}
<Tooltip content={`${item.info?.source ?? 'External'}`}> <Tooltip content={`${'External'}`}>
<div class=""> <div class="">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@ -278,13 +280,17 @@
</svg> </svg>
</div> </div>
</Tooltip> </Tooltip>
{:else} {:else if item.model.owned_by === 'ollama'}
<Tooltip <Tooltip
content={`${ content={`${
item.info?.details?.quantization_level item.model.ollama?.details?.quantization_level
? item.info?.details?.quantization_level + ' ' ? item.model.ollama?.details?.quantization_level + ' '
: '' : ''
}${item.info.size ? `(${(item.info.size / 1024 ** 3).toFixed(1)}GB)` : ''}`} }${
item.model.ollama?.size
? `(${(item.model.ollama?.size / 1024 ** 3).toFixed(1)}GB)`
: ''
}`}
> >
<div class=""> <div class="">
<svg <svg