Skip to content

fix(telegram): stop cutting long streamed responses (lost tails, deleted heads, raw markdown) - #43761

Merged
teknium1 merged 3 commits into
mainfrom
fix/telegram-cut-message-delivery
Jun 10, 2026
Merged

fix(telegram): stop cutting long streamed responses (lost tails, deleted heads, raw markdown)#43761
teknium1 merged 3 commits into
mainfrom
fix/telegram-cut-message-delivery

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Summary

Telegram users stop receiving cut-off model responses: long streamed replies no longer lose their tail when a continuation send fails, no longer lose their head when flood-control fallback kicks in, and no longer freeze as raw **markdown** when the stream is cancelled.

Salvages and extends two PRs by @GodsBoy (#42766, #43463), with every claim and fix live-validated against the real Telegram Bot API using the real TelegramAdapter + GatewayStreamConsumer with fault injection.

The three bugs (all reproduced live on main)

  1. Lost tail (fix(gateway): recover partial Telegram overflow streams #42766's target, "model sent only the first part"): _edit_overflow_split returned success=True after delivering 1/3 chunks. The consumer marked the turn delivered; the tail was gone forever.
  2. Lost head (new fix, "model sent only the second part"): _send_fallback_final computes a tail-only continuation against the visible prefix, sends the tail, then deletes the partial message containing the head ("so the user only sees the complete fallback response" — but it wasn't a complete re-send). Live repro: flood control during a long stream → head deleted → 54% of content visible. Neither open PR fixed this path.
  3. Raw markdown on cancel (fix(telegram): format final replies in bound topics #43463's confirmed fix): the gateway cancels the consumer ~5s after the stream finishes; the CancelledError handler re-delivered with finalize=False, leaving the entire reply as a raw plain-text preview while the success flags suppressed the gateway's formatted re-send.

What was salvaged vs. dropped (and why)

Salvaged from #42766 (as-is): partial-overflow SendResult contract — failed continuations return success=False + raw_response["partial_overflow"] metadata; consumer enters fallback, sends only the missing tail, preserves the visible prefix.

Salvaged from #43463: cancel-path finalize=True delivery; re-finalize skip after a split edit (prevents chunk duplication); _strip_mdv2() degradation in all overflow-split fallbacks (raw **/``` markers never hit the screen).

Dropped from #43463 — wrong premise, live-disproved: sizing finalize splits against the formatted text. Live API probes show Telegram's 4096 limit counts the text after entities parsing: a payload with wire length 6120 but parsed length 4080 is accepted; parsing only removes characters, so raw-text sizing can never produce MESSAGE_TOO_LONG. The format-first sizing over-estimates and caused premature splits with 100-char fragment messages in live testing. The raw-marker symptom in #43463's screenshot is fully explained by the cancel path + parse-failure fallbacks, both fixed here.

New fix (bug 2): _send_fallback_final only deletes the stale partial when the fallback re-sent the complete final text (continuation == final_text). Tail-only sends keep the head on screen.

Changes

  • gateway/platforms/telegram.py: partial-overflow SendResult (GodsBoy); _strip_mdv2 degradation in overflow-split fallbacks (first-chunk edit, continuations, reply-not-found retry)
  • gateway/stream_consumer.py: partial-overflow recovery + tail-only fallback (GodsBoy); cancel-path finalize (GodsBoy); re-finalize skip after split (_last_edit_overflowed); delete-guard for tail-only fallback
  • gateway/platforms/base.py: SendResult.raw_response partial-overflow contract doc (GodsBoy)
  • Tests: test_telegram_overflow_partial.py (new, GodsBoy), cancel-path + re-finalize classes in test_stream_consumer_fresh_final.py (GodsBoy), delete-guard pair in test_stream_consumer.py (ours, replacing the over-broad delete-always assertion)

Validation

Scenario (live Telegram API, fault-injected) main this PR
Overflow split, continuation hard-fails success=True, tail lost forever success=False + metadata, tail re-sent
Flood control → fallback final (long stream) head deleted, 54% visible head kept, 109% (full + small overlap)
Cancelled pre-done (gateway timeout path) raw **markdown** frozen rendered formatted
Clean long-markdown response 3 messages, complete 3 messages, complete (no premature splits)
got_done overflow, all sends succeed complete complete, no duplicated chunks

Targeted suites: 231 passed (test_stream_consumer.py, test_stream_consumer_fresh_final.py, test_telegram_overflow_partial.py, test_telegram_format.py, test_stream_consumer_thread_routing.py). Ruff clean.

Closes #42766. Closes #43463. Fixes #42765. Fixes #43441.

Contributor commits cherry-picked with authorship preserved (@GodsBoy); merge via rebase.

Infographic

telegram-cut-message-delivery

GodsBoy and others added 3 commits June 10, 2026 13:06
…ly delete guard

Follow-ups on top of the two salvaged GodsBoy commits, all live-validated
against the real Telegram Bot API:

- _edit_overflow_split finalize fallbacks degrade to _strip_mdv2() clean
  text instead of putting raw **markdown** markers on screen (salvaged
  from PR #43463 minus its format-first sizing — live probes show
  Telegram's 4096 limit counts PARSED text, so MarkdownV2 escape
  inflation cannot cause MESSAGE_TOO_LONG and sizing against formatted
  wire length only causes premature splits and fragment messages).
- Skip the redundant requires-finalize edit after a got_done edit that
  split-and-delivered (salvaged from PR #43463): re-finalizing re-splits
  the full text into the adopted continuation and duplicates chunks.
- _send_fallback_final only deletes the stale partial message when the
  fallback re-sent the COMPLETE final text. When the prefix dedup sent
  only the missing tail, the partial IS the head of the answer; deleting
  it left users with only the second half of long responses (live-
  reproduced: flood-control during a long stream -> head deleted,
  ratio 0.54 of content visible). This is the third bug behind the
  'Telegram cut messages' reports and was present on main and both PRs.
@github-actions

Copy link
Copy Markdown
Contributor

🔎 Lint report: fix/telegram-cut-message-delivery vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 10708 on HEAD, 10705 on base (🆕 +3)

🆕 New issues (2):

Rule Count
unresolved-attribute 1
unresolved-import 1
First entries
tests/gateway/test_telegram_overflow_partial.py:137: [unresolved-attribute] unresolved-attribute: Attribute `kwargs` is not defined on `None` in union `_Call | None`
tests/gateway/test_telegram_overflow_partial.py:6: [unresolved-import] unresolved-import: Cannot resolve imported module `pytest`

✅ Fixed issues: none

Unchanged: 5600 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@alt-glitch alt-glitch added type/bug Something isn't working comp/gateway Gateway runner, session dispatch, delivery platform/telegram Telegram bot adapter P2 Medium — degraded but workaround exists labels Jun 10, 2026
@teknium1
teknium1 merged commit 3b4c715 into main Jun 10, 2026
28 checks passed
@teknium1
teknium1 deleted the fix/telegram-cut-message-delivery branch June 10, 2026 22:09
@avxone

avxone commented Jun 10, 2026

Copy link
Copy Markdown

Same issue here — Telegram user in Russia, seeing raw **bold** markers on long audited responses. The truncate_message() split doesn't track paired formatting markers (bold, italic, strikethrough, spoiler) at chunk boundaries — only code fences and backticks are handled. Eagerly waiting for this fix. 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists platform/telegram Telegram bot adapter type/bug Something isn't working

Projects

None yet

4 participants