fix(gateway): edit streamed message instead of sending duplicate when response_transformed

When a transform_llm_output hook appends content after streaming, the previous
fix skipped the final-send suppression which caused the full response to be
sent as a NEW message (duplicate). Instead, edit the existing streamed message
in-place to append the transformed content, then set already_sent=True.

Added stream_consumer.message_id and .accumulated_text public properties.
This commit is contained in:
kenyonxu 2026-05-20 13:32:40 +08:00 committed by Teknium
parent a4ceead796
commit 5cb21e3fb5
2 changed files with 32 additions and 0 deletions

View file

@ -17692,6 +17692,28 @@ class GatewayRunner:
_content_delivered,
)
response["already_sent"] = True
elif not _is_empty_sentinel and _transformed and _sc is not None:
# Plugin hooks transformed the response after streaming — edit the
# existing streamed message instead of sending a duplicate.
_sc_msg_id = _sc.message_id
if _sc_msg_id:
try:
await _sc.adapter.edit_message(
chat_id=source.chat_id,
message_id=_sc_msg_id,
content=response["final_response"],
finalize=True,
)
response["already_sent"] = True
logger.info(
"Edited streamed message %s for session %s to include plugin-transformed content.",
_sc_msg_id, session_key or "?",
)
except Exception as _edit_err:
logger.warning(
"Failed to edit streamed message for session %s: %s",
session_key or "?", _edit_err,
)
# Schedule deletion of tracked temporary progress bubbles after the
# final response lands. Failed runs skip this so bubbles remain as

View file

@ -192,6 +192,16 @@ class GatewayStreamConsumer:
"""True when the stream consumer delivered the final assistant reply."""
return self._final_response_sent
@property
def message_id(self) -> str | None:
"""The Discord/chat message ID of the last-sent or edited message."""
return self._message_id
@property
def accumulated_text(self) -> str:
"""The accumulated streamed text (without think-block content)."""
return self._accumulated
@property
def final_content_delivered(self) -> bool:
"""True when the final response content reached the user, even if