mirror of
https://github.com/open-webui/open-webui.git
synced 2026-08-29 10:09:17 +02:00
fix: stop streaming responses breaking on a duplicate output key (#29053)
* fix: stop streaming responses breaking on a duplicate output key With reasoning-capable models the chat froze mid-stream: the first chunk of the answer appeared, nothing followed, and the whole message only showed up once generation finished. The browser console showed a Svelte each_key_duplicate error. When a stream event addresses an output slot past the end of the array, the missing slots were filled with the event's own item, id included, so a gap of two left two entries claiming the same id. The next chunk for that item was matched by id, landed in the first of the two, and the rendered list ended up with two items sharing a key, which Svelte refuses to update. Only the addressed slot now takes the event's item, and the slots before it are anonymous placeholders. Replayed the reported event sequence against the real code: keys are unique again and the chunks stay in order instead of being split across the copies. * fix: stream reasoning deltas when the provider also sends reasoning_details Providers such as OpenRouter emit reasoning_details alongside the reasoning text on the same delta. Merging those details cleared the pending event unconditionally, discarding the response.reasoning_text.delta that had just been built, so the client received no reasoning until the response completed and the thinking block only appeared after generation finished. The event is now only dropped when the details were all there was to report. Details persistence is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014uuEg4AXPs9zE3vVUfN1Fj --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -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 (
|
||||
|
||||
@@ -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];
|
||||
|
||||
Reference in New Issue
Block a user