fix(weixin): keep stream buffer until send succeeds so retries can re-deliver
send_delta popped the buffer before self.send ran, so a transient WeChat send failure dropped the completed streamed reply: ChannelManager _send_with_retry re-invokes the same _stream_end message, but the buffer was already gone, so the retry sent empty content and returned — turning a delivery retry into silent message loss. Build `full` from the buffer without popping, send, then clear only after a successful send. The _stream_end message's own content (set when the manager coalesces deltas into the end message) is folded into `full` via addition rather than appended to the buffer, so a retry recomputes the same `full` from an unchanged buffer instead of double-counting it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
committed by
Xubin Ren
co-authored by
Claude Opus 4.8
parent
edada598c8
commit
735a243849
@@ -1246,16 +1246,26 @@ class WeixinChannel(BaseChannel):
|
||||
meta = metadata or {}
|
||||
if meta.get("_reasoning_delta") or meta.get("_reasoning"):
|
||||
return
|
||||
if delta:
|
||||
is_end = meta.get("_stream_end")
|
||||
# Accumulate intermediate deltas. The _stream_end message's own content
|
||||
# (present when the manager coalesces deltas into the end message) is
|
||||
# folded into `full` below instead of appended here, so a send retry
|
||||
# recomputes the same `full` from an unchanged buffer rather than
|
||||
# double-counting that delta.
|
||||
if delta and not is_end:
|
||||
self._stream_buffers.setdefault(chat_id, []).append(delta)
|
||||
if not meta.get("_stream_end"):
|
||||
if not is_end:
|
||||
return
|
||||
full = "".join(self._stream_buffers.pop(chat_id, [])).strip()
|
||||
full = ("".join(self._stream_buffers.get(chat_id, [])) + (delta or "")).strip()
|
||||
await self._flush_tool_hints(chat_id)
|
||||
if full:
|
||||
# Send before clearing the buffer: if the send raises, the buffer is
|
||||
# left intact so ChannelManager._send_with_retry can re-deliver the
|
||||
# same _stream_end message instead of silently losing the reply.
|
||||
await self.send(
|
||||
OutboundMessage(channel=self.name, chat_id=chat_id, content=full)
|
||||
)
|
||||
self._stream_buffers.pop(chat_id, None)
|
||||
|
||||
async def _start_typing(self, chat_id: str, context_token: str = "") -> None:
|
||||
"""Start typing indicator immediately when a message is received."""
|
||||
|
||||
Reference in New Issue
Block a user