fix(feishu): send every chunk of a long markdown reply as msg_type=post (#26841) - #26848
Closed
xxxigm wants to merge 2 commits into
Closed
fix(feishu): send every chunk of a long markdown reply as msg_type=post (#26841)#26848xxxigm wants to merge 2 commits into
xxxigm wants to merge 2 commits into
Conversation
…ng markdown reply (NousResearch#26841) ``FeishuAdapter._build_outbound_payload`` picks ``msg_type=post`` only when the *current* chunk matches ``_MARKDOWN_HINT_RE``. When ``send`` splits a long markdown response at ``MAX_MESSAGE_LENGTH``, the first chunk can easily end up as plain prose (the headings / bold / code fences live in later chunks). The first chunk then goes out as ``msg_type=text`` and the user sees literal ``**bold**`` / ``## heading`` markers, while later chunks render correctly — a confusing per-message split UX on Feishu. Lock the decision at the whole-message level: ``send`` computes ``prefer_post = bool(_MARKDOWN_HINT_RE.search(formatted))`` once before chunking and passes it into ``_build_outbound_payload(chunk, prefer_post=...)``. Per-chunk table override still wins so chunks that carry a markdown table stay ``text`` (post-type ``md`` can't render tables and would otherwise blank the message). Single-chunk and non-markdown sends are unaffected.
…ption (NousResearch#26841) Two regression tests on ``FeishuAdapter.send``: - ``test_send_uses_post_for_every_chunk_of_multi_chunk_markdown`` — ``truncate_message`` is patched to return a plain-prose first chunk followed by a markdown second chunk; both API calls must use ``msg_type=post`` (was ``["text", "post"]`` before the fix). - ``test_send_keeps_table_chunks_as_text_even_when_message_is_markdown`` — the new whole-message override must not clobber the per-chunk table guard, so a chunk carrying a markdown table still goes as ``text`` even when ``prefer_post`` is on.
This was referenced May 20, 2026
Closed
Contributor
|
Thanks for isolating the per-chunk classification failure and preserving the table fallback. Problems
Suggested changes
Automated hermes-sweeper review. |
teknium1
pushed a commit
that referenced
this pull request
Jul 20, 2026
…ng markdown reply (#26841) Transplant of PR #26848 onto the plugin adapter path (plugins/platforms/feishu/adapter.py — the original PR targeted the since-removed gateway/platforms/feishu.py). ``send`` classifies each chunk independently, so chunk 1 of a long markdown reply (often plain prose) went out as msg_type=text while later chunks rendered as post — literal **bold**/## heading markers in the Feishu client. Lock the decision at the whole-message level: compute prefer_post once from the full formatted message and pass it to _build_outbound_payload per chunk. The original PR's per-chunk table exemption is intentionally dropped: tables now route through post/md (issue #52786 cluster fix), so the exemption would reintroduce the raw-table downgrade. Co-authored-by: Hermes Agent <hermes@nousresearch.com>
teknium1
pushed a commit
that referenced
this pull request
Jul 20, 2026
…ng markdown reply (#26841) Transplant of PR #26848 onto the plugin adapter path (plugins/platforms/feishu/adapter.py — the original PR targeted the since-removed gateway/platforms/feishu.py). ``send`` classifies each chunk independently, so chunk 1 of a long markdown reply (often plain prose) went out as msg_type=text while later chunks rendered as post — literal **bold**/## heading markers in the Feishu client. Lock the decision at the whole-message level: compute prefer_post once from the full formatted message and pass it to _build_outbound_payload per chunk. The original PR's per-chunk table exemption is intentionally dropped: tables now route through post/md (issue #52786 cluster fix), so the exemption would reintroduce the raw-table downgrade. Co-authored-by: Hermes Agent <hermes@nousresearch.com>
Contributor
|
Merged via #68122 — your whole-message |
randlee
pushed a commit
to randlee/hermes-agent
that referenced
this pull request
Aug 11, 2026
…ng markdown reply (NousResearch#26841) Transplant of PR NousResearch#26848 onto the plugin adapter path (plugins/platforms/feishu/adapter.py — the original PR targeted the since-removed gateway/platforms/feishu.py). ``send`` classifies each chunk independently, so chunk 1 of a long markdown reply (often plain prose) went out as msg_type=text while later chunks rendered as post — literal **bold**/## heading markers in the Feishu client. Lock the decision at the whole-message level: compute prefer_post once from the full formatted message and pass it to _build_outbound_payload per chunk. The original PR's per-chunk table exemption is intentionally dropped: tables now route through post/md (issue NousResearch#52786 cluster fix), so the exemption would reintroduce the raw-table downgrade. Co-authored-by: Hermes Agent <hermes@nousresearch.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Stops the Feishu (Lark) adapter from sending the first chunk of a long Markdown reply as
msg_type=textwhile later chunks go out asmsg_type=post— so users no longer see literal**bold**/## heading/ code fences in the first message and rendered Markdown in the rest.Issue #26841 reported that when Hermes produced a long, Markdown-rich response (headings + bold + code blocks), the first chunk in the Feishu client showed raw Markdown while subsequent chunks rendered correctly. The cause is per-chunk classification:
FeishuAdapter.sendformats the full reply, then callstruncate_message(which splits atMAX_MESSAGE_LENGTH = 8000) and walks the chunks._build_outbound_payload(chunk), which inspects only that chunk with_MARKDOWN_HINT_RE. If the chunk doesn't match (because, e.g., the intro paragraph happens to be plain prose), the function returnsmsg_type=textand the user sees literal Markdown markers.postand render correctly.There is no
if i == 0branch — the bug is implicit in the split point. This PR fixes it by locking the Markdown decision at the whole-message level:send()computesprefer_post = bool(_MARKDOWN_HINT_RE.search(formatted))once, before the chunk loop._build_outbound_payload(chunk, *, prefer_post=False)honoursprefer_postafter the table guard. Per-chunk table override still wins — any chunk that contains a Markdown table staystext(Feishu's post-typemdelement can't render tables and would otherwise blank that chunk).Single-chunk sends, non-Markdown sends, and the existing
post → textAPI-rejection fallback are unchanged.Related Issue
Fixes #26841
Type of Change
Changes Made
gateway/platforms/feishu.py::_build_outbound_payload— add keyword-onlyprefer_post: bool = False. After the table guard, fall into thepostbranch when either_MARKDOWN_HINT_RE.search(content)matches or the caller signalledprefer_post=True. Table chunks still short-circuit totext(unchanged).gateway/platforms/feishu.py::send— before the chunk loop, computeprefer_post = bool(_MARKDOWN_HINT_RE.search(formatted))from the full formatted message and pass it into each_build_outbound_payload(chunk, prefer_post=...)call.edit_messageis single-payload so it's unaffected.tests/gateway/test_feishu.py— two regression tests:test_send_uses_post_for_every_chunk_of_multi_chunk_markdown: patchestruncate_messageto return[plain_prose_first, markdown_second]; both API calls must usemsg_type=post(was["text", "post"]before the fix).test_send_keeps_table_chunks_as_text_even_when_message_is_markdown: a markdown table in chunk 2 still goes astexteven when the whole-messageprefer_postis on, so we don't accidentally blank table content in the client.How to Test
Checklist
Code
fix(feishu):,test(feishu):)Documentation & Housekeeping
cli-config.yaml.exampleif I added/changed config keys — N/A (no new config keys)CONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — N/A (no architecture change)msg_typeselection)Screenshots / Logs
Before the fix (issue #26841 reproduction,
+chat-messages-list):After the fix: