Skip to content

feat(feishu): render markdown tables as JSON 2.0 cards - #26429

Open
hikari0511 wants to merge 1 commit into
NousResearch:mainfrom
hikari0511:feishu-card-table-rendering
Open

feat(feishu): render markdown tables as JSON 2.0 cards#26429
hikari0511 wants to merge 1 commit into
NousResearch:mainfrom
hikari0511:feishu-card-table-rendering

Conversation

@hikari0511

Copy link
Copy Markdown

Problem

Feishu messages with markdown tables render as ugly plain text with pipe characters (|) because the adapter sends them as msg_type: "text" which does not support table syntax.

Solution

When the content contains a markdown table, send it as an interactive card with schema: "2.0" instead. Feishu JSON 2.0 cards natively render markdown tables with:

  • Proper borders and cell alignment
  • Header row styling
  • Pagination for large tables (max 5 data rows per page)
  • Up to 4 tables per markdown component

Change

In gateway/platforms/feishu.py, _build_outbound_payload():

Before: table content → msg_type: "text" (ugly plain text)
After: table content → msg_type: "interactive" with card JSON 2.0 (native table rendering)

if _MARKDOWN_TABLE_RE.search(content):
    card = {
        "schema": "2.0",
        "body": {
            "elements": [
                {"tag": "markdown", "content": content}
            ]
        },
    }
    return "interactive", json.dumps(card, ensure_ascii=False)

Testing

  • Verified on Feishu desktop client: tables now render with proper borders and alignment
  • Non-table markdown messages continue to use existing post / text paths (no behavior change)
  • The _MARKDOWN_TABLE_RE regex detection is unchanged — only the rendering path is affected

References

Feishu's post-type 'md' elements do not support markdown tables,
causing table content to render as ugly plain text with pipe characters.

This change detects markdown table content and sends it as an
interactive card with schema 2.0, which natively renders tables
with proper borders, alignment, and pagination (max 5 data rows).

The card format is:
{
  schema: '2.0',
  body: { elements: [{ tag: 'markdown', content: ... }] }
}
@daimon-nous daimon-nous Bot added type/feature New feature or request P2 Medium — degraded but workaround exists platform/feishu Feishu / Lark adapter comp/gateway Gateway runner, session dispatch, delivery labels May 15, 2026
@daimon-nous

daimon-nous Bot commented May 15, 2026

Copy link
Copy Markdown
Contributor

This is another entry in the saturated Feishu markdown table rendering cluster. Competing open PRs for the same fix: #24649 (Card JSON 2.0), #21000 (interactive card messages), #12114, #16194, #25453. Root issues: #9549, #25452, #21778.

@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 Feishu table-rendering fix. The underlying behavior is still present, but this patch no longer reaches the active adapter.

Problems

  • gateway/platforms/feishu.py was relocated by 552adbe0827c32df8ed9bb19e908c26eff43add7; current table handling remains in plugins/platforms/feishu/adapter.py:4524-4534, where it returns "text" for _MARKDOWN_TABLE_RE.
  • The PR diff changes only the old source file and adds no regression test. _build_outbound_payload feeds both normal sends (plugins/platforms/feishu/adapter.py:1904) and edits (:1958).

Suggested changes

  • Port this branch to plugins/platforms/feishu/adapter.py:_build_outbound_payload and add a plugin-path payload test asserting the interactive Card JSON 2.0 output for a detected table while retaining non-table paths.
  • The adapter already sends interactive cards through its normal send transport for approvals (plugins/platforms/feishu/adapter.py:2029-2033), so that is the established integration path to cover.

Automated hermes-sweeper review.

return "text", json.dumps(text_payload, ensure_ascii=False)
# JSON 2.0 card renders markdown tables natively with borders and alignment.
card = {
"schema": "2.0",

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.

Current main relocated the active Feishu adapter to plugins/platforms/feishu/adapter.py in 552adbe0827c32df8ed9bb19e908c26eff43add7; its live table branch is now at :4524-4530. Port this change there and add a payload-selection regression test, since this old path no longer affects runtime 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 13, 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 P2 Medium — degraded but workaround exists 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