diff --git a/backend/open_webui/constants.py b/backend/open_webui/constants.py index cb65e0d77..86d87a2c3 100644 --- a/backend/open_webui/constants.py +++ b/backend/open_webui/constants.py @@ -57,7 +57,7 @@ class ERROR_MESSAGES(str, Enum): ) FILE_NOT_SENT = "FILE_NOT_SENT" - FILE_NOT_SUPPORTED = "Oops! It seems like the file format you're trying to upload is not supported. Please upload a file with a supported format (e.g., JPG, PNG, PDF, TXT) and try again." + FILE_NOT_SUPPORTED = "Oops! It seems like the file format you're trying to upload is not supported. Please upload a file with a supported format and try again." NOT_FOUND = "We could not find what you're looking for :/" USER_NOT_FOUND = "We could not find what you're looking for :/" diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 11e36cecf..ffccdca87 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -1107,9 +1107,15 @@ async def process_chat_response( reasoning_duration = block.get("duration", None) if reasoning_duration: - content = f'{content}
\nThought for {reasoning_duration} seconds\n{reasoning_display_content}\n
\n' + if raw: + content = f'{content}<{block["tag"]}>{block["content"]}\n' + else: + content = f'{content}
\nThought for {reasoning_duration} seconds\n{reasoning_display_content}\n
\n' else: - content = f'{content}
\nThinking…\n{reasoning_display_content}\n
\n' + if raw: + content = f'{content}<{block["tag"]}>{block["content"]}\n' + else: + content = f'{content}
\nThinking…\n{reasoning_display_content}\n
\n' elif block["type"] == "code_interpreter": attributes = block.get("attributes", {}) @@ -1120,11 +1126,14 @@ async def process_chat_response( output = html.escape(json.dumps(output)) if raw: - content = f'{content}
\nAnalyzed\n```{lang}\n{block["content"]}\n```\n```output\n{output}\n```\n
\n' + content = f'{content}\n{block["content"]}\n\n```output\n{output}\n```\n' else: content = f'{content}
\nAnalyzed\n```{lang}\n{block["content"]}\n```\n
\n' else: - content = f'{content}
\nAnalyzing...\n```{lang}\n{block["content"]}\n```\n
\n' + if raw: + content = f'{content}\n{block["content"]}\n\n' + else: + content = f'{content}
\nAnalyzing...\n```{lang}\n{block["content"]}\n```\n
\n' else: block_content = str(block["content"]).strip() @@ -1355,6 +1364,15 @@ async def process_chat_response( if not content_blocks[-1]["content"]: content_blocks.pop() + await event_emitter( + { + "type": "chat:completion", + "data": { + "content": serialize_content_blocks(content_blocks), + }, + } + ) + if response.background: await response.background() diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index 89fedb919..4e8d002dc 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -45,7 +45,7 @@ promptTemplate, splitStream, sleep, - removeDetailsWithReasoning, + removeDetails, getPromptVariables } from '$lib/utils'; @@ -1338,8 +1338,17 @@ parentId: string, { modelId = null, modelIdx = null, newChat = false } = {} ) => { - const _chatId = JSON.parse(JSON.stringify($chatId)); + // If modelId is provided, use it, else use selected model + let selectedModelIds = modelId + ? [modelId] + : atSelectedModel !== undefined + ? [atSelectedModel.id] + : selectedModels; + // Create response messages for each selected model + const responseMessageIds: Record = {}; + + const _chatId = JSON.parse(JSON.stringify($chatId)); // Create new chat if newChat is true and first user message if ( newChat && @@ -1351,15 +1360,6 @@ await saveChatHandler(_chatId); } - // If modelId is provided, use it, else use selected model - let selectedModelIds = modelId - ? [modelId] - : atSelectedModel !== undefined - ? [atSelectedModel.id] - : selectedModels; - - // Create response messages for each selected model - const responseMessageIds: Record = {}; for (const [_modelIdx, modelId] of selectedModelIds.entries()) { const model = $models.filter((m) => m.id === modelId).at(0); @@ -1515,7 +1515,7 @@ : undefined, ...createMessagesList(history, responseMessageId).map((message) => ({ ...message, - content: removeDetailsWithReasoning(message.content) + content: removeDetails(message.content, ['reasoning', 'code_interpreter']) })) ] .filter((message) => message?.content?.trim()) diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts index 2ccc1bf5d..0a5b72e36 100644 --- a/src/lib/utils/index.ts +++ b/src/lib/utils/index.ts @@ -668,8 +668,15 @@ export const cleanText = (content: string) => { return removeFormattings(removeEmojis(content.trim())); }; -export const removeDetailsWithReasoning = (content) => { - return content.replace(/]*>.*?<\/details>/gis, '').trim(); +export const removeDetails = (content, types) => { + for (const type of types) { + content = content.replace( + new RegExp(`]*>.*?<\\/details>`, 'gis'), + '' + ); + } + + return content; }; // This regular expression matches code blocks marked by triple backticks @@ -741,7 +748,7 @@ export const extractSentencesForAudio = (text: string) => { }; export const getMessageContentParts = (content: string, split_on: string = 'punctuation') => { - content = removeDetailsWithReasoning(content); + content = removeDetails(content, ['reasoning', 'code_interpreter']); const messageContentParts: string[] = []; switch (split_on) {