perf(telegram): reuse prepared rich message plan (#127719)

Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
Peter Steinberger
2026-08-21 17:56:22 -07:00
committed by GitHub
parent f6b42ea223
commit 0382ca218a
3 changed files with 48 additions and 24 deletions
+21 -11
View File
@@ -219,18 +219,28 @@ export function buildTelegramRichBlocksPlan(
};
}
export function splitTelegramRichMessageTextChunks(params: {
text: string;
textLimit: number;
tableMode?: MarkdownTableMode;
skipEntityDetection?: boolean;
}): TelegramRichTextChunk[] {
export function splitTelegramRichMessageTextChunks(
params:
| {
text: string;
textLimit: number;
tableMode?: MarkdownTableMode;
skipEntityDetection?: boolean;
}
| {
plan: TelegramRichMessagePlan;
textLimit: number;
},
): TelegramRichTextChunk[] {
// Convert the full markdown document first so fences/tables stay intact, then
// enforce block/char limits on the typed block list (including oversized pre).
const plan = buildTelegramRichMarkdownPlan(params.text, {
tableMode: params.tableMode,
skipEntityDetection: params.skipEntityDetection,
});
const plan =
"plan" in params
? params.plan
: buildTelegramRichMarkdownPlan(params.text, {
tableMode: params.tableMode,
skipEntityDetection: params.skipEntityDetection,
});
// The render already committed to the document-level linkify decision (a
// skip anywhere disables our file-ref code-wrapping everywhere), so every
// chunk must carry the same wire flag; re-deriving per chunk would let
@@ -248,7 +258,7 @@ export function splitTelegramRichMessageTextChunks(params: {
degradationReasons: index === 0 ? plan.degradationReasons : [],
};
});
if (chunked.length === 0 && params.text.trim()) {
if (chunked.length === 0 && "text" in params && params.text.trim()) {
// Markdown that projects to zero blocks (e.g. link definitions only) must
// still send readable source text instead of silently dropping the reply.
const blocks: InputRichBlock[] = [{ type: "paragraph", text: params.text }];
@@ -106,18 +106,15 @@ export function planTelegramTextDeliveryPages(
if (richPlan.richMessage.blocks.length === 0 && params.text.trim()) {
return [plainPage(params.text)];
}
return splitTelegramRichMessageTextChunks({
text: params.text,
textLimit: maxChars,
tableMode: params.tableMode,
skipEntityDetection: params.skipEntityDetection,
}).map((chunk) => ({
plainText: chunk.plainText,
sourceText: chunk.plainText,
sourceTextMode: "markdown" as const,
richMessage: chunk.richMessage,
degradationReasons: chunk.degradationReasons,
}));
return splitTelegramRichMessageTextChunks({ plan: richPlan, textLimit: maxChars }).map(
(chunk) => ({
plainText: chunk.plainText,
sourceText: chunk.plainText,
sourceTextMode: "markdown" as const,
richMessage: chunk.richMessage,
degradationReasons: chunk.degradationReasons,
}),
);
}
if (params.textMode === "plain") {
return splitTelegramPlainTextChunks(params.text, maxChars)
@@ -7,7 +7,7 @@ import {
createPluginStateSyncKeyedStoreForTests,
resetPluginStateStoreForTests,
} from "openclaw/plugin-sdk/plugin-state-test-runtime";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { createTelegramCallbackMessageActions } from "./bot-handlers.callback-actions.js";
import { buildTelegramMessageContextForTest } from "./bot-message-context.test-harness.js";
import { telegramPlugin } from "./channel.js";
@@ -23,6 +23,21 @@ import { sendTypingTelegram } from "./send-actions.js";
import { sendMessageTelegram } from "./send-message.js";
import { sendPollTelegram } from "./send-special.js";
const richMarkdownProjection = vi.hoisted(() => ({ count: 0 }));
vi.mock("./rich-blocks.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("./rich-blocks.js")>();
return {
...actual,
markdownToTelegramRichBlocks: (
...args: Parameters<typeof actual.markdownToTelegramRichBlocks>
) => {
richMarkdownProjection.count += 1;
return actual.markdownToTelegramRichBlocks(...args);
},
};
});
type CapturedRequest = {
body: Buffer;
contentType: string;
@@ -151,6 +166,7 @@ describe("Telegram topic transport payloads", () => {
beforeEach(() => {
requests.length = 0;
richMarkdownProjection.count = 0;
resetPluginStateStoreForTests();
resetTelegramMessageCacheForTest();
installTelegramStateRuntimeForTest();
@@ -259,6 +275,7 @@ describe("Telegram topic transport payloads", () => {
direct_messages_topic_id: DIRECT_TOPIC_ID,
});
expect(request && parseJsonBody(request)).not.toHaveProperty("message_thread_id");
expect(richMarkdownProjection.count).toBe(1);
});
it("rejects poll and typing for channel Direct Messages without transport", async () => {