fix(telegram): split streaming preview per assistant block (#22613)

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

Prepared head SHA: 26f35f4411
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-21 18:05:23 +05:30
committed by GitHub
parent 36a0df423d
commit 8b1fe0d1e2
14 changed files with 277 additions and 19 deletions

View File

@@ -147,8 +147,7 @@ export const dispatchTelegramMessage = async ({
const canStreamReasoningDraft = canStreamAnswerDraft || streamReasoningDraft;
const draftReplyToMessageId =
replyToMode !== "off" && typeof msg.message_id === "number" ? msg.message_id : undefined;
const draftMinInitialChars =
previewStreamingEnabled || streamReasoningDraft ? 1 : DRAFT_MIN_INITIAL_CHARS;
const draftMinInitialChars = DRAFT_MIN_INITIAL_CHARS;
const mediaLocalRoots = getAgentScopedMediaLocalRoots(cfg, route.agentId);
type LaneName = "answer" | "reasoning";
type DraftLaneState = {
@@ -184,6 +183,8 @@ export const dispatchTelegramMessage = async ({
const reasoningLane = lanes.reasoning;
let splitReasoningOnNextStream = false;
const reasoningStepState = createTelegramReasoningStepState();
type ArchivedPreview = { messageId: number; textSnapshot: string };
const archivedAnswerPreviews: ArchivedPreview[] = [];
type SplitLaneSegment = { lane: LaneName; text: string };
const splitTextIntoLaneSegments = (text?: string): SplitLaneSegment[] => {
const split = splitTelegramReasoningText(text);
@@ -353,6 +354,8 @@ export const dispatchTelegramMessage = async ({
updateLaneSnapshot?: boolean;
skipRegressive: "always" | "existingOnly";
context: "final" | "update";
previewMessageId?: number;
previewTextSnapshot?: string;
}): Promise<boolean> => {
const {
lane,
@@ -363,19 +366,26 @@ export const dispatchTelegramMessage = async ({
updateLaneSnapshot = false,
skipRegressive,
context,
previewMessageId: previewMessageIdOverride,
previewTextSnapshot,
} = params;
if (!lane.stream) {
return false;
}
const hadPreviewMessage = typeof lane.stream.messageId() === "number";
const lanePreviewMessageId = lane.stream.messageId();
const hadPreviewMessage =
typeof previewMessageIdOverride === "number" || typeof lanePreviewMessageId === "number";
if (stopBeforeEdit) {
await lane.stream.stop();
}
const previewMessageId = lane.stream.messageId();
const previewMessageId =
typeof previewMessageIdOverride === "number"
? previewMessageIdOverride
: lane.stream.messageId();
if (typeof previewMessageId !== "number") {
return false;
}
const currentPreviewText = getLanePreviewText(lane);
const currentPreviewText = previewTextSnapshot ?? getLanePreviewText(lane);
const shouldSkipRegressive =
Boolean(currentPreviewText) &&
currentPreviewText.startsWith(text) &&
@@ -446,6 +456,36 @@ export const dispatchTelegramMessage = async ({
!hasMedia && text.length > 0 && text.length <= draftMaxChars && !payload.isError;
if (infoKind === "final") {
if (laneName === "answer" && archivedAnswerPreviews.length > 0) {
const archivedPreview = archivedAnswerPreviews.shift();
if (archivedPreview) {
if (canEditViaPreview) {
const finalized = await tryUpdatePreviewForLane({
lane,
laneName,
text,
previewButtons,
stopBeforeEdit: false,
skipRegressive: "existingOnly",
context: "final",
previewMessageId: archivedPreview.messageId,
previewTextSnapshot: archivedPreview.textSnapshot,
});
if (finalized) {
return "preview-finalized";
}
}
try {
await bot.api.deleteMessage(chatId, archivedPreview.messageId);
} catch (err) {
logVerbose(
`telegram: archived answer preview cleanup failed (${archivedPreview.messageId}): ${String(err)}`,
);
}
const delivered = await sendPayload(applyTextToPayload(payload, text));
return delivered ? "sent" : "skipped";
}
}
if (canEditViaPreview && !finalizedPreviewByLane[laneName]) {
await flushDraftLane(lane);
const finalized = await tryUpdatePreviewForLane({
@@ -628,8 +668,18 @@ export const dispatchTelegramMessage = async ({
}
: undefined,
onAssistantMessageStart: answerLane.stream
? () => {
? async () => {
reasoningStepState.resetForNextStep();
if (answerLane.hasStreamedMessage) {
const previewMessageId = answerLane.stream?.messageId();
if (typeof previewMessageId === "number") {
archivedAnswerPreviews.push({
messageId: previewMessageId,
textSnapshot: answerLane.lastPartialText,
});
}
answerLane.stream?.forceNewMessage();
}
resetDraftLaneState(answerLane);
}
: undefined,
@@ -676,6 +726,15 @@ export const dispatchTelegramMessage = async ({
await stream.clear();
}
}
for (const archivedPreview of archivedAnswerPreviews) {
try {
await bot.api.deleteMessage(chatId, archivedPreview.messageId);
} catch (err) {
logVerbose(
`telegram: archived answer preview cleanup failed (${archivedPreview.messageId}): ${String(err)}`,
);
}
}
}
let sentFallback = false;
if (