From a51579a84bec3e2ea50ea741b4566823c288eef1 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Fri, 21 Nov 2025 03:49:49 -0500 Subject: [PATCH] refac/pref: chat import optimization Co-Authored-By: G30 <50341825+silentoplayz@users.noreply.github.com> --- backend/open_webui/models/chats.py | 72 ++++++++++--------- backend/open_webui/routers/chats.py | 21 ++---- src/lib/apis/chats/index.ts | 17 +---- .../chat/Settings/DataControls.svelte | 43 ++++++----- src/lib/components/layout/Sidebar.svelte | 59 +++++++-------- .../layout/Sidebar/RecursiveFolder.svelte | 23 +++--- 6 files changed, 116 insertions(+), 119 deletions(-) diff --git a/backend/open_webui/models/chats.py b/backend/open_webui/models/chats.py index f1607f0707..f1f7f03642 100644 --- a/backend/open_webui/models/chats.py +++ b/backend/open_webui/models/chats.py @@ -92,6 +92,10 @@ class ChatImportForm(ChatForm): updated_at: Optional[int] = None +class ChatsImportForm(BaseModel): + chats: list[ChatImportForm] + + class ChatTitleMessagesForm(BaseModel): title: str messages: list[dict] @@ -148,42 +152,44 @@ class ChatTable: db.refresh(result) return ChatModel.model_validate(result) if result else None - def import_chat( + def _chat_import_form_to_chat_model( self, user_id: str, form_data: ChatImportForm - ) -> Optional[ChatModel]: - with get_db() as db: - id = str(uuid.uuid4()) - chat = ChatModel( - **{ - "id": id, - "user_id": user_id, - "title": ( - form_data.chat["title"] - if "title" in form_data.chat - else "New Chat" - ), - "chat": form_data.chat, - "meta": form_data.meta, - "pinned": form_data.pinned, - "folder_id": form_data.folder_id, - "created_at": ( - form_data.created_at - if form_data.created_at - else int(time.time()) - ), - "updated_at": ( - form_data.updated_at - if form_data.updated_at - else int(time.time()) - ), - } - ) + ) -> ChatModel: + id = str(uuid.uuid4()) + chat = ChatModel( + **{ + "id": id, + "user_id": user_id, + "title": ( + form_data.chat["title"] if "title" in form_data.chat else "New Chat" + ), + "chat": form_data.chat, + "meta": form_data.meta, + "pinned": form_data.pinned, + "folder_id": form_data.folder_id, + "created_at": ( + form_data.created_at if form_data.created_at else int(time.time()) + ), + "updated_at": ( + form_data.updated_at if form_data.updated_at else int(time.time()) + ), + } + ) + return chat - result = Chat(**chat.model_dump()) - db.add(result) + def import_chats( + self, user_id: str, chats: list[ChatImportForm] + ) -> list[ChatModel]: + with get_db() as db: + chats = [] + + for form_data in chats: + chat = self._chat_import_form_to_chat_model(user_id, form_data) + chats.append(Chat(**chat.model_dump())) + + db.add_all(chats) db.commit() - db.refresh(result) - return ChatModel.model_validate(result) if result else None + return [ChatModel.model_validate(chat) for chat in chats] def update_chat_by_id(self, id: str, chat: dict) -> Optional[ChatModel]: try: diff --git a/backend/open_webui/routers/chats.py b/backend/open_webui/routers/chats.py index 2587c5ff8e..9f2ed85882 100644 --- a/backend/open_webui/routers/chats.py +++ b/backend/open_webui/routers/chats.py @@ -7,9 +7,11 @@ from open_webui.socket.main import get_event_emitter from open_webui.models.chats import ( ChatForm, ChatImportForm, + ChatBulkImportForm, ChatResponse, Chats, ChatTitleIdResponse, + ChatsImportForm, ) from open_webui.models.tags import TagModel, Tags from open_webui.models.folders import Folders @@ -142,26 +144,15 @@ async def create_new_chat(form_data: ChatForm, user=Depends(get_verified_user)): ############################ -# ImportChat +# ImportChats ############################ @router.post("/import", response_model=Optional[ChatResponse]) -async def import_chat(form_data: ChatImportForm, user=Depends(get_verified_user)): +async def import_chats(form_data: ChatsImportForm, user=Depends(get_verified_user)): try: - chat = Chats.import_chat(user.id, form_data) - if chat: - tags = chat.meta.get("tags", []) - for tag_id in tags: - tag_id = tag_id.replace(" ", "_").lower() - tag_name = " ".join([word.capitalize() for word in tag_id.split("_")]) - if ( - tag_id != "none" - and Tags.get_tag_by_name_and_user_id(tag_name, user.id) is None - ): - Tags.insert_new_tag(tag_name, user.id) - - return ChatResponse(**chat.model_dump()) + chats = Chats.import_chats(user.id, form_data.chats) + return [ChatResponse(**chat.model_dump()) for chat in chats] except Exception as e: log.exception(e) raise HTTPException( diff --git a/src/lib/apis/chats/index.ts b/src/lib/apis/chats/index.ts index c548a71dc2..010c80a56f 100644 --- a/src/lib/apis/chats/index.ts +++ b/src/lib/apis/chats/index.ts @@ -65,15 +65,7 @@ export const unarchiveAllChats = async (token: string) => { return res; }; -export const importChat = async ( - token: string, - chat: object, - meta: object | null, - pinned?: boolean, - folderId?: string | null, - createdAt: number | null = null, - updatedAt: number | null = null -) => { +export const importChats = async (token: string, chats: object[]) => { let error = null; const res = await fetch(`${WEBUI_API_BASE_URL}/chats/import`, { @@ -84,12 +76,7 @@ export const importChat = async ( authorization: `Bearer ${token}` }, body: JSON.stringify({ - chat: chat, - meta: meta ?? {}, - pinned: pinned, - folder_id: folderId, - created_at: createdAt ?? null, - updated_at: updatedAt ?? null + chats }) }) .then(async (res) => { diff --git a/src/lib/components/chat/Settings/DataControls.svelte b/src/lib/components/chat/Settings/DataControls.svelte index 3aef3b1c62..f4e8bb76f2 100644 --- a/src/lib/components/chat/Settings/DataControls.svelte +++ b/src/lib/components/chat/Settings/DataControls.svelte @@ -16,8 +16,8 @@ deleteAllChats, getAllChats, getChatList, - importChat, - getPinnedChatList + getPinnedChatList, + importChats } from '$lib/apis/chats'; import { getImportOrigin, convertOpenAIChats } from '$lib/utils'; import { onMount, getContext } from 'svelte'; @@ -52,7 +52,7 @@ console.log('Unable to import chats:', error); } } - importChats(chats); + importChatsHandler(chats); }; if (importFiles.length > 0) { @@ -60,24 +60,33 @@ } } - const importChats = async (_chats) => { - for (const chat of _chats) { - console.log(chat); - + const importChatsHandler = async (_chats) => { + const chats = _chats.map((chat) => { if (chat.chat) { - await importChat( - localStorage.token, - chat.chat, - chat.meta ?? {}, - false, - null, - chat?.created_at ?? null, - chat?.updated_at ?? null - ); + return { + chat: chat.chat, + meta: chat.meta ?? {}, + pinned: false, + folder_id: chat?.folder_id ?? null, + created_at: chat?.created_at ?? null, + updated_at: chat?.updated_at ?? null + }; } else { // Legacy format - await importChat(localStorage.token, chat, {}, false, null); + return { + chat: chat, + meta: {}, + pinned: false, + folder_id: null, + created_at: chat?.created_at ?? null, + updated_at: chat?.updated_at ?? null + }; } + }); + + const res = await importChats(localStorage.token, chats); + if (res) { + toast.success(`Successfully imported ${res.length} chats.`); } currentChatPage.set(1); diff --git a/src/lib/components/layout/Sidebar.svelte b/src/lib/components/layout/Sidebar.svelte index 1ccb2c718b..915ec41cf5 100644 --- a/src/lib/components/layout/Sidebar.svelte +++ b/src/lib/components/layout/Sidebar.svelte @@ -38,7 +38,7 @@ toggleChatPinnedStatusById, getChatById, updateChatFolderIdById, - importChat + importChats } from '$lib/apis/chats'; import { createNewFolder, getFolders, updateFolderParentIdById } from '$lib/apis/folders'; import { WEBUI_API_BASE_URL, WEBUI_BASE_URL } from '$lib/constants'; @@ -227,15 +227,16 @@ for (const item of items) { console.log(item); if (item.chat) { - await importChat( - localStorage.token, - item.chat, - item?.meta ?? {}, - pinned, - folderId, - item?.created_at ?? null, - item?.updated_at ?? null - ); + await importChats(localStorage.token, [ + { + chat: item.chat, + meta: item?.meta ?? {}, + pinned: pinned, + folder_id: folderId, + created_at: item?.created_at ?? null, + updated_at: item?.updated_at ?? null + } + ]); } } @@ -999,15 +1000,16 @@ return null; }); if (!chat && item) { - chat = await importChat( - localStorage.token, - item.chat, - item?.meta ?? {}, - false, - null, - item?.created_at ?? null, - item?.updated_at ?? null - ); + chat = await importChats(localStorage.token, [ + { + chat: item.chat, + meta: item?.meta ?? {}, + pinned: false, + folder_id: null, + created_at: item?.created_at ?? null, + updated_at: item?.updated_at ?? null + } + ]); } if (chat) { @@ -1064,15 +1066,16 @@ return null; }); if (!chat && item) { - chat = await importChat( - localStorage.token, - item.chat, - item?.meta ?? {}, - false, - null, - item?.created_at ?? null, - item?.updated_at ?? null - ); + chat = await importChats(localStorage.token, [ + { + chat: item.chat, + meta: item?.meta ?? {}, + pinned: false, + folder_id: null, + created_at: item?.created_at ?? null, + updated_at: item?.updated_at ?? null + } + ]); } if (chat) { diff --git a/src/lib/components/layout/Sidebar/RecursiveFolder.svelte b/src/lib/components/layout/Sidebar/RecursiveFolder.svelte index be8343c90d..331719647d 100644 --- a/src/lib/components/layout/Sidebar/RecursiveFolder.svelte +++ b/src/lib/components/layout/Sidebar/RecursiveFolder.svelte @@ -24,8 +24,8 @@ getChatById, getChatsByFolderId, getChatListByFolderId, - importChat, - updateChatFolderIdById + updateChatFolderIdById, + importChats } from '$lib/apis/chats'; import ChevronDown from '../../icons/ChevronDown.svelte'; @@ -152,15 +152,16 @@ return null; }); if (!chat && item) { - chat = await importChat( - localStorage.token, - item.chat, - item?.meta ?? {}, - false, - null, - item?.created_at ?? null, - item?.updated_at ?? null - ).catch((error) => { + chat = await importChats(localStorage.token, [ + { + chat: item.chat, + meta: item?.meta ?? {}, + pinned: false, + folder_id: null, + created_at: item?.created_at ?? null, + updated_at: item?.updated_at ?? null + } + ]).catch((error) => { toast.error(`${error}`); return null; });