fix: split telegram reasoning and answer draft streams (#20774)

Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 7458444144
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Reviewed-by: @obviyus
This commit is contained in:
Ayaan Zaidi
2026-02-20 11:14:39 +05:30
committed by GitHub
parent beb2b74b5b
commit ab256b8ec7
7 changed files with 1194 additions and 279 deletions

View File

@@ -15,6 +15,11 @@ export type TelegramDraftStream = {
forceNewMessage: () => void;
};
type TelegramDraftPreview = {
text: string;
parseMode?: "HTML";
};
export function createTelegramDraftStream(params: {
api: Bot["api"];
chatId: number;
@@ -24,6 +29,8 @@ export function createTelegramDraftStream(params: {
throttleMs?: number;
/** Minimum chars before sending first message (debounce for push notifications) */
minInitialChars?: number;
/** Optional preview renderer (e.g. markdown -> HTML + parse mode). */
renderText?: (text: string) => TelegramDraftPreview;
log?: (message: string) => void;
warn?: (message: string) => void;
}): TelegramDraftStream {
@@ -42,6 +49,7 @@ export function createTelegramDraftStream(params: {
let streamMessageId: number | undefined;
let lastSentText = "";
let lastSentParseMode: "HTML" | undefined;
let stopped = false;
let isFinal = false;
@@ -54,33 +62,52 @@ export function createTelegramDraftStream(params: {
if (!trimmed) {
return false;
}
if (trimmed.length > maxChars) {
const rendered = params.renderText?.(trimmed) ?? { text: trimmed };
const renderedText = rendered.text.trimEnd();
const renderedParseMode = rendered.parseMode;
if (!renderedText) {
return false;
}
if (renderedText.length > maxChars) {
// Telegram text messages/edits cap at 4096 chars.
// Stop streaming once we exceed the cap to avoid repeated API failures.
stopped = true;
params.warn?.(
`telegram stream preview stopped (text length ${trimmed.length} > ${maxChars})`,
`telegram stream preview stopped (text length ${renderedText.length} > ${maxChars})`,
);
return false;
}
if (trimmed === lastSentText) {
if (renderedText === lastSentText && renderedParseMode === lastSentParseMode) {
return true;
}
// Debounce first preview send for better push notification quality.
if (typeof streamMessageId !== "number" && minInitialChars != null && !isFinal) {
if (trimmed.length < minInitialChars) {
if (renderedText.length < minInitialChars) {
return false;
}
}
lastSentText = trimmed;
lastSentText = renderedText;
lastSentParseMode = renderedParseMode;
try {
if (typeof streamMessageId === "number") {
await params.api.editMessageText(chatId, streamMessageId, trimmed);
if (renderedParseMode) {
await params.api.editMessageText(chatId, streamMessageId, renderedText, {
parse_mode: renderedParseMode,
});
} else {
await params.api.editMessageText(chatId, streamMessageId, renderedText);
}
return true;
}
const sent = await params.api.sendMessage(chatId, trimmed, replyParams);
const sendParams = renderedParseMode
? {
...replyParams,
parse_mode: renderedParseMode,
}
: replyParams;
const sent = await params.api.sendMessage(chatId, renderedText, sendParams);
const sentMessageId = sent?.message_id;
if (typeof sentMessageId !== "number" || !Number.isFinite(sentMessageId)) {
stopped = true;
@@ -138,6 +165,7 @@ export function createTelegramDraftStream(params: {
const forceNewMessage = () => {
streamMessageId = undefined;
lastSentText = "";
lastSentParseMode = undefined;
loop.resetPending();
};