Skip to content

feat(gateway): render markdown tables as native CardKit v2 table comp… - #48334

Open
protectione055 wants to merge 1 commit into
NousResearch:mainfrom
protectione055:feat/feishu-cardkit-tables
Open

feat(gateway): render markdown tables as native CardKit v2 table comp…#48334
protectione055 wants to merge 1 commit into
NousResearch:mainfrom
protectione055:feat/feishu-cardkit-tables

Conversation

@protectione055

Copy link
Copy Markdown

What changed and why

Feishu's md tag does not render markdown tables, causing messages with tables to render blank on the client. This PR adds CardKit v2 table rendering as the primary rendering path, with a multi-tier fallback.

Before: Any message containing markdown tables was force-sent as plain text — loss of table structure, no alignment, no formatting.

After: A 3-tier dispatch in _build_outbound_payload:

  1. CardKit v2 interactive card with native table component — tables render properly with column headers and aligned rows
  2. Post with code_block fallback — monospace alignment for mixed prose+table content
  3. Standard post with md — unchanged for non-table content (zero regression)

How to test

  1. Run hermes gateway run locally with Feishu configured
  2. Send a message containing markdown tables (e.g., from a cron job or subagent output)
  3. Verify tables render as native Feishu CardKit v2 table components, not as code blocks or plain text
  4. Test edge cases: pure tables, mixed prose+tables, code blocks (should not be affected), multiple tables, no-table content

Platforms tested

  • Linux (Ubuntu 22.04) with local Hermes gateway + Feishu messaging

Attribution

This implementation is based on the approach pioneered by chapaofan/Hermes-feishu-to-table.

…onents in Feishu

Feishu's `md` tag does not render markdown tables, causing messages
with tables to render blank on the client. This commit adds CardKit v2
table rendering as the primary rendering path, with a multi-tier fallback
to code_block post rows for alignment preservation.

This implementation is based on the approach pioneered by
chapaofan/Hermes-feishu-to-table (https://github.com/chapaofan/Hermes-feishu-to-table)
which solved the same problem with CardKit v2 table components.

Co-authored-by: chapaofan <chapaofan@users.noreply.github.com>
@daimon-nous daimon-nous Bot added type/feature New feature or request comp/gateway Gateway runner, session dispatch, delivery platform/feishu Feishu / Lark adapter P3 Low — cosmetic, nice to have duplicate This issue or pull request already exists labels Jun 18, 2026
@daimon-nous

daimon-nous Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Duplicate of #12114 — same feature, same mechanism: render markdown tables as native Feishu CardKit v2 table components in gateway/platforms/feishu.py. #12114 is the open canonical for this saturated cluster (#17006 already marked duplicate of it). This PR adds a code_block/md fallback tier, which is a useful refinement worth contributing onto #12114 rather than as a separate competing PR. Related: #17006 (earlier CardKit v2 attempt), #27469.

@protectione055

Copy link
Copy Markdown
Author

Duplicate of #12114 — same feature, same mechanism: render markdown tables as native Feishu CardKit v2 table components in gateway/platforms/feishu.py. #12114 is the open canonical for this saturated cluster (#17006 already marked duplicate of it). This PR adds a code_block/md fallback tier, which is a useful refinement worth contributing onto #12114 rather than as a separate competing PR. Related: #17006 (earlier CardKit v2 attempt), #27469.

Thanks for the review! A few points on why I believe this PR has value beyond #12114:

Key differences from #12114

  1. 3-tier dispatch, not just CardKitfix(feishu): render markdown tables via card v2 table component #12114 routes all table-containing content to an interactive card with no fallback. If the card JSON fails for any reason (e.g. schema validation, oversized payload, card rendering error), the message is lost entirely. Our PR adds a code_block fallback so the table data is still readable in monospace even if the card path fails.

  2. More robust table detection — Our _parse_markdown_table() is fence-aware (correctly ignores table-looking lines inside code fences) and handles edge cases like multiple tables in one message, empty cells, and mixed prose+table content.

  3. Zero-regression path — Non-table content routes through the exact same post path as before, unchanged. The card path is purely additive.

  4. fix(feishu): render markdown tables via card v2 table component #12114 has been open for 2 months with no reviewer activity, no CI checks, and no merge progress. Rather than adding onto a stalled branch, a complete independent PR lets reviewers compare both approaches side by side.

Happy to incorporate feedback or rebase if maintainers prefer a different direction.

@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 native-table approach. Current main still sends detected Markdown tables as plain text at plugins/platforms/feishu/adapter.py:4528-4530, so the feature premise remains valid.

Problems

  • The claimed delivery fallback is unreachable for a rejected interactive card: PR head gateway/platforms/feishu.py:4573-4574 returns interactive, but the unchanged fallback conditions in the same head only accept msg_type == "post" (:2075, :2086, :2124).
  • _parse_markdown_table() has no fenced-code state and starts a table for any pipe row (gateway/platforms/feishu.py:225-230), so pipe rows inside a fenced code block are converted despite the stated fence-aware behavior.
  • The PR adds no tests for table payloads, fenced code, or interactive-card rejection.
  • The adapter moved to plugins/platforms/feishu/adapter.py in 560010547; this branch modifies the old path and is currently dirty.

Suggested changes

  • Port the focused behavior to the bundled plugin, add interactive rejection fallback in both send and edit paths, and cover the parser and fallback matrix in tests/gateway/test_feishu.py.

Automated hermes-sweeper review.

table_lines = []

for line in lines:
if _MARKDOWN_TABLE_LINE_RE.match(line):

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 starts a native table for any pipe-delimited row, including rows inside a fenced code block because this parser has no fence state. Require a GFM header-plus-divider sequence and track fences before converting segments.

# Force plain text for anything that looks like a markdown table.
# 1. Try CardKit v2 interactive card with native table rendering first
card = _build_interactive_card_with_tables(content)
if card is not None:

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 return makes the later post/code-block branch unreachable for every parsed table. The send/edit fallback code still only handles msg_type == "post", so an API-rejected interactive card has no delivery fallback; add an interactive rejection path in both send and edit flows.

@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 duplicate This issue or pull request already exists P3 Low — cosmetic, nice to have platform/feishu Feishu / Lark 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/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants