From 6cfd6987e5b220040c2e476105779dcc10d3d14b Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Thu, 8 Oct 2026 14:58:06 +0400 Subject: [PATCH] refac --- backend/open_webui/main.py | 1 + backend/open_webui/models/chats.py | 17 ++++-- backend/open_webui/routers/chats.py | 44 +++++++++++--- src/lib/components/chat/Chat.svelte | 7 ++- src/lib/components/chat/Messages.svelte | 2 + .../components/chat/Messages/Message.svelte | 2 +- .../chat/Messages/ResponseMessage.svelte | 2 +- .../chat/Messages/UserMessage.svelte | 60 ++++++++++++------- src/routes/s/[id]/+page.svelte | 2 +- 9 files changed, 101 insertions(+), 36 deletions(-) diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index 5b0820ed6c..0ed3d753ea 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -1404,6 +1404,7 @@ async def chat_completion( if user_message_id and user_message: user_message['childrenIds'] = all_assistant_ids user_message['user_id'] = user.id + user_message['user'] = {'id': user.id, 'name': user.name} history_messages[user_message_id] = user_message for entry in message_ids: diff --git a/backend/open_webui/models/chats.py b/backend/open_webui/models/chats.py index 983600dfa0..cb2eac689f 100644 --- a/backend/open_webui/models/chats.py +++ b/backend/open_webui/models/chats.py @@ -603,7 +603,9 @@ class ChatTable: await ChatMessages.upsert_message( message_id=message_id, chat_id=id, - user_id=user_id, + user_id=message.get('user_id') + or (message['user'].get('id') if isinstance(message.get('user'), dict) else None) + or user_id, data=message, ) except Exception as e: @@ -677,7 +679,11 @@ class ChatTable: messages = list((chat.chat.get('history', {}).get('messages') or {}).values()) messages.extend(chat.chat.get('messages') or []) for message in messages: - message['user_id'] = user_id + message['user_id'] = ( + message.get('user_id') + or (message['user'].get('id') if isinstance(message.get('user'), dict) else None) + or user_id + ) return chat async def import_chats( @@ -729,7 +735,9 @@ class ChatTable: await ChatMessages.upsert_message( message_id=message_id, chat_id=imported_chat.id, - user_id=user_id, + user_id=message.get('user_id') + or (message['user'].get('id') if isinstance(message.get('user'), dict) else None) + or user_id, data=message, ) except Exception as e: @@ -1839,7 +1847,8 @@ class ChatTable: if ( chat.user_id == user.id or user.role == 'admin' - and (ENABLE_ADMIN_CHAT_ACCESS or permission == 'read' and is_internal_chat(chat.meta)) + and permission == 'read' + and (ENABLE_ADMIN_CHAT_ACCESS or is_internal_chat(chat.meta)) ): return ChatModel.model_validate(chat) from open_webui.models.shared_chats import SharedChats diff --git a/backend/open_webui/routers/chats.py b/backend/open_webui/routers/chats.py index 5dd86fcbdf..f03b9a5098 100644 --- a/backend/open_webui/routers/chats.py +++ b/backend/open_webui/routers/chats.py @@ -127,7 +127,9 @@ async def can_read_shared_chat(user, shared, db: AsyncSession) -> bool: ) -def shared_chat_response(chat, user=None): +async def shared_chat_response(chat, user=None, db=None): + from open_webui.models.users import Users + data = ChatResponse.model_validate(chat, from_attributes=True).model_dump() if user is None or chat.user_id != user.id: data['variables'] = {} @@ -135,9 +137,24 @@ def shared_chat_response(chat, user=None): data['chat'].pop(key, None) messages = list((data['chat'].get('history', {}).get('messages') or {}).values()) messages.extend(data['chat'].get('messages') or []) + authors = {} for message in messages: + if message.get('role') == 'user': + author = message.get('user') + author = author if isinstance(author, dict) else {} + author_id = message.get('user_id') or author.get('id') or chat.user_id + message['user_id'] = author_id + if author.get('id') == author_id and author.get('name'): + authors[author_id] = {'id': author_id, 'name': author['name']} if user is None or (message.get('user_id') or chat.user_id) != user.id: message.pop('meta', None) + missing_ids = {message['user_id'] for message in messages if message.get('role') == 'user'} - authors.keys() + if missing_ids: + for author in await Users.get_users_by_user_ids(list(missing_ids), db=db): + authors[author.id] = {'id': author.id, 'name': author.name} + for message in messages: + if message.get('role') == 'user': + message['user'] = authors.get(message['user_id'], {'id': message['user_id'], 'name': ''}) return data @@ -1240,7 +1257,7 @@ async def get_shared_chat_by_id( live = ( shared.chat.get('share_mode') == 'continue' and user is not None - and await can_read_shared_chat(user, shared, db=db) + and await Chats.get_accessible_chat_by_id(shared.chat_id, user, db=db, permission='write') is not None ) chat = ( await Chats.get_chat_by_id(shared.chat_id, db=db) @@ -1248,7 +1265,7 @@ async def get_shared_chat_by_id( else await Chats.get_chat_by_share_id(share_id, db=db) ) if chat: - data = shared_chat_response(chat, user) + data = await shared_chat_response(chat, user, db=db) data['chat']['share_mode'] = 'continue' if live else None return data @@ -1262,7 +1279,13 @@ async def get_shared_chat_by_id( if user is not None and user.role == 'admin' and ENABLE_ADMIN_CHAT_ACCESS: chat = await Chats.get_chat_by_id(share_id, db=db) if chat: - return ChatResponse.model_validate(chat, from_attributes=True) + data = await shared_chat_response(chat, user, db=db) + data['chat']['share_mode'] = ( + 'continue' + if await Chats.get_accessible_chat_by_id(chat.id, user, db=db, permission='write', chat=chat) + else None + ) + return data raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND) @@ -1370,9 +1393,11 @@ async def get_chat_by_id( ) if chat: - data = shared_chat_response(chat, user) + data = await shared_chat_response(chat, user, db=db) data['chat']['share_mode'] = ( - 'continue' if await Chats.get_accessible_chat_by_id(id, user, db=db, permission='write', chat=chat) else None + 'continue' + if await Chats.get_accessible_chat_by_id(id, user, db=db, permission='write', chat=chat) + else None ) data = overlay_response_streams( data, @@ -1746,11 +1771,14 @@ async def fork_chat_by_id( detail=detail, ) from exc - # An unfinished message is stale unless it is awaiting tool approval for message in fork_history['messages'].values(): + author = (history.get('messages', {}).get(message['id']) or {}).get('user') + if isinstance(author, dict) and author.get('id') == message.get('user_id'): + message['user'] = author if message.get('role') != 'assistant' or message.get('done') is not False: continue + # An unfinished message is stale unless it is awaiting tool approval output = message.get('output') if isinstance(output, list) and any( isinstance(item, dict) @@ -1910,7 +1938,7 @@ async def clone_shared_chat_by_id( ) updated_chat = { - **shared_chat_response(chat, user)['chat'], + **(await shared_chat_response(chat, user, db=db))['chat'], 'originalChatId': chat.id, 'branchPointMessageId': chat.chat['history']['currentId'], 'title': f'Clone of {chat.title}', diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index 0883bb983a..7a0178e961 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -2997,6 +2997,8 @@ parentId: parentMessage ? parentMessage.id : null, childrenIds: [responseMessageId], role: 'user', + user_id: $user?.id, + user: { id: $user?.id, name: $user?.name }, content: userPrompt ? userPrompt : `[PROMPT] ${userMessageId}`, timestamp: Math.floor(Date.now() / 1000) }; @@ -3279,6 +3281,7 @@ parentId: history.currentId ?? null, childrenIds: [], role: 'user', + user: { id: $user?.id, name: $user?.name }, user_id: $user?.id, content: inputContent, files: _files.length > 0 ? _files : undefined, @@ -4188,6 +4191,8 @@ parentId: parentId, childrenIds: [], role: 'user', + user_id: $user?.id, + user: { id: $user?.id, name: $user?.name }, content: userPrompt, models: selectedModels, timestamp: Math.floor(Date.now() / 1000) // Unix epoch @@ -4775,7 +4780,7 @@ diff --git a/src/lib/components/chat/Messages/UserMessage.svelte b/src/lib/components/chat/Messages/UserMessage.svelte index 5768234d9b..9e6fed5611 100644 --- a/src/lib/components/chat/Messages/UserMessage.svelte +++ b/src/lib/components/chat/Messages/UserMessage.svelte @@ -57,6 +57,17 @@ let editScrollContainer: HTMLDivElement; let message = structuredClone(history.messages[messageId]); + $: authorId = message.user_id || message.user?.id || user?.id; + $: author = + message.user?.id === authorId + ? message.user + : $_user?.id === authorId + ? $_user + : user?.id === authorId + ? user + : null; + $: isOwn = Boolean($_user?.id && authorId === $_user.id); + $: authorName = author?.name || $i18n.t('Unknown User'); let timerExpanded = false; $: if (history.messages) { const source = history.messages[messageId]; @@ -136,14 +147,16 @@ id="message-{message.id}" style="scroll-margin-top: 3rem;" > - {#if !($settings?.chatBubble ?? true) && !(message?.meta?.internal === true && message?.meta?.type === 'subagent') && !(message?.meta?.internal === true && message?.meta?.type === 'timer')} -