diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 4238254a7..6ed1bc1c3 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -2400,190 +2400,131 @@ async def process_chat_payload(request, form_data, user, metadata, model): return form_data, metadata, events -async def process_chat_response( - request, response, form_data, user, metadata, model, events, tasks -): - async def background_tasks_handler(): - message = None +async def background_tasks_handler(request, form_data, user, metadata, tasks): + event_emitter = None + event_caller = None + if ( + "session_id" in metadata + and metadata["session_id"] + and "chat_id" in metadata + and metadata["chat_id"] + and "message_id" in metadata + and metadata["message_id"] + ): + event_emitter = get_event_emitter(metadata) + event_caller = get_event_call(metadata) + + message = None + messages = [] + + if "chat_id" in metadata and not metadata["chat_id"].startswith("local:"): + messages_map = Chats.get_messages_map_by_chat_id(metadata["chat_id"]) + message = messages_map.get(metadata["message_id"]) if messages_map else None + + message_list = get_message_list(messages_map, metadata["message_id"]) + + # Remove details tags and files from the messages. + # as get_message_list creates a new list, it does not affect + # the original messages outside of this handler + messages = [] + for message in message_list: + content = message.get("content", "") + if isinstance(content, list): + for item in content: + if item.get("type") == "text": + content = item["text"] + break - if "chat_id" in metadata and not metadata["chat_id"].startswith("local:"): - messages_map = Chats.get_messages_map_by_chat_id(metadata["chat_id"]) - message = messages_map.get(metadata["message_id"]) if messages_map else None + if isinstance(content, str): + content = re.sub( + r"]*>.*?<\/details>|!\[.*?\]\(.*?\)", + "", + content, + flags=re.S | re.I, + ).strip() - message_list = get_message_list(messages_map, metadata["message_id"]) + messages.append( + { + **message, + "role": message.get( + "role", "assistant" + ), # Safe fallback for missing role + "content": content, + } + ) + else: + # Local temp chat, get the model and message from the form_data + message = get_last_user_message_item(form_data.get("messages", [])) + messages = form_data.get("messages", []) + if message: + message["model"] = form_data.get("model") - # Remove details tags and files from the messages. - # as get_message_list creates a new list, it does not affect - # the original messages outside of this handler - - messages = [] - for message in message_list: - content = message.get("content", "") - if isinstance(content, list): - for item in content: - if item.get("type") == "text": - content = item["text"] - break - - if isinstance(content, str): - content = re.sub( - r"]*>.*?<\/details>|!\[.*?\]\(.*?\)", - "", - content, - flags=re.S | re.I, - ).strip() - - messages.append( + if message and "model" in message: + if tasks and messages: + if ( + TASKS.FOLLOW_UP_GENERATION in tasks + and tasks[TASKS.FOLLOW_UP_GENERATION] + ): + res = await generate_follow_ups( + request, { - **message, - "role": message.get( - "role", "assistant" - ), # Safe fallback for missing role - "content": content, - } + "model": message["model"], + "messages": messages, + "message_id": metadata["message_id"], + "chat_id": metadata["chat_id"], + }, + user, ) - else: - # Local temp chat, get the model and message from the form_data - message = get_last_user_message_item(form_data.get("messages", [])) - messages = form_data.get("messages", []) - if message: - message["model"] = form_data.get("model") - if message and "model" in message: - if tasks and messages: - if ( - TASKS.FOLLOW_UP_GENERATION in tasks - and tasks[TASKS.FOLLOW_UP_GENERATION] - ): - res = await generate_follow_ups( - request, - { - "model": message["model"], - "messages": messages, - "message_id": metadata["message_id"], - "chat_id": metadata["chat_id"], - }, - user, - ) + if res and isinstance(res, dict): + if len(res.get("choices", [])) == 1: + response_message = res.get("choices", [])[0].get("message", {}) - if res and isinstance(res, dict): - if len(res.get("choices", [])) == 1: - response_message = res.get("choices", [])[0].get( - "message", {} - ) + follow_ups_string = response_message.get( + "content" + ) or response_message.get("reasoning_content", "") + else: + follow_ups_string = "" - follow_ups_string = response_message.get( - "content" - ) or response_message.get("reasoning_content", "") - else: - follow_ups_string = "" + follow_ups_string = follow_ups_string[ + follow_ups_string.find("{") : follow_ups_string.rfind("}") + 1 + ] - follow_ups_string = follow_ups_string[ - follow_ups_string.find("{") : follow_ups_string.rfind("}") - + 1 - ] - - try: - follow_ups = json.loads(follow_ups_string).get( - "follow_ups", [] - ) - await event_emitter( - { - "type": "chat:message:follow_ups", - "data": { - "follow_ups": follow_ups, - }, - } - ) - - if not metadata.get("chat_id", "").startswith("local:"): - Chats.upsert_message_to_chat_by_id_and_message_id( - metadata["chat_id"], - metadata["message_id"], - { - "followUps": follow_ups, - }, - ) - - except Exception as e: - pass - - if not metadata.get("chat_id", "").startswith( - "local:" - ): # Only update titles and tags for non-temp chats - if TASKS.TITLE_GENERATION in tasks: - user_message = get_last_user_message(messages) - if user_message and len(user_message) > 100: - user_message = user_message[:100] + "..." - - title = None - if tasks[TASKS.TITLE_GENERATION]: - res = await generate_title( - request, - { - "model": message["model"], - "messages": messages, - "chat_id": metadata["chat_id"], + try: + follow_ups = json.loads(follow_ups_string).get("follow_ups", []) + await event_emitter( + { + "type": "chat:message:follow_ups", + "data": { + "follow_ups": follow_ups, }, - user, - ) + } + ) - if res and isinstance(res, dict): - if len(res.get("choices", [])) == 1: - response_message = res.get("choices", [])[0].get( - "message", {} - ) - - title_string = ( - response_message.get("content") - or response_message.get( - "reasoning_content", - ) - or message.get("content", user_message) - ) - else: - title_string = "" - - title_string = title_string[ - title_string.find("{") : title_string.rfind("}") + 1 - ] - - try: - title = json.loads(title_string).get( - "title", user_message - ) - except Exception as e: - title = "" - - if not title: - title = messages[0].get("content", user_message) - - Chats.update_chat_title_by_id( - metadata["chat_id"], title - ) - - await event_emitter( - { - "type": "chat:title", - "data": title, - } - ) - - if title == None and len(messages) == 2: - title = messages[0].get("content", user_message) - - Chats.update_chat_title_by_id(metadata["chat_id"], title) - - await event_emitter( + if not metadata.get("chat_id", "").startswith("local:"): + Chats.upsert_message_to_chat_by_id_and_message_id( + metadata["chat_id"], + metadata["message_id"], { - "type": "chat:title", - "data": message.get("content", user_message), - } + "followUps": follow_ups, + }, ) - if TASKS.TAGS_GENERATION in tasks and tasks[TASKS.TAGS_GENERATION]: - res = await generate_chat_tags( + except Exception as e: + pass + + if not metadata.get("chat_id", "").startswith( + "local:" + ): # Only update titles and tags for non-temp chats + if TASKS.TITLE_GENERATION in tasks: + user_message = get_last_user_message(messages) + if user_message and len(user_message) > 100: + user_message = user_message[:100] + "..." + + title = None + if tasks[TASKS.TITLE_GENERATION]: + res = await generate_title( request, { "model": message["model"], @@ -2599,31 +2540,97 @@ async def process_chat_response( "message", {} ) - tags_string = response_message.get( - "content" - ) or response_message.get("reasoning_content", "") + title_string = ( + response_message.get("content") + or response_message.get( + "reasoning_content", + ) + or message.get("content", user_message) + ) else: - tags_string = "" + title_string = "" - tags_string = tags_string[ - tags_string.find("{") : tags_string.rfind("}") + 1 + title_string = title_string[ + title_string.find("{") : title_string.rfind("}") + 1 ] try: - tags = json.loads(tags_string).get("tags", []) - Chats.update_chat_tags_by_id( - metadata["chat_id"], tags, user - ) - - await event_emitter( - { - "type": "chat:tags", - "data": tags, - } + title = json.loads(title_string).get( + "title", user_message ) except Exception as e: - pass + title = "" + if not title: + title = messages[0].get("content", user_message) + + Chats.update_chat_title_by_id(metadata["chat_id"], title) + + await event_emitter( + { + "type": "chat:title", + "data": title, + } + ) + + if title == None and len(messages) == 2: + title = messages[0].get("content", user_message) + + Chats.update_chat_title_by_id(metadata["chat_id"], title) + + await event_emitter( + { + "type": "chat:title", + "data": message.get("content", user_message), + } + ) + + if TASKS.TAGS_GENERATION in tasks and tasks[TASKS.TAGS_GENERATION]: + res = await generate_chat_tags( + request, + { + "model": message["model"], + "messages": messages, + "chat_id": metadata["chat_id"], + }, + user, + ) + + if res and isinstance(res, dict): + if len(res.get("choices", [])) == 1: + response_message = res.get("choices", [])[0].get( + "message", {} + ) + + tags_string = response_message.get( + "content" + ) or response_message.get("reasoning_content", "") + else: + tags_string = "" + + tags_string = tags_string[ + tags_string.find("{") : tags_string.rfind("}") + 1 + ] + + try: + tags = json.loads(tags_string).get("tags", []) + Chats.update_chat_tags_by_id( + metadata["chat_id"], tags, user + ) + + await event_emitter( + { + "type": "chat:tags", + "data": tags, + } + ) + except Exception as e: + pass + + +async def process_chat_response( + request, response, form_data, user, metadata, model, events, tasks +): event_emitter = None event_caller = None if ( @@ -2756,7 +2763,9 @@ async def process_chat_response( }, ) - await background_tasks_handler() + await background_tasks_handler( + request, form_data, user, metadata, tasks + ) if events and isinstance(events, list): extra_response = {} @@ -3513,7 +3522,9 @@ async def process_chat_response( # Normalize usage data to standard format raw_usage = data.get("usage", {}) or {} - raw_usage.update(data.get("timings", {})) # llama.cpp + raw_usage.update( + data.get("timings", {}) + ) # llama.cpp if raw_usage: usage = normalize_usage(raw_usage) await event_emitter( @@ -4346,7 +4357,9 @@ async def process_chat_response( } ) - await background_tasks_handler() + await background_tasks_handler( + request, form_data, user, metadata, tasks + ) except asyncio.CancelledError: log.warning("Task was cancelled!") await event_emitter({"type": "chat:tasks:cancel"})