Skip to content

feat(chat): render agent-bubble LaTeX with KaTeX and safe math detection#2697

Open
YellowSnnowmann wants to merge 11 commits into
tinyhumansai:mainfrom
YellowSnnowmann:feat/agent-bubble-latex-rendering
Open

feat(chat): render agent-bubble LaTeX with KaTeX and safe math detection#2697
YellowSnnowmann wants to merge 11 commits into
tinyhumansai:mainfrom
YellowSnnowmann:feat/agent-bubble-latex-rendering

Conversation

@YellowSnnowmann
Copy link
Copy Markdown
Contributor

@YellowSnnowmann YellowSnnowmann commented May 26, 2026

Summary

  • Added LaTeX rendering in AgentMessageBubble/table markdown using remark-math + rehype-katex.
  • Introduced delimiter normalization so model outputs like [...], (...), and certain bare [ ... ] LaTeX blocks render correctly.
  • Added a conservative hasLatexContent() heuristic to enable math plugins only for likely-math content.
  • Prevented false positives for normal text patterns like currency ($10) and markdown links.
  • Added focused unit tests for both the LaTeX utility layer and bubble rendering behavior.

Problem

  • Agent responses sometimes include LaTeX in formats react-markdown does not render by default, so users saw raw source instead of readable math.
  • Enabling math parsing globally risks misinterpreting normal content (for example currency amounts) as math, causing UX regressions.
  • Without tests around normalization/detection, parser behavior can regress silently as message formatting evolves.

Solution

  • Added app/src/utils/latex.ts with:normalizeLatexDelimiters() to convert unsupported delimiters into remark-math-friendly forms.

  • hasLatexContent() to gate math parsing only when content strongly signals LaTeX.

  • Updated AgentMessageBubble.tsx and table-cell markdown rendering to:Normalize content only when needed.

  • Conditionally apply remarkMath/rehypeKatex.

  • Imported KaTeX stylesheet in main.tsx for proper rendered output styling.

  • Added tests in:app/src/utils/tests/latex.test.ts

  • app/src/pages/conversations/components/AgentMessageBubble.test.tsx

  • Tradeoff: heuristic detection is intentionally conservative to avoid non-math regressions, at the cost of potentially not auto-rendering some unusual math-like text.

Submission Checklist

  • If a section does not apply to this change, mark the item as N/A with a one-line reason. Do not delete items.
  • Tests added or updated (happy path + at least one failure / edge case) per Testing Strategy
  • Diff coverage ≥ 80% — changed lines (Vitest + cargo-llvm-cov merged via diff-cover) meet the gate enforced by .github/workflows/coverage.yml. Run pnpm test:coverage and pnpm test:rust locally; PRs below 80% on changed lines will not merge.
  • Coverage matrix updated — N/A: no feature row added/removed/renamed; existing chat messaging rows still apply.
  • All affected feature IDs from the matrix are listed in the PR description under ## Related
  • No new external network dependencies introduced (mock backend used per Testing Strategy)
  • Manual smoke checklist updated if this touches release-cut surfaces (docs/RELEASE-MANUAL-SMOKE.md) — N/A: does not touch release-cut/manual-only surfaces.
  • Linked issue closed via Closes #NNN in the ## Related section — N/A: no issue linked yet.

Impact

  • Runtime/platform: Frontend-only (app/) rendering behavior; affects desktop chat UI message display.
  • Performance: Minimal overhead from regex detection/normalization; math plugins are conditionally enabled to avoid unnecessary parsing.
  • Security: No new external network calls or trust-boundary changes.
  • Migration/compatibility: No data migration; dependency additions are katex, remark-math, and rehype-katex.

Related

Summary by CodeRabbit

Release Notes

  • New Features
    • Added mathematical expression rendering in conversations. Agent and user messages now properly display LaTeX-style mathematical notation with professional formatting.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 26, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5479857d-75ae-4a29-818e-b5560f478209

📥 Commits

Reviewing files that changed from the base of the PR and between 1312e34 and 2f09c2b.

📒 Files selected for processing (3)
  • app/src/pages/conversations/components/AgentMessageBubble.test.tsx
  • app/src/utils/__tests__/latex.test.ts
  • app/src/utils/latex.ts
