feat: block-level and inline markdown rendering for LLM responses - #4501
Closed
KUSH42 wants to merge 6 commits into
Closed
feat: block-level and inline markdown rendering for LLM responses#4501KUSH42 wants to merge 6 commits into
KUSH42 wants to merge 6 commits into
Conversation
Introduces agent/rich_output.py — a self-contained Rich/Pygments rendering toolkit with no project-specific imports. Public API: - LanguageDetector: extension map + content-pattern heuristics - SyntaxHighlighter: Pygments → Rich markup → ANSI string - FilePathFormatter: per-filetype icons, compact relative paths - DiffRenderer: unified diff → Rich Text with line numbers - clean_command_output: strip venv/stacktrace noise from command output DiffRenderer replaces _render_inline_unified_diff in display.py: - Intra-line character-level highlighting via SequenceMatcher (threshold 0.5) - Per-run del/add pairing to avoid cross-hunk false matches - Summary header: ● filename.py Added N lines, removed M lines - Console width from shutil.get_terminal_size, not hardcoded Tests: 51 passing in tests/test_rich_output.py
Pygments emits Error tokens for content its markdown lexer cannot tokenize (emoji in headings, unknown syntax, etc.). Mapping Error to "bold red on red" matched the diff-deletion colour, causing spurious red backgrounds on unrelated text. Changed to "bold red" (text colour only), consistent with Generic.Error.
Wires the SyntaxHighlighter/LanguageDetector from rich_output.py into tool result display and LLM response rendering. execute_code preview: - Highlighted Python block printed after successful execution - Gated on _result_succeeded — nothing shown after a failed run - Cute-msg drops the inline snippet when highlight is active (no duplication) read_file preview: - ┊ 📄 filename.py header + syntax-highlighted content - Language from extension only; unknown types skipped silently terminal preview: - Verb-based language detection from the command - _FILE_EXEC_COMMANDS blocklist (node, python3, bash, …) prevents runtime stdout from being mistaken for source code LLM response rendering: - format_response() highlights fenced code blocks in complete responses - StreamingCodeBlockHighlighter state machine for streaming: buffers fenced blocks, flushes highlighted on closing fence, plain text passes through immediately with response text colour preserved Plumbing: - Verbosity gate: all previews suppressed when tool_progress_mode == "off" - display.code_highlight config key + /code-highlight toggle - set_code_highlight_active() keeps display.py decoupled from CLI state - Module-level _rich_detector singleton (no per-call instantiation) Tests: 135 passing (tests/test_display.py + tests/test_rich_output.py)
format_response wrapped highlighted code in ``` delimiters under the theory that "the Panel still looks like a code block". In practice the ANSI-highlighted block reads cleanly without them, and keeping the fences caused raw backtick lines to appear in the rendered response.
…responses Adds apply_block_line (headings, hr, blockquotes, lists, ref-link suppression) and extends apply_inline_markdown (images, links, HTML inline tags). format_response gains a pass-2 that applies both renderers to every non-highlighted line. The streaming path chains the same pair in _emit_stream_text and _flush_stream. Bug fixes included: - _code_highlight_active was gating apply_block_line/apply_inline_markdown in the streaming path; removed the guard (_RICH_RESPONSE is the correct gate; _code_highlight_active controls only tool-output highlighting) - _MD_ITALIC_UNDER_RE rejected phrases with spaces; changed [^_\s\n]+ to [^_\n]+ (word-boundary lookbehind prevents snake_case false positives) - _MD_REF_LINK_RE had a $ anchor that blocked titled reference-link definitions from being suppressed; removed $ - Blockquote inline spans reset to terminal default; added reset_suffix to the apply_inline_markdown call in the blockquote branch - format_response splitlines(keepends=True) fed \n into capture groups, silently dropping block elements; switched to splitlines() + manual join - CommonMark backslash escapes (\] → ]) were passed through literally; added step-7 re.sub pass in apply_inline_markdown - Pygments plain-text lexer emits lines with no ANSI codes; pass-2 guard "\x1b" in l then fell through and applied markdown to code-fence content (### was stripped, ** rendered, etc.); _highlight now prepends \x1b[0m to bare lines so the guard is always satisfied
…e correctness - Anchor code-fence regex to line start ((?m)^) so fences prefixed with > are not incorrectly consumed as code block openers; previously caused blockquote lines to be syntax-highlighted and the \x1b guard to skip apply_block_line, rendering raw > instead of the ▌ gutter - Remove fence delimiter preservation from format_response; add \033[0m fallback for plain-text lexer output so pass 2 always skips code content - Update _highlight group numbers to match new 3-group regex (backticks, lang, code) - Keep URL visible in link rendering ([text](url) → underlined text (url)) so users can copy and ctrl+click
Contributor
Author
|
Superseded by #4504. |
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
apply_block_line(line)— single-line-detectable block elements rendered to ANSIapply_inline_markdownwith images, links, and HTML inline tagsapply_block_linebeforeapply_inline_markdownin bothformat_responseand the streaming pathformat_responsecode-fence regex and blockquote rendering bugWhat's rendered
# Foo#stripped## Foo### Foo#### Foo---/***/___─line across terminal> text▌gutter + dim text>> text▌- item•at depth 0,◦at 1,▸at 2,·at 31. item[ref]: https://…[img: alt]dim placeholder[text](url)text (url)— URL kept for copy/ctrl+click<em><em>text</em><strong><strong>text</strong>Bug fixes included
format_responsecode-fence regex was unanchored and matched```mid-line (inside> ```python), causing blockquote lines to be syntax-highlighted; the\x1bguard then skippedapply_block_line, rendering raw>instead of▌. Fixed by anchoring with(?m)^.format_responsefence delimiter leakage — fence lines were preserved in output and leaked into the Panel display. Fixed by consuming fences entirely; added\033[0mfallback for plain-text lexer lines so pass 2 always skips code content.format_responsenewline loss —splitlines(keepends=True)fed\n-bearing strings intoapply_block_line; matched block elements were returned without\nand concatenated with the following line. Fixed bysplitlines()+ manual"\n".join.reset_suffix— inline spans inside blockquotes reset to terminal default instead of restoring the dim gutter style. Fixed by passingreset_suffix=_BLOCKQUOTE_ANSI._MD_REF_LINK_REtitled forms —$anchor prevented suppression of titled reference-link definitions. Removed$.Known interim regression
---immediately after a paragraph renders as an hr instead of a setext h2. Corrected in PR4 (StreamingBlockBuffer; seedocs/spec-markdown-stateful-blocks.md).Non-goals (deferred to PR4)