feat(feishu): render Markdown tables as native card table component - #38453
feat(feishu): render Markdown tables as native card table component#38453TonyHe-Lab wants to merge 2 commits into
Conversation
…onent - Parse Markdown pipe tables into Feishu JSON 2.0 'table' component via new _parse_markdown_table() and _split_content_and_tables() methods - Rewrite _build_outbound_payload() to always return 'interactive' card with schema 2.0, mixing markdown elements and table components - Expand _POST_CONTENT_INVALID_RE to match additional Feishu card error messages (failed to create card content, card contains images) - Update fallback logic in send_message, edit_message, and _feishu_send_with_retry to handle 'interactive' msg_type alongside 'post' - Update 7 tests to assert 'interactive' card structure instead of 'post' - All 205 feishu adapter tests pass
Previously table cells used data_type='text' with manual markdown stripping. Feishu table component supports data_type='markdown' (v7.14+) which renders bold, italic, links, images etc natively.
2c842f6 to
d2cd44f
Compare
teknium1
left a comment
There was a problem hiding this comment.
Thanks for addressing a real Feishu rendering gap. Current main still falls back to plain text for detected tables at plugins/platforms/feishu/adapter.py:4528-4530, but this implementation needs rework before it can be salvaged.
Problems
gateway/platforms/feishu.py:4396accepts any two pipe-prefixed lines as a table, then_parse_markdown_table()unconditionally drops line two as a separator at:4344. Validate the GFM separator row.gateway/platforms/feishu.py:4410says a non-table pipe line is normal text, but advances past it without emitting it;| literal |is lost.gateway/platforms/feishu.py:4446changes every outbound message tointeractive, replacing main's established text/post routing (plugins/platforms/feishu/adapter.py:4528-4534) without coverage for that compatibility change.- The target adapter moved in
5600105478ffde29d7566b45421b100eaa29c4ef; current work belongs inplugins/platforms/feishu/adapter.py.
Suggested changes
- Port the table-only behavior to the plugin adapter, validate separators, preserve non-table pipe lines, and add regression tests for both parser cases and existing non-table routing.
Automated hermes-sweeper review.
| # Detect potential table start: line begins with | | ||
| if "|" in line and line.strip().startswith("|"): | ||
| # Confirm it is a real table by checking the next line (separator) | ||
| if i + 1 < n and "|" in lines[i + 1] and lines[i + 1].strip().startswith("|"): |
There was a problem hiding this comment.
This only verifies that the next line starts with |; it does not verify a GFM separator row. _parse_markdown_table() then skips that line unconditionally at line 4344, so two ordinary pipe-prefixed lines lose content. Validate separator cells (dashes with optional alignment colons) before entering the table path.
| elements.append({"tag": "markdown", "content": "\n".join(table_lines)}) | ||
| else: | ||
| # Single | line without separator — treat as normal text | ||
| i += 1 |
There was a problem hiding this comment.
This branch drops the current line: it advances i and emits no markdown element. A standalone | literal | line is therefore silently lost despite the comment saying it should be normal text. Preserve it in the surrounding markdown buffer and add a regression test.
| "config": {"wide_screen_mode": True}, | ||
| "body": {"elements": elements}, | ||
| } | ||
| return "interactive", json.dumps(card, ensure_ascii=False) |
There was a problem hiding this comment.
This routes all text and Markdown through interactive cards, not only confirmed tables. Current main still deliberately selects text/post for non-table content in plugins/platforms/feishu/adapter.py:4528-4534; retain that routing unless the wider behavior change is explicitly covered and validated.
Summary
Markdown tables in Feishu messages currently display as raw pipe-separated text because Feishu's
postmessage type does not support table rendering.This PR switches outbound messages to use Feishu interactive cards (JSON schema 2.0) with the native
tablecomponent, while keeping non-table content asmarkdownelements within the same card.Changes
gateway/platforms/feishu.py_parse_markdown_table(): parses| col | col |lines into Feishu table component JSON (columns,rows,header_style)_split_content_and_tables(): line-by-line scanner that groups consecutive table lines and converts them, collecting non-table lines into markdown elements_build_outbound_payload(): always returnsinteractivemsg_type with schema 2.0 card structure, mixing markdown + table elements_POST_CONTENT_INVALID_REregex to match additional Feishu card error messagessend_message,edit_message,_feishu_send_with_retryto handleinteractivealongsideposttests/gateway/test_feishu.pyinteractivecard structure withschema: "2.0"andbody.elementsinstead ofposttypeTest Results
All 205 Feishu adapter tests pass.
Before / After
Before: Table messages show as raw
| | |plain textAfter: Tables render natively as Feishu card table component with headers and cell borders