✅ Files skipped from review due to trivial changes (1)
  • app/src/utils/tests/latex.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/src/pages/conversations/components/AgentMessageBubble.test.tsx
  • app/src/utils/latex.ts

📝 Walkthrough

Walkthrough

This PR adds KaTeX-based LaTeX math rendering to markdown content. It introduces utility functions to detect and normalize LaTeX delimiters, adds the required dependencies and stylesheet, and integrates conditional math plugin configuration into markdown rendering components with comprehensive test coverage.

Changes

LaTeX Math Rendering Feature

Layer / File(s) Summary
LaTeX detection and normalization utilities
app/src/utils/latex.ts, app/src/utils/__tests__/latex.test.ts
Exports normalizeLatexDelimiters to convert LLM-emitted LaTeX formats (\[...\], \(...\), and bare brackets) to remark-math-friendly delimiters, and hasLatexContent to detect LaTeX patterns via regex heuristics. Tests validate delimiter conversion, code-block/code-span preservation, and false-positive rejection (currency strings, plain text).
Dependency setup and KaTeX stylesheet
app/package.json, app/src/main.tsx
Adds katex, remark-math, and rehype-katex to dependencies, and imports KaTeX CSS stylesheet at the app entrypoint.
Markdown rendering with conditional math plugins
app/src/pages/conversations/components/AgentMessageBubble.tsx, app/src/pages/conversations/components/AgentMessageBubble.test.tsx
Updates BubbleMarkdown and TableCellMarkdown to detect LaTeX, normalize delimiters, and conditionally apply remark-math/rehype-katex plugins. Tests verify block/inline math rendering, vmatrix glyphs, and currency-string preservation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 With whiskers twitching, math appears,
LaTeX delimiters met with cheers!
KaTeX renders equations clean,
The finest math the LLM's seen! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (1 warning, 2 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check ❓ Inconclusive The linked issue #2596 contains only a screenshot with no textual requirements or acceptance criteria to validate against. The linked issue lacks explicit coding requirements or acceptance criteria. Verify separately that the PR implementation aligns with the intent shown in the screenshot.
Out of Scope Changes check ❓ Inconclusive The PR focuses on LaTeX rendering via KaTeX integration and math detection utilities. However, one commit note mentions an unrelated OAuth listener timeout adjustment that appears out of scope. Clarify whether the loopback OAuth listener timeout change is intentional or an accidental modification unrelated to LaTeX rendering feature.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding LaTeX rendering to agent message bubbles using KaTeX with safe math detection.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Comment @coderabbitai help to get the list of available commands and usage tips.

@YellowSnnowmann YellowSnnowmann marked this pull request as ready for review May 26, 2026 14:20
@YellowSnnowmann YellowSnnowmann requested a review from a team May 26, 2026 14:20
@coderabbitai coderabbitai Bot added the feature Net-new user-facing capability or product behavior. label May 26, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
app/src/pages/conversations/components/AgentMessageBubble.test.tsx (1)

81-100: ⚡ Quick win

Prefer behavior-level assertions over .katex internals.

At Line 83, Line 88, and Line 95, querying .katex ties tests to renderer internals. Assert rendered output behavior instead (e.g., raw delimiters are gone and math text remains visible) so tests survive non-functional markup changes.

♻️ Suggested test assertion shape
-    expect(container.querySelector('.katex')).not.toBeNull();
+    expect(container.textContent).toContain('x2+y2=z2');
+    expect(container.textContent).not.toContain('\\[');
+    expect(container.textContent).not.toContain('\\]');

-    expect(container.querySelector('.katex')).not.toBeNull();
+    expect(container.textContent).toContain('a+b');
+    expect(container.textContent).not.toContain('\\(');
+    expect(container.textContent).not.toContain('\\)');

