From 7186039be13dba487d24f2a78031bd94701c802f Mon Sep 17 00:00:00 2001 From: Xubin Ren <52506698+Re-bin@users.noreply.github.com> Date: Wed, 10 Jun 2026 15:30:29 +0800 Subject: [PATCH] fix(websocket): limit final stream text to inline endings --- nanobot/channels/websocket.py | 2 +- tests/channels/test_websocket_channel.py | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/nanobot/channels/websocket.py b/nanobot/channels/websocket.py index 62eb04cc5..3c18d8e98 100644 --- a/nanobot/channels/websocket.py +++ b/nanobot/channels/websocket.py @@ -1063,7 +1063,7 @@ class WebSocketChannel(BaseChannel): buffered.append(delta) full_text = "".join(buffered) rewritten = self._media.rewrite_local_markdown_images(full_text) - if full_text: + if delta or rewritten != full_text: body["text"] = rewritten else: body = { diff --git a/tests/channels/test_websocket_channel.py b/tests/channels/test_websocket_channel.py index b624df11c..b74b54ad6 100644 --- a/tests/channels/test_websocket_channel.py +++ b/tests/channels/test_websocket_channel.py @@ -1016,6 +1016,28 @@ async def test_send_delta_emits_delta_and_stream_end() -> None: assert second["event"] == "stream_end" assert second["chat_id"] == "chat-1" assert second["stream_id"] == "sid" + assert "text" not in second + + +@pytest.mark.asyncio +async def test_send_delta_stream_end_includes_inline_final_text() -> None: + bus = MagicMock() + channel = WebSocketChannel({"enabled": True, "allowFrom": ["*"], "streaming": True}, bus, gateway=_basic_handler(bus)) + mock_ws = AsyncMock() + channel._attach(mock_ws, "chat-1") + + await channel.send_delta( + "chat-1", + "merged plain text", + {"_stream_delta": True, "_stream_end": True, "_stream_id": "sid"}, + ) + + mock_ws.send.assert_awaited_once() + final = json.loads(mock_ws.send.await_args.args[0]) + assert final["event"] == "stream_end" + assert final["chat_id"] == "chat-1" + assert final["stream_id"] == "sid" + assert final["text"] == "merged plain text" @pytest.mark.asyncio