Потокова передача повідомлень (stream)
Цей плагін дозволяє потоково передавати довгі текстові повідомлення до Telegram. Будь-який ітератор фрагментів рядків може передаватися потоково безпосередньо до будь-якого приватного чату.
Наприклад, ви можете зробити так, щоб відповідь LLM (large language model, велика мовна модель) зʼявлялася поступово під час генерування.
Початок роботи
Плагін додає метод ctx до обʼєкта контексту.
Потокова передача повідомлень дуже швидко виконує дуже багато запитів до API. Настійно рекомендується використовувати плагін
autoразом із плагіном-retry stream.
import { Bot, type Context } from "grammy";
import { autoRetry } from "@grammyjs/auto-retry";
import { stream, type StreamFlavor } from "@grammyjs/stream";
type MyContext = StreamFlavor<Context>;
const bot = new Bot<MyContext>("");
bot.api.config.use(autoRetry()); // Настійно рекомендується!
bot.use(stream());
async function* slowText() {
// Імітуємо повільне генерування тексту.
yield "Це деяки";
await new Promise((r) => setTimeout(r, 2000));
yield "й повільно зге";
await new Promise((r) => setTimeout(r, 2000));
yield "нерований текст";
}
// Telegram підтримує потокову передачу лише у приватних чатах.
bot.chatType("private")
.command("stream", async (ctx) => {
// Передаємо повідомлення потоково!
await ctx.replyWithStream(slowText());
});
bot.start();2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const { Bot } = require("grammy");
const { autoRetry } = require("@grammyjs/auto-retry");
const { stream } = require("@grammyjs/stream");
const bot = new Bot("");
bot.api.config.use(autoRetry()); // Настійно рекомендується!
bot.use(stream());
async function* slowText() {
// Імітуємо повільне генерування тексту.
yield "Це деяки";
await new Promise((r) => setTimeout(r, 2000));
yield "й повільно зге";
await new Promise((r) => setTimeout(r, 2000));
yield "нерований текст";
}
// Telegram підтримує потокову передачу лише у приватних чатах.
bot.chatType("private")
.command("stream", async (ctx) => {
// Передаємо повідомлення потоково!
await ctx.replyWithStream(slowText());
});
bot.start();2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { Bot, type Context } from "https://deno.land/x/grammy@v1.42.0/mod.ts";
import { autoRetry } from "https://deno.land/x/grammy_auto_retry@v2.0.2/mod.ts";
import {
stream,
type StreamFlavor,
} from "https://deno.land/x/grammy_stream@v1.0.1/mod.ts";
type MyContext = StreamFlavor<Context>;
const bot = new Bot<MyContext>("");
bot.api.config.use(autoRetry()); // Настійно рекомендується!
bot.use(stream());
async function* slowText() {
// Імітуємо повільне генерування тексту.
yield "Це деяки";
await new Promise((r) => setTimeout(r, 2000));
yield "й повільно зге";
await new Promise((r) => setTimeout(r, 2000));
yield "нерований текст";
}
// Telegram підтримує потокову передачу лише у приватних чатах.
bot.chatType("private")
.command("stream", async (ctx) => {
// Передаємо повідомлення потоково!
await ctx.replyWithStream(slowText());
});
bot.start();2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Ось і все!
Інтеграція з LLM
Більшість інтеграцій з LLM дозволяють передавати результат потоково під час його генерування. Ви можете використовувати цей плагін, щоб відповідь LLM зʼявлялася поступово у будь-якому приватному чаті.
Наприклад, якщо ви використовуєте AI SDK, налаштування може виглядати так:
import { streamText } from "ai";
import { google } from "@ai-sdk/google";
bot.chatType("private")
.command("credits", async (ctx) => {
// Надсилаємо запит до LLM:
const { textStream } = streamText({
model: google("gemini-2.5-flash"),
prompt: "Наскільки класні боти grammY?",
});
// Автоматично передаємо відповідь потоково за допомогою grammY:
await ctx.replyWithStream(textStream);
});2
3
4
5
6
7
8
9
10
11
12
13
14
import { streamText } from "npm:ai";
import { google } from "npm:@ai-sdk/google";
bot.chatType("private")
.command("credits", async (ctx) => {
// Надсилаємо запит до LLM:
const { textStream } = streamText({
model: google("gemini-2.5-flash"),
prompt: "Наскільки класні боти grammY?",
});
// Автоматично передаємо відповідь потоково за допомогою grammY:
await ctx.replyWithStream(textStream);
});2
3
4
5
6
7
8
9
10
11
12
13
14
Не забудьте замінити gemini на найновішу модель.
Потокова передача стилізованих повідомлень
Це значно складніше, ніж здається.
- LLM генерують імовірнісний Markdown. Він часто правильний, але не завжди. Він не дотримується жодного конкретного стандарту. Зокрема, LLM не завжди генерують Markdown, сумісний із Telegram. Це означає, що спроба надіслати або передати його потоково до Telegram не вдасться.
- LLM генерують часткові сутності Markdown. Навіть якщо результат ідеально відповідає специфікації MarkdownV2 від Telegram, окремі вихідні фрагменти можуть бути неправильними. Якщо ви почнете курсивний текст, але завершите його лише в наступному фрагменті, потокова передача обірветься і жодне повідомлення не буде надіслане.
- LLM іноді генерують форматування, яке не підтримується Telegram (навіть якщо ви інструктуєте їх не робити цього). Наприклад, більшість LLM обожнюють таблиці, марковані та нумеровані списки. Клієнти Telegram не можуть відображати ці елементи.
Telegram також приймає HTML
-форматування . Воно має точно такі самі проблеми, як і Markdown. Крім того, HTML споживає значно більше токенів, що є зайвою витратою.
Тож… що тепер?
На жаль, гарного рішення немає. Проте ось кілька ідей:
- Попросіть вашу LLM виводити текст без форматування.
- Сподівайтеся, що ваша LLM не помилиться у генеруванні Markdown, і просто повторіть запит зі звичайним текстом, якщо це не вдасться.
- Використовуйте HTML-форматування і сподівайтеся, що це трохи покращить ситуацію.
- Напишіть власний перетворювач, який автоматично повторюватиме невдалі запити.
- Використовуйте потоковий парсер Markdown і створюйте власні масиви
Messageдля форматування кожногоEntity Message.Draft Piece - Передавайте Markdown у вигляді звичайного тексту, а потім застосовуйте форматування за допомогою звичайного парсера Markdown лише після завершення потокової передачі та надсилання всіх повідомлень.
- Придумайте геніальне рішення, про яке ніхто ще не здогадався, і розкажіть нам про нього у груповому чаті.