diff --git a/CHANGELOG.md b/CHANGELOG.md index 36fbf58c6..6fc731a56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.3.30] - 2024-09-26 + +### Fixed + +- **🍞 Update Available Toast Dismissal**: Enhanced user experience by ensuring that once the update available notification is dismissed, it won't reappear for 24 hours. +- **📋 Ollama /embed Form Data**: Adjusted the integration inaccuracies in the /embed form data to ensure it perfectly matches with Ollama's specifications. +- **🔧 O1 Max Completion Tokens Issue**: Resolved compatibility issues with OpenAI's o1 models max_completion_tokens param to ensure smooth operation. +- **🔄 Pip Install Database Issue**: Fixed a critical issue where database changes during pip installations were reverting and not saving chat logs, now ensuring data persistence and reliability in chat operations. +- **🏷️ Chat Rename Tab Update**: Fixed the functionality to change the web browser's tab title simultaneously when a chat is renamed, keeping tab titles consistent. + ## [0.3.29] - 2023-09-25 ### Fixed diff --git a/backend/open_webui/apps/ollama/main.py b/backend/open_webui/apps/ollama/main.py index 6c639268e..1337fbb31 100644 --- a/backend/open_webui/apps/ollama/main.py +++ b/backend/open_webui/apps/ollama/main.py @@ -543,10 +543,18 @@ class GenerateEmbeddingsForm(BaseModel): keep_alive: Optional[Union[int, str]] = None +class GenerateEmbedForm(BaseModel): + model: str + input: str + truncate: Optional[bool] + options: Optional[dict] = None + keep_alive: Optional[Union[int, str]] = None + + @app.post("/api/embed") @app.post("/api/embed/{url_idx}") async def generate_embeddings( - form_data: GenerateEmbeddingsForm, + form_data: GenerateEmbedForm, url_idx: Optional[int] = None, user=Depends(get_verified_user), ): diff --git a/backend/open_webui/apps/openai/main.py b/backend/open_webui/apps/openai/main.py index 93155063f..e0a40a1f5 100644 --- a/backend/open_webui/apps/openai/main.py +++ b/backend/open_webui/apps/openai/main.py @@ -415,6 +415,9 @@ async def generate_chat_completion( payload["max_tokens"] = payload["max_completion_tokens"] del payload["max_completion_tokens"] else: + if payload["model"].lower().startswith("o1-") and "max_tokens" in payload: + payload["max_completion_tokens"] = payload["max_tokens"] + del payload["max_tokens"] if "max_tokens" in payload and "max_completion_tokens" in payload: del payload["max_tokens"] diff --git a/backend/open_webui/env.py b/backend/open_webui/env.py index bc4d94ee4..4f1403e97 100644 --- a/backend/open_webui/env.py +++ b/backend/open_webui/env.py @@ -221,6 +221,12 @@ if FROM_INIT_PY: else: shutil.copy2(item, dest) + # Zip the data directory + shutil.make_archive(DATA_DIR.parent / "open_webui_data", "zip", DATA_DIR) + + # Remove the old data directory + shutil.rmtree(DATA_DIR) + DATA_DIR = Path(os.getenv("DATA_DIR", OPEN_WEBUI_DIR / "data")) diff --git a/package-lock.json b/package-lock.json index 72a3cc6c8..5ab728c9c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "open-webui", - "version": "0.3.29", + "version": "0.3.30", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "open-webui", - "version": "0.3.29", + "version": "0.3.30", "dependencies": { "@codemirror/lang-javascript": "^6.2.2", "@codemirror/lang-python": "^6.1.6", diff --git a/package.json b/package.json index ba3f5295d..77bc183fd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "open-webui", - "version": "0.3.29", + "version": "0.3.30", "private": true, "scripts": { "dev": "npm run pyodide:fetch && vite dev --host", diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index b5db7d078..38987a1e1 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -31,7 +31,8 @@ currentChatPage, temporaryChatEnabled, mobile, - showOverview + showOverview, + chatTitle } from '$lib/stores'; import { convertMessagesToHistory, @@ -102,7 +103,6 @@ let chat = null; let tags = []; - let title = ''; let history = { messages: {}, currentId: null @@ -296,6 +296,8 @@ const chatInput = document.getElementById('chat-textarea'); chatInput?.focus(); + + chats.subscribe(() => {}); }); onDestroy(() => { @@ -313,10 +315,11 @@ window.history.replaceState(history.state, '', `/`); } - await chatId.set(''); autoScroll = true; - title = ''; + await chatId.set(''); + await chatTitle.set(''); + history = { messages: {}, currentId: null @@ -398,7 +401,8 @@ (chatContent?.history ?? undefined) !== undefined ? chatContent.history : convertMessagesToHistory(chatContent.messages); - title = chatContent.title; + + chatTitle.set(chatContent.title); const userSettings = await getUserSettings(localStorage.token); @@ -1207,8 +1211,8 @@ const messages = createMessagesList(responseMessageId); if (messages.length == 2 && messages.at(-1).content !== '' && selectedModels[0] === model.id) { window.history.replaceState(history.state, '', `/c/${_chatId}`); - const _title = await generateChatTitle(userPrompt); - await setChatTitle(_chatId, _title); + const title = await generateChatTitle(userPrompt); + await setChatTitle(_chatId, title); } return _response; @@ -1497,8 +1501,8 @@ const messages = createMessagesList(responseMessageId); if (messages.length == 2 && selectedModels[0] === model.id) { window.history.replaceState(history.state, '', `/c/${_chatId}`); - const _title = await generateChatTitle(userPrompt); - await setChatTitle(_chatId, _title); + const title = await generateChatTitle(userPrompt); + await setChatTitle(_chatId, title); } return _response; @@ -1672,13 +1676,13 @@ } }; - const setChatTitle = async (_chatId, _title) => { + const setChatTitle = async (_chatId, title) => { if (_chatId === $chatId) { - title = _title; + chatTitle.set(title); } if (!$temporaryChatEnabled) { - chat = await updateChatById(localStorage.token, _chatId, { title: _title }); + chat = await updateChatById(localStorage.token, _chatId, { title: title }); currentChatPage.set(1); await chats.set(await getChatList(localStorage.token, $currentChatPage)); @@ -1817,8 +1821,8 @@ - {title - ? `${title.length > 30 ? `${title.slice(0, 30)}...` : title} | ${$WEBUI_NAME}` + {$chatTitle + ? `${$chatTitle.length > 30 ? `${$chatTitle.slice(0, 30)}...` : $chatTitle} | ${$WEBUI_NAME}` : `${$WEBUI_NAME}`} @@ -1863,7 +1867,13 @@ /> {/if} - + diff --git a/src/lib/components/layout/Sidebar/ChatItem.svelte b/src/lib/components/layout/Sidebar/ChatItem.svelte index 5b2542d7d..4a633fddf 100644 --- a/src/lib/components/layout/Sidebar/ChatItem.svelte +++ b/src/lib/components/layout/Sidebar/ChatItem.svelte @@ -14,7 +14,15 @@ getChatListByTagName, updateChatById } from '$lib/apis/chats'; - import { chatId, chats, mobile, pinnedChats, showSidebar, currentChatPage } from '$lib/stores'; + import { + chatId, + chatTitle as _chatTitle, + chats, + mobile, + pinnedChats, + showSidebar, + currentChatPage + } from '$lib/stores'; import ChatMenu from './ChatMenu.svelte'; import ShareChatModal from '$lib/components/chat/ShareChatModal.svelte'; @@ -33,14 +41,18 @@ let chatTitle = chat.title; - const editChatTitle = async (id, _title) => { - if (_title === '') { + const editChatTitle = async (id, title) => { + if (title === '') { toast.error($i18n.t('Title cannot be an empty string.')); } else { await updateChatById(localStorage.token, id, { - title: _title + title: title }); + if (id === $chatId) { + _chatTitle.set(title); + } + currentChatPage.set(1); await chats.set(await getChatList(localStorage.token, $currentChatPage)); await pinnedChats.set(await getChatListByTagName(localStorage.token, 'pinned')); diff --git a/src/lib/components/layout/UpdateInfoToast.svelte b/src/lib/components/layout/UpdateInfoToast.svelte index 3b8eb3020..bb75de94f 100644 --- a/src/lib/components/layout/UpdateInfoToast.svelte +++ b/src/lib/components/layout/UpdateInfoToast.svelte @@ -14,7 +14,7 @@
{$i18n.t(`A new version (v{{LATEST_VERSION}}) is now available.`, { @@ -35,6 +35,7 @@ class=" hover:text-blue-900 dark:hover:text-blue-300 transition" on:click={() => { console.log('closeToast'); + localStorage.setItem('dismissedUpdateToast', Date.now().toString()); dispatch('closeToast'); }} > diff --git a/src/lib/stores/index.ts b/src/lib/stores/index.ts index 5fe44edc0..b96bf4a98 100644 --- a/src/lib/stores/index.ts +++ b/src/lib/stores/index.ts @@ -19,7 +19,9 @@ export const activeUserCount: Writable = writable(null); export const USAGE_POOL: Writable = writable(null); export const theme = writable('system'); + export const chatId = writable(''); +export const chatTitle = writable(''); export const chats = writable([]); export const pinnedChats = writable([]); diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte index a59d0bd53..ad2f085cf 100644 --- a/src/routes/(app)/+layout.svelte +++ b/src/routes/(app)/+layout.svelte @@ -46,6 +46,7 @@ import { compareVersion } from '$lib/utils'; import UpdateInfoToast from '$lib/components/layout/UpdateInfoToast.svelte'; + import { fade } from 'svelte/transition'; const i18n = getContext('i18n'); @@ -53,6 +54,8 @@ let DB = null; let localDBChats = []; + let version; + const getModels = async () => { return _getModels(localStorage.token); }; @@ -195,10 +198,20 @@ temporaryChatEnabled.set(true); } + // Check for version updates if ($user.role === 'admin') { - checkForVersionUpdates(); - } + // Check if the user has dismissed the update toast in the last 24 hours + if (localStorage.dismissedUpdateToast) { + const dismissedUpdateToast = new Date(Number(localStorage.dismissedUpdateToast)); + const now = new Date(); + if (now - dismissedUpdateToast > 24 * 60 * 60 * 1000) { + await checkForVersionUpdates(); + } + } else { + await checkForVersionUpdates(); + } + } await tick(); } @@ -206,28 +219,24 @@ }); const checkForVersionUpdates = async () => { - const version = await getVersionUpdates(localStorage.token).catch((error) => { + version = await getVersionUpdates(localStorage.token).catch((error) => { return { current: WEBUI_VERSION, latest: WEBUI_VERSION }; }); - - if (compareVersion(version.latest, version.current)) { - toast.custom(UpdateInfoToast, { - duration: Number.POSITIVE_INFINITY, - position: 'bottom-right', - componentProps: { - version - } - }); - } }; +{#if version && compareVersion(version.latest, version.current)} +
+ +
+{/if} +