fix: address telegram follow-up review comments

This commit is contained in:
Ayaan Zaidi
2026-03-09 16:48:45 +05:30
parent eb81960037
commit d5d708be18
2 changed files with 67 additions and 2 deletions

View File

@@ -505,6 +505,42 @@ describe("dispatchTelegramMessage draft streaming", () => {
expect(deliverReplies).not.toHaveBeenCalled();
});
it("preserves earlier inline buttons when a later final only changes text", async () => {
const answerDraftStream = createSequencedDraftStream(1001);
const reasoningDraftStream = createDraftStream();
const buttons = [[{ text: "Open", callback_data: "open" }]];
createTelegramDraftStream
.mockImplementationOnce(() => answerDraftStream)
.mockImplementationOnce(() => reasoningDraftStream);
dispatchReplyWithBufferedBlockDispatcher.mockImplementation(
async ({ dispatcherOptions, replyOptions }) => {
await replyOptions?.onPartialReply?.({ text: "Message A partial" });
await replyOptions?.onAssistantMessageStart?.();
await replyOptions?.onPartialReply?.({ text: "Message B partial" });
await dispatcherOptions.deliver(
{
text: "Message A final",
channelData: { telegram: { buttons } },
},
{ kind: "final" },
);
await dispatcherOptions.deliver({ text: "Message B final" }, { kind: "final" });
return { queuedFinal: true };
},
);
deliverReplies.mockResolvedValue({ delivered: true });
editMessageTelegram.mockResolvedValue({ ok: true, chatId: "123", messageId: "1001" });
await dispatchWithContext({ context: createContext(), streamMode: "partial" });
expect(editMessageTelegram).toHaveBeenCalledWith(
123,
1001,
"Message A final Message B final",
expect.objectContaining({ buttons }),
);
});
it.each(["partial", "block"] as const)(
"keeps finalized text preview when the next assistant message is media-only (%s mode)",
async (streamMode) => {
@@ -1230,6 +1266,21 @@ describe("dispatchTelegramMessage draft streaming", () => {
expect(draftStream.clear).toHaveBeenCalledTimes(1);
});
it("clears stale reasoning preview when streamed turn ends without a final", async () => {
const { reasoningDraftStream } = setupDraftStreams({
answerMessageId: 999,
reasoningMessageId: 111,
});
dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async ({ replyOptions }) => {
await replyOptions?.onReasoningStream?.({ text: "Reasoning:\n_step one_" });
return { queuedFinal: false };
});
await dispatchWithContext({ context: createReasoningStreamContext(), streamMode: "partial" });
expect(reasoningDraftStream.clear).toHaveBeenCalledTimes(1);
});
it("falls back when all finals are skipped and clears preview", async () => {
const draftStream = createDraftStream(999);
createTelegramDraftStream.mockReturnValue(draftStream);

View File

@@ -309,8 +309,22 @@ export const dispatchTelegramMessage = async ({
const getCurrentAnswerText = () => composeAnswerSegmentsText();
const getLastAnswerSegment = () => answerSegments[answerSegments.length - 1];
const getUnfinalizedAnswerSegments = () => answerSegments.filter((segment) => !segment.finalized);
const hasBufferedAnswerPayloadMetadata = (payload: ReplyPayload) => {
const previewButtons = (
payload.channelData?.telegram as { buttons?: TelegramInlineButtons } | undefined
)?.buttons;
return (
Boolean(payload.mediaUrl) || (payload.mediaUrls?.length ?? 0) > 0 || Boolean(previewButtons)
);
};
const bufferAnswerFinal = (payload: ReplyPayload) => {
bufferedAnswerFinal = { payload, text: composeAnswerSegmentsText() };
const bufferedPayload =
bufferedAnswerFinal &&
hasBufferedAnswerPayloadMetadata(bufferedAnswerFinal.payload) &&
!hasBufferedAnswerPayloadMetadata(payload)
? bufferedAnswerFinal.payload
: payload;
bufferedAnswerFinal = { payload: bufferedPayload, text: composeAnswerSegmentsText() };
};
const createAnswerSegment = (segmentStartsAfterFinal: boolean): AnswerSegmentState => {
const segment: AnswerSegmentState = {
@@ -754,7 +768,7 @@ export const dispatchTelegramMessage = async ({
},
}));
await flushBufferedAnswerFinal();
if (reasoningLane.hasStreamedMessage) {
if (queuedFinal && reasoningLane.hasStreamedMessage) {
finalizedPreviewByLane.reasoning = true;
}
} catch (err) {