{"slug": "tg-rich-converter-streaming-llm-markdown-and-latex-to-telegram-bot-api-10-1", "title": "Tg-Rich-Converter: Streaming LLM Markdown and LaTeX to Telegram Bot API 10.1", "summary": "A new zero-dependency Python library, tg-rich-converter, converts LLM Markdown, LaTeX formulas, reasoning tags, and tables into native Telegram Bot API 10.1 Rich HTML for the sendRichMessage endpoint. The library supports Telegram's 32,768-character Rich Message limit, native tables, inline and display LaTeX via tg-math and tg-math-block tags, and expandable details blocks for reasoning models including DeepSeek-R1, OpenAI o1/o3, Qwen, and Gemini. It is installable via pip install tg-rich-converter and ships with a tg-rich CLI, a streaming mode that auto-balances unclosed code blocks, think tags, and LaTeX delimiters to prevent Telegram 400 Bad Request parse errors, and a local preview.html generator.", "body_md": "A lightweight, zero-dependency Python library that converts standard LLM Markdown, LaTeX formulas, thinking processes, and tables into **native Telegram Bot API 10.1+ Rich HTML** (`sendRichMessage`).\n\nStarting with **Telegram Bot API 10.1**, Telegram introduced **Rich Messages** (`sendRichMessage`) supporting:\n\n- Messages up to **32,768 characters** (no more 4,096-character limit!).\n- **Native interactive tables** with borders and striping.\n- **Native LaTeX math rendering** (both inline and display equations).\n- **Expandable spoiler/details blocks** for reasoning models (`DeepSeek-R1` ,`OpenAI o1/o3` ,`Qwen` ,`Gemini` ).\n- **Native lists and advanced typography** (`<ul>` ,`<ol>` ,`<u>` ,`<mark>` ).\n\nHowever, LLMs (OpenAI, Anthropic, DeepSeek, Ollama) still output plain Markdown and LaTeX. `tg-rich-converter` bridges this gap seamlessly in **a single function call** or via the **CLI command line tool**.\n\n- 🌊 **LLM Streaming Mode (`streaming=True`):** Auto-balances unclosed code blocks (```` ``` ```` ), reasoning tags (`<think>` ), unclosed LaTeX formulas (`$$` /`$` ), and unclosed inline styles (`**` ,`||` ,`++` ,`==` ,`~~` ) during token-by-token streaming, completely preventing Telegram`400 Bad Request: can't parse entities` errors on live message edits.\n- 📊 **Robust Native Tables:** Converts standard Markdown pipe tables into`<table bordered striped>` with column alignment (`left` ,`center` ,`right` ). Safely handles formulas with pipes (`$|\\psi\\rangle$` ,`$|x| \\ge 0$` ) and escaped pipes (`\\|` ) inside table cells without breaking columns.\n- 🧮 **LaTeX Math:** Converts`$$...$$` into`<tg-math-block>` and`$x$` into`<tg-math>` .\n- 🧠 **Customizable AI Thinking Blocks:** Converts`<think>...</think>` tags from reasoning models into expandable`<details><summary>Размышления</summary>...</details>` blocks with configurable summary titles.\n- 🛡️ **HTML-Safe:** Automatically escapes raw`<` ,`>` , and`&` in regular text (e.g. mathematical conditions like`x < 5` and`y > 10` ), completely preventing Telegram API`400 Bad Request: can't parse entities` errors.\n- 📋 **Native Lists:** Converts unordered (`-` ,`*` ,`+` ) and ordered (`1.` ) lists into native`<ul>` and`<ol>` tags, avoiding conflicts between asterisk bullet markers and italics.\n- 🎨 **Rich Typography:** Supports bold (`**` ), italic (`*` /`_` ), strikethrough (`~~` ), underline (`++text++` ), highlight (`==text==` ), and spoilers (`||spoiler||` ) with snake_case protection.\n- 💻 **Syntax-Highlighted Code:** Converts markdown code fences into`<pre><code class=\"language-...\">` preserving language classes, indentation, and copy buttons.\n- ✂️ **Smart Message Splitter:** Safely splits long texts up to 32,768 (Telegram Rich limit) or 4,096 (Classic limit) characters. Automatically closes and re-opens nested tags with attributes (`<pre><code class=\"...\">` ,`<blockquote>` ), and protects LaTeX formulas from fragmentation.\n- 👁️ **Local HTML Preview:** Instantly generates a standalone`preview.html` styled with authentic Telegram Web dark theme and KaTeX client-side math rendering to visually inspect output without launching a bot.\n- 🚀 **Built-in CLI (`tg-rich`):** Fast command-line utility with TrueColor ANSI logo, preview generator, auto-opening in browser, pipeline support (`stdin` /`stdout` ), and batch message splitter.\n- ⚡ **Thread-Safe & Zero Dependencies:** Pure standard Python (`re` ,`html` ,`argparse` ). Fully reentrant and async-safe for high-concurrency bot environments.\n\n```\npip install tg-rich-converter\npython\nfrom tg_rich_converter import to_rich\n\nllm_output = \"\"\"\n# Quantum Computing Report\n\n| Algorithm | State | Complexity | Option |\n|:----------|:-----:|:----------:|-------:|\n| Linear Search | $N$ items | $O(N)$ | Mode A \\\\| B |\n| State Vector  | $|\\\\psi\\\\rangle$ | $O(1)$ | Basic |\n\n### Key Formula\n$$|\\\\psi\\\\rangle = \\\\alpha |0\\\\rangle + \\\\beta |1\\\\rangle$$\n\nStability requires delta < 0.05 and alpha > 0.\n\n<think>\nEvaluating time complexity and qubit entanglement...\n</think>\n\"\"\"\n\n# Default thinking summary is \"Размышления\"\nrich_html = to_rich(llm_output)\n\n# Or specify a custom summary:\nrich_html_en = to_rich(llm_output, thinking_summary=\"Reasoning Process\")\n```\n\nWhen streaming LLM responses token-by-token (`OpenAI`, `DeepSeek-R1`, `Anthropic Claude`, `Ollama`), intermediate tokens frequently contain unfinished Markdown/LaTeX structures:\n\n- Unclosed code fences: ``` python\\ndef run():`` (missing closing``` )\n- Open reasoning blocks: `<think>Analyzing steps...` (missing`</think>` )\n- Half-written formulas: `$E = mc^2` or`$$\\int_0^\\infty`\n- Incomplete typography: `**bold text` ,`||secret key` ,`++underlined`\n\nPassing `streaming=True` automatically balances and virtually closes all open structures in LIFO order for every frame:\n\n``` python\nimport time\nfrom tg_rich_converter import to_rich\n\n# 1. Send initial placeholder message via sendRichMessage\nsent_msg = await bot.send_rich_message(\n    chat_id=chat_id,\n    rich_message={\"html\": \"<i>⏳ Thinking...</i>\"}\n)\n\nbuffer = \"\"\nlast_edit_time = time.time()\nTHROTTLE_SECONDS = 0.7  # Recommended: 0.6 - 0.8s to avoid Telegram 429 Flood Limits\n\n# 2. Stream tokens from LLM\nasync for chunk in openai_client.chat.completions.create(..., stream=True):\n    buffer += chunk.choices[0].delta.content or \"\"\n    now = time.time()\n    \n    # Edit message with rate-limiting throttle\n    if now - last_edit_time >= THROTTLE_SECONDS:\n        safe_frame_html = to_rich(buffer, streaming=True)\n        await bot.edit_message_text(\n            chat_id=chat_id,\n            message_id=sent_msg.message_id,\n            rich_message={\"html\": safe_frame_html}  # Must pass rich_message, NOT text/parse_mode!\n        )\n        last_edit_time = now\n\n# 3. Final complete render (streaming=False)\nfinal_html = to_rich(buffer, streaming=False)\nawait bot.edit_message_text(\n    chat_id=chat_id,\n    message_id=sent_msg.message_id,\n    rich_message={\"html\": final_html}\n)\n```\n\n- **Cause:** Calling`sendMessage` or`editMessageText` with legacy`text=\"<details>...\"` and`parse_mode=\"HTML\"` . The legacy Telegram parser does not support`<details>` ,`<tg-math>` , or`<table>` .\n- **Fix:** Use the Telegram Bot API 10.1+ Rich Message format with`rich_message={\"html\": ...}` :\n\n```\n# ❌ WRONG (Triggers 400 Bad Request):\nawait bot.edit_message_text(chat_id=chat_id, message_id=msg_id, text=rich_html, parse_mode=\"HTML\")\n\n# ✔ CORRECT (Native Rich Messages):\nawait bot.edit_message_text(chat_id=chat_id, message_id=msg_id, rich_message={\"html\": rich_html})\n```\n\n- **Cause:** Sending`editMessageText` on every single incoming LLM token.\n- **Fix:** Throttle live edits to once every**0.6 – 0.8 seconds** (or every 40–60 characters).\n\n- **Fix:** Customize the default title via`thinking_summary` :\n\n```\nhtml_output = to_rich(markdown_text, thinking_summary=\"Chain of Thought\")\n```\n\nAfter installation, the `tg-rich` command is available in your terminal:\n\nConvert a markdown file and open the interactive Telegram preview directly in your default browser:\n\n```\ntg-rich prompt_response.md --preview --open\ntg-rich document.md -o output.html\ncat llm_output.md | tg-rich > telegram_message.html\n```\n\nSplit a large document into chunks respecting Telegram character limits:\n\n```\n# Split for Rich Messages (32k limit)\ntg-rich big_report.md --split\n\n# Split for Classic Messages (4k limit) into separate files\ntg-rich big_report.md --split --limit 4096 -o chunk.html\n```\n\n| Flag | Description | Default | \n|---|---|---|\n| `input_file` | Path to markdown file (or `-` / pipe for stdin) | `-` | \n| `-o, --output FILE` | Write converted HTML to file instead of stdout | `stdout` | \n| `-p, --preview [FILE]` | Generate standalone preview.html with KaTeX & Telegram Dark theme | `preview.html` | \n| `--open` | Automatically open the preview in default web browser | `False` | \n| `-s, --split` | Split long message into safe Telegram chunks | `False` | \n| `-l, --limit INT` | Maximum character length for splitting | `32768` | \n| `-t, --thinking-summary TEXT` | Custom header for `<think>` reasoning blocks | `Размышления` | \n| `--lang {ru,en}` | Interface and error message language | `auto` | \n| `-q, --quiet` | Suppress banner and progress messages in stderr | `False` | \n| `-v, --version` | Display current library version |  | \n\nWhen LLM output exceeds Telegram limits (32,768 chars for Rich Messages or 4,096 chars for standard messages), naive slicing breaks open HTML tags and crashes the bot. Use `split_rich_message`:\n\n``` python\nfrom tg_rich_converter import split_rich_message\n\n# Automatically converts Markdown to Rich HTML and splits into safe chunks\nchunks = split_rich_message(\n    long_llm_response,\n    max_length=32768,      # 32,768 for Rich Messages (default) or 4,096 for Classic\n    is_markdown=True,      # Automatically runs to_rich()\n    thinking_summary=\"Reasoning\"\n)\n\n# Each chunk is guaranteed to be valid HTML with all open tags closed and reopened\nfor chunk in chunks:\n    await bot.send_rich_message(chat_id=chat_id, rich_message={\"html\": chunk})\n```\n\nVisualize how your message will look in Telegram Desktop/Mobile without running a bot or sending messages:\n\n``` python\nfrom tg_rich_converter import save_preview\n\n# Generates preview.html with Telegram dark theme, KaTeX formulas, and interactive spoilers\nsave_preview(\n    llm_output,\n    file_path=\"preview.html\",\n    title=\"LLM Telegram Preview\"\n)\n```\n\nDouble click `preview.html` to open it in your browser!\n\n``` python\nfrom aiogram import Bot\nfrom tg_rich_converter import to_rich\n\nbot = Bot(token=\"YOUR_BOT_TOKEN\")\n\nrich_html = to_rich(llm_response)\nawait bot.send_rich_message(\n    chat_id=chat_id,\n    rich_message={\"html\": rich_html}\n)\npython\nimport telebot\nfrom tg_rich_converter import to_rich\n\nbot = telebot.TeleBot(\"YOUR_BOT_TOKEN\")\n\nrich_html = to_rich(llm_response)\nbot.send_rich_message(\n    chat_id=chat_id,\n    rich_message={\"html\": rich_html}\n)\npython\nimport requests\nfrom tg_rich_converter import to_rich\n\nrich_html = to_rich(llm_response)\n\nrequests.post(\n    f\"https://api.telegram.org/bot{BOT_TOKEN}/sendRichMessage\",\n    json={\n        \"chat_id\": chat_id,\n        \"rich_message\": {\n            \"html\": rich_html\n        }\n    }\n)\n```\n\nRun the comprehensive test suite locally (44 tests, 100% pass):\n\n```\npytest\n```\n\nTest real-time LLM token-by-token streaming with rate-limiting throttle (0.7s) directly in your Telegram chat:\n\n```\n# Direct CLI arguments\npython demo_streaming.py --token \"YOUR_BOT_TOKEN\" --chat-id \"YOUR_CHAT_ID\"\n\n# Or via environment variables\nexport BOT_TOKEN=\"YOUR_BOT_TOKEN\"\nexport CHAT_ID=\"YOUR_CHAT_ID\"\npython demo_streaming.py\n\n# Windows PowerShell\n$env:BOT_TOKEN=\"YOUR_BOT_TOKEN\"; $env:CHAT_ID=\"YOUR_CHAT_ID\"; python demo_streaming.py\n```\n\nValidate intermediate streaming frames in terminal without sending requests to Telegram:\n\n```\npython demo_streaming.py\n```\n\nMIT License. Free for commercial and personal use.", "url": "https://wpnews.pro/news/tg-rich-converter-streaming-llm-markdown-and-latex-to-telegram-bot-api-10-1", "canonical_source": "https://github.com/kobaltgit/tg-rich-converter", "published_at": "2026-09-20 19:40:30+00:00", "updated_at": "2026-09-20 19:52:54.267350+00:00", "lang": "en", "topics": ["ai-tools", "large-language-models", "developer-tools", "ai-agents"], "entities": ["Telegram", "Telegram Bot API 10.1", "tg-rich-converter", "DeepSeek-R1", "OpenAI o1/o3", "Qwen", "Gemini", "Python"], "alternates": {"html": "https://wpnews.pro/news/tg-rich-converter-streaming-llm-markdown-and-latex-to-telegram-bot-api-10-1", "markdown": "https://wpnews.pro/news/tg-rich-converter-streaming-llm-markdown-and-latex-to-telegram-bot-api-10-1.md", "text": "https://wpnews.pro/news/tg-rich-converter-streaming-llm-markdown-and-latex-to-telegram-bot-api-10-1.txt", "jsonld": "https://wpnews.pro/news/tg-rich-converter-streaming-llm-markdown-and-latex-to-telegram-bot-api-10-1.jsonld"}}