fix(telegram): skip redundant final edit in partial streaming mode#19479
fix(telegram): skip redundant final edit in partial streaming mode#19479v8hid wants to merge 1 commit intoopenclaw:mainfrom
Conversation
Additional Comments (1)
This matters for the skip-edit optimization in The new test added in Fix: save the previous value and restore it on catch, or move the assignment after the successful call. Prompt To Fix With AIThis is a comment left during a code review.
Path: src/telegram/draft-stream.ts
Line: 78:99
Comment:
**`lastSentText` updated before the API call succeeds**
`lastSentText = trimmed` is assigned on line 78, before entering the `try` block. When `editMessageText` throws (caught at line 93), `lastSentText` already reflects the attempted text rather than the last confirmed-delivered text, and `stopped = true` is set.
This matters for the skip-edit optimization in `bot-message-dispatch.ts`: if a streaming edit fails mid-stream (network hiccup, etc.), `lastSentText` will equal the text of the failed attempt, even though the Telegram message still shows the prior successful text. When the final text also equals that failed-attempt text, the optimization skips the final edit — leaving the visible message outdated.
The new test added in `draft-stream.test.ts` (`"keeps lastSentText at the last successful value when an edit fails"`) asserts `stream.lastSentText()` === `"Hello"` after a failed edit to `"Hello again"`, which will fail against this implementation.
Fix: save the previous value and restore it on catch, or move the assignment after the successful call.
```suggestion
const prevLastSentText = lastSentText;
lastSentText = trimmed;
try {
if (typeof streamMessageId === "number") {
await params.api.editMessageText(chatId, streamMessageId, trimmed);
return true;
}
const sent = await params.api.sendMessage(chatId, trimmed, replyParams);
const sentMessageId = sent?.message_id;
if (typeof sentMessageId !== "number" || !Number.isFinite(sentMessageId)) {
stopped = true;
params.warn?.("telegram stream preview stopped (missing message id from sendMessage)");
lastSentText = prevLastSentText;
return false;
}
streamMessageId = Math.trunc(sentMessageId);
return true;
} catch (err) {
stopped = true;
lastSentText = prevLastSentText;
params.warn?.(
`telegram stream preview failed: ${err instanceof Error ? err.message : String(err)}`,
);
return false;
}
```
How can I resolve this? If you propose a fix, please make it concise. |
|
This pull request has been automatically marked as stale due to inactivity. |
5c2221e to
156b16a
Compare
Instead of trying to edit a preview message with identical content, finalize the existing preview in place. Editing with the same content can return a Telegram API error, which then triggers failover cleanup/send and may duplicate output. - Move lastSentText/lastSentParseMode updates to after successful API call so failed edits do not corrupt tracked last-sent state - Expose lastSentText() on TelegramDraftStream - Guard editPreview in bot-message-dispatch to skip the final edit when the rendered text already matches what the draft stream last sent, and no markup mutation (buttons, linkPreview) is required
fda4f9a to
c1df1fb
Compare
updated. ready to merge. |
|
This pull request has been automatically marked as stale due to inactivity. |
|
This pull request has been automatically marked as stale due to inactivity. |
|
Closing due to inactivity. |
Summary
message is not modified), which trigger failover cleanup/send and create duplicate visible messages plus error logs.lastAppliedText()tracking indraft-stream.tsand used it inbot-message-dispatch.tsto short-circuit unchanged final edits when no mutation is needed.linkPreview=false) and expanded tests.Rebased cleanly onto main — turns out merging main into a feature branch makes the bot think you're trying to smuggle 200 unrelated commits into your PR. Lesson learned. 2 clean commits now, as intended.
Replaces #17766 (auto-closed by bot after a merge commit polluted the history).
Change Type
Scope
Related Fix
Commit ac2ede5 by @steipete already treats
message is not modifiedAPI errors as success. This PR adds a complementary optimization that prevents the unnecessary API call from being attempted in the first place.Both fixes work together:
ac2ede5bb: Reactive safety net at the API error handling layerGreptile Summary
This PR adds a proactive optimization to skip the redundant
editMessageTextTelegram API call when the preview stream already shows the exact final text, complementing the existing reactive error-handling safety net.The approach is sound — adding
lastSentText()toTelegramDraftStreamand checking it inbot-message-dispatch.tsbefore attempting the final edit. The skip conditions correctly account for formatting passes (renderTelegramHtmlTextcomparison), inline button mutations, andlinkPreview=falsemutations.Key issue found:
lastSentTextis assigned optimistically before the API call (line 78 ofdraft-stream.ts). WheneditMessageTextorsendMessagethrows,lastSentTextalready reflects the attempted text, not the last successfully delivered text. This contradicts the test assertion added indraft-stream.test.ts("keeps lastSentText at the last successful value when an edit fails") — that test will fail against the current implementation.lastSentTextequals the failed-attempt text. If the final text also equals that value, the final edit is skipped, leaving the Telegram message showing the last successfully delivered (older) content rather than the final text.lastSentTextvalue before the assignment, and restore it in the catch block (and in thesendMessageearly-return path).Everything else looks good:
needsFormattingPasscheck usingrenderTelegramHtmlTextis correct and matches howeditMessageTelegramrenders text internally.hasFinalEditMutationcheck for buttons andlinkPreviewcorrectly identifies cases requiring the final edit.bot-message-dispatch.test.tsis comprehensive (plain text match, formatting pass, buttons, linkPreview).lastSentText: () => string | undefinedtype is slightly wider than the actual""initialization (always returnsstring), but this is harmless.Confidence Score: 3/5
lastSentTextvariable is set before the API call (optimistic update), but the test and the dispatch optimization require it to reflect the last confirmed delivery. A failing streaming edit leaveslastSentTextpointing to the undelivered text, allowing the final edit to be incorrectly skipped. The newdraft-stream.test.tstest ("keeps lastSentText at the last successful value when an edit fails") will fail against the current implementation. Fixing it requires restoringlastSentTextto its prior value in the catch block.sendOrEditStreamMessagefunction needs to restorelastSentTexton API failure.Last reviewed commit: 1999e63
(4/5) You can add custom instructions or style guidelines for the agent here!