From 4a8aa99bff872b0621fdde505cb8f4af7fed79f4 Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Tue, 29 Sep 2026 13:18:11 +0200 Subject: [PATCH] fix: a /skills:create message is sent differently on later turns A message starting with /skills:create was expanded into the skill-creation instructions only for the request that answered it. Every later request sent the raw /skills:create text in its place, so the model lost the instructions it had been given and the provider's prompt cache missed from that message on. The message is now sent exactly as it was first answered on every later turn, even after the Open Terminal is switched on or off, while the chat still shows what the user typed. Fixes #31591 --- backend/open_webui/utils/middleware.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 6a3e4b6f5b..786375ef5f 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -2224,6 +2224,13 @@ async def load_messages_from_db(chat_id: str, message_id: str) -> Optional[list[ if not db_messages: return None + for idx, msg in enumerate(db_messages): + skills_create = (msg.get('meta') or {}).get('skills_create') + # Resend the first expansion to keep the cached prefix, unless the message was edited since. + if not skills_create or skills_create.get('original_text') != msg.get('content'): + continue + db_messages[idx] = {**msg, 'content': skills_create['expanded_text']} + return [ {k: v for k, v in msg.items() if k in MESSAGE_REPLAY_KEYS} for msg in db_messages @@ -2753,11 +2760,28 @@ async def process_chat_payload(request, form_data, user, metadata, model): metadata['terminal_id'] = terminal_id skill_authoring_allowed = bool(terminal_id) and has_prior_real_chat_content(form_data.get('messages', [])) skill_create_denial_reason = 'empty_chat' if terminal_id else 'disabled' - apply_skills_create_prompt( + skills_create_text = get_last_user_message(form_data.get('messages', [])) + skills_create_applied = apply_skills_create_prompt( form_data.get('messages', []), allowed=skill_authoring_allowed, denial_reason=skill_create_denial_reason, ) + if skills_create_applied and is_saved_chat_id(chat_id) and user_message_id: + stored_user_message = await Chats.get_message_by_id_and_message_id(chat_id, user_message_id) or {} + await Chats.upsert_message_to_chat_by_id_and_message_id( + chat_id, + user_message_id, + { + 'meta': { + **(stored_user_message.get('meta') or {}), + 'skills_create': { + 'original_text': skills_create_text, + 'expanded_text': get_last_user_message(form_data['messages']), + }, + } + }, + touch=False, + ) # If the original caller provided tools, use them as-is (skip resolution). # Otherwise, save any tools that filter inlets added for merging later.