fix(telegram): streaming finalize — drop preview-deletion race, finalize MarkdownV2, accept nested config (salvage of #13542, #25724, #25694)#25923
Merged
Conversation
… not skipped When the final streamed text is identical to the last plain-text edit, stream_consumer._send_or_edit short-circuits and never calls adapter.edit_message(finalize=True). For Telegram, this skips the plain-text → MarkdownV2 conversion, leaving raw Markdown syntax visible to the user. Set REQUIRES_EDIT_FINALIZE = True on TelegramAdapter so the finalize edit is always delivered, matching the existing DingTalk pattern. Fixes #25710
`hermes config set gateway.streaming.*` writes the streaming block nested under a `gateway:` key in config.yaml, but the config loader only checked for a top-level `streaming:` key — silently ignoring the nested variant. Fall back to `yaml_cfg['gateway']['streaming']` when the top-level key is absent, matching the pattern already used for other nested config sections. Closes #25676
…iled When the stream consumer's got_done handler successfully delivers the final response content via _send_or_edit but the subsequent edit (e.g. cursor removal) fails, final_response_sent remains False even though the user has already received the final answer. The gateway's fallback send path then re-delivers the same content, causing the user to see the response twice on Telegram. Introduce a new _final_content_delivered flag on the stream consumer, set by the got_done handler when the final content has reached the user. The _run_agent suppression logic now treats this flag as an additional signal (alongside final_response_sent and response_previewed) that final delivery is already complete. This preserves the existing behavior for intermediate-text-only streams (where already_sent=True but no final content has been delivered) — those still receive the gateway's fallback send, matching the test expectation in test_partial_stream_output_does_not_set_already_sent. Adds TestFinalContentDeliveredSuppression with two cases covering both the suppression (content delivered + edit failed) and the non-suppression (intermediate text only) branches.
Contributor
🔎 Lint report:
|
| Rule | Count |
|---|---|
unresolved-import |
1 |
First entries
tests/test_gateway_streaming_nested_config.py:6: [unresolved-import] unresolved-import: Cannot resolve imported module `pytest`
✅ Fixed issues: none
Unchanged: 4388 pre-existing issues carried over.
Diagnostics are surfaced as warnings — this check never fails the build.
This was referenced May 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the Telegram streaming class of bugs where a streamed response would visibly disappear, duplicate, or render with raw Markdown syntax. Salvages three contributor PRs onto current main.
Cherry-picks
REQUIRES_EDIT_FINALIZE=TrueonTelegramAdapterso the finalize edit isn't short-circuited when raw text equals the last streamed frame. Without this, MarkdownV2 conversion was being skipped and users saw raw**bold**/ backticks in the final message.load_gateway_config()now falls back to nestedgateway.streaming.*keys.hermes config set gateway.streaming.enabled truewrites the nested shape, which was being silently dropped._final_content_deliveredflag onGatewayStreamConsumerset as soon as final content reaches the user (before the cosmetic cursor-clear edit is attempted). The suppression check in_run_agenttreats this as an additional signal so a failing cosmetic edit no longer triggers a duplicate fresh-message send (and on Telegram, the subsequentdelete_messageof the preview that left users staring at the "particles" animation with nothing remaining).Conflict resolution
#13542conflicted in two spots against current main:gateway/stream_consumer.py— main already has the stricter_final_response_sent = chunks_deliveredcheck (fix(gateway): streaming mode silently drops final response when already_sent is true #10748). Kept that; layered_final_content_delivered = Trueon top, gated onchunks_deliveredrather than_already_sentso it inherits the stricter precondition. The PR's looser_already_sentgate would have re-introduced the very bug fix(gateway): streaming mode silently drops final response when already_sent is true #10748 fixed.gateway/run.py— main already had the_content_deliveredfield wired into the suppression predicate; the conflict was a log-format mismatch. Kept main's log format with the newcontent_delivered=%sfield appended.Validation
test_duplicate_reply_suppression,test_gateway_streaming_nested_config,test_stream_consumer,test_stream_consumer_fresh_finaltests/gateway/ -k "stream or telegram or duplicate"Co-authored-by preserved via cherry-pick; authorship visible per-commit.
Closes #25710, #25676. Likely fixes user-visible regression of #16668.