fix(webui): merge length recovery stream segments

This commit is contained in:
chengyongru
2026-07-27 01:39:46 +08:00
committed by Xubin Ren
parent 3cc5a98d9f
commit 27a00c7a4f
26 changed files with 325 additions and 29 deletions
+26 -5
View File
@@ -4,6 +4,7 @@ from __future__ import annotations
import asyncio
import dataclasses
import inspect
import os
import time
from collections.abc import Mapping
@@ -860,9 +861,9 @@ class AgentLoop:
"""Run the agent iteration loop.
*on_stream*: called with each content delta during streaming.
*on_stream_end(resuming)*: called when a streaming session finishes.
``resuming=True`` means tool calls follow (spinner should restart);
``resuming=False`` means this is the final response.
*on_stream_end(resuming, merge_next)*: called when a streaming session finishes.
``resuming=True`` means the active turn continues. ``merge_next=True`` means
the next text segment belongs to the same user-visible assistant message.
Returns (final_content, tools_used, messages, stop_reason, had_injections).
"""
@@ -1385,6 +1386,19 @@ class AgentLoop:
if ctx.on_stream is not None:
stream_callback = ctx.on_stream
stream_end_callback = ctx.on_stream_end
stream_end_accepts_merge_next = False
if stream_end_callback is not None:
try:
stream_end_signature = inspect.signature(stream_end_callback)
stream_end_accepts_merge_next = (
"merge_next" in stream_end_signature.parameters
or any(
parameter.kind is inspect.Parameter.VAR_KEYWORD
for parameter in stream_end_signature.parameters.values()
)
)
except (TypeError, ValueError):
pass
segment_streamed_content = False
async def _tracked_stream(delta: str) -> None:
@@ -1393,12 +1407,19 @@ class AgentLoop:
segment_streamed_content = True
await stream_callback(delta)
async def _tracked_stream_end(*, resuming: bool = False) -> None:
async def _tracked_stream_end(
*,
resuming: bool = False,
merge_next: bool = False,
) -> None:
nonlocal segment_streamed_content
ctx.streamed_content = segment_streamed_content
segment_streamed_content = False
if stream_end_callback is not None:
await stream_end_callback(resuming=resuming)
if merge_next and stream_end_accepts_merge_next:
await stream_end_callback(resuming=resuming, merge_next=True)
else:
await stream_end_callback(resuming=resuming)
ctx.on_stream = _tracked_stream
ctx.on_stream_end = _tracked_stream_end