From 1ad9be9c07ddc91ffb1c5a40b9a044fd3fa305e9 Mon Sep 17 00:00:00 2001 From: Kangyun Wang Date: Sun, 12 Jan 2025 12:03:21 +0100 Subject: [PATCH 01/36] Check OAuth name type with fallback --- backend/open_webui/utils/oauth.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/backend/open_webui/utils/oauth.py b/backend/open_webui/utils/oauth.py index 70d130804..0569da98d 100644 --- a/backend/open_webui/utils/oauth.py +++ b/backend/open_webui/utils/oauth.py @@ -277,8 +277,13 @@ class OAuthManager: picture_url = "" if not picture_url: picture_url = "/user.png" + username_claim = auth_manager_config.OAUTH_USERNAME_CLAIM + username = user_data.get(username_claim) + if not isinstance(username, str): + username = "User" + role = self.get_user_role(None, user_data) user = Auths.insert_new_auth( @@ -286,7 +291,7 @@ class OAuthManager: password=get_password_hash( str(uuid.uuid4()) ), # Random password, not used - name=user_data.get(username_claim, "User"), + name=username, profile_image_url=picture_url, role=role, oauth_sub=provider_sub, From 412923dc915065fd264228ad18db7991db88b7dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antti=20Pyykk=C3=B6nen?= Date: Thu, 23 Jan 2025 16:16:50 +0200 Subject: [PATCH 02/36] feat: separate cookie settings between session & auth cookies Introducing two new env config options to control cookies settings regarding authentication. These values are taken into use when setting 'token' and 'oauth_id_token'. To maintain backwards compatibility, the original session cookie values are used as fallback. Separation is done to prevent issues with the session cookie. When the config value was set as 'strict', the oauth flow was broken (since the session cookie was not provided after the callback). Providing a separate config for auth & session cookies allows us to keep the 'strict' settings for auth related cookies, while also allowing the session cookie to behave as intended (e.g., by configuring it as 'lax'). The original config was added in commit #af4f8aa. However a later commit #a2e889c reused this config option for other type of cookies, which was not the original intent. --- backend/open_webui/env.py | 17 +++++++++-------- backend/open_webui/routers/auths.py | 16 ++++++++-------- backend/open_webui/utils/oauth.py | 10 +++++----- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/backend/open_webui/env.py b/backend/open_webui/env.py index 77e632ccc..b589e9490 100644 --- a/backend/open_webui/env.py +++ b/backend/open_webui/env.py @@ -356,15 +356,16 @@ WEBUI_SECRET_KEY = os.environ.get( ), # DEPRECATED: remove at next major version ) -WEBUI_SESSION_COOKIE_SAME_SITE = os.environ.get( - "WEBUI_SESSION_COOKIE_SAME_SITE", - os.environ.get("WEBUI_SESSION_COOKIE_SAME_SITE", "lax"), -) +WEBUI_SESSION_COOKIE_SAME_SITE = os.environ.get("WEBUI_SESSION_COOKIE_SAME_SITE", "lax") -WEBUI_SESSION_COOKIE_SECURE = os.environ.get( - "WEBUI_SESSION_COOKIE_SECURE", - os.environ.get("WEBUI_SESSION_COOKIE_SECURE", "false").lower() == "true", -) +WEBUI_SESSION_COOKIE_SECURE = os.environ.get("WEBUI_SESSION_COOKIE_SECURE", "false").lower() == "true" + +WEBUI_AUTH_COOKIE_SAME_SITE = os.environ.get("WEBUI_AUTH_COOKIE_SAME_SITE", WEBUI_SESSION_COOKIE_SAME_SITE) + +WEBUI_AUTH_COOKIE_SECURE = os.environ.get( + "WEBUI_AUTH_COOKIE_SECURE", + os.environ.get("WEBUI_SESSION_COOKIE_SECURE", "false") +).lower() == "true" if WEBUI_AUTH and WEBUI_SECRET_KEY == "": raise ValueError(ERROR_MESSAGES.ENV_VAR_NOT_FOUND) diff --git a/backend/open_webui/routers/auths.py b/backend/open_webui/routers/auths.py index 47baeb0ac..d7c4fa013 100644 --- a/backend/open_webui/routers/auths.py +++ b/backend/open_webui/routers/auths.py @@ -25,8 +25,8 @@ from open_webui.env import ( WEBUI_AUTH, WEBUI_AUTH_TRUSTED_EMAIL_HEADER, WEBUI_AUTH_TRUSTED_NAME_HEADER, - WEBUI_SESSION_COOKIE_SAME_SITE, - WEBUI_SESSION_COOKIE_SECURE, + WEBUI_AUTH_COOKIE_SAME_SITE, + WEBUI_AUTH_COOKIE_SECURE, SRC_LOG_LEVELS, ) from fastapi import APIRouter, Depends, HTTPException, Request, status @@ -95,8 +95,8 @@ async def get_session_user( value=token, expires=datetime_expires_at, httponly=True, # Ensures the cookie is not accessible via JavaScript - samesite=WEBUI_SESSION_COOKIE_SAME_SITE, - secure=WEBUI_SESSION_COOKIE_SECURE, + samesite=WEBUI_AUTH_COOKIE_SAME_SITE, + secure=WEBUI_AUTH_COOKIE_SECURE, ) user_permissions = get_permissions( @@ -378,8 +378,8 @@ async def signin(request: Request, response: Response, form_data: SigninForm): value=token, expires=datetime_expires_at, httponly=True, # Ensures the cookie is not accessible via JavaScript - samesite=WEBUI_SESSION_COOKIE_SAME_SITE, - secure=WEBUI_SESSION_COOKIE_SECURE, + samesite=WEBUI_AUTH_COOKIE_SAME_SITE, + secure=WEBUI_AUTH_COOKIE_SECURE, ) user_permissions = get_permissions( @@ -473,8 +473,8 @@ async def signup(request: Request, response: Response, form_data: SignupForm): value=token, expires=datetime_expires_at, httponly=True, # Ensures the cookie is not accessible via JavaScript - samesite=WEBUI_SESSION_COOKIE_SAME_SITE, - secure=WEBUI_SESSION_COOKIE_SECURE, + samesite=WEBUI_AUTH_COOKIE_SAME_SITE, + secure=WEBUI_AUTH_COOKIE_SECURE, ) if request.app.state.config.WEBHOOK_URL: diff --git a/backend/open_webui/utils/oauth.py b/backend/open_webui/utils/oauth.py index 1ae6d4aa7..b336f0631 100644 --- a/backend/open_webui/utils/oauth.py +++ b/backend/open_webui/utils/oauth.py @@ -35,7 +35,7 @@ from open_webui.config import ( AppConfig, ) from open_webui.constants import ERROR_MESSAGES, WEBHOOK_MESSAGES -from open_webui.env import WEBUI_SESSION_COOKIE_SAME_SITE, WEBUI_SESSION_COOKIE_SECURE +from open_webui.env import WEBUI_AUTH_COOKIE_SAME_SITE, WEBUI_AUTH_COOKIE_SECURE from open_webui.utils.misc import parse_duration from open_webui.utils.auth import get_password_hash, create_token from open_webui.utils.webhook import post_webhook @@ -323,8 +323,8 @@ class OAuthManager: key="token", value=jwt_token, httponly=True, # Ensures the cookie is not accessible via JavaScript - samesite=WEBUI_SESSION_COOKIE_SAME_SITE, - secure=WEBUI_SESSION_COOKIE_SECURE, + samesite=WEBUI_AUTH_COOKIE_SAME_SITE, + secure=WEBUI_AUTH_COOKIE_SECURE, ) if ENABLE_OAUTH_SIGNUP.value: @@ -333,8 +333,8 @@ class OAuthManager: key="oauth_id_token", value=oauth_id_token, httponly=True, - samesite=WEBUI_SESSION_COOKIE_SAME_SITE, - secure=WEBUI_SESSION_COOKIE_SECURE, + samesite=WEBUI_AUTH_COOKIE_SAME_SITE, + secure=WEBUI_AUTH_COOKIE_SECURE, ) # Redirect back to the frontend with the JWT token redirect_url = f"{request.base_url}auth#token={jwt_token}" From 6e306beaa2f496b01466ee7e726f9c85fb436f50 Mon Sep 17 00:00:00 2001 From: KarlLee830 <61072264+KarlLee830@users.noreply.github.com> Date: Fri, 24 Jan 2025 16:02:35 +0800 Subject: [PATCH 03/36] i18n: Update Chinese Translation --- src/lib/i18n/locales/zh-CN/translation.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/i18n/locales/zh-CN/translation.json b/src/lib/i18n/locales/zh-CN/translation.json index 75e9aa159..803d0d89a 100644 --- a/src/lib/i18n/locales/zh-CN/translation.json +++ b/src/lib/i18n/locales/zh-CN/translation.json @@ -494,7 +494,7 @@ "I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "我已阅读并理解我的行为所带来的影响,明白执行任意代码所涉及的风险。且我已验证代码来源可信度。", "ID": "ID", "Ignite curiosity": "点燃好奇心", - "Image": "图像", + "Image": "图像生成", "Image Compression": "图像压缩", "Image generation": "图像生成", "Image Generation": "图像生成", @@ -934,7 +934,7 @@ "This will delete all models including custom models and cannot be undone.": "这将删除所有模型,包括自定义模型,且无法撤销。", "This will reset the knowledge base and sync all files. Do you wish to continue?": "这将重置知识库并替换所有文件为目录下文件。确认继续?", "Thorough explanation": "解释较为详细", - "Thought for {{DURATION}}": "", + "Thought for {{DURATION}}": "思考 {{DURATION}}", "Tika": "Tika", "Tika Server URL required.": "请输入 Tika 服务器地址。", "Tiktoken": "Tiktoken", From dda945f4ad0c1482fd3ac0e8e55f7817b50ffaf4 Mon Sep 17 00:00:00 2001 From: Sharon Fox Date: Sun, 26 Jan 2025 02:44:26 -0500 Subject: [PATCH 04/36] feat: Localized dates and times --- src/lib/components/admin/Users/UserList.svelte | 4 +++- .../components/admin/Users/UserList/EditUserModal.svelte | 4 +++- .../components/admin/Users/UserList/UserChatsModal.svelte | 4 +++- src/lib/components/channel/Messages/Message.svelte | 8 +++++--- .../components/chat/Messages/MultiResponseMessages.svelte | 4 +++- src/lib/components/chat/Messages/ResponseMessage.svelte | 2 +- src/lib/components/chat/Messages/UserMessage.svelte | 4 +++- .../chat/Settings/Personalization/ManageModal.svelte | 6 +++--- .../components/layout/Sidebar/ArchivedChatsModal.svelte | 5 ++++- src/lib/utils/index.ts | 8 +++++--- src/routes/s/[id]/+page.svelte | 4 +++- 11 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/lib/components/admin/Users/UserList.svelte b/src/lib/components/admin/Users/UserList.svelte index 2ee4297a4..ce730571f 100644 --- a/src/lib/components/admin/Users/UserList.svelte +++ b/src/lib/components/admin/Users/UserList.svelte @@ -6,7 +6,9 @@ import dayjs from 'dayjs'; import relativeTime from 'dayjs/plugin/relativeTime'; + import localizedFormat from 'dayjs/plugin/localizedFormat'; dayjs.extend(relativeTime); + dayjs.extend(localizedFormat); import { toast } from 'svelte-sonner'; @@ -364,7 +366,7 @@ - {dayjs(user.created_at * 1000).format($i18n.t('MMMM DD, YYYY'))} + {dayjs(user.created_at * 1000).format('LL')} {user.oauth_sub ?? ''} diff --git a/src/lib/components/admin/Users/UserList/EditUserModal.svelte b/src/lib/components/admin/Users/UserList/EditUserModal.svelte index 868951d57..9b2edb407 100644 --- a/src/lib/components/admin/Users/UserList/EditUserModal.svelte +++ b/src/lib/components/admin/Users/UserList/EditUserModal.svelte @@ -7,9 +7,11 @@ import { updateUserById } from '$lib/apis/users'; import Modal from '$lib/components/common/Modal.svelte'; + import localizedFormat from 'dayjs/plugin/localizedFormat'; const i18n = getContext('i18n'); const dispatch = createEventDispatcher(); + dayjs.extend(localizedFormat); export let show = false; export let selectedUser; @@ -87,7 +89,7 @@
{$i18n.t('Created at')} - {dayjs(selectedUser.created_at * 1000).format($i18n.t('MMMM DD, YYYY'))} + {dayjs(selectedUser.created_at * 1000).format('LL')}
diff --git a/src/lib/components/admin/Users/UserList/UserChatsModal.svelte b/src/lib/components/admin/Users/UserList/UserChatsModal.svelte index 92970b658..a95df8291 100644 --- a/src/lib/components/admin/Users/UserList/UserChatsModal.svelte +++ b/src/lib/components/admin/Users/UserList/UserChatsModal.svelte @@ -2,8 +2,10 @@ import { toast } from 'svelte-sonner'; import dayjs from 'dayjs'; import { getContext, createEventDispatcher } from 'svelte'; + import localizedFormat from 'dayjs/plugin/localizedFormat'; const dispatch = createEventDispatcher(); + dayjs.extend(localizedFormat); import { getChatListByUserId, deleteChatById, getArchivedChatList } from '$lib/apis/chats'; @@ -130,7 +132,7 @@
- {dayjs(chat.updated_at * 1000).format($i18n.t('MMMM DD, YYYY HH:mm'))} + {dayjs(chat.updated_at * 1000).format('LLL')}
diff --git a/src/lib/components/channel/Messages/Message.svelte b/src/lib/components/channel/Messages/Message.svelte index ef17feb7f..a84077823 100644 --- a/src/lib/components/channel/Messages/Message.svelte +++ b/src/lib/components/channel/Messages/Message.svelte @@ -3,10 +3,12 @@ import relativeTime from 'dayjs/plugin/relativeTime'; import isToday from 'dayjs/plugin/isToday'; import isYesterday from 'dayjs/plugin/isYesterday'; + import localizedFormat from 'dayjs/plugin/localizedFormat'; dayjs.extend(relativeTime); dayjs.extend(isToday); dayjs.extend(isYesterday); + dayjs.extend(localizedFormat); import { getContext, onMount } from 'svelte'; const i18n = getContext>('i18n'); @@ -154,9 +156,9 @@ class="mt-1.5 flex flex-shrink-0 items-center text-xs self-center invisible group-hover:visible text-gray-500 font-medium first-letter:capitalize" > - {dayjs(message.created_at / 1000000).format('HH:mm')} + {dayjs(message.created_at / 1000000).format('LT')} {/if} @@ -175,7 +177,7 @@ class=" self-center text-xs invisible group-hover:visible text-gray-400 font-medium first-letter:capitalize ml-0.5 translate-y-[1px]" > {formatDate(message.created_at / 1000000)} diff --git a/src/lib/components/chat/Messages/MultiResponseMessages.svelte b/src/lib/components/chat/Messages/MultiResponseMessages.svelte index e7874df63..ad42c0f26 100644 --- a/src/lib/components/chat/Messages/MultiResponseMessages.svelte +++ b/src/lib/components/chat/Messages/MultiResponseMessages.svelte @@ -16,7 +16,9 @@ import Markdown from './Markdown.svelte'; import Name from './Name.svelte'; import Skeleton from './Skeleton.svelte'; + import localizedFormat from 'dayjs/plugin/localizedFormat'; const i18n = getContext('i18n'); + dayjs.extend(localizedFormat); export let chatId; export let history; @@ -264,7 +266,7 @@ {/if} diff --git a/src/lib/components/chat/Messages/ResponseMessage.svelte b/src/lib/components/chat/Messages/ResponseMessage.svelte index 1768d584d..d6b31e6a0 100644 --- a/src/lib/components/chat/Messages/ResponseMessage.svelte +++ b/src/lib/components/chat/Messages/ResponseMessage.svelte @@ -500,7 +500,7 @@ diff --git a/src/lib/components/chat/Messages/UserMessage.svelte b/src/lib/components/chat/Messages/UserMessage.svelte index db1d31548..bf22e558a 100644 --- a/src/lib/components/chat/Messages/UserMessage.svelte +++ b/src/lib/components/chat/Messages/UserMessage.svelte @@ -13,8 +13,10 @@ import FileItem from '$lib/components/common/FileItem.svelte'; import Markdown from './Markdown.svelte'; import Image from '$lib/components/common/Image.svelte'; + import localizedFormat from 'dayjs/plugin/localizedFormat'; const i18n = getContext('i18n'); + dayjs.extend(localizedFormat); export let user; @@ -112,7 +114,7 @@ diff --git a/src/lib/components/chat/Settings/Personalization/ManageModal.svelte b/src/lib/components/chat/Settings/Personalization/ManageModal.svelte index e4613b5fe..a9f72ccbb 100644 --- a/src/lib/components/chat/Settings/Personalization/ManageModal.svelte +++ b/src/lib/components/chat/Settings/Personalization/ManageModal.svelte @@ -11,8 +11,10 @@ import Tooltip from '$lib/components/common/Tooltip.svelte'; import { error } from '@sveltejs/kit'; import EditMemoryModal from './EditMemoryModal.svelte'; + import localizedFormat from 'dayjs/plugin/localizedFormat'; const i18n = getContext('i18n'); + dayjs.extend(localizedFormat); export let show = false; @@ -84,9 +86,7 @@
- {dayjs(memory.updated_at * 1000).format( - $i18n.t('MMMM DD, YYYY hh:mm:ss A') - )} + {dayjs(memory.updated_at * 1000).format('LLL')}
diff --git a/src/lib/components/layout/Sidebar/ArchivedChatsModal.svelte b/src/lib/components/layout/Sidebar/ArchivedChatsModal.svelte index 6626cfed7..7af0c6ded 100644 --- a/src/lib/components/layout/Sidebar/ArchivedChatsModal.svelte +++ b/src/lib/components/layout/Sidebar/ArchivedChatsModal.svelte @@ -4,6 +4,9 @@ import { toast } from 'svelte-sonner'; import dayjs from 'dayjs'; import { getContext, createEventDispatcher } from 'svelte'; + import localizedFormat from 'dayjs/plugin/localizedFormat'; + + dayjs.extend(localizedFormat); const dispatch = createEventDispatcher(); @@ -159,7 +162,7 @@
- {dayjs(chat.created_at * 1000).format($i18n.t('MMMM DD, YYYY HH:mm'))} + {dayjs(chat.created_at * 1000).format('LLL')}
diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts index 24d6f23b5..20f44f49b 100644 --- a/src/lib/utils/index.ts +++ b/src/lib/utils/index.ts @@ -5,10 +5,12 @@ import dayjs from 'dayjs'; import relativeTime from 'dayjs/plugin/relativeTime'; import isToday from 'dayjs/plugin/isToday'; import isYesterday from 'dayjs/plugin/isYesterday'; +import localizedFormat from 'dayjs/plugin/localizedFormat'; dayjs.extend(relativeTime); dayjs.extend(isToday); dayjs.extend(isYesterday); +dayjs.extend(localizedFormat); import { WEBUI_BASE_URL } from '$lib/constants'; import { TTS_RESPONSE_SPLIT } from '$lib/types'; @@ -295,11 +297,11 @@ export const formatDate = (inputDate) => { const now = dayjs(); if (date.isToday()) { - return `Today at ${date.format('HH:mm')}`; + return `Today at ${date.format('LT')}`; } else if (date.isYesterday()) { - return `Yesterday at ${date.format('HH:mm')}`; + return `Yesterday at ${date.format('LT')}`; } else { - return `${date.format('DD/MM/YYYY')} at ${date.format('HH:mm')}`; + return `${date.format('L')} at ${date.format('LT')}`; } }; diff --git a/src/routes/s/[id]/+page.svelte b/src/routes/s/[id]/+page.svelte index dfef68513..36ea3541e 100644 --- a/src/routes/s/[id]/+page.svelte +++ b/src/routes/s/[id]/+page.svelte @@ -16,8 +16,10 @@ import { getUserById } from '$lib/apis/users'; import { getModels } from '$lib/apis'; import { toast } from 'svelte-sonner'; + import localizedFormat from 'dayjs/plugin/localizedFormat'; const i18n = getContext('i18n'); + dayjs.extend(localizedFormat); let loaded = false; @@ -138,7 +140,7 @@
- {dayjs(chat.chat.timestamp).format($i18n.t('MMMM DD, YYYY'))} + {dayjs(chat.chat.timestamp).format('LLL')}
From 2d562ed317f34f1ee3c7d79a6fc751715a590ca1 Mon Sep 17 00:00:00 2001 From: kokutaro Date: Sun, 26 Jan 2025 17:51:20 +0900 Subject: [PATCH 05/36] Fixed the type on translation.json Fixed the typo on Japanese translation file --- src/lib/i18n/locales/ja-JP/translation.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/i18n/locales/ja-JP/translation.json b/src/lib/i18n/locales/ja-JP/translation.json index 6ddb781f1..492acce1b 100644 --- a/src/lib/i18n/locales/ja-JP/translation.json +++ b/src/lib/i18n/locales/ja-JP/translation.json @@ -766,7 +766,7 @@ "Reset": "", "Reset All Models": "", "Reset Upload Directory": "アップロードディレクトリをリセット", - "Reset Vector Storage/Knowledge": "ベクターストレージとナレッジべーうをリセット", + "Reset Vector Storage/Knowledge": "ベクターストレージとナレッジベースをリセット", "Reset view": "", "Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "", "Response splitting": "応答の分割", From 6f3c92f6d522d34e89252e2f227c3fde58b0a170 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sun, 26 Jan 2025 23:17:58 -0800 Subject: [PATCH 06/36] enh: chat loading screen --- src/lib/components/chat/Chat.svelte | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index 331806a96..24abe6110 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -82,10 +82,12 @@ import EventConfirmDialog from '../common/ConfirmDialog.svelte'; import Placeholder from './Placeholder.svelte'; import NotificationToast from '../NotificationToast.svelte'; + import Spinner from '../common/Spinner.svelte'; export let chatIdProp = ''; - let loaded = false; + let loading = false; + const eventTarget = new EventTarget(); let controlPane; let controlPaneComponent; @@ -133,6 +135,7 @@ $: if (chatIdProp) { (async () => { + loading = true; console.log(chatIdProp); prompt = ''; @@ -141,11 +144,9 @@ webSearchEnabled = false; imageGenerationEnabled = false; - loaded = false; - if (chatIdProp && (await loadChat())) { await tick(); - loaded = true; + loading = false; if (localStorage.getItem(`chat-input-${chatIdProp}`)) { try { @@ -1861,7 +1862,7 @@ : ' '} w-full max-w-full flex flex-col" id="chat-container" > - {#if !chatIdProp || (loaded && chatIdProp)} + {#if chatIdProp === '' || (!loading && chatIdProp)} {#if $settings?.backgroundImageUrl ?? null}
+
+ +
+
{/if} From 6eb51ab62e3c3bee3590b91cc596ed2d94ce28ca Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sun, 26 Jan 2025 23:30:46 -0800 Subject: [PATCH 07/36] enh: file upload permission indicator --- .../chat/MessageInput/InputMenu.svelte | 53 +++++++++++++------ 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/src/lib/components/chat/MessageInput/InputMenu.svelte b/src/lib/components/chat/MessageInput/InputMenu.svelte index 20b297eb3..3c8eaf008 100644 --- a/src/lib/components/chat/MessageInput/InputMenu.svelte +++ b/src/lib/components/chat/MessageInput/InputMenu.svelte @@ -48,6 +48,9 @@ init(); } + let fileUploadEnabled = true; + $: fileUploadEnabled = $user.role === 'admin' || $user?.permissions?.chat?.file_upload; + const init = async () => { if ($_tools === null) { await _tools.set(await getTools(localStorage.token)); @@ -166,26 +169,44 @@ {/if} {#if !$mobile} - { - screenCaptureHandler(); - }} + - -
{$i18n.t('Capture')}
-
+ { + if (fileUploadEnabled) { + screenCaptureHandler(); + } + }} + > + +
{$i18n.t('Capture')}
+
+ {/if} - { - uploadFilesHandler(); - }} + - -
{$i18n.t('Upload Files')}
-
+ { + if (fileUploadEnabled) { + uploadFilesHandler(); + } + }} + > + +
{$i18n.t('Upload Files')}
+
+ {#if $config?.features?.enable_google_drive_integration} Date: Mon, 27 Jan 2025 13:09:44 +0100 Subject: [PATCH 08/36] Fallback using email - Use Email ass fallback for missing "name" field - "email" because the email scope is required unlike the profile scope --- backend/open_webui/utils/oauth.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/open_webui/utils/oauth.py b/backend/open_webui/utils/oauth.py index b9681c81c..8fdd4b227 100644 --- a/backend/open_webui/utils/oauth.py +++ b/backend/open_webui/utils/oauth.py @@ -279,9 +279,9 @@ class OAuthManager: username_claim = auth_manager_config.OAUTH_USERNAME_CLAIM - username = user_data.get(username_claim) - if not isinstance(username, str): - username = "User" + name = user_data.get(username_claim) + if not isinstance(user, str): + name = email role = self.get_user_role(None, user_data) @@ -290,7 +290,7 @@ class OAuthManager: password=get_password_hash( str(uuid.uuid4()) ), # Random password, not used - name=username, + name=name, profile_image_url=picture_url, role=role, oauth_sub=provider_sub, From 751a61a3647c65c76b210ace75f235463cdf345c Mon Sep 17 00:00:00 2001 From: tarmst Date: Mon, 27 Jan 2025 18:11:52 +0000 Subject: [PATCH 09/36] Adding more checks for write access. Adding accessRoles to Model & Knowledge creation --- backend/open_webui/routers/knowledge.py | 36 +++++++++++++++---- backend/open_webui/routers/models.py | 6 +++- backend/open_webui/routers/prompts.py | 6 +++- backend/open_webui/routers/tools.py | 6 +++- .../Knowledge/CreateKnowledgeBase.svelte | 5 ++- .../workspace/Models/ModelEditor.svelte | 5 ++- 6 files changed, 53 insertions(+), 11 deletions(-) diff --git a/backend/open_webui/routers/knowledge.py b/backend/open_webui/routers/knowledge.py index cce3d6311..a85ccd05e 100644 --- a/backend/open_webui/routers/knowledge.py +++ b/backend/open_webui/routers/knowledge.py @@ -264,7 +264,10 @@ def add_file_to_knowledge_by_id( detail=ERROR_MESSAGES.NOT_FOUND, ) - if knowledge.user_id != user.id and user.role != "admin": + if (knowledge.user_id != user.id + and not has_access(user.id, "write", knowledge.access_control) + and user.role != "admin" + ): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.ACCESS_PROHIBITED, @@ -342,7 +345,12 @@ def update_file_from_knowledge_by_id( detail=ERROR_MESSAGES.NOT_FOUND, ) - if knowledge.user_id != user.id and user.role != "admin": + if ( + knowledge.user_id != user.id + and not has_access(user.id, "write", knowledge.access_control) + and user.role != "admin" + ): + raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.ACCESS_PROHIBITED, @@ -406,7 +414,11 @@ def remove_file_from_knowledge_by_id( detail=ERROR_MESSAGES.NOT_FOUND, ) - if knowledge.user_id != user.id and user.role != "admin": + if ( + knowledge.user_id != user.id + and not has_access(user.id, "write", knowledge.access_control) + and user.role != "admin" + ): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.ACCESS_PROHIBITED, @@ -484,7 +496,11 @@ async def delete_knowledge_by_id(id: str, user=Depends(get_verified_user)): detail=ERROR_MESSAGES.NOT_FOUND, ) - if knowledge.user_id != user.id and user.role != "admin": + if ( + knowledge.user_id != user.id + and not has_access(user.id, "write", knowledge.access_control) + and user.role != "admin" + ): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.ACCESS_PROHIBITED, @@ -543,7 +559,11 @@ async def reset_knowledge_by_id(id: str, user=Depends(get_verified_user)): detail=ERROR_MESSAGES.NOT_FOUND, ) - if knowledge.user_id != user.id and user.role != "admin": + if ( + knowledge.user_id != user.id + and not has_access(user.id, "write", knowledge.access_control) + and user.role != "admin" + ): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.ACCESS_PROHIBITED, @@ -582,7 +602,11 @@ def add_files_to_knowledge_batch( detail=ERROR_MESSAGES.NOT_FOUND, ) - if knowledge.user_id != user.id and user.role != "admin": + if ( + knowledge.user_id != user.id + and not has_access(user.id, "write", knowledge.access_control) + and user.role != "admin" + ): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.ACCESS_PROHIBITED, diff --git a/backend/open_webui/routers/models.py b/backend/open_webui/routers/models.py index 6c8519b2c..a45814d32 100644 --- a/backend/open_webui/routers/models.py +++ b/backend/open_webui/routers/models.py @@ -183,7 +183,11 @@ async def delete_model_by_id(id: str, user=Depends(get_verified_user)): detail=ERROR_MESSAGES.NOT_FOUND, ) - if model.user_id != user.id and user.role != "admin": + if ( + user.role == "admin" + or model.user_id == user.id + or has_access(user.id, "write", model.access_control) + ): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.UNAUTHORIZED, diff --git a/backend/open_webui/routers/prompts.py b/backend/open_webui/routers/prompts.py index 014e5652e..9fb946c6e 100644 --- a/backend/open_webui/routers/prompts.py +++ b/backend/open_webui/routers/prompts.py @@ -147,7 +147,11 @@ async def delete_prompt_by_command(command: str, user=Depends(get_verified_user) detail=ERROR_MESSAGES.NOT_FOUND, ) - if prompt.user_id != user.id and user.role != "admin": + if ( + prompt.user_id != user.id + and not has_access(user.id, "write", prompt.access_control) + and user.role != "admin" + ): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.ACCESS_PROHIBITED, diff --git a/backend/open_webui/routers/tools.py b/backend/open_webui/routers/tools.py index 7b9144b4c..d6a5c5532 100644 --- a/backend/open_webui/routers/tools.py +++ b/backend/open_webui/routers/tools.py @@ -227,7 +227,11 @@ async def delete_tools_by_id( detail=ERROR_MESSAGES.NOT_FOUND, ) - if tools.user_id != user.id and user.role != "admin": + if ( + tools.user_id != user.id + and not has_access(user.id, "write", tools.access_control) + and user.role != "admin" + ): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.UNAUTHORIZED, diff --git a/src/lib/components/workspace/Knowledge/CreateKnowledgeBase.svelte b/src/lib/components/workspace/Knowledge/CreateKnowledgeBase.svelte index 5d1e79808..8253c1f68 100644 --- a/src/lib/components/workspace/Knowledge/CreateKnowledgeBase.svelte +++ b/src/lib/components/workspace/Knowledge/CreateKnowledgeBase.svelte @@ -112,7 +112,10 @@
- +
diff --git a/src/lib/components/workspace/Models/ModelEditor.svelte b/src/lib/components/workspace/Models/ModelEditor.svelte index 58628f0d1..ae10c6eba 100644 --- a/src/lib/components/workspace/Models/ModelEditor.svelte +++ b/src/lib/components/workspace/Models/ModelEditor.svelte @@ -531,7 +531,10 @@
- +
From 45e19fb9bd5a0fb4c0a882334af772883d58df7a Mon Sep 17 00:00:00 2001 From: Tiancong Li Date: Tue, 28 Jan 2025 05:08:19 +0800 Subject: [PATCH 10/36] i18n: update zh-TW --- src/lib/i18n/locales/zh-TW/translation.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/i18n/locales/zh-TW/translation.json b/src/lib/i18n/locales/zh-TW/translation.json index 029fb9968..239cec25f 100644 --- a/src/lib/i18n/locales/zh-TW/translation.json +++ b/src/lib/i18n/locales/zh-TW/translation.json @@ -934,7 +934,7 @@ "This will delete all models including custom models and cannot be undone.": "這將刪除所有模型,包括自訂模型,且無法復原。", "This will reset the knowledge base and sync all files. Do you wish to continue?": "這將重設知識庫並同步所有檔案。您確定要繼續嗎?", "Thorough explanation": "詳細解釋", - "Thought for {{DURATION}}": "", + "Thought for {{DURATION}}": "{{DURATION}} 思考中", "Tika": "Tika", "Tika Server URL required.": "需要 Tika 伺服器 URL。", "Tiktoken": "Tiktoken", From 95f4d99e3b54c6cca308136b496900e391d813d6 Mon Sep 17 00:00:00 2001 From: Orion Date: Tue, 28 Jan 2025 09:53:22 +1000 Subject: [PATCH 11/36] Update misc.py Include empty delta object on openai_chat_chunk_message_template per OpenAI API documentation. https://platform.openai.com/docs/api-reference/chat/streaming#chat/streaming --- backend/open_webui/utils/misc.py | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/open_webui/utils/misc.py b/backend/open_webui/utils/misc.py index a83733d63..8792b1cfc 100644 --- a/backend/open_webui/utils/misc.py +++ b/backend/open_webui/utils/misc.py @@ -149,6 +149,7 @@ def openai_chat_chunk_message_template( template["choices"][0]["delta"] = {"content": message} else: template["choices"][0]["finish_reason"] = "stop" + template["choices"][0]["delta"] = {} if usage: template["usage"] = usage From a1b5c18ef795ee3d17d1d587fd3a038afeef77ea Mon Sep 17 00:00:00 2001 From: Andrew King Date: Tue, 28 Jan 2025 09:51:21 -0500 Subject: [PATCH 12/36] ldap pass user permissions into response --- backend/open_webui/routers/auths.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/backend/open_webui/routers/auths.py b/backend/open_webui/routers/auths.py index 47baeb0ac..9184e575c 100644 --- a/backend/open_webui/routers/auths.py +++ b/backend/open_webui/routers/auths.py @@ -164,7 +164,7 @@ async def update_password( ############################ # LDAP Authentication ############################ -@router.post("/ldap", response_model=SigninResponse) +@router.post("/ldap", response_model=SessionUserResponse) async def ldap_auth(request: Request, response: Response, form_data: LdapForm): ENABLE_LDAP = request.app.state.config.ENABLE_LDAP LDAP_SERVER_LABEL = request.app.state.config.LDAP_SERVER_LABEL @@ -288,6 +288,10 @@ async def ldap_auth(request: Request, response: Response, form_data: LdapForm): httponly=True, # Ensures the cookie is not accessible via JavaScript ) + user_permissions = get_permissions( + user.id, request.app.state.config.USER_PERMISSIONS + ) + return { "token": token, "token_type": "Bearer", @@ -296,6 +300,7 @@ async def ldap_auth(request: Request, response: Response, form_data: LdapForm): "name": user.name, "role": user.role, "profile_image_url": user.profile_image_url, + "permissions": user_permissions, } else: raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED) From c021aba094cec2d00b846763e4a7906da050cce7 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 28 Jan 2025 12:49:06 -0800 Subject: [PATCH 13/36] enh: chat "clone" i18n --- backend/open_webui/routers/chats.py | 10 ++++++++-- src/lib/apis/chats/index.ts | 7 +++++-- src/lib/components/layout/Sidebar/ChatItem.svelte | 8 +++++++- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/backend/open_webui/routers/chats.py b/backend/open_webui/routers/chats.py index a001dd01f..2efd043ef 100644 --- a/backend/open_webui/routers/chats.py +++ b/backend/open_webui/routers/chats.py @@ -444,15 +444,21 @@ async def pin_chat_by_id(id: str, user=Depends(get_verified_user)): ############################ +class CloneForm(BaseModel): + title: Optional[str] = None + + @router.post("/{id}/clone", response_model=Optional[ChatResponse]) -async def clone_chat_by_id(id: str, user=Depends(get_verified_user)): +async def clone_chat_by_id( + form_data: CloneForm, id: str, user=Depends(get_verified_user) +): chat = Chats.get_chat_by_id_and_user_id(id, user.id) if chat: updated_chat = { **chat.chat, "originalChatId": chat.id, "branchPointMessageId": chat.chat["history"]["currentId"], - "title": f"Clone of {chat.title}", + "title": form_data.title if form_data.title else f"Clone of {chat.title}", } chat = Chats.insert_new_chat(user.id, ChatForm(**{"chat": updated_chat})) diff --git a/src/lib/apis/chats/index.ts b/src/lib/apis/chats/index.ts index 1772529d3..7af504cc7 100644 --- a/src/lib/apis/chats/index.ts +++ b/src/lib/apis/chats/index.ts @@ -580,7 +580,7 @@ export const toggleChatPinnedStatusById = async (token: string, id: string) => { return res; }; -export const cloneChatById = async (token: string, id: string) => { +export const cloneChatById = async (token: string, id: string, title?: string) => { let error = null; const res = await fetch(`${WEBUI_API_BASE_URL}/chats/${id}/clone`, { @@ -589,7 +589,10 @@ export const cloneChatById = async (token: string, id: string) => { Accept: 'application/json', 'Content-Type': 'application/json', ...(token && { authorization: `Bearer ${token}` }) - } + }, + body: JSON.stringify({ + ...(title && { title: title }) + }) }) .then(async (res) => { if (!res.ok) throw await res.json(); diff --git a/src/lib/components/layout/Sidebar/ChatItem.svelte b/src/lib/components/layout/Sidebar/ChatItem.svelte index 748e1adb9..1e9224093 100644 --- a/src/lib/components/layout/Sidebar/ChatItem.svelte +++ b/src/lib/components/layout/Sidebar/ChatItem.svelte @@ -87,7 +87,13 @@ }; const cloneChatHandler = async (id) => { - const res = await cloneChatById(localStorage.token, id).catch((error) => { + const res = await cloneChatById( + localStorage.token, + id, + $i18n.t('Clone of {{TITLE}}', { + TITLE: title + }) + ).catch((error) => { toast.error(`${error}`); return null; }); From b4be58f6b6f4199e17f3dfc16705b5cf7753c9bd Mon Sep 17 00:00:00 2001 From: Jonathan Respeto Date: Tue, 28 Jan 2025 18:27:41 -0500 Subject: [PATCH 14/36] Update ollama.py Missing await in coroutine causing 500 errors on /ollama/api/tags since v0.5.0 --- backend/open_webui/routers/ollama.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/open_webui/routers/ollama.py b/backend/open_webui/routers/ollama.py index 261cd5ba3..d9124c29f 100644 --- a/backend/open_webui/routers/ollama.py +++ b/backend/open_webui/routers/ollama.py @@ -395,7 +395,7 @@ async def get_ollama_tags( ) if user.role == "user" and not BYPASS_MODEL_ACCESS_CONTROL: - models["models"] = get_filtered_models(models, user) + models["models"] = await get_filtered_models(models, user) return models From 941836406219f51dc5f807ea426795b42884b688 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 28 Jan 2025 18:36:23 -0800 Subject: [PATCH 15/36] chore: fastapi bump --- backend/requirements.txt | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index eecb9c4a5..31b4767e7 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,4 +1,4 @@ -fastapi==0.111.0 +fastapi==0.115.7 uvicorn[standard]==0.30.6 pydantic==2.9.2 python-multipart==0.0.18 diff --git a/pyproject.toml b/pyproject.toml index edd01db8f..7be043cb9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ authors = [ ] license = { file = "LICENSE" } dependencies = [ - "fastapi==0.111.0", + "fastapi==0.115.7", "uvicorn[standard]==0.30.6", "pydantic==2.9.2", "python-multipart==0.0.18", From bd24ed953a6499daea9232efbcc982977c6bfc7b Mon Sep 17 00:00:00 2001 From: iidx Date: Wed, 29 Jan 2025 15:57:00 +0900 Subject: [PATCH 16/36] i18n: Corrected typos in korean --- src/lib/i18n/locales/ko-KR/translation.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib/i18n/locales/ko-KR/translation.json b/src/lib/i18n/locales/ko-KR/translation.json index dc9090b1e..ee6f68978 100644 --- a/src/lib/i18n/locales/ko-KR/translation.json +++ b/src/lib/i18n/locales/ko-KR/translation.json @@ -8,7 +8,7 @@ "{{COUNT}} Replies": "", "{{user}}'s Chats": "{{user}}의 채팅", "{{webUIName}} Backend Required": "{{webUIName}} 백엔드가 필요합니다.", - "*Prompt node ID(s) are required for image generation": "사진 생성을 위해 프롬포트 노드 ID가 필요합니다", + "*Prompt node ID(s) are required for image generation": "사진 생성을 위해 프롬프트 노드 ID가 필요합니다", "A new version (v{{LATEST_VERSION}}) is now available.": "최신 버전 (v{{LATEST_VERSION}})이 가능합니다", "A task model is used when performing tasks such as generating titles for chats and web search queries": "작업 모델은 채팅 및 웹 검색 쿼리에 대한 제목 생성 등의 작업 수행 시 사용됩니다.", "a user": "사용자", @@ -252,7 +252,7 @@ "Delete folder?": "폴더를 삭제하시겠습니까?", "Delete function?": "함수를 삭제하시겠습니까?", "Delete Message": "", - "Delete prompt?": "프롬포트를 삭제하시겠습니까?", + "Delete prompt?": "프롬프트를 삭제하시겠습니까?", "delete this link": "이 링크를 삭제합니다.", "Delete tool?": "도구를 삭제하시겠습니까?", "Delete User": "사용자 삭제", @@ -370,7 +370,7 @@ "Enter server label": "", "Enter server port": "", "Enter stop sequence": "중지 시퀀스 입력", - "Enter system prompt": "시스템 프롬포트 입력", + "Enter system prompt": "시스템 프롬프트 입력", "Enter Tavily API Key": "Tavily API 키 입력", "Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "WebUI의 공개 URL을 입력해 주세요. 이 URL은 알림에서 링크를 생성하는 데 사용합니다.", "Enter Tika Server URL": "Tika 서버 URL 입력", @@ -556,7 +556,7 @@ "Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "", "Leave empty to include all models from \"{{URL}}/models\" endpoint": "", "Leave empty to include all models or select specific models": "특정 모델을 선택하거나 모든 모델을 포함하고 싶으면 빈칸으로 남겨두세요", - "Leave empty to use the default prompt, or enter a custom prompt": "기본 프롬포트를 사용하기 위해 빈칸으로 남겨두거나, 커스텀 프롬포트를 입력하세요", + "Leave empty to use the default prompt, or enter a custom prompt": "기본 프롬프트를 사용하기 위해 빈칸으로 남겨두거나, 커스텀 프롬프트를 입력하세요", "Light": "라이트", "Listening...": "듣는 중...", "Llama.cpp": "", @@ -715,7 +715,7 @@ "Plain text (.txt)": "일반 텍스트(.txt)", "Playground": "놀이터", "Please carefully review the following warnings:": "다음 주의를 조심히 확인해주십시오", - "Please enter a prompt": "프롬포트를 입력해주세요", + "Please enter a prompt": "프롬프트를 입력해주세요", "Please fill in all fields.": "모두 빈칸없이 채워주세요", "Please select a model first.": "", "Please select a reason": "이유를 선택하주세요", @@ -895,7 +895,7 @@ "System Instructions": "시스템 설명서", "System Prompt": "시스템 프롬프트", "Tags Generation": "태그 생성", - "Tags Generation Prompt": "태그 생성 프롬포트트", + "Tags Generation Prompt": "태그 생성 프롬프트", "Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "", "Tap to interrupt": "탭하여 중단", "Tavily API Key": "Tavily API 키", From f502e7db51ecef69e6440f0f8e6e0fb6ea5d64a0 Mon Sep 17 00:00:00 2001 From: iidx Date: Wed, 29 Jan 2025 17:18:13 +0900 Subject: [PATCH 17/36] i18n: some korean update --- src/lib/i18n/locales/ko-KR/translation.json | 130 ++++++++++---------- 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/src/lib/i18n/locales/ko-KR/translation.json b/src/lib/i18n/locales/ko-KR/translation.json index ee6f68978..58ccb4144 100644 --- a/src/lib/i18n/locales/ko-KR/translation.json +++ b/src/lib/i18n/locales/ko-KR/translation.json @@ -13,34 +13,34 @@ "A task model is used when performing tasks such as generating titles for chats and web search queries": "작업 모델은 채팅 및 웹 검색 쿼리에 대한 제목 생성 등의 작업 수행 시 사용됩니다.", "a user": "사용자", "About": "정보", - "Access": "", - "Access Control": "", + "Access": "접근", + "Access Control": "접근 제어", "Accessible to all users": "모든 사용자가 접근 가능", "Account": "계정", "Account Activation Pending": "계정 활성화 대기", "Accurate information": "정확한 정보", "Actions": "행동", - "Activate this command by typing \"/{{COMMAND}}\" to chat input.": "", + "Activate this command by typing \"/{{COMMAND}}\" to chat input.": "채팅에서 \"{{COMMAND}}\"을 입력하여 이 명령을 활성화할 수 있습니다.", "Active Users": "활성 사용자", "Add": "추가", - "Add a model ID": "", + "Add a model ID": "모델 ID 추가", "Add a short description about what this model does": "모델의 기능에 대한 간단한 설명 추가", "Add a tag": "태그 추가", "Add Arena Model": "아레나 모델 추가", - "Add Connection": "", + "Add Connection": "연결 추가", "Add Content": "내용 추가", "Add content here": "여기에 내용을 추가하세요", "Add custom prompt": "사용자 정의 프롬프트 추가", "Add Files": "파일 추가", - "Add Group": "", + "Add Group": "그룹 추가", "Add Memory": "메모리 추가", "Add Model": "모델 추가", - "Add Reaction": "", + "Add Reaction": "리액션 추가", "Add Tag": "태그 추가", "Add Tags": "태그 추가", "Add text content": "글 추가", "Add User": "사용자 추가", - "Add User Group": "", + "Add User Group": "사용자 그룹 추가", "Adjusting these settings will apply changes universally to all users.": "위와 같이 설정시 모든 사용자에게 적용됩니다.", "admin": "관리자", "Admin": "관리자", @@ -50,12 +50,12 @@ "Advanced Parameters": "고급 매개변수", "Advanced Params": "고급 매개변수", "All Documents": "모든 문서", - "All models deleted successfully": "", - "Allow Chat Controls": "", - "Allow Chat Delete": "", + "All models deleted successfully": "성공적으로 모든 모델이 삭제되었습니다", + "Allow Chat Controls": "채팅 제어 허용", + "Allow Chat Delete": "채팅 삭제 허용", "Allow Chat Deletion": "채팅 삭제 허용", - "Allow Chat Edit": "", - "Allow File Upload": "", + "Allow Chat Edit": "채팅 수정 허용", + "Allow File Upload": "파일 업로드 허용", "Allow non-local voices": "외부 음성 허용", "Allow Temporary Chat": "임시 채팅 허용", "Allow User Location": "사용자 위치 활용 허용", @@ -75,14 +75,14 @@ "API keys": "API 키", "Application DN": "", "Application DN Password": "", - "applies to all users with the \"user\" role": "", + "applies to all users with the \"user\" role": "\"사용자\" 권한의 모든 사용자에게 적용됩니다", "April": "4월", "Archive": "보관", "Archive All Chats": "모든 채팅 보관", "Archived Chats": "보관된 채팅", "archived-chat-export": "", - "Are you sure you want to delete this channel?": "", - "Are you sure you want to delete this message?": "", + "Are you sure you want to delete this channel?": "정말 이 채널을 삭제하시겠습니까?", + "Are you sure you want to delete this message?": "정말 이 메세지를 삭제하시겠습니까?", "Are you sure you want to unarchive all archived chats?": "", "Are you sure?": "확실합니까?", "Arena Models": "아레나 모델", @@ -141,7 +141,7 @@ "Chat Controls": "채팅 제어", "Chat direction": "채팅 방향", "Chat Overview": "채팅", - "Chat Permissions": "", + "Chat Permissions": "채팅 권한", "Chat Tags Auto-Generation": "채팅 태그 자동생성", "Chats": "채팅", "Check Again": "다시 확인", @@ -210,16 +210,16 @@ "Copy Link": "링크 복사", "Copy to clipboard": "클립보드에 복사", "Copying to clipboard was successful!": "성공적으로 클립보드에 복사되었습니다!", - "Create": "", - "Create a knowledge base": "", - "Create a model": "모델 만들기", - "Create Account": "계정 만들기", - "Create Admin Account": "", - "Create Channel": "", - "Create Group": "", - "Create Knowledge": "지식 만들기", - "Create new key": "새 키 만들기", - "Create new secret key": "새 비밀 키 만들기", + "Create": "생성", + "Create a knowledge base": "지식 기반 생성", + "Create a model": "모델 생성", + "Create Account": "계정 생성", + "Create Admin Account": "관리자 계정 생성", + "Create Channel": "채널 생성", + "Create Group": "그룹 생성", + "Create Knowledge": "지식 생성", + "Create new key": "새로운 키 생성", + "Create new secret key": "새로운 비밀 키 생성", "Created at": "생성일", "Created At": "생성일", "Created by": "생성자", @@ -236,8 +236,8 @@ "Default Model": "기본 모델", "Default model updated": "기본 모델이 업데이트되었습니다.", "Default Models": "기본 모델", - "Default permissions": "", - "Default permissions updated successfully": "", + "Default permissions": "기본 권한", + "Default permissions updated successfully": "성공적으로 기본 권한이 수정되었습니다", "Default Prompt Suggestions": "기본 프롬프트 제안", "Default to 389 or 636 if TLS is enabled": "", "Default to ALL": "", @@ -245,7 +245,7 @@ "Delete": "삭제", "Delete a model": "모델 삭제", "Delete All Chats": "모든 채팅 삭제", - "Delete All Models": "", + "Delete All Models": "모든 모델 삭제", "Delete chat": "채팅 삭제", "Delete Chat": "채팅 삭제", "Delete chat?": "채팅을 삭제하겠습니까?", @@ -258,7 +258,7 @@ "Delete User": "사용자 삭제", "Deleted {{deleteModelTag}}": "{{deleteModelTag}} 삭제됨", "Deleted {{name}}": "{{name}}을(를) 삭제했습니다.", - "Deleted User": "", + "Deleted User": "삭제된 사용자", "Describe your knowledge base and objectives": "", "Description": "설명", "Didn't fully follow instructions": "완전히 지침을 따르지 않음", @@ -419,10 +419,10 @@ "Failed to save models configuration": "", "Failed to update settings": "설정 업데이트에 실패하였습니다.", "Failed to upload file.": "파일 업로드에 실패했습니다", - "Features Permissions": "", + "Features Permissions": "기능 권한", "February": "2월", "Feedback History": "피드백 기록", - "Feedbacks": "", + "Feedbacks": "피드백", "Feel free to add specific details": "자세한 내용을 자유롭게 추가하세요.", "File": "파일", "File added successfully.": "성공적으로 파일이 추가되었습니다", @@ -472,12 +472,12 @@ "Google Drive": "", "Google PSE API Key": "Google PSE API 키", "Google PSE Engine Id": "Google PSE 엔진 ID", - "Group created successfully": "", - "Group deleted successfully": "", - "Group Description": "", - "Group Name": "", - "Group updated successfully": "", - "Groups": "", + "Group created successfully": "성공적으로 그룹을 생성했습니다", + "Group deleted successfully": "성공적으로 그룹을 삭제했습니다", + "Group Description": "그룹 설명", + "Group Name": "그룹 명", + "Group updated successfully": "성공적으로 그룹을 수정했습니다", + "Groups": "그룹", "h:mm a": "h:mm a", "Haptic Feedback": "햅틱 피드백", "has no conversations.": "대화가 없습니다.", @@ -494,13 +494,13 @@ "I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "", "ID": "ID", "Ignite curiosity": "", - "Image": "", + "Image": "이미지", "Image Compression": "이미지 압축", - "Image generation": "", - "Image Generation": "", + "Image generation": "이미지 생성", + "Image Generation": "이미지 생성", "Image Generation (Experimental)": "이미지 생성(실험적)", "Image Generation Engine": "이미지 생성 엔진", - "Image Max Compression Size": "", + "Image Max Compression Size": "이미지 최대 압축 크기", "Image Prompt Generation": "", "Image Prompt Generation Prompt": "", "Image Settings": "이미지 설정", @@ -538,7 +538,7 @@ "Key": "", "Keyboard shortcuts": "키보드 단축키", "Knowledge": "지식 기반", - "Knowledge Access": "", + "Knowledge Access": "지식 접근", "Knowledge created successfully.": "성공적으로 지식 기반이 생성되었습니다", "Knowledge deleted successfully.": "성공적으로 지식 기반이 삭제되었습니다", "Knowledge reset successfully.": "성공적으로 지식 기반이 초기화되었습니다", @@ -617,13 +617,13 @@ "Model updated successfully": "성공적으로 모델이 업데이트되었습니다", "Modelfile Content": "Modelfile 내용", "Models": "모델", - "Models Access": "", + "Models Access": "모델 접근", "Models configuration saved successfully": "", "Mojeek Search API Key": "Mojeek Search API 키", "more": "더보기", "More": "더보기", "Name": "이름", - "Name your knowledge base": "", + "Name your knowledge base": "지식 기반 이름을 지정하세요", "New Chat": "새 채팅", "New Folder": "", "New Password": "새 비밀번호", @@ -667,8 +667,8 @@ "Ollama API settings updated": "", "Ollama Version": "Ollama 버전", "On": "켜기", - "Only alphanumeric characters and hyphens are allowed": "", - "Only alphanumeric characters and hyphens are allowed in the command string.": "명령어 문자열에는 영문자, 숫자 및 하이픈만 허용됩니다.", + "Only alphanumeric characters and hyphens are allowed": "영문자, 숫자 및 하이픈(-)만 허용됨", + "Only alphanumeric characters and hyphens are allowed in the command string.": "명령어 문자열에는 영문자, 숫자 및 하이픈(-)만 허용됩니다.", "Only collections can be edited, create a new knowledge base to edit/add documents.": "가지고 있는 컬렉션만 수정 가능합니다, 새 지식 기반을 생성하여 문서를 수정 혹은 추가하십시오", "Only select users and groups with permission can access": "권한이 있는 사용자와 그룹만 접근 가능합니다", "Oops! Looks like the URL is invalid. Please double-check and try again.": "이런! URL이 잘못된 것 같습니다. 다시 한번 확인하고 다시 시도해주세요.", @@ -688,7 +688,7 @@ "OpenAI API settings updated": "", "OpenAI URL/Key required.": "OpenAI URL/키가 필요합니다.", "or": "또는", - "Organize your users": "", + "Organize your users": "사용자를 ", "Other": "기타", "OUTPUT": "출력력", "Output format": "출력 형식", @@ -702,7 +702,7 @@ "Permission denied when accessing media devices": "미디어 장치 접근 권한이 거부되었습니다.", "Permission denied when accessing microphone": "마이크 접근 권한이 거부되었습니다.", "Permission denied when accessing microphone: {{error}}": "마이크 접근 권환이 거부되었습니다: {{error}}", - "Permissions": "", + "Permissions": "권한", "Personalization": "개인화", "Pin": "고정", "Pinned": "고정됨", @@ -710,7 +710,7 @@ "Pipeline deleted successfully": "성공적으로 파이프라인이 삭제되었습니다", "Pipeline downloaded successfully": "성공적으로 파이프라인이 설치되었습니다", "Pipelines": "파이프라인", - "Pipelines Not Detected": "파이프라인 발견되지않음", + "Pipelines Not Detected": "파이프라인이 발견되지 않음", "Pipelines Valves": "파이프라인 밸브", "Plain text (.txt)": "일반 텍스트(.txt)", "Playground": "놀이터", @@ -718,8 +718,8 @@ "Please enter a prompt": "프롬프트를 입력해주세요", "Please fill in all fields.": "모두 빈칸없이 채워주세요", "Please select a model first.": "", - "Please select a reason": "이유를 선택하주세요", - "Port": "", + "Please select a reason": "이유를 선택해주세요", + "Port": "포트", "Positive attitude": "긍정적인 자세", "Prefix ID": "", "Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "", @@ -728,11 +728,11 @@ "Profile Image": "프로필 이미지", "Prompt (e.g. Tell me a fun fact about the Roman Empire)": "프롬프트 (예: 로마 황제에 대해 재미있는 사실을 알려주세요)", "Prompt Content": "프롬프트 내용", - "Prompt created successfully": "", + "Prompt created successfully": "성공적으로 프롬프트를 생성했습니다", "Prompt suggestions": "프롬프트 제안", - "Prompt updated successfully": "", + "Prompt updated successfully": "성공적으로 프롬프트를 수정했습니다", "Prompts": "프롬프트", - "Prompts Access": "", + "Prompts Access": "프롬프트 접근", "Proxy URL": "프록시 URL", "Pull \"{{searchValue}}\" from Ollama.com": "Ollama.com에서 \"{{searchValue}}\" 가져오기", "Pull a model from Ollama.com": "Ollama.com에서 모델 가져오기(pull)", @@ -792,13 +792,13 @@ "Search a model": "모델 검색", "Search Base": "", "Search Chats": "채팅 검색", - "Search Collection": "컬렉션검색", - "Search Filters": "", + "Search Collection": "컬렉션 검색", + "Search Filters": "필터 검색", "search for tags": "태그 검색", "Search Functions": "함수 검색", "Search Knowledge": "지식 기반 검색", "Search Models": "모델 검색", - "Search options": "", + "Search options": "옵션 검색", "Search Prompts": "프롬프트 검색", "Search Result Count": "검색 결과 수", "Search the web": "", @@ -969,7 +969,7 @@ "Tool Name": "", "Tool updated successfully": "성공적으로 도구가 업데이트되었습니다", "Tools": "도구", - "Tools Access": "", + "Tools Access": "도구 접근", "Tools are a function calling system with arbitrary code execution": "도구는 임이의 코드를 실행시키는 함수를 불러오는 시스템입니다", "Tools have a function calling system that allows arbitrary code execution": "도구가 임이의 코드를 실행시키는 함수를 가지기", "Tools have a function calling system that allows arbitrary code execution.": "도구가 임이의 코드를 실행시키는 함수를 가지고 있습니다.", @@ -1009,7 +1009,7 @@ "URL Mode": "URL 모드", "Use '#' in the prompt input to load and include your knowledge.": "프롬프트 입력에서 '#'를 사용하여 지식 기반을 불러오고 포함하세요.", "Use Gravatar": "Gravatar 사용", - "Use groups to group your users and assign permissions.": "", + "Use groups to group your users and assign permissions.": "그룹을 사용하여 사용자를 그룹화하고 권한을 할당하세요.", "Use Initials": "초성 사용", "use_mlock (Ollama)": "use_mlock (올라마)", "use_mmap (Ollama)": "use_mmap (올라마)", @@ -1034,7 +1034,7 @@ "Voice Input": "음성 입력", "Warning": "경고", "Warning:": "주의:", - "Warning: Enabling this will allow users to upload arbitrary code on the server.": "", + "Warning: Enabling this will allow users to upload arbitrary code on the server.": "주의: 이 기능을 활성화하면 사용자가 서버에 임의 코드를 업로드할 수 있습니다.", "Warning: If you update or change your embedding model, you will need to re-import all documents.": "주의: 기존 임베딩 모델을 변경 또는 업데이트하는 경우, 모든 문서를 다시 가져와야 합니다.", "Web": "웹", "Web API": "웹 API", @@ -1048,7 +1048,7 @@ "WebUI will make requests to \"{{url}}/api/chat\"": "WebUI가 \"{{url}}/api/chat\"로 요청을 보냅니다", "WebUI will make requests to \"{{url}}/chat/completions\"": "WebUI가 \"{{url}}/chat/completions\"로 요청을 보냅니다", "What are you trying to achieve?": "", - "What are you working on?": "", + "What are you working on?": "어떤 작업을 하고 계신가요?", "What’s New in": "새로운 기능:", "When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "활성화하면 모델이 각 채팅 메시지에 실시간으로 응답하여 사용자가 메시지를 보내는 즉시 응답을 생성합니다. 이 모드는 실시간 채팅 애플리케이션에 유용하지만, 느린 하드웨어에서는 성능에 영향을 미칠 수 있습니다.", "wherever you are": "", @@ -1058,12 +1058,12 @@ "Won": "승리", "Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "", "Workspace": "워크스페이스", - "Workspace Permissions": "", + "Workspace Permissions": "워크스페이스 권한", "Write": "", "Write a prompt suggestion (e.g. Who are you?)": "프롬프트 제안 작성 (예: 당신은 누구인가요?)", "Write a summary in 50 words that summarizes [topic or keyword].": "[주제 또는 키워드]에 대한 50단어 요약문 작성.", "Write something...": "아무거나 쓰세요...", - "Write your model template content here": "", + "Write your model template content here": "여기에 모델 템플릿 내용을 입력하세요", "Yesterday": "어제", "You": "당신", "You can only chat with a maximum of {{maxCount}} file(s) at a time.": "동시에 최대 {{maxCount}} 파일과만 대화할 수 있습니다 ", From 938a151f121c2cd8a720ce96d7a0af48114154f7 Mon Sep 17 00:00:00 2001 From: iidx Date: Wed, 29 Jan 2025 17:30:53 +0900 Subject: [PATCH 18/36] i18n: some korean update --- src/lib/i18n/locales/ko-KR/translation.json | 24 ++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/lib/i18n/locales/ko-KR/translation.json b/src/lib/i18n/locales/ko-KR/translation.json index 58ccb4144..ff33f8b96 100644 --- a/src/lib/i18n/locales/ko-KR/translation.json +++ b/src/lib/i18n/locales/ko-KR/translation.json @@ -83,7 +83,7 @@ "archived-chat-export": "", "Are you sure you want to delete this channel?": "정말 이 채널을 삭제하시겠습니까?", "Are you sure you want to delete this message?": "정말 이 메세지를 삭제하시겠습니까?", - "Are you sure you want to unarchive all archived chats?": "", + "Are you sure you want to unarchive all archived chats?": "정말 보관된 모든 채팅을 보관 해제하시겠습니까?", "Are you sure?": "확실합니까?", "Arena Models": "아레나 모델", "Artifacts": "아티팩트", @@ -259,7 +259,7 @@ "Deleted {{deleteModelTag}}": "{{deleteModelTag}} 삭제됨", "Deleted {{name}}": "{{name}}을(를) 삭제했습니다.", "Deleted User": "삭제된 사용자", - "Describe your knowledge base and objectives": "", + "Describe your knowledge base and objectives": "지식 기반에 대한 설명과 목적을 입력하세요", "Description": "설명", "Didn't fully follow instructions": "완전히 지침을 따르지 않음", "Disabled": "제한됨", @@ -963,16 +963,16 @@ "Too verbose": "말이 너무 많은", "Tool created successfully": "성공적으로 도구가 생성되었습니다", "Tool deleted successfully": "성공적으로 도구가 삭제되었습니다", - "Tool Description": "", - "Tool ID": "", + "Tool Description": "도구 설명", + "Tool ID": "도구 ID", "Tool imported successfully": "성공적으로 도구를 가져왔습니다", - "Tool Name": "", + "Tool Name": "도구 이름", "Tool updated successfully": "성공적으로 도구가 업데이트되었습니다", "Tools": "도구", "Tools Access": "도구 접근", - "Tools are a function calling system with arbitrary code execution": "도구는 임이의 코드를 실행시키는 함수를 불러오는 시스템입니다", - "Tools have a function calling system that allows arbitrary code execution": "도구가 임이의 코드를 실행시키는 함수를 가지기", - "Tools have a function calling system that allows arbitrary code execution.": "도구가 임이의 코드를 실행시키는 함수를 가지고 있습니다.", + "Tools are a function calling system with arbitrary code execution": "도구는 임의 코드를 실행시키는 함수를 불러오는 시스템입니다", + "Tools have a function calling system that allows arbitrary code execution": "도구에 임의 코드 실행을 허용하는 함수가 포함되어 있습니다", + "Tools have a function calling system that allows arbitrary code execution.": "도구에 임의 코드 실행을 허용하는 함수가 포함되어 있습니다.", "Top K": "Top K", "Top P": "Top P", "Transformers": "", @@ -984,9 +984,9 @@ "Type Hugging Face Resolve (Download) URL": "Hugging Face Resolve (다운로드) URL 입력", "Uh-oh! There was an issue with the response.": "", "UI": "UI", - "Unarchive All": "", - "Unarchive All Archived Chats": "", - "Unarchive Chat": "", + "Unarchive All": "모두 보관 해제", + "Unarchive All Archived Chats": "보관된 모든 채팅을 보관 해제", + "Unarchive Chat": "채팅 보관 해제", "Unlock mysteries": "", "Unpin": "고정 해제", "Unravel secrets": "", @@ -1047,7 +1047,7 @@ "WebUI URL": "", "WebUI will make requests to \"{{url}}/api/chat\"": "WebUI가 \"{{url}}/api/chat\"로 요청을 보냅니다", "WebUI will make requests to \"{{url}}/chat/completions\"": "WebUI가 \"{{url}}/chat/completions\"로 요청을 보냅니다", - "What are you trying to achieve?": "", + "What are you trying to achieve?": "무엇을 성취하고 싶으신가요?", "What are you working on?": "어떤 작업을 하고 계신가요?", "What’s New in": "새로운 기능:", "When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "활성화하면 모델이 각 채팅 메시지에 실시간으로 응답하여 사용자가 메시지를 보내는 즉시 응답을 생성합니다. 이 모드는 실시간 채팅 애플리케이션에 유용하지만, 느린 하드웨어에서는 성능에 영향을 미칠 수 있습니다.", From a32782e5279fc6a40439590bdd8dd9fa159d8b47 Mon Sep 17 00:00:00 2001 From: Jonathan Respeto Date: Wed, 29 Jan 2025 08:47:54 -0500 Subject: [PATCH 19/36] fix: Update openai.py need to await get_filtered_models --- backend/open_webui/routers/openai.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/open_webui/routers/openai.py b/backend/open_webui/routers/openai.py index f7d7fd294..2139be4ef 100644 --- a/backend/open_webui/routers/openai.py +++ b/backend/open_webui/routers/openai.py @@ -489,7 +489,7 @@ async def get_models( raise HTTPException(status_code=500, detail=error_detail) if user.role == "user" and not BYPASS_MODEL_ACCESS_CONTROL: - models["data"] = get_filtered_models(models, user) + models["data"] = await get_filtered_models(models, user) return models From c2e742afe1f525841902d2b0360025974e54a4b6 Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 29 Jan 2025 19:28:09 +0100 Subject: [PATCH 20/36] Fix max_tokens not being set properly --- backend/open_webui/utils/middleware.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 6b2329be1..815e69739 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -666,6 +666,9 @@ def apply_params_to_form_data(form_data, model): if "temperature" in params: form_data["temperature"] = params["temperature"] + if "max_tokens" in params: + form_data["max_tokens"] = params["max_tokens"] + if "top_p" in params: form_data["top_p"] = params["top_p"] From 4abede9a2bad7902e23e8bff2de93fff2c163ce4 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 29 Jan 2025 14:20:51 -0800 Subject: [PATCH 21/36] fix: uploaded file should not be deleted --- backend/open_webui/routers/knowledge.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/backend/open_webui/routers/knowledge.py b/backend/open_webui/routers/knowledge.py index a85ccd05e..aac16e851 100644 --- a/backend/open_webui/routers/knowledge.py +++ b/backend/open_webui/routers/knowledge.py @@ -264,7 +264,8 @@ def add_file_to_knowledge_by_id( detail=ERROR_MESSAGES.NOT_FOUND, ) - if (knowledge.user_id != user.id + if ( + knowledge.user_id != user.id and not has_access(user.id, "write", knowledge.access_control) and user.role != "admin" ): @@ -349,7 +350,7 @@ def update_file_from_knowledge_by_id( knowledge.user_id != user.id and not has_access(user.id, "write", knowledge.access_control) and user.role != "admin" - ): + ): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, @@ -418,7 +419,7 @@ def remove_file_from_knowledge_by_id( knowledge.user_id != user.id and not has_access(user.id, "write", knowledge.access_control) and user.role != "admin" - ): + ): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.ACCESS_PROHIBITED, @@ -441,10 +442,6 @@ def remove_file_from_knowledge_by_id( if VECTOR_DB_CLIENT.has_collection(collection_name=file_collection): VECTOR_DB_CLIENT.delete_collection(collection_name=file_collection) - # Delete physical file - if file.path: - Storage.delete_file(file.path) - # Delete file from database Files.delete_file_by_id(form_data.file_id) @@ -500,7 +497,7 @@ async def delete_knowledge_by_id(id: str, user=Depends(get_verified_user)): knowledge.user_id != user.id and not has_access(user.id, "write", knowledge.access_control) and user.role != "admin" - ): + ): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.ACCESS_PROHIBITED, @@ -563,7 +560,7 @@ async def reset_knowledge_by_id(id: str, user=Depends(get_verified_user)): knowledge.user_id != user.id and not has_access(user.id, "write", knowledge.access_control) and user.role != "admin" - ): + ): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.ACCESS_PROHIBITED, @@ -606,7 +603,7 @@ def add_files_to_knowledge_batch( knowledge.user_id != user.id and not has_access(user.id, "write", knowledge.access_control) and user.role != "admin" - ): + ): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.ACCESS_PROHIBITED, From 5420c165c60edb23a841f1b469d2fec381775cbf Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 29 Jan 2025 14:40:36 -0800 Subject: [PATCH 22/36] refac: title generation --- backend/open_webui/config.py | 26 ++++++++++++++++++-------- backend/open_webui/routers/tasks.py | 4 ++-- backend/open_webui/utils/middleware.py | 22 +++++++++++++++------- 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index c37b831de..f65726c3c 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -1094,16 +1094,26 @@ TITLE_GENERATION_PROMPT_TEMPLATE = PersistentConfig( os.environ.get("TITLE_GENERATION_PROMPT_TEMPLATE", ""), ) -DEFAULT_TITLE_GENERATION_PROMPT_TEMPLATE = """Create a concise, 3-5 word title with an emoji as a title for the chat history, in the given language. Suitable Emojis for the summary can be used to enhance understanding but avoid quotation marks or special formatting. RESPOND ONLY WITH THE TITLE TEXT. +DEFAULT_TITLE_GENERATION_PROMPT_TEMPLATE = """### Task: +Generate a concise, 3-5 word title with an emoji summarizing the chat history. +### Guidelines: +- The title should clearly represent the main theme or subject of the conversation. +- Use emojis that enhance understanding of the topic, but avoid quotation marks or special formatting. +- Write the title in the chat's primary language; default to English if multilingual. +- Prioritize accuracy over excessive creativity; keep it clear and simple. -Examples of titles: -📉 Stock Market Trends -🍪 Perfect Chocolate Chip Recipe -Evolution of Music Streaming -Remote Work Productivity Tips -Artificial Intelligence in Healthcare -🎮 Video Game Development Insights +### Examples: +- 📉 Stock Market Trends +- 🍪 Perfect Chocolate Chip Recipe +- Evolution of Music Streaming +- Remote Work Productivity Tips +- Artificial Intelligence in Healthcare +- 🎮 Video Game Development Insights +### Output: +JSON format: { "title": "your concise title here" } + +### Chat History: {{MESSAGES:END:2}} """ diff --git a/backend/open_webui/routers/tasks.py b/backend/open_webui/routers/tasks.py index 6d7343c8a..2dc58b4a5 100644 --- a/backend/open_webui/routers/tasks.py +++ b/backend/open_webui/routers/tasks.py @@ -175,10 +175,10 @@ async def generate_title( "messages": [{"role": "user", "content": content}], "stream": False, **( - {"max_tokens": 50} + {"max_tokens": 1000} if models[task_model_id]["owned_by"] == "ollama" else { - "max_completion_tokens": 50, + "max_completion_tokens": 1000, } ), "metadata": { diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 815e69739..00f4dd402 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -892,16 +892,24 @@ async def process_chat_response( if res and isinstance(res, dict): if len(res.get("choices", [])) == 1: - title = ( + title_string = ( res.get("choices", [])[0] .get("message", {}) - .get( - "content", - message.get("content", "New Chat"), - ) - ).strip() + .get("content", message.get("content", "New Chat")) + ) else: - title = None + title_string = "" + + title_string = title_string[ + title_string.find("{") : title_string.rfind("}") + 1 + ] + + try: + title = json.loads(title_string).get( + "title", "New Chat" + ) + except Exception as e: + pass if not title: title = messages[0].get("content", "New Chat") From 08ad4ee018dfcc97d3c5efa2f08305a035d4c5e1 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 29 Jan 2025 14:59:23 -0800 Subject: [PATCH 23/36] enh: check for reasoning tags --- backend/open_webui/routers/tasks.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/backend/open_webui/routers/tasks.py b/backend/open_webui/routers/tasks.py index 2dc58b4a5..df86b4c10 100644 --- a/backend/open_webui/routers/tasks.py +++ b/backend/open_webui/routers/tasks.py @@ -4,6 +4,7 @@ from fastapi.responses import JSONResponse, RedirectResponse from pydantic import BaseModel from typing import Optional import logging +import re from open_webui.utils.chat import generate_chat_completion from open_webui.utils.task import ( @@ -161,9 +162,22 @@ async def generate_title( else: template = DEFAULT_TITLE_GENERATION_PROMPT_TEMPLATE + messages = form_data["messages"] + + # Remove reasoning details from the messages + for message in messages: + message["content"] = re.sub( + r"]*>.*?<\/details>", + "", + message["content"], + flags=re.S, + ).strip() + + print(messages) + content = title_generation_template( template, - form_data["messages"], + messages, { "name": user.name, "location": user.info.get("location") if user.info else None, From 6658e67113518441ba987b17b53ede44eace1770 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 29 Jan 2025 20:03:46 -0800 Subject: [PATCH 24/36] fix: title gen --- backend/open_webui/utils/middleware.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 00f4dd402..833db503a 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -909,7 +909,7 @@ async def process_chat_response( "title", "New Chat" ) except Exception as e: - pass + title = "" if not title: title = messages[0].get("content", "New Chat") From 52010accc90e0afb23771d89910bf00164aed2b1 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 29 Jan 2025 20:11:37 -0800 Subject: [PATCH 25/36] refac --- backend/open_webui/config.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index f65726c3c..d60f3d436 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -1101,24 +1101,20 @@ Generate a concise, 3-5 word title with an emoji summarizing the chat history. - Use emojis that enhance understanding of the topic, but avoid quotation marks or special formatting. - Write the title in the chat's primary language; default to English if multilingual. - Prioritize accuracy over excessive creativity; keep it clear and simple. - -### Examples: -- 📉 Stock Market Trends -- 🍪 Perfect Chocolate Chip Recipe -- Evolution of Music Streaming -- Remote Work Productivity Tips -- Artificial Intelligence in Healthcare -- 🎮 Video Game Development Insights - ### Output: JSON format: { "title": "your concise title here" } - +### Examples: +- { "title": "📉 Stock Market Trends" }, +- { "title": "🍪 Perfect Chocolate Chip Recipe" }, +- { "title": "Evolution of Music Streaming" }, +- { "title": "Remote Work Productivity Tips" }, +- { "title": "Artificial Intelligence in Healthcare" }, +- { "title": "🎮 Video Game Development Insights" } ### Chat History: {{MESSAGES:END:2}} """ - TAGS_GENERATION_PROMPT_TEMPLATE = PersistentConfig( "TAGS_GENERATION_PROMPT_TEMPLATE", "task.tags.prompt_template", From 7f61d01fde34e582aa83e537f748ff0b0a52d6f8 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 29 Jan 2025 20:35:37 -0800 Subject: [PATCH 26/36] refac: playground --- src/lib/components/playground/Chat.svelte | 27 ++++++- .../components/playground/Chat/Message.svelte | 76 +++++++++++++++++++ .../playground/Chat/Messages.svelte | 73 ++---------------- 3 files changed, 110 insertions(+), 66 deletions(-) create mode 100644 src/lib/components/playground/Chat/Message.svelte diff --git a/src/lib/components/playground/Chat.svelte b/src/lib/components/playground/Chat.svelte index 5b89003c8..08a293f5b 100644 --- a/src/lib/components/playground/Chat.svelte +++ b/src/lib/components/playground/Chat.svelte @@ -33,6 +33,7 @@ let loading = false; let stopResponseFlag = false; + let systemTextareaElement: HTMLTextAreaElement; let messagesContainerElement: HTMLDivElement; let showSystem = false; @@ -58,8 +59,29 @@ console.log('stopResponse'); }; + const resizeSystemTextarea = async () => { + await tick(); + if (systemTextareaElement) { + systemTextareaElement.style.height = ''; + systemTextareaElement.style.height = Math.min(systemTextareaElement.scrollHeight, 555) + 'px'; + } + }; + + $: if (showSystem) { + resizeSystemTextarea(); + } + const chatCompletionHandler = async () => { + if (selectedModelId === '') { + toast.error($i18n.t('Please select a model.')); + return; + } + const model = $models.find((model) => model.id === selectedModelId); + if (!model) { + selectedModelId = ''; + return; + } const [res, controller] = await chatCompletion( localStorage.token, @@ -258,10 +280,13 @@