Merge pull request #3668 from open-webui/dev

dev
This commit is contained in:
Timothy Jaeryang Baek 2024-07-05 22:29:13 -07:00 committed by GitHub
commit d60f06608e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 214 additions and 68 deletions

View File

@ -19,8 +19,13 @@ from apps.webui.routers import (
functions,
)
from apps.webui.models.functions import Functions
from apps.webui.models.models import Models
from apps.webui.utils import load_function_module_by_id
from utils.misc import stream_message_template
from utils.task import prompt_template
from config import (
WEBUI_BUILD_HASH,
@ -186,6 +191,77 @@ async def get_pipe_models():
async def generate_function_chat_completion(form_data, user):
model_id = form_data.get("model")
model_info = Models.get_model_by_id(model_id)
if model_info:
if model_info.base_model_id:
form_data["model"] = model_info.base_model_id
model_info.params = model_info.params.model_dump()
if model_info.params:
if model_info.params.get("temperature", None) is not None:
form_data["temperature"] = float(model_info.params.get("temperature"))
if model_info.params.get("top_p", None):
form_data["top_p"] = int(model_info.params.get("top_p", None))
if model_info.params.get("max_tokens", None):
form_data["max_tokens"] = int(model_info.params.get("max_tokens", None))
if model_info.params.get("frequency_penalty", None):
form_data["frequency_penalty"] = int(
model_info.params.get("frequency_penalty", None)
)
if model_info.params.get("seed", None):
form_data["seed"] = model_info.params.get("seed", None)
if model_info.params.get("stop", None):
form_data["stop"] = (
[
bytes(stop, "utf-8").decode("unicode_escape")
for stop in model_info.params["stop"]
]
if model_info.params.get("stop", None)
else None
)
system = model_info.params.get("system", None)
if system:
system = prompt_template(
system,
**(
{
"user_name": user.name,
"user_location": (
user.info.get("location") if user.info else None
),
}
if user
else {}
),
)
# Check if the payload already has a system message
# If not, add a system message to the payload
if form_data.get("messages"):
for message in form_data["messages"]:
if message.get("role") == "system":
message["content"] = system + message["content"]
break
else:
form_data["messages"].insert(
0,
{
"role": "system",
"content": system,
},
)
else:
pass
async def job():
pipe_id = form_data["model"]
if "." in pipe_id:

View File

@ -198,7 +198,7 @@ class FunctionsTable:
try:
user = Users.get_user_by_id(user_id)
user_settings = user.settings.model_dump()
user_settings = user.settings.model_dump() if user.settings else {}
# Check if user has "functions" and "valves" settings
if "functions" not in user_settings:
@ -217,7 +217,7 @@ class FunctionsTable:
try:
user = Users.get_user_by_id(user_id)
user_settings = user.settings.model_dump()
user_settings = user.settings.model_dump() if user.settings else {}
# Check if user has "functions" and "valves" settings
if "functions" not in user_settings:

View File

@ -149,7 +149,7 @@ class ToolsTable:
) -> Optional[dict]:
try:
user = Users.get_user_by_id(user_id)
user_settings = user.settings.model_dump()
user_settings = user.settings.model_dump() if user.settings else {}
# Check if user has "tools" and "valves" settings
if "tools" not in user_settings:
@ -167,7 +167,7 @@ class ToolsTable:
) -> Optional[dict]:
try:
user = Users.get_user_by_id(user_id)
user_settings = user.settings.model_dump()
user_settings = user.settings.model_dump() if user.settings else {}
# Check if user has "tools" and "valves" settings
if "tools" not in user_settings:

View File

