feat: save temporary chats

This commit is contained in:
Timothy Jaeryang Baek 2025-08-19 02:37:18 +04:00
parent 47ec443728
commit 575db66295
4 changed files with 110 additions and 33 deletions

View file

@ -129,13 +129,16 @@ def upload_file(
id = str(uuid.uuid4()) id = str(uuid.uuid4())
name = filename name = filename
filename = f"{id}_{filename}" filename = f"{id}_{filename}"
tags = { contents, file_path = Storage.upload_file(
file.file,
filename,
{
"OpenWebUI-User-Email": user.email, "OpenWebUI-User-Email": user.email,
"OpenWebUI-User-Id": user.id, "OpenWebUI-User-Id": user.id,
"OpenWebUI-User-Name": user.name, "OpenWebUI-User-Name": user.name,
"OpenWebUI-File-Id": id, "OpenWebUI-File-Id": id,
} },
contents, file_path = Storage.upload_file(file.file, filename, tags) )
file_item = Files.insert_new_file( file_item = Files.insert_new_file(
user.id, user.id,

View file

@ -2196,6 +2196,42 @@
shareEnabled={!!history.currentId} shareEnabled={!!history.currentId}
{initNewChat} {initNewChat}
showBanners={!showCommands} 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'));
}
}}
/> />
<div class="flex flex-col flex-auto z-10 w-full @container overflow-auto"> <div class="flex flex-col flex-auto z-10 w-full @container overflow-auto">

View file

@ -18,6 +18,7 @@
import { slide } from 'svelte/transition'; import { slide } from 'svelte/transition';
import { page } from '$app/stores'; import { page } from '$app/stores';
import { goto } from '$app/navigation';
import ShareChatModal from '../chat/ShareChatModal.svelte'; import ShareChatModal from '../chat/ShareChatModal.svelte';
import ModelSelector from '../chat/ModelSelector.svelte'; import ModelSelector from '../chat/ModelSelector.svelte';
@ -34,7 +35,7 @@
import ChatBubbleDottedChecked from '../icons/ChatBubbleDottedChecked.svelte'; import ChatBubbleDottedChecked from '../icons/ChatBubbleDottedChecked.svelte';
import EllipsisHorizontal from '../icons/EllipsisHorizontal.svelte'; import EllipsisHorizontal from '../icons/EllipsisHorizontal.svelte';
import { goto } from '$app/navigation'; import ChatPlus from '../icons/ChatPlus.svelte';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
@ -48,6 +49,8 @@
export let showModelSelector = true; export let showModelSelector = true;
export let showBanners = true; export let showBanners = true;
export let onSaveTempChat: () => {};
let closedBannerIds = []; let closedBannerIds = [];
let showShareChatModal = false; let showShareChatModal = false;
@ -105,7 +108,8 @@
<div class="self-start flex flex-none items-center text-gray-600 dark:text-gray-400"> <div class="self-start flex flex-none items-center text-gray-600 dark:text-gray-400">
<!-- <div class="md:hidden flex self-center w-[1px] h-5 mx-2 bg-gray-300 dark:bg-stone-700" /> --> <!-- <div class="md:hidden flex self-center w-[1px] h-5 mx-2 bg-gray-300 dark:bg-stone-700" /> -->
{#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}
<Tooltip content={$i18n.t(`Temporary Chat`)}> <Tooltip content={$i18n.t(`Temporary Chat`)}>
<button <button
class="flex cursor-pointer px-2 py-2 rounded-xl hover:bg-gray-50 dark:hover:bg-gray-850 transition" class="flex cursor-pointer px-2 py-2 rounded-xl hover:bg-gray-50 dark:hover:bg-gray-850 transition"
@ -130,6 +134,21 @@
</div> </div>
</button> </button>
</Tooltip> </Tooltip>
{:else if $temporaryChatEnabled}
<Tooltip content={$i18n.t(`Save Chat`)}>
<button
class="flex cursor-pointer px-2 py-2 rounded-xl hover:bg-gray-50 dark:hover:bg-gray-850 transition"
id="save-temporary-chat-button"
on:click={async () => {
onSaveTempChat();
}}
>
<div class=" m-auto self-center">
<ChatPlus className=" size-4.5" strokeWidth="1.5" />
</div>
</button>
</Tooltip>
{/if}
{/if} {/if}
{#if shareEnabled && chat && (chat.id || $temporaryChatEnabled)} {#if shareEnabled && chat && (chat.id || $temporaryChatEnabled)}

View file

@ -0,0 +1,19 @@
<script lang="ts">
export let className = 'w-4 h-4';
export let strokeWidth = '1.5';
</script>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width={strokeWidth}
stroke="currentColor"
class={className}
><path d="M9 12H12M15 12H12M12 12V9M12 12V15" stroke-linecap="round" stroke-linejoin="round"
></path><path
d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 13.8214 2.48697 15.5291 3.33782 17L2.5 21.5L7 20.6622C8.47087 21.513 10.1786 22 12 22Z"
stroke-linecap="round"
stroke-linejoin="round"
></path></svg
>