fix(discord): auto-convert markdown tables to bullet groups - #53284
Merged
kshitijk4poor merged 4 commits intoJun 26, 2026
Merged
Conversation
Collaborator
Salvage of #45781 (@yashiels) onto current main, authorship preserved. Same bullet-group mechanism as the earlier canonical #45781 (still open) and closed #53067. Not a duplicate — #45781 remains the canonical predecessor; maintainers should pick one to merge. |
Move table-detection regex, row-splitting, and table-to-bullet conversion into gateway/platforms/helpers.py so both Discord and Telegram adapters can share them. Co-authored-by: Yashiel Sookdeo <yashiel@skyner.co.za>
Discord does not render GFM pipe tables — raw pipe characters display as garbage text. format_message now rewrites tables into bold-heading + bullet groups using the shared helpers. Fixes NousResearch#21168 Co-authored-by: Yashiel Sookdeo <yashiel@skyner.co.za>
…pers.py Replace local _TABLE_SEPARATOR_RE, _is_table_row, and _split_markdown_table_row with imports from the shared module. Telegram-specific rendering stays local. Co-authored-by: Yashiel Sookdeo <yashiel@skyner.co.za>
The PR's original refactor commit only replaced the primitives (regex, is_table_row, split_markdown_table_row) with shared imports but left the verbatim-copied renderer (_render_table_block_for_telegram) and driver (_wrap_markdown_tables) in place. Both are logic-identical to the shared convert_table_to_bullets in gateway/platforms/helpers.py. Replace both with a direct import alias. _TABLE_SEPARATOR_RE is still imported separately because it's used by the rich-message routing logic (lines 1024, 1044) to detect whether content contains tables. Found by 3-agent parallel code-reuse review.
kshitijk4poor
force-pushed
the
salvage/45781-discord-table-bullets
branch
from
June 26, 2026 22:17
0fc9456 to
97d0b84
Compare
This was referenced Jun 26, 2026
13 tasks
|
I tested this out and it does not look good at all, hopefully it is made into a table like layout at some point. |
pai-scaffolde
pushed a commit
to Scaffolde/hermes-agent
that referenced
this pull request
Jun 28, 2026
PR NousResearch#53284 salvage (discord markdown table-to-bullet conversion; NousResearch#21168)
waefrebeorn
pushed a commit
to waefrebeorn/slermes
that referenced
this pull request
Jul 2, 2026
PR NousResearch#53284 salvage (discord markdown table-to-bullet conversion; NousResearch#21168)
habarmc1223-sudo
pushed a commit
to habarmc1223-sudo/hermes-agent-fluxmem
that referenced
this pull request
Jul 8, 2026
PR NousResearch#53284 salvage (discord markdown table-to-bullet conversion; NousResearch#21168)
santhreal
pushed a commit
to santhreal/hermes-agent
that referenced
this pull request
Jul 13, 2026
PR NousResearch#53284 salvage (discord markdown table-to-bullet conversion; NousResearch#21168)
Gravezzz
pushed a commit
to Gravezzz/hermes-agent
that referenced
this pull request
Jul 21, 2026
PR NousResearch#53284 salvage (discord markdown table-to-bullet conversion; NousResearch#21168)
sam7894604
added a commit
to sam7894604/hermes-agent
that referenced
this pull request
Jul 22, 2026
LINE's text bubble renders zero Markdown, so a GFM pipe table from the model landed in the chat as literal "| Item | Cost |" / "|------|" rows. Discord and Telegram already solve this with the shared convert_table_to_bullets() helper (gateway/platforms/helpers.py, added in PR NousResearch#53284) — LINE was simply never wired into it. Wire LINE into the same shared converter rather than growing a LINE-specific table implementation. The call goes at the top of strip_markdown_preserving_urls(), which every outbound send path already funnels through (postback reply, _send_text_chunks, format_message, standalone send), so all of them are covered by one hook. Order matters: the converter runs BEFORE the existing code-fence un-fencing. The shared converter deliberately skips fenced blocks, but once the fences are stripped a table inside a code block would look like a real table and be wrongly converted. Running it first preserves fence-skipping. The two steps compose cleanly: the converter emits "**heading**" + "• field: value"; the existing strip removes the bold markers, and the "•" bullets pass through _MD_BULLET_RE untouched (it only matches -/*/+ markers). +3 tests (table → bullets with no raw pipes; fenced table left verbatim; no-table behaviour unchanged). 213 LINE + table-helper tests green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
sam7894604
added a commit
to sam7894604/hermes-agent
that referenced
this pull request
Jul 22, 2026
LINE's text bubble renders zero Markdown, so a GFM pipe table from the model landed in the chat as literal "| Item | Cost |" / "|------|" rows. Discord and Telegram already solve this with the shared convert_table_to_bullets() helper (gateway/platforms/helpers.py, added in PR NousResearch#53284) — LINE was simply never wired into it. Wire LINE into the same shared converter rather than growing a LINE-specific table implementation. The call goes at the top of strip_markdown_preserving_urls(), which every outbound send path already funnels through (postback reply, _send_text_chunks, format_message, standalone send), so all of them are covered by one hook. Order matters: the converter runs BEFORE the existing code-fence un-fencing. The shared converter deliberately skips fenced blocks, but once the fences are stripped a table inside a code block would look like a real table and be wrongly converted. Running it first preserves fence-skipping. The two steps compose cleanly: the converter emits "**heading**" + "• field: value"; the existing strip removes the bold markers, and the "•" bullets pass through _MD_BULLET_RE untouched (it only matches -/*/+ markers). +3 tests (table → bullets with no raw pipes; fenced table left verbatim; no-table behaviour unchanged). 213 LINE + table-helper tests green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
leewenjie
pushed a commit
to leewenjie/hermes-agent
that referenced
this pull request
Aug 7, 2026
PR NousResearch#53284 salvage (discord markdown table-to-bullet conversion; NousResearch#21168)
sam7894604
added a commit
to sam7894604/hermes-agent
that referenced
this pull request
Aug 9, 2026
LINE's text bubble renders zero Markdown, so a GFM pipe table from the model landed in the chat as literal "| Item | Cost |" / "|------|" rows. Discord and Telegram already solve this with the shared convert_table_to_bullets() helper (gateway/platforms/helpers.py, added in PR NousResearch#53284) — LINE was simply never wired into it. Wire LINE into the same shared converter rather than growing a LINE-specific table implementation. The call goes at the top of strip_markdown_preserving_urls(), which every outbound send path already funnels through (postback reply, _send_text_chunks, format_message, standalone send), so all of them are covered by one hook. Order matters: the converter runs BEFORE the existing code-fence un-fencing. The shared converter deliberately skips fenced blocks, but once the fences are stripped a table inside a code block would look like a real table and be wrongly converted. Running it first preserves fence-skipping. The two steps compose cleanly: the converter emits "**heading**" + "• field: value"; the existing strip removes the bold markers, and the "•" bullets pass through _MD_BULLET_RE untouched (it only matches -/*/+ markers). +3 tests (table → bullets with no raw pipes; fenced table left verbatim; no-table behaviour unchanged). 213 LINE + table-helper tests green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
sam7894604
added a commit
to sam7894604/hermes-agent
that referenced
this pull request
Aug 11, 2026
LINE's text bubble renders zero Markdown, so a GFM pipe table from the model landed in the chat as literal "| Item | Cost |" / "|------|" rows. Discord and Telegram already solve this with the shared convert_table_to_bullets() helper (gateway/platforms/helpers.py, added in PR NousResearch#53284) — LINE was simply never wired into it. Wire LINE into the same shared converter rather than growing a LINE-specific table implementation. The call goes at the top of strip_markdown_preserving_urls(), which every outbound send path already funnels through (postback reply, _send_text_chunks, format_message, standalone send), so all of them are covered by one hook. Order matters: the converter runs BEFORE the existing code-fence un-fencing. The shared converter deliberately skips fenced blocks, but once the fences are stripped a table inside a code block would look like a real table and be wrongly converted. Running it first preserves fence-skipping. The two steps compose cleanly: the converter emits "**heading**" + "• field: value"; the existing strip removes the bold markers, and the "•" bullets pass through _MD_BULLET_RE untouched (it only matches -/*/+ markers). +3 tests (table → bullets with no raw pipes; fenced table left verbatim; no-table behaviour unchanged). 213 LINE + table-helper tests green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
sam7894604
added a commit
to sam7894604/hermes-agent
that referenced
this pull request
Sep 2, 2026
LINE's text bubble renders zero Markdown, so a GFM pipe table from the model landed in the chat as literal "| Item | Cost |" / "|------|" rows. Discord and Telegram already solve this with the shared convert_table_to_bullets() helper (gateway/platforms/helpers.py, added in PR NousResearch#53284) — LINE was simply never wired into it. Wire LINE into the same shared converter rather than growing a LINE-specific table implementation. The call goes at the top of strip_markdown_preserving_urls(), which every outbound send path already funnels through (postback reply, _send_text_chunks, format_message, standalone send), so all of them are covered by one hook. Order matters: the converter runs BEFORE the existing code-fence un-fencing. The shared converter deliberately skips fenced blocks, but once the fences are stripped a table inside a code block would look like a real table and be wrongly converted. Running it first preserves fence-skipping. The two steps compose cleanly: the converter emits "**heading**" + "• field: value"; the existing strip removes the bold markers, and the "•" bullets pass through _MD_BULLET_RE untouched (it only matches -/*/+ markers). +3 tests (table → bullets with no raw pipes; fenced table left verbatim; no-table behaviour unchanged). 213 LINE + table-helper tests green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
melon-xf
added a commit
to melon-xf/hermes-agent
that referenced
this pull request
Sep 3, 2026
PR NousResearch#53284 salvage (discord markdown table-to-bullet conversion; NousResearch#21168)
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.
Summary
Discord doesn't render GFM pipe tables — raw pipe characters show as garbage text. This PR adds automatic table-to-bullet conversion in the Discord adapter's
format_message, matching the pattern Telegram already uses, and extracts the shared table-detection primitives intogateway/platforms/helpers.py.Salvage of #45781 (@yashiels). Closes #21168. Closes #45781.
Changes
gateway/platforms/helpers.py: AddedTABLE_SEPARATOR_RE,is_table_row,split_markdown_table_row,_render_table_block, andconvert_table_to_bullets— shared table-to-bullet conversion logic.plugins/platforms/discord/adapter.py:format_messagenow callsconvert_table_to_bullets()instead of returning content as-is. Import moved to top-level.plugins/platforms/telegram/adapter.py: Replaced local_TABLE_SEPARATOR_RE,_is_table_row,_split_markdown_table_rowwith imports from shared helpers. Telegram-specific MarkdownV2 renderer stays local.tests/gateway/test_table_helpers.py: 18 new tests for shared helpers.tests/gateway/test_discord_format.py: 4 new tests for Discord formatting.Salvage notes
main. Cherry-picked all 3 contributor commits.gateway/platforms/telegram.pytoplugins/platforms/telegram/adapter.pysince the PR's base. Kept the chunk-indicator code that landed on main, applied the shared-import refactor on top.gateway.platforms.helpersat line 105).Validation