Skip to content

feat(cli): render markdown tables and formatting in non-streaming responses - #4245

Closed
crazywriter1 wants to merge 1 commit into
NousResearch:mainfrom
crazywriter1:fix/markdown-rendering-non-streaming
Closed

feat(cli): render markdown tables and formatting in non-streaming responses#4245
crazywriter1 wants to merge 1 commit into
NousResearch:mainfrom
crazywriter1:fix/markdown-rendering-non-streaming

Conversation

@crazywriter1

Copy link
Copy Markdown
Contributor

Summary

Fixes #3621 - CLI now renders markdown (tables, headings, code blocks, lists) properly in non-streaming mode instead of displaying raw syntax like | header | header |.

Changes

  • Added _looks_like_markdown() heuristic that detects markdown content by checking for 2+ indicators (tables, headings, fenced code blocks, ordered/unordered lists)
  • Added _render_response_content() that returns rich.Markdown when markdown is detected, falls back to existing _rich_text_from_ansi() for plain text
  • Updated all 3 non-streaming Panel render sites:
    • Main response panel (~L6067)
    • Background task response panel (~L4184)
    • /btw side-question response panel (~L4308)

What's NOT changed

  • Streaming behavior is untouched, token-by-token rendering remains as-is
  • No new dependencies - rich.Markdown is already available via the existing rich>=14.3.3 requirement

Why a heuristic?

Blindly passing every response through rich.Markdown would break plain text and ANSI colored tool output. The 2-indicator threshold avoids false positives (e.g. a line starting with # in a shell command won't trigger markdown rendering on its own).

Test plan

  • Syntax check passes
  • 10/10 custom heuristic unit tests pass (detection + return type)
  • 86/86 existing CLI tests pass (4 pre-existing async failures unrelated to this change)
  • Manual verification: send a query that returns a markdown table with stream: false and confirm it renders as a formatted table

Future work

Streaming markdown rendering (#3621 streaming case) is intentionally deferred to a separate PR. It involves UX tradeoffs (buffering vs re-rendering vs incremental parsing) that deserve their own discussion.

Use rich.Markdown for non-streaming Panel output when markdown
content is detected (tables, headings, code blocks, lists).
Falls back to existing ANSI rendering for plain text.

Fixes NousResearch#3621
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the PR, @crazywriter1! This has since been implemented on main in a more complete form.

This is an automated hermes-sweeper review.

What landed on main:

  • _render_final_assistant_content() in cli.py (line 1239) supports three modes: render (passes through rich.Markdown — the behavior you added), strip (plain text, the new default), and raw (ANSI passthrough).
  • Controlled via display.final_response_markdown: render | strip | raw in hermes_cli/config.py (line 634).
  • All three Panel render sites this PR targeted are covered: main response panel, background task panel, and /btw panel — see commits 177e6eb3d and 09ced16e.
  • Shipped in release v2026.4.23.

One design difference: the main implementation requires users to opt in with display.final_response_markdown: render rather than auto-detecting markdown. This was intentional to avoid false positives on shell output. If you feel auto-detection is still worth adding on top of this, a new focused PR would be welcome.

Closing as implemented_on_main.

@teknium1 teknium1 closed this Apr 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Native Markdown and md table rendering in CLI

2 participants