From 092884fec54c1957532e5bbb415e89e768a1a32f Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Tue, 2 Jan 2024 16:06:11 -0800 Subject: [PATCH 01/77] fix: chat general --- src/lib/components/chat/MessageInput.svelte | 2 +- src/lib/components/chat/ModelSelector.svelte | 7 +++++++ src/routes/(app)/+page.svelte | 8 ++++++-- src/routes/(app)/c/[id]/+page.svelte | 9 ++++++--- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/lib/components/chat/MessageInput.svelte b/src/lib/components/chat/MessageInput.svelte index 55683329c3..1468310d49 100644 --- a/src/lib/components/chat/MessageInput.svelte +++ b/src/lib/components/chat/MessageInput.svelte @@ -298,7 +298,7 @@ id="chat-textarea" class=" dark:bg-gray-800 dark:text-gray-100 outline-none w-full py-3 px-2 {fileUploadEnabled ? '' - : ' pl-4'} rounded-xl resize-none" + : ' pl-4'} rounded-xl resize-none h-[48px]" placeholder={speechRecognitionListening ? 'Listening...' : 'Send a message'} bind:value={prompt} on:keypress={(e) => { diff --git a/src/lib/components/chat/ModelSelector.svelte b/src/lib/components/chat/ModelSelector.svelte index b4a0f47c78..9e7ff4dd34 100644 --- a/src/lib/components/chat/ModelSelector.svelte +++ b/src/lib/components/chat/ModelSelector.svelte @@ -1,5 +1,6 @@
diff --git a/src/routes/(app)/+page.svelte b/src/routes/(app)/+page.svelte index b62a9caa8c..4e3347ff1f 100644 --- a/src/routes/(app)/+page.svelte +++ b/src/routes/(app)/+page.svelte @@ -109,10 +109,14 @@ await Promise.all( selectedModels.map(async (model) => { console.log(model); - if ($models.filter((m) => m.name === model)[0].external) { + const modelTag = $models.filter((m) => m.name === model).at(0); + + if (modelTag?.external) { await sendPromptOpenAI(model, prompt, parentId, _chatId); - } else { + } else if (modelTag) { await sendPromptOllama(model, prompt, parentId, _chatId); + } else { + toast.error(`Model ${model} not found`); } }) ); diff --git a/src/routes/(app)/c/[id]/+page.svelte b/src/routes/(app)/c/[id]/+page.svelte index 48bc86cadd..62c271bf41 100644 --- a/src/routes/(app)/c/[id]/+page.svelte +++ b/src/routes/(app)/c/[id]/+page.svelte @@ -136,17 +136,20 @@ await Promise.all( selectedModels.map(async (model) => { console.log(model); - if ($models.filter((m) => m.name === model)[0].external) { + const modelTag = $models.filter((m) => m.name === model).at(0); + + if (modelTag?.external) { await sendPromptOpenAI(model, prompt, parentId, _chatId); - } else { + } else if (modelTag) { await sendPromptOllama(model, prompt, parentId, _chatId); + } else { + toast.error(`Model ${model} not found`); } }) ); await chats.set(await getChatList(localStorage.token)); }; - const sendPromptOllama = async (model, userPrompt, parentId, _chatId) => { // Create response message let responseMessageId = uuidv4(); From d8754b4486130c7f73e953622f50e3e6e0b905aa Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Tue, 2 Jan 2024 16:22:48 -0800 Subject: [PATCH 02/77] feat/fix: email format validation --- backend/apps/web/routers/auths.py | 56 +++++++++++++++++-------------- backend/constants.py | 1 + backend/utils/misc.py | 7 ++++ 3 files changed, 39 insertions(+), 25 deletions(-) diff --git a/backend/apps/web/routers/auths.py b/backend/apps/web/routers/auths.py index fb1139898f..714982e342 100644 --- a/backend/apps/web/routers/auths.py +++ b/backend/apps/web/routers/auths.py @@ -8,6 +8,7 @@ from pydantic import BaseModel import time import uuid + from apps.web.models.auths import ( SigninForm, SignupForm, @@ -20,7 +21,7 @@ from apps.web.models.users import Users from utils.utils import get_password_hash, get_current_user, create_token -from utils.misc import get_gravatar_url +from utils.misc import get_gravatar_url, validate_email_format from constants import ERROR_MESSAGES @@ -95,33 +96,38 @@ async def signin(form_data: SigninForm): @router.post("/signup", response_model=SigninResponse) async def signup(request: Request, form_data: SignupForm): if request.app.state.ENABLE_SIGNUP: - if not Users.get_user_by_email(form_data.email.lower()): - try: - role = "admin" if Users.get_num_users() == 0 else "pending" - hashed = get_password_hash(form_data.password) - user = Auths.insert_new_auth( - form_data.email.lower(), hashed, form_data.name, role - ) + if validate_email_format(form_data.email.lower()): + if not Users.get_user_by_email(form_data.email.lower()): + try: + role = "admin" if Users.get_num_users() == 0 else "pending" + hashed = get_password_hash(form_data.password) + user = Auths.insert_new_auth( + form_data.email.lower(), hashed, form_data.name, role + ) - if user: - token = create_token(data={"email": user.email}) - # response.set_cookie(key='token', value=token, httponly=True) + if user: + token = create_token(data={"email": user.email}) + # response.set_cookie(key='token', value=token, httponly=True) - return { - "token": token, - "token_type": "Bearer", - "id": user.id, - "email": user.email, - "name": user.name, - "role": user.role, - "profile_image_url": user.profile_image_url, - } - else: - raise HTTPException(500, detail=ERROR_MESSAGES.CREATE_USER_ERROR) - except Exception as err: - raise HTTPException(500, detail=ERROR_MESSAGES.DEFAULT(err)) + return { + "token": token, + "token_type": "Bearer", + "id": user.id, + "email": user.email, + "name": user.name, + "role": user.role, + "profile_image_url": user.profile_image_url, + } + else: + raise HTTPException( + 500, detail=ERROR_MESSAGES.CREATE_USER_ERROR + ) + except Exception as err: + raise HTTPException(500, detail=ERROR_MESSAGES.DEFAULT(err)) + else: + raise HTTPException(400, detail=ERROR_MESSAGES.EMAIL_TAKEN) else: - raise HTTPException(400, detail=ERROR_MESSAGES.EMAIL_TAKEN) + raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_EMAIL_FORMAT) else: raise HTTPException(400, detail=ERROR_MESSAGES.ACCESS_PROHIBITED) diff --git a/backend/constants.py b/backend/constants.py index 761507f2ba..ec3ce33767 100644 --- a/backend/constants.py +++ b/backend/constants.py @@ -21,6 +21,7 @@ class ERROR_MESSAGES(str, Enum): "Your session has expired or the token is invalid. Please sign in again." ) INVALID_CRED = "The email or password provided is incorrect. Please check for typos and try logging in again." + INVALID_EMAIL_FORMAT = "The email format you entered is invalid. Please double-check and make sure you're using a valid email address (e.g., yourname@example.com)." INVALID_PASSWORD = ( "The password provided is incorrect. Please check for typos and try again." ) diff --git a/backend/utils/misc.py b/backend/utils/misc.py index c6508487a3..5635c57ac9 100644 --- a/backend/utils/misc.py +++ b/backend/utils/misc.py @@ -1,4 +1,5 @@ import hashlib +import re def get_gravatar_url(email): @@ -21,3 +22,9 @@ def calculate_sha256(file): for chunk in iter(lambda: file.read(8192), b""): sha256.update(chunk) return sha256.hexdigest() + + +def validate_email_format(email: str) -> bool: + if not re.match(r"[^@]+@[^@]+\.[^@]+", email): + return False + return True From 7bc0c09b25e27baf06d3956a46b47bdb7edc5f0c Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Tue, 2 Jan 2024 16:48:10 -0800 Subject: [PATCH 03/77] fix: openai issue --- backend/apps/web/main.py | 12 ++++++-- backend/apps/web/routers/configs.py | 41 ++++++++++++++++++++++++++++ src/lib/apis/configs/index.ts | 31 +++++++++++++++++++++ src/routes/(app)/+page.svelte | 31 +++++++++++++-------- src/routes/(app)/c/[id]/+page.svelte | 14 +++++----- 5 files changed, 108 insertions(+), 21 deletions(-) create mode 100644 backend/apps/web/routers/configs.py create mode 100644 src/lib/apis/configs/index.ts diff --git a/backend/apps/web/main.py b/backend/apps/web/main.py index f62857b79a..078ae2a37f 100644 --- a/backend/apps/web/main.py +++ b/backend/apps/web/main.py @@ -1,7 +1,7 @@ from fastapi import FastAPI, Depends from fastapi.routing import APIRoute from fastapi.middleware.cors import CORSMiddleware -from apps.web.routers import auths, users, chats, modelfiles, utils +from apps.web.routers import auths, users, chats, modelfiles, configs, utils from config import WEBUI_VERSION, WEBUI_AUTH app = FastAPI() @@ -9,6 +9,7 @@ app = FastAPI() origins = ["*"] app.state.ENABLE_SIGNUP = True +app.state.DEFAULT_MODELS = "llava:13b" app.add_middleware( CORSMiddleware, @@ -19,13 +20,18 @@ app.add_middleware( ) app.include_router(auths.router, prefix="/auths", tags=["auths"]) - app.include_router(users.router, prefix="/users", tags=["users"]) app.include_router(chats.router, prefix="/chats", tags=["chats"]) app.include_router(modelfiles.router, prefix="/modelfiles", tags=["modelfiles"]) +app.include_router(configs.router, prefix="/configs", tags=["configs"]) app.include_router(utils.router, prefix="/utils", tags=["utils"]) @app.get("/") async def get_status(): - return {"status": True, "version": WEBUI_VERSION, "auth": WEBUI_AUTH} + return { + "status": True, + "version": WEBUI_VERSION, + "auth": WEBUI_AUTH, + "default_models": app.state.DEFAULT_MODELS, + } diff --git a/backend/apps/web/routers/configs.py b/backend/apps/web/routers/configs.py new file mode 100644 index 0000000000..b57fae3d5f --- /dev/null +++ b/backend/apps/web/routers/configs.py @@ -0,0 +1,41 @@ +from fastapi import Response, Request +from fastapi import Depends, FastAPI, HTTPException, status +from datetime import datetime, timedelta +from typing import List, Union + +from fastapi import APIRouter +from pydantic import BaseModel +import time +import uuid + +from apps.web.models.users import Users + + +from utils.utils import get_password_hash, get_current_user, create_token +from utils.misc import get_gravatar_url, validate_email_format +from constants import ERROR_MESSAGES + +router = APIRouter() + + +class SetDefaultModelsForm(BaseModel): + models: str + + +############################ +# SetDefaultModels +############################ + + +@router.post("/default/models", response_model=str) +async def set_global_default_models( + request: Request, form_data: SetDefaultModelsForm, user=Depends(get_current_user) +): + if user.role == "admin": + request.app.state.DEFAULT_MODELS = form_data.models + return request.app.state.DEFAULT_MODELS + else: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail=ERROR_MESSAGES.ACCESS_PROHIBITED, + ) diff --git a/src/lib/apis/configs/index.ts b/src/lib/apis/configs/index.ts new file mode 100644 index 0000000000..9762c41fdd --- /dev/null +++ b/src/lib/apis/configs/index.ts @@ -0,0 +1,31 @@ +import { WEBUI_API_BASE_URL } from '$lib/constants'; + +export const setDefaultModels = async (token: string, models: string) => { + let error = null; + + const res = await fetch(`${WEBUI_API_BASE_URL}/configs/default/models`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}` + }, + body: JSON.stringify({ + models: models + }) + }) + .then(async (res) => { + if (!res.ok) throw await res.json(); + return res.json(); + }) + .catch((err) => { + console.log(err); + error = err.detail; + return null; + }); + + if (error) { + throw error; + } + + return res; +}; diff --git a/src/routes/(app)/+page.svelte b/src/routes/(app)/+page.svelte index 4e3347ff1f..309201407a 100644 --- a/src/routes/(app)/+page.svelte +++ b/src/routes/(app)/+page.svelte @@ -6,7 +6,7 @@ import { goto } from '$app/navigation'; import { page } from '$app/stores'; - import { models, modelfiles, user, settings, chats, chatId } from '$lib/stores'; + import { models, modelfiles, user, settings, chats, chatId, config } from '$lib/stores'; import { OLLAMA_API_BASE_URL } from '$lib/constants'; import { generateChatCompletion, generateTitle } from '$lib/apis/ollama'; @@ -90,9 +90,18 @@ messages: {}, currentId: null }; - selectedModels = $page.url.searchParams.get('models') - ? $page.url.searchParams.get('models')?.split(',') - : $settings.models ?? ['']; + + console.log($config); + + if ($page.url.searchParams.get('models')) { + selectedModels = $page.url.searchParams.get('models')?.split(','); + } else if ($settings?.models) { + selectedModels = $settings?.models; + } else if ($config?.default_models) { + selectedModels = $config?.default_models.split(','); + } else { + selectedModels = ['']; + } let _settings = JSON.parse(localStorage.getItem('settings') ?? '{}'); settings.set({ @@ -383,13 +392,13 @@ } : { content: message.content }) })), - seed: $settings.options.seed ?? undefined, - stop: $settings.options.stop ?? undefined, - temperature: $settings.options.temperature ?? undefined, - top_p: $settings.options.top_p ?? undefined, - num_ctx: $settings.options.num_ctx ?? undefined, - frequency_penalty: $settings.options.repeat_penalty ?? undefined, - max_tokens: $settings.options.num_predict ?? undefined + seed: $settings?.options?.seed ?? undefined, + stop: $settings?.options?.stop ?? undefined, + temperature: $settings?.options?.temperature ?? undefined, + top_p: $settings?.options?.top_p ?? undefined, + num_ctx: $settings?.options?.num_ctx ?? undefined, + frequency_penalty: $settings?.options?.repeat_penalty ?? undefined, + max_tokens: $settings?.options?.num_predict ?? undefined }) } ).catch((err) => { diff --git a/src/routes/(app)/c/[id]/+page.svelte b/src/routes/(app)/c/[id]/+page.svelte index 62c271bf41..e3a70ca257 100644 --- a/src/routes/(app)/c/[id]/+page.svelte +++ b/src/routes/(app)/c/[id]/+page.svelte @@ -409,13 +409,13 @@ } : { content: message.content }) })), - seed: $settings.options.seed ?? undefined, - stop: $settings.options.stop ?? undefined, - temperature: $settings.options.temperature ?? undefined, - top_p: $settings.options.top_p ?? undefined, - num_ctx: $settings.options.num_ctx ?? undefined, - frequency_penalty: $settings.options.repeat_penalty ?? undefined, - max_tokens: $settings.options.num_predict ?? undefined + seed: $settings?.options?.seed ?? undefined, + stop: $settings?.options?.stop ?? undefined, + temperature: $settings?.options?.temperature ?? undefined, + top_p: $settings?.options?.top_p ?? undefined, + num_ctx: $settings?.options?.num_ctx ?? undefined, + frequency_penalty: $settings?.options?.repeat_penalty ?? undefined, + max_tokens: $settings?.options?.num_predict ?? undefined }) } ).catch((err) => { From 09e1458d592799df798819ed3cf294357c934884 Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Tue, 2 Jan 2024 16:48:49 -0800 Subject: [PATCH 04/77] fix: default models value should be None --- backend/apps/web/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/apps/web/main.py b/backend/apps/web/main.py index 078ae2a37f..23b3922482 100644 --- a/backend/apps/web/main.py +++ b/backend/apps/web/main.py @@ -9,7 +9,7 @@ app = FastAPI() origins = ["*"] app.state.ENABLE_SIGNUP = True -app.state.DEFAULT_MODELS = "llava:13b" +app.state.DEFAULT_MODELS = None app.add_middleware( CORSMiddleware, From a60fbac8a5d63794068435f05a03d0f7931f1be7 Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Tue, 2 Jan 2024 17:08:35 -0800 Subject: [PATCH 05/77] feat: set title auto generate model support --- src/lib/components/chat/SettingsModal.svelte | 72 ++++++++++++++++---- src/routes/(app)/+page.svelte | 2 +- 2 files changed, 61 insertions(+), 13 deletions(-) diff --git a/src/lib/components/chat/SettingsModal.svelte b/src/lib/components/chat/SettingsModal.svelte index a220bd4ebc..84e95d39ad 100644 --- a/src/lib/components/chat/SettingsModal.svelte +++ b/src/lib/components/chat/SettingsModal.svelte @@ -74,17 +74,20 @@ let deleteModelTag = ''; + // External + + let OPENAI_API_KEY = ''; + let OPENAI_API_BASE_URL = ''; + // Addons let titleAutoGenerate = true; let speechAutoSend = false; let responseAutoCopy = false; let gravatarEmail = ''; - let OPENAI_API_KEY = ''; - let OPENAI_API_BASE_URL = ''; + let titleAutoGenerateModel = ''; // Chats - let importFiles; let showDeleteConfirm = false; @@ -656,13 +659,14 @@ options = { ...options, ...settings.options }; options.stop = (settings?.options?.stop ?? []).join(','); + OPENAI_API_KEY = settings.OPENAI_API_KEY ?? ''; + OPENAI_API_BASE_URL = settings.OPENAI_API_BASE_URL ?? 'https://api.openai.com/v1'; + titleAutoGenerate = settings.titleAutoGenerate ?? true; speechAutoSend = settings.speechAutoSend ?? false; responseAutoCopy = settings.responseAutoCopy ?? false; - + titleAutoGenerateModel = settings.titleAutoGenerateModel ?? ''; gravatarEmail = settings.gravatarEmail ?? ''; - OPENAI_API_KEY = settings.OPENAI_API_KEY ?? ''; - OPENAI_API_BASE_URL = settings.OPENAI_API_BASE_URL ?? 'https://api.openai.com/v1'; authEnabled = settings.authHeader !== undefined ? true : false; if (authEnabled) { @@ -1548,10 +1552,6 @@
{ - saveSettings({ - gravatarEmail: gravatarEmail !== '' ? gravatarEmail : undefined, - gravatarUrl: gravatarEmail !== '' ? getGravatarURL(gravatarEmail) : undefined - }); show = false; }} > @@ -1561,7 +1561,7 @@
-
Title Auto Generation
+
Title Auto-Generation
+
+
+ +
diff --git a/src/routes/(app)/+page.svelte b/src/routes/(app)/+page.svelte index 309201407a..9638861be8 100644 --- a/src/routes/(app)/+page.svelte +++ b/src/routes/(app)/+page.svelte @@ -597,7 +597,7 @@ const title = await generateTitle( $settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL, localStorage.token, - selectedModels[0], + $settings?.titleAutoGenerateModel ?? selectedModels[0], userPrompt ); From dfde45d365613766fb29b563eacfc1ac77636bf9 Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Tue, 2 Jan 2024 17:26:19 -0800 Subject: [PATCH 06/77] feat: set default models globally --- src/lib/apis/configs/index.ts | 2 +- src/lib/components/chat/ModelSelector.svelte | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/lib/apis/configs/index.ts b/src/lib/apis/configs/index.ts index 9762c41fdd..76256f4d85 100644 --- a/src/lib/apis/configs/index.ts +++ b/src/lib/apis/configs/index.ts @@ -4,7 +4,7 @@ export const setDefaultModels = async (token: string, models: string) => { let error = null; const res = await fetch(`${WEBUI_API_BASE_URL}/configs/default/models`, { - method: 'GET', + method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` diff --git a/src/lib/components/chat/ModelSelector.svelte b/src/lib/components/chat/ModelSelector.svelte index 9e7ff4dd34..47afbbe13a 100644 --- a/src/lib/components/chat/ModelSelector.svelte +++ b/src/lib/components/chat/ModelSelector.svelte @@ -1,12 +1,13 @@ + +
+
+
+
+
My Prompts
+
+ +
+
+
+ + + +
+ +
+ + +
+ + {#if $prompts.length === 0} +
+ {:else} + {#each $prompts.filter((p) => query === '' || p.command.includes(query)) as prompt} +
+
+
+
+
{prompt.command}
+
+ {prompt.title} +
+
+
+
+ + + + + + + + + +
+
+ {/each} + {/if} + +
+ +
+
+ + + + + +
+
+ + +
+
+
diff --git a/src/routes/(app)/prompts/create/+page.svelte b/src/routes/(app)/prompts/create/+page.svelte new file mode 100644 index 0000000000..5cdf594054 --- /dev/null +++ b/src/routes/(app)/prompts/create/+page.svelte @@ -0,0 +1,224 @@ + + +
+
+
+
My Prompts
+ + +
+ + { + submitHandler(); + }} + > +
+
Title*
+ +
+ +
+
+ +
+
Command*
+ +
+
+ / +
+ +
+ +
+ Only alphanumeric characters and hyphens + are allowed; Activate this command by typing " + /{command} + " to chat input. +
+
+ +
+
+
Prompt
+
+ +
+
Content*
+ +
+