-    expect(container.querySelector('.katex')).not.toBeNull();
+    expect(container.textContent).toContain('-2');
+    expect(container.textContent).not.toContain('\\begin{vmatrix}');
As per coding guidelines: "app/**/*.test.{ts,tsx}: Prefer testing behavior over implementation details."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/pages/conversations/components/AgentMessageBubble.test.tsx` around
lines 81 - 100, Replace assertions that query the renderer internals
(container.querySelector('.katex')) in the tests for BubbleMarkdown with
behavior-level checks: for the tests "renders [ ... ] block math via KaTeX",
"renders inline \\( ... \\) math via KaTeX", and "renders bare bracket vmatrix
block via KaTeX" assert that the raw TeX delimiters (e.g., "\[", "\]", "\(",
"\)", "["/"]" wrappers) are not present in container.textContent and that the
rendered math text (e.g., "x^2 + y^2 = z^2", "a+b", "1 2 3 4" or a recognizable
substring of the matrix) is visible; for "does NOT treat currency mentions as
math" assert that the dollar signs and numeric amounts ("$10", "$20") remain in
container.textContent and are rendered as plain text. Use the BubbleMarkdown
component tests to locate and update the assertions accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@app/src/pages/conversations/components/AgentMessageBubble.test.tsx`:
- Around line 81-100: Replace assertions that query the renderer internals
(container.querySelector('.katex')) in the tests for BubbleMarkdown with
behavior-level checks: for the tests "renders [ ... ] block math via KaTeX",
"renders inline \\( ... \\) math via KaTeX", and "renders bare bracket vmatrix
block via KaTeX" assert that the raw TeX delimiters (e.g., "\[", "\]", "\(",
"\)", "["/"]" wrappers) are not present in container.textContent and that the
rendered math text (e.g., "x^2 + y^2 = z^2", "a+b", "1 2 3 4" or a recognizable
substring of the matrix) is visible; for "does NOT treat currency mentions as
math" assert that the dollar signs and numeric amounts ("$10", "$20") remain in
container.textContent and are rendered as plain text. Use the BubbleMarkdown
component tests to locate and update the assertions accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e7f67dc5-bec6-41e4-9ae9-d069a0fd8204

📥 Commits

Reviewing files that changed from the base of the PR and between 87f8ef4 and 1312e34.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (8)
  • app/package.json
  • app/src/main.tsx
  • app/src/pages/conversations/components/AgentMessageBubble.test.tsx
  • app/src/pages/conversations/components/AgentMessageBubble.tsx
  • app/src/utils/__tests__/latex.test.ts
  • app/src/utils/__tests__/loopbackOauthListener.test.ts
  • app/src/utils/latex.ts
  • src/openhuman/config/ops_tests.rs

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 26, 2026
Copy link
Copy Markdown
Contributor

@graycyrus graycyrus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good feature — LaTeX rendering in agent bubbles is a real UX gap and the conservative heuristic approach is the right call. The hasLatexContent gate, constant plugin arrays, and delimiter normalization are all well thought out. Tests cover the key cases.

One real bug to fix before this ships, plus a couple of nits.

Area Files Verdict
Utility latex.ts Normalization mangles code blocks (see below)
Component AgentMessageBubble.tsx Clean integration, good plugin gating
Tests latex.test.ts, AgentMessageBubble.test.tsx Solid coverage, missing code-block case
Deps package.json, pnpm-lock.yaml katex 0.16.47, remark-math 6, rehype-katex 7 — all current
Unrelated loopbackOauthListener.test.ts, ops_tests.rs Should be separate commits

Comment thread app/src/utils/latex.ts
Comment thread app/src/utils/__tests__/loopbackOauthListener.test.ts Outdated
Copy link
Copy Markdown
Contributor

@graycyrus graycyrus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed fixed — all prior feedback is now addressed.

Code-block mangling [major]

  • Implemented placeholder extraction for fenced code blocks (...) and inline code (...)
  • Tests added for the exact scenario flagged: LaTeX delimiters in backtick-wrapped code spans
  • Verified math bodies containing digits are preserved

Unrelated bundled changes [minor]

  • Removed timeout change from loopbackOauthListener.test.ts (300→60)
  • Removed ops_tests.rs unrelated modification

CI & coverage

  • All 22 CI checks green
  • Diff coverage ≥ 80% on changed lines
  • Component + utility tests comprehensive

The PR is ready to approve and merge. Excellent work addressing the feedback quickly and thoroughly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Net-new user-facing capability or product behavior.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

小问题

2 participants