Skip to content

fix(telegram): preserve pipe tables on rich-message and standalone send paths - #45327

Open
liuhao1024 wants to merge 1 commit into
NousResearch:mainfrom
liuhao1024:fix/telegram-rich-table-preservation
Open

fix(telegram): preserve pipe tables on rich-message and standalone send paths#45327
liuhao1024 wants to merge 1 commit into
NousResearch:mainfrom
liuhao1024:fix/telegram-rich-table-preservation

Conversation

@liuhao1024

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds a rewrite_tables keyword parameter to TelegramAdapter.format_message() to control whether GFM pipe tables are rewritten into bullet groups. When rich messages are enabled (platforms.telegram.extra.rich_messages: true), tables are preserved for native Telegram rendering instead of being flattened.

Related Issue

Fixes #45323

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)

Changes Made

  • gateway/platforms/telegram.py: Added rewrite_tables: bool = True parameter to format_message(). When False, skips _wrap_markdown_tables() so pipe table syntax survives MarkdownV2 escaping for Telegram's native table renderer.
  • tools/send_message_tool.py: Standalone _send_telegram now reads platforms.telegram.extra.rich_messages from config and passes rewrite_tables=False when rich messages are enabled.
  • tests/gateway/test_telegram_format.py: 5 new tests covering rewrite_tables=True (default), rewrite_tables=False (rich path), backward compatibility, no-bullet-groups, and code-block isolation.

How to Test

  1. Enable rich messages: hermes config set platforms.telegram.extra.rich_messages true
  2. Send a message containing a Markdown table via Telegram gateway
  3. Verify the table renders as a native Telegram table (not bullet groups)
  4. Disable rich messages and verify tables are still rewritten to bullets (legacy behavior)
  5. Run pytest tests/gateway/test_telegram_format.py -xvs — all 106 tests should pass

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: macOS

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

Code Intelligence

  • Analyzed: gateway/platforms/telegram.py::format_message (callers: 20+ across telegram.py and send_message_tool.py)
  • Blast radius: LOW — new keyword parameter with backward-compatible default
  • Related patterns: _wrap_markdown_tables() (table rewriting), _should_attempt_rich() (rich message routing), _rich_message_payload() (raw markdown path)

…nd paths

When rich messages are enabled, Telegram renders pipe tables natively via
sendRichMessage — the shared format_message() rewriter was destroying the
pipe syntax before the rich path could see it. The standalone send_message
tool path had the same issue: it always called format_message() with table
rewriting enabled, flattening tables into bullet groups.

Add a rewrite_tables keyword parameter to format_message() (default True
for backward compatibility). The rich-message path and the standalone
send_message path now pass rewrite_tables=False when rich messages are
enabled in config.

Fixes NousResearch#45323
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/gateway Gateway runner, session dispatch, delivery platform/telegram Telegram bot adapter comp/tools Tool registry, model_tools, toolsets labels Jun 13, 2026

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

Verdict: Approved

Clean, well-scoped fix/feature with comprehensive tests. No issues found.

  • Logic is correct and focused
  • Tests cover the new behavior
  • No security concerns
  • Good error handling

Reviewed by Hermes Agent

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the focused report and tests. The rich gateway defect described by #45323 is already addressed on current main: TelegramAdapter.send() sends raw content through sendRichMessage before the legacy formatter (plugins/platforms/telegram/adapter.py:3587-3610), and _rich_message_payload() explicitly forbids format_message() because MarkdownV2 escapes table pipes (plugins/platforms/telegram/adapter.py:1506-1521).

Problems

  • The standalone change does not produce native tables. It still sends with ParseMode.MARKDOWN_V2 after formatting (tools/send_message_tool.py:1136-1144); disabling only the bullet rewrite leaves MarkdownV2, which the current adapter documents as unable to preserve rich table syntax.
  • The gateway target moved from gateway/platforms/telegram.py to plugins/platforms/telegram/adapter.py in 5600105478ffde29d7566b45421b100eaa29c4ef, so the gateway portion needs re-scoping rather than a direct port.

Suggested changes

  • Retire the superseded gateway formatter change.
  • If standalone rich delivery is intended, implement and test an explicit sendRichMessage path with capability-safe fallback rather than passing raw tables to MarkdownV2.

Automated hermes-sweeper review.

pass
try:
from gateway.platforms.telegram import TelegramAdapter
_adapter = TelegramAdapter.__new__(TelegramAdapter)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still sends the result through ParseMode.MARKDOWN_V2 immediately below, so rewrite_tables=False cannot yield a native Telegram table: the formatter escapes table pipes and this path never invokes sendRichMessage. Please use an explicit rich endpoint with fallback if native standalone table rendering is the intended behavior.

@teknium1 teknium1 added sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery comp/tools Tool registry, model_tools, toolsets P2 Medium — degraded but workaround exists platform/telegram Telegram bot adapter sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Telegram rich tables are rewritten into bullets by the shared formatter

4 participants