@ -999,12 +999,16 @@ async def get_all_models():
model["info"] = custom_model.model_dump()
else:
owned_by = "openai"
pipe = None
for model in models:
if (
custom_model.base_model_id == model["id"]
or custom_model.base_model_id == model["id"].split(":")[0]
):
owned_by = model["owned_by"]
if "pipe" in model:
pipe = model["pipe"]
break
models.append(
@ -1016,11 +1020,11 @@ async def get_all_models():
"owned_by": owned_by,
"info": custom_model.model_dump(),
"preset": True,
**({"pipe": pipe} if pipe is not None else {}),
}
)
app.state.MODELS = {model["id"]: model for model in models}
webui_app.state.MODELS = app.state.MODELS
return models

View File

@ -1,5 +1,5 @@
<script>
import { getContext, tick } from 'svelte';
import { getContext, tick, onMount } from 'svelte';
import { toast } from 'svelte-sonner';
import Database from './Settings/Database.svelte';
@ -21,11 +21,24 @@
const i18n = getContext('i18n');
let selectedTab = 'general';
onMount(() => {
const containerElement = document.getElementById('admin-settings-tabs-container');
if (containerElement) {
containerElement.addEventListener('wheel', function (event) {
if (event.deltaY !== 0) {
// Adjust horizontal scroll position based on vertical scroll
containerElement.scrollLeft += event.deltaY;
}
});
}
});
</script>
<div class="flex flex-col lg:flex-row w-full h-full py-2 lg:space-x-4">
<div
class="tabs flex flex-row overflow-x-auto space-x-1 max-w-full lg:space-x-0 lg:space-y-1 lg:flex-col lg:flex-none lg:w-44 dark:text-gray-200 text-xs text-left scrollbar-none"
id="admin-settings-tabs-container" class="tabs flex flex-row overflow-x-auto space-x-1 max-w-full lg:space-x-0 lg:space-y-1 lg:flex-col lg:flex-none lg:w-44 dark:text-gray-200 text-xs text-left scrollbar-none"
>
<button
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 lg:flex-none flex text-right transition {selectedTab ===

View File

@ -158,12 +158,14 @@
return;
}
const [res, controller] = await pullModel(localStorage.token, sanitizedModelTag, '0').catch(
(error) => {
toast.error(error);
return null;
}
);
const [res, controller] = await pullModel(
localStorage.token,
sanitizedModelTag,
selectedOllamaUrlIdx
).catch((error) => {
toast.error(error);
return null;
});
if (res) {
const reader = res.body

View File

@ -126,21 +126,29 @@
})();
}
const chatEventHandler = async (data) => {
if (data.chat_id === $chatId) {
const chatEventHandler = async (event) => {
if (event.chat_id === $chatId) {
await tick();
console.log(data);
let message = history.messages[data.message_id];
console.log(event);
let message = history.messages[event.message_id];
const status = {
done: data?.data?.done ?? null,
description: data?.data?.status ?? null
};
const type = event?.data?.type ?? null;
const data = event?.data?.data ?? null;
if (message.statusHistory) {
message.statusHistory.push(status);
if (type === 'status') {
if (message.statusHistory) {
message.statusHistory.push(data);
} else {
message.statusHistory = [data];
}
} else if (type === 'citation') {
if (message.citations) {
message.citations.push(data);
} else {
message.citations = [data];
}
} else {
message.statusHistory = [status];
console.log('Unknown message type', data);
}
messages = messages;

View File

@ -2,32 +2,25 @@
import ChevronDown from '$lib/components/icons/ChevronDown.svelte';
import ChevronUp from '$lib/components/icons/ChevronUp.svelte';
import MagnifyingGlass from '$lib/components/icons/MagnifyingGlass.svelte';
import { Collapsible } from 'bits-ui';
import { slide } from 'svelte/transition';
import Collapsible from '$lib/components/common/Collapsible.svelte';
export let status = { urls: [], query: '' };
let state = false;
</script>
<Collapsible.Root class="w-full space-y-1" bind:open={state}>
<Collapsible.Trigger>
<div
class="flex items-center gap-2 text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition"
>
<slot />
{#if state}
<ChevronUp strokeWidth="3.5" className="size-3.5 " />
{:else}
<ChevronDown strokeWidth="3.5" className="size-3.5 " />
{/if}
</div>
</Collapsible.Trigger>
<Collapsible.Content
class=" text-sm border border-gray-300/30 dark:border-gray-700/50 rounded-xl"
transition={slide}
<Collapsible bind:open={state} className="w-full space-y-1">
<div
class="flex items-center gap-2 text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition"
>
<slot />
{#if state}
<ChevronUp strokeWidth="3.5" className="size-3.5 " />
{:else}
<ChevronDown strokeWidth="3.5" className="size-3.5 " />
{/if}
</div>
<div class="text-sm border border-gray-300/30 dark:border-gray-700/50 rounded-xl" slot="content">
{#if status?.query}
<a
href="https://www.google.com/search?q={status.query}"
@ -93,5 +86,5 @@
</div>
</a>
{/each}
</Collapsible.Content>
</Collapsible.Root>
</div>
</Collapsible>

View File

@ -1,9 +1,10 @@
<script lang="ts">
import { getContext } from 'svelte';
import { getContext, tick } from 'svelte';
import { toast } from 'svelte-sonner';
import { models, settings, user } from '$lib/stores';
import { updateUserSettings } from '$lib/apis/users';
import { getModels as _getModels } from '$lib/apis';
import { goto } from '$app/navigation';
import Modal from '../common/Modal.svelte';
import Account from './Settings/Account.svelte';
@ -14,8 +15,6 @@
import Chats from './Settings/Chats.svelte';
import User from '../icons/User.svelte';
import Personalization from './Settings/Personalization.svelte';
import { updateUserSettings } from '$lib/apis/users';
import { goto } from '$app/navigation';
import Valves from './Settings/Valves.svelte';
const i18n = getContext('i18n');
@ -34,6 +33,37 @@
};
let selectedTab = 'general';
// Function to handle sideways scrolling
const scrollHandler = (event) => {
const settingsTabsContainer = document.getElementById('settings-tabs-container');
if (settingsTabsContainer) {
event.preventDefault(); // Prevent default vertical scrolling
settingsTabsContainer.scrollLeft += event.deltaY; // Scroll sideways
}
};
const addScrollListener = async () => {
await tick();
const settingsTabsContainer = document.getElementById('settings-tabs-container');
if (settingsTabsContainer) {
settingsTabsContainer.addEventListener('wheel', scrollHandler);
}
};
const removeScrollListener = async () => {
await tick();
const settingsTabsContainer = document.getElementById('settings-tabs-container');
if (settingsTabsContainer) {
settingsTabsContainer.removeEventListener('wheel', scrollHandler);
}
};
$: if (show) {
addScrollListener();
} else {
removeScrollListener();
}
</script>
<Modal bind:show>
@ -61,6 +91,7 @@
<div class="flex flex-col md:flex-row w-full p-4 md:space-x-4">
<div
id="settings-tabs-container"
class="tabs flex flex-row overflow-x-auto space-x-1 md:space-x-0 md:space-y-1 md:flex-col flex-1 md:flex-none md:w-40 dark:text-gray-200 text-xs text-left mb-3 md:mb-0"
>
<button

View File

@ -0,0 +1,18 @@
<script lang="ts">
import { slide } from 'svelte/transition';
import { quintOut } from 'svelte/easing';
export let open = false;
export let className = '';
</script>
<div class={className}>
<button on:click={() => (open = !open)}>
<slot />
</button>
{#if open}
<div transition:slide={{ duration: 300, easing: quintOut, axis: 'y' }}>
<slot name="content" />
</div>
{/if}
</div>

View File

@ -186,6 +186,7 @@
goto('/');
}
await chats.set(await getChatList(localStorage.token));
await pinnedChats.set(await getChatListByTagName(localStorage.token, 'pinned'));
}
};
</script>

View File

@ -126,7 +126,7 @@
"Connections": "Connexions",
"Contact Admin for WebUI Access": "Posat en contacte amb l'administrador per accedir a WebUI",
"Content": "Contingut",
"Content Extraction": "",
"Content Extraction": "Extraccció de contingut",
"Context Length": "Mida del context",
"Continue Response": "Continuar la resposta",
"Continue with {{provider}}": "Continuar amb {{provider}}",
@ -213,7 +213,7 @@
"Enable Community Sharing": "Activar l'ús compartit amb la comunitat",
"Enable New Sign Ups": "Permetre nous registres",
"Enable Web Search": "Activar la cerca web",
"Engine": "",
"Engine": "Motor",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Assegura't que els teus fitxers CSV inclouen 4 columnes en aquest ordre: Nom, Correu electrònic, Contrasenya, Rol.",
"Enter {{role}} message here": "Introdueix aquí el missatge de {{role}}",
"Enter a detail about yourself for your LLMs to recall": "Introdueix un detall sobre tu què els teus models de llenguatge puguin recordar",
@ -235,7 +235,7 @@
"Enter Serpstack API Key": "Introdueix la clau API Serpstack",
"Enter stop sequence": "Introdueix la seqüència de parada",
"Enter Tavily API Key": "Introdueix la clau API de Tavily",
"Enter Tika Server URL": "",
"Enter Tika Server URL": "Introdueix l'URL del servidor Tika",
"Enter Top K": "Introdueix Top K",
"Enter URL (e.g. http://127.0.0.1:7860/)": "Introdueix l'URL (p. ex. http://127.0.0.1:7860/)",
"Enter URL (e.g. http://localhost:11434)": "Introdueix l'URL (p. ex. http://localhost:11434)",
@ -412,7 +412,7 @@
"Open": "Obre",
"Open AI (Dall-E)": "Open AI (Dall-E)",
"Open new chat": "Obre un xat nou",
"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "La versió d'Open WebUI (v{{OPEN_WEBUI_VERSION}}) és inferior a la versió requerida (v{{REQUIRED_VERSION}})",
"OpenAI": "OpenAI",
"OpenAI API": "API d'OpenAI",
"OpenAI API Config": "Configuració de l'API d'OpenAI",
@ -428,8 +428,8 @@
"Permission denied when accessing microphone": "Permís denegat en accedir al micròfon",
"Permission denied when accessing microphone: {{error}}": "Permís denegat en accedir al micròfon: {{error}}",
"Personalization": "Personalització",
"Pin": "",
"Pinned": "",
"Pin": "Fixar",
"Pinned": "Fixat",
"Pipeline deleted successfully": "Pipeline eliminada correctament",
"Pipeline downloaded successfully": "Pipeline descarregada correctament",
"Pipelines": "Pipelines",
@ -580,8 +580,8 @@
"This setting does not sync across browsers or devices.": "Aquesta preferència no es sincronitza entre navegadors ni dispositius.",
"This will delete": "Això eliminarà",
"Thorough explanation": "Explicació en detall",
"Tika": "",
"Tika Server URL required.": "",
"Tika": "Tika",
"Tika Server URL required.": "La URL del servidor Tika és obligatòria.",
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Consell: Actualitza les diverses variables consecutivament prement la tecla de tabulació en l'entrada del xat després de cada reemplaçament.",
"Title": "Títol",
"Title (e.g. Tell me a fun fact)": "Títol (p. ex. Digues-me quelcom divertit)",
@ -616,7 +616,7 @@
"Uh-oh! There was an issue connecting to {{provider}}.": "Oh! Hi ha hagut un problema connectant a {{provider}}.",
"UI": "UI",
"Unknown file type '{{file_type}}'. Proceeding with the file upload anyway.": "Tipus de fitxer desconegut '{{file_type}}'. Continuant amb la càrrega del fitxer.",
"Unpin": "",
"Unpin": "Alliberar",
"Update": "Actualitzar",
"Update and Copy Link": "Actualitzar i copiar l'enllaç",
"Update password": "Actualitzar la contrasenya",

View File

@ -126,7 +126,7 @@
"Connections": "外部连接",
"Contact Admin for WebUI Access": "请联系管理员以获取访问权限",
"Content": "内容",
"Content Extraction": "",
"Content Extraction": "内容提取",
"Context Length": "上下文长度",
"Continue Response": "继续生成",
"Continue with {{provider}}": "使用 {{provider}} 继续",
@ -213,7 +213,7 @@
"Enable Community Sharing": "启用分享至社区",
"Enable New Sign Ups": "允许新用户注册",
"Enable Web Search": "启用网络搜索",
"Engine": "",
"Engine": "引擎",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "确保您的 CSV 文件按以下顺序包含 4 列: 姓名、电子邮箱、密码、角色。",
"Enter {{role}} message here": "在此处输入 {{role}} 信息",
"Enter a detail about yourself for your LLMs to recall": "输入一个关于你自己的详细信息,方便你的大语言模型记住这些内容",
@ -235,7 +235,7 @@
"Enter Serpstack API Key": "输入 Serpstack API 密钥",
"Enter stop sequence": "输入停止序列 (Stop Sequence)",
"Enter Tavily API Key": "输入 Tavily API 密钥",
"Enter Tika Server URL": "",
"Enter Tika Server URL": "输入 Tika 服务器地址",
"Enter Top K": "输入 Top K",
"Enter URL (e.g. http://127.0.0.1:7860/)": "输入地址 (例如http://127.0.0.1:7860/)",
"Enter URL (e.g. http://localhost:11434)": "输入地址 (例如http://localhost:11434)",
@ -412,7 +412,7 @@
"Open": "打开",
"Open AI (Dall-E)": "Open AI (Dall-E)",
"Open new chat": "打开新对话",
"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "当前 Open WebUI 版本 (v{{OPEN_WEBUI_VERSION}}) 低于所需的版本 (v{{REQUIRED_VERSION}})",
"OpenAI": "OpenAI",
"OpenAI API": "OpenAI API",
"OpenAI API Config": "OpenAI API 配置",
@ -428,8 +428,8 @@
"Permission denied when accessing microphone": "申请麦克风权限被拒绝",
"Permission denied when accessing microphone: {{error}}": "申请麦克风权限被拒绝:{{error}}",
"Personalization": "个性化",
"Pin": "",
"Pinned": "",
"Pin": "置顶",
"Pinned": "已置顶",
"Pipeline deleted successfully": "Pipeline 删除成功",
"Pipeline downloaded successfully": "Pipeline 下载成功",
"Pipelines": "Pipeline",
@ -578,8 +578,8 @@
"This setting does not sync across browsers or devices.": "此设置不会在浏览器或设备之间同步。",
"This will delete": "这将删除",
"Thorough explanation": "解释较为详细",
"Tika": "",
"Tika Server URL required.": "",
"Tika": "Tika",
"Tika Server URL required.": "请输入 Tika 服务器地址。",
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "提示:在每次替换后,在对话输入中按 Tab 键可以连续更新多个变量。",
"Title": "标题",
"Title (e.g. Tell me a fun fact)": "标题(例如 给我讲一个有趣的事实)",
@ -614,7 +614,7 @@
"Uh-oh! There was an issue connecting to {{provider}}.": "糟糕!连接到 {{provider}} 时出现问题。",
"UI": "界面",
"Unknown file type '{{file_type}}'. Proceeding with the file upload anyway.": "未知文件类型“{{file_type}}”,将无视继续上传文件。",
"Unpin": "",
"Unpin": "取消置顶",
"Update": "更新",
"Update and Copy Link": "更新和复制链接",
"Update password": "更新密码",