diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index e11830d4f9..6c18eb26ba 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -5223,7 +5223,12 @@ async def streaming_chat_response_handler(response, ctx): reasoning_detail_items, ) await save_current_response_stream() - data = None + # Providers such as OpenRouter send reasoning_details + # alongside the reasoning text: only drop the event when + # the details were all there was to report, otherwise the + # reasoning delta never reaches the client. + if not reasoning_content: + data = None if value: if ( diff --git a/src/lib/components/chat/Messages/structuredOutput.ts b/src/lib/components/chat/Messages/structuredOutput.ts index 96d5bd869f..d4ebe0493e 100644 --- a/src/lib/components/chat/Messages/structuredOutput.ts +++ b/src/lib/components/chat/Messages/structuredOutput.ts @@ -462,9 +462,12 @@ function ensureOutputItem( fallback?: OutputItem ): OutputItem { while (output.length <= outputIndex) { - output.push( - fallback ?? { type: 'message', status: 'in_progress', role: 'assistant', content: [] } - ); + // Only the addressed slot gets the event's item; filler slots must not reuse its id. + const item = + output.length === outputIndex && fallback + ? { ...fallback } + : { type: 'message', status: 'in_progress', role: 'assistant', content: [] }; + output.push(item); } output[outputIndex] = { ...output[outputIndex] }; return output[outputIndex];