diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index 96a85be50e..63f5fc6ccb 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -1560,9 +1560,10 @@ async def chat_completion( for mid, message in turn['messages'].items() } await event_emitter({'type': 'chat:messages', 'data': turn}) - await emit_chat_list_event({**metadata, 'message_id': user_message['id']}, chat_id) + user_message_id = user_message.get('id') + await emit_chat_list_event({**metadata, 'message_id': user_message_id}, chat_id) for message_id, message in turn['messages'].items(): - if message_id != user_message['id'] and message.get('parentId') != user_message['id']: + if message_id != user_message_id and message.get('parentId') != user_message_id: continue await publish_event( request, @@ -1571,7 +1572,10 @@ async def chat_completion( subject_id=message_id, data={'chat_id': chat_id, 'role': message['role'], 'model': message.get('model')}, ) - if not getattr(request.state, 'internal', False): + if user_message_id and not ( + getattr(request.state, 'internal', False) + or (user_message.get('meta') or {}).get('internal') + ): try: from open_webui.utils.timers import cancel_timers_for_chat diff --git a/backend/open_webui/models/chats.py b/backend/open_webui/models/chats.py index cb2eac689f..cfc59bc0e7 100644 --- a/backend/open_webui/models/chats.py +++ b/backend/open_webui/models/chats.py @@ -1196,35 +1196,52 @@ class ChatTable: raise HTTPException(404, 'Chat not found.') history = (chat.chat or {}).get('history') or {'messages': {}, 'currentId': None} messages = history.get('messages') or {} - ids = [user_message.get('id'), *[entry.get('message_id') for entry in message_ids]] - if ( - not all(isinstance(mid, str) and mid for mid in ids) - or len(set(ids)) != len(ids) - or any(mid in messages for mid in ids[1:]) - ): + user_message_id = user_message.get('id') + assistant_ids = [entry.get('message_id') for entry in message_ids] + ids = [user_message_id, *assistant_ids] if user_message else assistant_ids + if not all(isinstance(mid, str) and mid for mid in ids) or len(set(ids)) != len(ids): raise HTTPException(409, 'Message already exists or has an invalid ID.') - existing = messages.get(ids[0]) - if existing and (existing.get('role') != 'user' or (existing.get('user_id') or chat.user_id) != user.id): - raise HTTPException(403, 'You can only regenerate your own messages.') - parent_id = existing.get('parentId') if existing else user_message.get('parentId') - if parent_id is not None and parent_id not in messages: - raise HTTPException(409, 'Parent message no longer exists.') - message = existing or ( - dict(user_message) - if chat.user_id == user.id - else {key: user_message[key] for key in ('content', 'files', 'models') if key in user_message} - ) - if not existing: - message.update( - id=ids[0], - parentId=parent_id, - role='user', - childrenIds=[], - timestamp=int(time.time()), - user_id=user.id, - user={'id': user.id, 'name': user.name}, + # An empty reply may be prepared before the request. + for mid in assistant_ids: + existing_reply = messages.get(mid) + if existing_reply and ( + existing_reply.get('role') != 'assistant' + or existing_reply.get('done') + or existing_reply.get('content') + or existing_reply.get('output') + or (existing_reply.get('user_id') or chat.user_id) != user.id + or (user_message and existing_reply.get('parentId') != user_message_id) + ): + raise HTTPException(409, 'Message already exists or has an invalid ID.') + turn = {} + parent_id = None + if user_message: + existing = messages.get(user_message_id) + if existing and ( + existing.get('role') != 'user' or (existing.get('user_id') or chat.user_id) != user.id + ): + raise HTTPException(403, 'You can only regenerate your own messages.') + parent_id = existing.get('parentId') if existing else user_message.get('parentId') + if parent_id is not None and parent_id not in messages: + raise HTTPException(409, 'Parent message no longer exists.') + message = existing or ( + dict(user_message) + if chat.user_id == user.id + else {key: user_message[key] for key in ('content', 'files', 'models') if key in user_message} + ) + if not existing: + message.update( + id=user_message_id, + parentId=parent_id, + role='user', + childrenIds=[], + timestamp=int(time.time()), + user_id=user.id, + user={'id': user.id, 'name': user.name}, + ) + turn[user_message_id] = self.upsert_message_to_history( + history, user_message_id, self._clean_null_bytes(message) ) - turn = {ids[0]: self.upsert_message_to_history(history, ids[0], self._clean_null_bytes(message))} for entry in message_ids: mid = entry['message_id'] turn[mid] = self.upsert_message_to_history( @@ -1232,7 +1249,7 @@ class ChatTable: mid, { 'id': mid, - 'parentId': ids[0], + 'parentId': user_message_id, 'childrenIds': [], 'role': 'assistant', 'content': '',