Telegram's Bot API 10.1 introduced Rich Messages with native tables, LaTeX rendering, expandable reasoning blocks, and a 32,768-character limit. LLMs still output Markdown. A new Python library called tg-rich-converter closes that gap in a single function call, converting Markdown, LaTeX, and thinking tags into the HTML that Telegram's rich format expects.

What Telegram's rich format actually supports

Telegram Bot API 10.1 added sendRichMessage, which accepts structured HTML instead of plain text with parse_mode. The new format supports tables with borders and column striping, inline and block LaTeX math via tg-math and tg-math-block tags, expandable details blocks for reasoning model output, native ordered and unordered lists, and advanced typography including underline, highlight, and spoiler tags. Messages can be up to 32,768 characters, eight times the previous 4,096-character limit.

The problem is that every major LLM, OpenAI, Anthropic, DeepSeek, Ollama, still outputs standard Markdown. The table syntax, LaTeX formulas, and code fences that LLMs produce do not map directly to Telegram's rich HTML. Bots that try to send Markdown through the legacy parse_mode="HTML" path hit parsing errors on details tags, math tags, and table elements. The tg-rich-converter library handles the translation.

Streaming is the hard part

The library's most practical feature is streaming mode. When an LLM produces tokens one at a time, intermediate frames contain unfinished structures: an opening code fence with no closing, a half-written LaTeX formula, an unclosed reasoning block, or incomplete bold markers. Passing these frames directly to Telegram's editMessageText triggers 400 Bad Request errors because the HTML is malformed.

to_rich with streaming=True automatically closes open structures in LIFO order for every frame. An unclosed code fence gets a temporary closing fence. A half-written formula gets a closing delimiter. An open details block gets a closing tag. The result is valid HTML at every intermediate frame, which means the bot can edit the message on every token without crashing. The recommended throttle is 0.6 to 0.8 seconds between edits to avoid Telegram's 429 flood limits.

The library also handles the case where LLM output includes LaTeX with pipe characters, like |psi> or |x| ≥ 0, inside table cells. Standard Markdown parsers would interpret the pipe as a column delimiter. tg-rich-converter recognizes the LaTeX context and preserves the formula without breaking the table structure.

The conversion pipeline

The library is pure Python with no dependencies beyond the standard library. It uses re for pattern matching, html for escaping, and argparse for the CLI. The to_rich function accepts a Markdown string and returns Telegram-compatible HTML. Tables become bordered, striped HTML tables with column alignment. LaTeX block formulas become tg-math-block elements. Inline formulas become tg-math elements. Code fences become pre/code blocks with language class attributes. Lists become native ul and ol tags. Raw angle brackets and ampersands in regular text are escaped to prevent parsing errors.

The split_rich_message function handles long outputs that exceed Telegram's character limits. It splits the text into chunks while closing and reopening nested tags across boundaries, protecting LaTeX formulas from fragmentation, and preserving the structure of code blocks and details elements. Each chunk is independently valid HTML.

Local preview without a bot

The save_preview function generates a standalone HTML file styled with Telegram's dark theme and client-side KaTeX rendering. This lets developers inspect the output visually without deploying a bot or sending messages to Telegram. The CLI command tg-rich with the --preview flag does the same thing and can auto-open the result in a browser.

The CLI also supports batch processing, stdin/stdout pipelines, and message splitting with configurable character limits. The command tg-rich big_report.md --split produces separate files for each chunk, respecting either the 32,768 rich limit or the 4,096 classic limit.

Integration with bot frameworks

The library works with aiogram, pyTelegramBotAPI, and raw HTTP requests. The pattern is the same across all three: call to_rich on the LLM output, then pass the result as rich_message={"html": rich_html} instead of the legacy text and parse_mode parameters. The library includes a streaming demo script that can be run against a live Telegram bot with rate-limiting built in.

The library has 44 tests, all passing. It is MIT licensed, installable via pip, and requires Python. The project is at github.com/kobaltgit/tg-rich-converter.

Why this matters for LLM bot developers

Every Telegram bot that displays LLM output faces the same problem: the model produces Markdown, Telegram wants HTML, and the two formats do not overlap cleanly. Before this library, developers either stripped formatting, wrote custom converters that broke on LaTeX or tables, or sent plain text and lost the rich rendering entirely. The tg-rich-converter gives bot developers a single function that handles the full range of LLM output, including the edge cases that matter most in production: streaming frames, formula-containing tables, and long outputs that need splitting.