diff --git a/backend/open_webui/routers/files.py b/backend/open_webui/routers/files.py index 0a2b4ac97f..e63a93a9c4 100644 --- a/backend/open_webui/routers/files.py +++ b/backend/open_webui/routers/files.py @@ -129,13 +129,16 @@ def upload_file( id = str(uuid.uuid4()) name = filename filename = f"{id}_{filename}" - tags = { - "OpenWebUI-User-Email": user.email, - "OpenWebUI-User-Id": user.id, - "OpenWebUI-User-Name": user.name, - "OpenWebUI-File-Id": id, - } - contents, file_path = Storage.upload_file(file.file, filename, tags) + contents, file_path = Storage.upload_file( + file.file, + filename, + { + "OpenWebUI-User-Email": user.email, + "OpenWebUI-User-Id": user.id, + "OpenWebUI-User-Name": user.name, + "OpenWebUI-File-Id": id, + }, + ) file_item = Files.insert_new_file( user.id, diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index 98a492ccfc..c2a5361578 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -2196,6 +2196,42 @@ shareEnabled={!!history.currentId} {initNewChat} showBanners={!showCommands} + onSaveTempChat={async () => { + try { + if (!history?.currentId || !Object.keys(history.messages).length) { + toast.error($i18n.t('No conversation to save')); + return; + } + const messages = createMessagesList(history, history.currentId); + const title = + messages.find((m) => m.role === 'user')?.content ?? $i18n.t('New Chat'); + + const savedChat = await createNewChat( + localStorage.token, + { + id: uuidv4(), + title: title.length > 50 ? `${title.slice(0, 50)}...` : title, + models: selectedModels, + history: history, + messages: messages, + timestamp: Date.now() + }, + null + ); + + if (savedChat) { + temporaryChatEnabled.set(false); + chatId.set(savedChat.id); + chats.set(await getChatList(localStorage.token, $currentChatPage)); + + await goto(`/c/${savedChat.id}`); + toast.success($i18n.t('Conversation saved successfully')); + } + } catch (error) { + console.error('Error saving conversation:', error); + toast.error($i18n.t('Failed to save conversation')); + } + }} />
diff --git a/src/lib/components/chat/Navbar.svelte b/src/lib/components/chat/Navbar.svelte index 7012583aea..5c48fd90fb 100644 --- a/src/lib/components/chat/Navbar.svelte +++ b/src/lib/components/chat/Navbar.svelte @@ -18,6 +18,7 @@ import { slide } from 'svelte/transition'; import { page } from '$app/stores'; + import { goto } from '$app/navigation'; import ShareChatModal from '../chat/ShareChatModal.svelte'; import ModelSelector from '../chat/ModelSelector.svelte'; @@ -34,7 +35,7 @@ import ChatBubbleDottedChecked from '../icons/ChatBubbleDottedChecked.svelte'; import EllipsisHorizontal from '../icons/EllipsisHorizontal.svelte'; - import { goto } from '$app/navigation'; + import ChatPlus from '../icons/ChatPlus.svelte'; const i18n = getContext('i18n'); @@ -48,6 +49,8 @@ export let showModelSelector = true; export let showBanners = true; + export let onSaveTempChat: () => {}; + let closedBannerIds = []; let showShareChatModal = false; @@ -105,31 +108,47 @@
- {#if !chat?.id && ($user?.role === 'user' ? ($user?.permissions?.chat?.temporary ?? true) && !($user?.permissions?.chat?.temporary_enforced ?? false) : true)} - - - + {#if $user?.role === 'user' ? ($user?.permissions?.chat?.temporary ?? true) && !($user?.permissions?.chat?.temporary_enforced ?? false) : true} + {#if !chat?.id} + + + + {:else if $temporaryChatEnabled} + + + + {/if} {/if} {#if shareEnabled && chat && (chat.id || $temporaryChatEnabled)} diff --git a/src/lib/components/icons/ChatPlus.svelte b/src/lib/components/icons/ChatPlus.svelte new file mode 100644 index 0000000000..4d669500e7 --- /dev/null +++ b/src/lib/components/icons/ChatPlus.svelte @@ -0,0 +1,19 @@ + + +