feat(feishu): send markdown tables as interactive cards - #20028
feat(feishu): send markdown tables as interactive cards#20028yiyouguisu wants to merge 2 commits into
Conversation
…to lists Feishu interactive card format renders Markdown tables correctly, unlike the post/md pipeline. Add automatic card dispatch: - _table_block_present(): detect real table blocks outside code blocks - _build_markdown_card_payload(): wrap content in a Feishu card - _build_outbound_payload now checks for tables first, sends card - Fallback: if card fails, fall back to post (which sanitizes tables to lists) so content is never lost - Both send() and edit_message() have the same fallback chain: interactive -> post -> text This works alongside the existing _sanitize_markdown_tables() which serves as the safety net for the post pipeline.
cae0d9d to
4174a92
Compare
|
Related to #18756. Competes with multiple PRs. |
The PR branch referenced _sanitize_markdown_tables() but never defined it — any post fallback path would crash with NameError. Implementation: scan lines outside code fences, detect contiguous table rows (^|...+|$), drop separator rows (|---|---|), and convert data rows to '- |...|' bullet-list format so information remains visible in Feishu's post/md channel. This is the fallback for when the interactive card path fails.
|
Confirmed. We hit this exact issue — Markdown tables sent via Hermes to Feishu render as garbled plain text. Our local patch using Local verification:
This PR solves the problem. Adding |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for preserving table content and documenting the card fallback approach. Current main now avoids the original blank-table symptom with text routing in plugins/platforms/feishu/adapter.py:4524-4534, so this should be treated as a structured-rendering enhancement rather than a direct restoration of the old path.
Problems
- The advertised
interactive → post → textchain is incomplete. After the interactive exception fallback atgateway/platforms/feishu.py:1828-1836, the response fallback at:1839-1851still checks the originalmsg_type == "post"; it remains"interactive". A rejected post fallback therefore does not degrade to text.edit_message()likewise stops afterinteractive → postat:1900-1908. - The PR changes the legacy adapter path, while current main migrated Feishu to
plugins/platforms/feishu/adapter.py(560010547; active routing at:4524-4534). The implementation and coverage need to target that active surface.
Suggested changes
- Use a shared fallback helper/state transition so both send and edit retry post failure as text, including exception and unsuccessful-response cases.
- Port the change to the plugin adapter and add focused gateway tests for the routing and both fallback stages.
Automated hermes-sweeper review.
| ) | ||
| elif msg_type == "interactive": | ||
| logger.warning("[Feishu] Interactive card rejected by API; falling back to post") | ||
| response = await self._feishu_send_with_retry( |
There was a problem hiding this comment.
This post fallback is not followed by the post-to-text fallback below: msg_type remains "interactive", so the guard at lines 1839-1851 is skipped if this post response is rejected. Track the effective fallback type or route retries through a helper that can continue to text.
| fallback_request = self._build_update_message_request(message_id=message_id, request_body=fallback_body) | ||
| fallback_response = await asyncio.to_thread(self._client.im.v1.message.update, fallback_request) | ||
| result = self._finalize_send_result(fallback_response, "update failed") | ||
| elif msg_type == "interactive": |
There was a problem hiding this comment.
The edit path also stops after interactive → post; if that post update fails, it returns the failed result rather than the claimed final text fallback. Apply the same complete fallback state handling here.
Problem
Feishu's
mdrenderer hides content inside Markdown tables — rows after the header/separator become invisible. Users reported that "标题下内容看不到" (content under headers is not visible).Hermes only used the
post/mdtag for rich text, which does not support tables. Meanwhile theinteractivecard format renders tables correctly.Solution
Add automatic card dispatch for content containing Markdown tables:
New functions:
_table_block_present()— detect real table blocks (≥2 contiguous|…|lines outside code blocks)_build_markdown_card_payload()— wrap content in a Feishu interactive card ({"tag": "markdown", "content": …})_TABLE_ROW_RE,_TABLE_SEP_REfor table detectionMessage routing (
_build_outbound_payload):"interactive"(card) — tables render correctly"post"/"text"(existing logic)Fallback chain (both
send()andedit_message()):This ensures content is never lost: best case is a rendered card, worst case is a bullet list.
Related
sanitize_markdown_tablealready existed in the Yuanbao adapter but was never ported to Feishu_sanitize_markdown_tables()(the post-pipeline safety net)Testing
|code|) → not detected (requires ≥2 rows)