mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-25 23:47:20 +00:00
fix(telegram): preserve finalized previews on mixed text+voice turns
This commit is contained in:
@@ -691,6 +691,52 @@ describe("dispatchTelegramMessage draft streaming", () => {
|
||||
expect(deliverReplies).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each(["partial", "block"] as const)(
|
||||
"keeps finalized text preview when the next assistant message is media-only (%s mode)",
|
||||
async (streamMode) => {
|
||||
let answerMessageId: number | undefined = 1001;
|
||||
const answerDraftStream = {
|
||||
update: vi.fn(),
|
||||
flush: vi.fn().mockResolvedValue(undefined),
|
||||
messageId: vi.fn().mockImplementation(() => answerMessageId),
|
||||
clear: vi.fn().mockResolvedValue(undefined),
|
||||
stop: vi.fn().mockResolvedValue(undefined),
|
||||
forceNewMessage: vi.fn().mockImplementation(() => {
|
||||
answerMessageId = undefined;
|
||||
}),
|
||||
};
|
||||
const reasoningDraftStream = createDraftStream();
|
||||
createTelegramDraftStream
|
||||
.mockImplementationOnce(() => answerDraftStream)
|
||||
.mockImplementationOnce(() => reasoningDraftStream);
|
||||
dispatchReplyWithBufferedBlockDispatcher.mockImplementation(
|
||||
async ({ dispatcherOptions, replyOptions }) => {
|
||||
await replyOptions?.onPartialReply?.({ text: "First message preview" });
|
||||
await dispatcherOptions.deliver({ text: "First message final" }, { kind: "final" });
|
||||
await replyOptions?.onAssistantMessageStart?.();
|
||||
await dispatcherOptions.deliver({ mediaUrl: "file:///tmp/voice.ogg" }, { kind: "final" });
|
||||
return { queuedFinal: true };
|
||||
},
|
||||
);
|
||||
deliverReplies.mockResolvedValue({ delivered: true });
|
||||
editMessageTelegram.mockResolvedValue({ ok: true, chatId: "123", messageId: "1001" });
|
||||
const bot = createBot();
|
||||
|
||||
await dispatchWithContext({ context: createContext(), streamMode, bot });
|
||||
|
||||
expect(editMessageTelegram).toHaveBeenCalledWith(
|
||||
123,
|
||||
1001,
|
||||
"First message final",
|
||||
expect.any(Object),
|
||||
);
|
||||
const deleteMessageCalls = (
|
||||
bot.api as unknown as { deleteMessage: { mock: { calls: unknown[][] } } }
|
||||
).deleteMessage.mock.calls;
|
||||
expect(deleteMessageCalls).not.toContainEqual([123, 1001]);
|
||||
},
|
||||
);
|
||||
|
||||
it("maps finals correctly when archived preview id arrives during final flush", async () => {
|
||||
let answerMessageId: number | undefined;
|
||||
let answerDraftParams:
|
||||
|
||||
@@ -567,7 +567,10 @@ export const dispatchTelegramMessage = async ({
|
||||
reasoningStepState.resetForNextStep();
|
||||
if (answerLane.hasStreamedMessage) {
|
||||
const previewMessageId = answerLane.stream?.messageId();
|
||||
if (typeof previewMessageId === "number") {
|
||||
// Only archive previews that still need a matching final text update.
|
||||
// Once a preview has already been finalized, archiving it here causes
|
||||
// cleanup to delete a user-visible final message on later media-only turns.
|
||||
if (typeof previewMessageId === "number" && !finalizedPreviewByLane.answer) {
|
||||
archivedAnswerPreviews.push({
|
||||
messageId: previewMessageId,
|
||||
textSnapshot: answerLane.lastPartialText,
|
||||
@@ -576,6 +579,8 @@ export const dispatchTelegramMessage = async ({
|
||||
answerLane.stream?.forceNewMessage();
|
||||
}
|
||||
resetDraftLaneState(answerLane);
|
||||
// New assistant message boundary: this lane now tracks a fresh preview lifecycle.
|
||||
finalizedPreviewByLane.answer = false;
|
||||
}
|
||||
: undefined,
|
||||
onReasoningEnd: reasoningLane.stream
|
||||
|
||||
Reference in New Issue
Block a user