Repository navigation
feat(telegram)!: post-and-edit streaming by default, native drafts opt-in, link-preserving truncation (#226) - #296
Conversation
…t-in, link-preserving truncation (#226) Ports vercel/chat 3bbf3ff5 (#822) and the Telegram half of 745fdf5a (#826) from chat@4.38.0, and f893470e (#915, chat@4.41.0); test-only port of 43dba3de (#900). - stream(): native drafts only when native_streaming and the chat is a DM; everything else goes through the adapter-owned post-and-edit loop (1100 ms private / 3100 ms group edit floors, retry_after handling, final edit retried once and raised on failure). Never returns None. - TelegramAdapterConfig gains native_streaming and streaming_edit_interval_ms. - truncate_for_telegram returns under-limit text unchanged (drops the chat#446 under-limit trim); the over-limit trimmer is one _scan_delimiters pass that skips code, escapes and link URLs and pairs __ separately from _. BREAKING CHANGE: Telegram DMs stream via post-and-edit by default; set native_streaming=True for draft bubbles. Group edits are paced at 3.1 s. Closes #226
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 39 minutes. View limit detailsLimit details: You’ve used the included review currently available. Review configuration: ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (7)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…pacing tests; doc stream-error divergence scope (#226)
# Conflicts: # CHANGELOG.md
|
Merge gate: CI green on d2ee325 (Lint & Type Check, test (3.12), test (3.13), Analyze (python), Analyze (actions), CodeQL, CodeRabbit). Local Codex review (gpt-6-astra, xhigh, --base origin/main) on cb90b85: "No actionable regressions were found against the specified merge base. All 587 Telegram tests, 229 thread tests, and lint checks for changed Python files passed." That took 4 completed astra rounds. d2ee325 only merges origin/main (#293, Teams installation/join events, which doesn't touch the Telegram or thread modules). The only conflict was in CHANGELOG.md, resolved by keeping both bullets. The full local validation passed: 7560 passed, strict fidelity 730/730 real, pyrefly 0 errors, fidelity target delta +0. Bots: CodeRabbit rate-limited/draft-skipped, no human or bot reviews outstanding. Merging with --admin (Protect Main requires a code-owner approval). |
Summary
Telegram streaming and MarkdownV2 truncation, matching
chat@4.41.1.stream()uses native draft bubbles only when the newnative_streaming=Trueis set and the chat is a DM. Every other stream, DMs included by default, now runs through the new adapter-owned_post_and_edit_stream, andstream()never returnsNone._post_and_edit_streamis a port of upstreampostAndEditStream:streaming_edit_interval_msoverrides the floor, and a largerupdate_interval_msstill wins.retry_afterhas passed.UNSETposts"...",Noneposts no placeholder, and a string is posted as given (""is rejected bypost_message). This uses the tri-state from [4.41/C5] Streaming & thread API: lightweight threads, placeholder sentinel, restore streaming settings, Plan.auto_complete_previous, AG-UI streams #199.truncate_for_telegramreturns text that fits the limit unchanged, in every parse mode. Over the limit,_trim_to_markdown_v2_safe_boundarymakes a single_scan_delimiterspass (upstreamscanDelimiters) that skips code, escapes and link URLs. It pairs__separately, treats two trailing backticks as a cut fence, and does not count a]that ends the slice as closing a link. Three helpers that are now dead were removed.Upstream commits mapped
3bbf3ff5fix(telegram): make native draft streaming opt-in (#822)745fdf5afix(adapters): harden Telegram streaming… (#826), Telegram half43dba3deperf(telegram): skip unused plain draft rendering (#900)f893470efix(telegram): preserve links during truncation (#915)937cac98fix(telegram): … URLs with entity-marker characters (#610)Tests ported
index.test.tsandmarkdown.test.tsare not fidelity-mapped (#78), so the ports are intests/test_telegram_streaming.pyandtests/test_telegram_api.py.it.eachrenders plain text only when selected for rich / markdown / plain native draftsindex.test.ts): preserves URL backticks and trailing text in a legacy MarkdownV2 post, in a legacy MarkdownV2 edit, and in a MarkdownV2 file caption.markdown.test.ts,describe("truncateForTelegram"):[inside a link URL*__openerit.each)markdown.test.ts,describe("trimToMarkdownV2SafeBoundary"): every case, including the "link URLs with raw entity-marker characters" block.None,Nonewith held-back text,Nonewith no text,"")retry_after(None/ non-numeric / numeric)CancelledErrorupdate_interval_msbeats the floortest_strips_unpaired_marker_under_limit_streaming_leaktest_returns_null_for_nondm_streaming_so_chat_sdk_can_use_fallback_streamingfind_unescaped_positionstestsnative_streaming=True.Fidelity
Delta vs committed report (HEAD): missing 104 -> 104 (+0)(after merging main atcb90b85). The Telegram test files are outsideMAPPING/TARGET_MAPPING(#78), soscripts/fidelity_target.jsonis unchanged. The strict check at the pin is 733/733.Divergences
There are no new rows in the non-parity table.
docs/UPSTREAM_SYNC.mdunder "Telegram streaming and MarkdownV2 truncation":_monotonic_ms()(time.monotonic), not the epoch_now_ms()that is persisted for polling.streaming_edit_interval_msignoresbool, matching upstream'stypeofcheck.retry_aftercounts as missing (1 s), because Python would multiply a string by 1000." "when a fallback stream produces only whitespace. That no longer applies to Telegram, whose own loop now leaves"..."on a whitespace-only stream, as upstream's does.Consumer impact (BREAKING for Telegram streaming)
"..."message that is edited as text arrives, not as a draft bubble. SetTelegramAdapterConfig(native_streaming=True)to get drafts back.streaming_edit_interval_ms(0edits on every chunk).AdapterRateLimitErroror the edit's own error. It no longer returns a message that still shows truncated text.find_unescaped_positions,_find_unescaped_positions_outside_codeand_find_unclosed_link_dest_open_bracketwere removed; none was exported from the package."..."placeholder (or the last paced edit) visible.Verify-first results
resolve_parse_modesends plain strings andrawpayloads with no parse mode. Every MarkdownV2 body is rendered byTelegramFormatConverter, and acan't parse entitiesrejection still retries as plain text throughwith_telegram_markdown_fallback.937cac98behaviour is covered by the new scanner. It skips the link URL, and the "preserves a link whose URL contains entity markers" and "keeps a rendered link whose URL has odd underscores" cases pass.Validation
Full validation from the task passed:
--check-docspasses, and--strictreports 733/733.cb90b85).pyrefly checkreports 0 errors.Review (gpt-6-astra)
Round 3 PASS (on
0c8cb00): no actionable findings. Rounds 1 and 2 each raised two P2 findings, and in all four the flagged behavior is identical to upstreamchat@4.41.1. I did not add divergent machinery; each site has a code comment noting upstream parity, anddocs/UPSTREAM_SYNC.mdrecords the behavior.flushFinalEdit(index.ts:2069-2102) retries onlyAdapterRateLimitError.for awaitloop (index.ts:2104-2138) has no try/finally.index.ts:2019-2049). The core fallback already took this path for groups before this PR.applyEditsetslastEditAtonly on success (index.ts:2025-2036).Merge gate
Independent review findings (4):
docs/UPSTREAM_SYNC.mdrow "Fallback streaming stream-exception capture" still applied to Telegram. It is now titled "(non-Teams, non-Telegram adapters)" and says Telegram's post-and-edit loop propagates stream errors without a partial flush, as upstreampostAndEditStreamdoes. Two reviewers raised it._FakeClockstarted at 0, which equals the initiallast_edit_at. The clock now starts at 10 000 ms, and the floor tests assert times relative to that start. A new test,test_no_placeholder_paces_edits_from_the_first_post, covers the no-placeholder path. Aretry_afterof exactly 5 s is now covered on both cap checks (a strict>, as upstreamindex.ts:2077/:2094). I mutation-checked the four mutants the reviewer reported, and each one now fails the tests: removing eitherlast_edit_atstamp (9 and 1 failures), and changing>to>=on either cap check (1 failure each).)close a link URL still fails the ported tests.gpt-6-astra: after this round's fixes and the merge of main, the run on
cb90b85is PASS: "No actionable regressions were found". An earlier attempt on the same SHA was interrupted by model capacity and re-run. There were 3 rounds before this one; see above.Bots: CodeRabbit skipped the PR while it was a draft. There are no inline or review comments to handle.
CI on
cb90b85: Tests (3.12 and 3.13), CodeQL and Analyze are green. The PR'sLint & Type Checkcheck is shown as skipped, because it ran on the push while the PR was still a draft; marking it ready did not start a new run. I ran the same workflow withworkflow_dispatchoncb90b85and it passed: https://github.com/Chinchill-AI/chat-sdk-python/actions/runs/36830341966.Local full validation on
cb90b85: ruff check and format are clean, the audit reports 0 hard failures,--check-docspasses,--strictreports 733/733, pytest has 7506 passed and 24 skipped, and pyrefly reports 0 errors.Closes #226
Part of #184