Skip to content

fix(feishu): route markdown tables through post+tag:md, fix streaming edit fallback - #53453

Closed
Zjrua wants to merge 1 commit into
NousResearch:mainfrom
Zjrua:fix/feishu-table-post-routing-v2
Closed

fix(feishu): route markdown tables through post+tag:md, fix streaming edit fallback#53453
Zjrua wants to merge 1 commit into
NousResearch:mainfrom
Zjrua:fix/feishu-table-post-routing-v2

Conversation

@Zjrua

@Zjrua Zjrua commented Jun 27, 2026

Copy link
Copy Markdown

Bug

Markdown tables in Feishu messages render as raw pipe-delimited source code instead of being rendered as proper tables. When a table appears in a message, the entire message is downgraded to msg_type: text, stripping all markdown formatting (bold, headings, code blocks, lists) from the same reply.

Related issues: #52786, #52046, #23938, #18704, #9549, #21866, #25452

Root cause

Commit 8e18d1031 (Apr 2026) added _MARKDOWN_TABLE_RE to force msg_type: text for any content containing a markdown table. This was correct at the time — Feishu's post md element did not render tables, and sending table content as post caused blank messages.

However, Feishu's post + tag: md element now renders GFM tables natively. The workaround is obsolete and actively harmful: it downgrades the entire message to plain text, displaying raw | pipe-delimited source code and stripping all other formatting.

Fix

1. Route tables through post (core fix)

Merge the table-detection pattern into _MARKDOWN_HINT_RE so tables are treated like any other markdown content — routed to post + tag: md. Remove the now-unused _MARKDOWN_TABLE_RE and its force-text branch in _build_outbound_payload().

2. Streaming edit fallback (edge case no other PR handles)

During streaming, the first chunk may not yet contain a table (partial content), so it's sent as text. When the full content arrives and edit_message() detects the table, _build_outbound_payload returns post. But Feishu does not allow changing msg_type on message update, so the edit fails and the message stays as raw markdown source.

Added a text -> post fallback in edit_message(): when a text update fails, retry as post with markdown rendering.

Prior work

…ce-text

Feishu's post + tag:md element now renders GFM tables natively. The
historic _MARKDOWN_TABLE_RE workaround (commit 8e18d10) force-downgraded
the entire message to msg_type: text whenever a table appeared, which
stripped formatting from every other markdown element in the same reply
and displayed raw pipe-delimited source code.

Changes:
- Merge table pattern into _MARKDOWN_HINT_RE so tables route to post like
  all other markdown content. Remove the now-unused _MARKDOWN_TABLE_RE.
- Remove the force-text branch in _build_outbound_payload().
- Add text→post fallback in edit_message() for the streaming edge case:
  when the first chunk is sent as text (no table visible yet) and the
  final content now routes to text, Feishu rejects the msg_type change
  on update. Retry as post so the message stays readable.

Closes NousResearch#52786, NousResearch#52046, NousResearch#23938, NousResearch#9549, NousResearch#18704

Co-authored-by: WuTianyi <wtyopenclaw@gmail.com>

@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

Feishu markdown table and heading level fixes are well-scoped and targeted. The changes route tables through the post+tag:md path and normalize heading levels, which are common pain points for Feishu message formatting.


Reviewed by Hermes Agent

@alt-glitch alt-glitch added type/bug Something isn't working comp/plugins Plugin system and bundled plugins platform/feishu Feishu / Lark adapter P3 Low — cosmetic, nice to have labels Jun 27, 2026

@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 isolating the force-text table workaround; the core routing change is supported by current main, where plugins/platforms/feishu/adapter.py:4524-4534 still returns text before markdown detection for _MARKDOWN_TABLE_RE.

Problems

  • plugins/platforms/feishu/adapter.py:1857 in d443e16 only retries when the attempted update is text. The reported stream transition is a previously text message whose final table content is classified as post; that post update bypasses this new branch. Retrying post after the inverse post-to-text failure does not cover the stated case.
  • The new fallback test in tests/gateway/test_feishu.py in d443e16 uses plain final content and validates text → post retry, not a final table/post update after an earlier text stream message.

Suggested changes

  • Preserve the table-routing change, but rework the stream type-transition behavior around the actual text-origin → post-final case and add a regression test for it.

Automated hermes-sweeper review.

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")
if not result.success and msg_type == "text":

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 guard covers a post-origin message becoming plain text, but the PR description’s failure is the reverse: a text streaming message whose final table is classified as post. That attempted update has msg_type == "post", so this fallback is skipped; please handle or re-scope that actual transition.

@teknium1 teknium1 added sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 15, 2026
@teknium1 teknium1 added the area/streaming Streaming responses: gateway delivery, provider wire label Jul 19, 2026
@Zjrua

Zjrua commented Jul 26, 2026

Copy link
Copy Markdown
Author

Closing this PR — the core table-routing change (removing the _MARKDOWN_TABLE_RE force-text downgrade, routing tables through post+tag:md) has already landed on main independently (issue #52786). The _MARKDOWN_HINT_RE now includes the pipe-table pattern natively, and a prefer_post parameter was added to keep chunk consistency on long messages.

The streaming edit fallback (text→post on update rejection) is a separate concern; I'll revisit it if it becomes reproducible on current main.

Thanks for the review feedback!

@Zjrua Zjrua closed this Jul 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/streaming Streaming responses: gateway delivery, provider wire comp/plugins Plugin system and bundled plugins 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-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.

4 participants