feat: add rendering of markdown for the generated reports

This commit is contained in:
Kevin Bruton 2025-09-29 20:15:04 +02:00
parent df99d7deb2
commit c2470c4833
9 changed files with 2945 additions and 2830 deletions

View File

@ -170,6 +170,17 @@ In addition to the CLI, a new web-based frontend is available to visualize the a
4. Open your web browser and go to `http://127.0.0.1:8000`.
5. Enter a company symbol (e.g., `AAPL`) in the configuration form and click "Start Process" to begin the analysis.
### Rendered Reports (Markdown Support)
Agent-generated reports (analysis summaries, debate histories, plans, and risk assessments) are produced in Markdown. The web frontend now renders these Markdown documents as styled HTML instead of showing raw markup. This includes support for:
- Headings, emphasis, lists, and blockquotes
- Tables (for structured metrics)
- Fenced code blocks and inline code
Security: Markdown is sanitized serverside using `bleach` to strip unsafe tags/attributes while preserving semantic structure. If you need to extend allowed tags (e.g., to permit additional formatting), modify `ALLOWED_TAGS` / `ALLOWED_ATTRIBUTES` in `webapp/main.py`.
## TradingAgents Package
### Implementation Details

Binary file not shown.

View File

@ -37,4 +37,6 @@ dependencies = [
"uvicorn",
"python-multipart",
"jinja2",
"markdown>=3.6",
"bleach>=6.1.0",
]

View File

@ -28,3 +28,5 @@ fastapi
uvicorn
python-multipart
jinja2
markdown
bleach

View File

@ -0,0 +1,14 @@
from webapp.main import render_markdown
def test_markdown_basic_headers():
md_text = "# Title\n\nSome **bold** text and a table:\n\n| Col1 | Col2 |\n| ---- | ---- |\n| A | B |\n"
html = render_markdown(md_text)
assert '<h1>' in html and 'Title' in html
assert '<strong>' in html and 'bold' in html
assert '<table>' in html
def test_markdown_code_block():
md_text = "```python\nprint('hi')\n```"
html = render_markdown(md_text)
# Sanitized but should keep code element
assert 'print' in html and '<code' in html

5683
uv.lock

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,8 @@ from fastapi import FastAPI, Request, Form, BackgroundTasks, HTTPException
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
import jinja2
import markdown as md
import bleach
import os
from typing import Dict, Any
import threading
@ -56,6 +58,26 @@ app.mount("/static", StaticFiles(directory="webapp/static"), name="static")
template_dir = os.path.join(os.path.dirname(__file__), "templates")
jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_dir))
# Allowed tags and attributes for sanitized markdown rendering
ALLOWED_TAGS = list(bleach.sanitizer.ALLOWED_TAGS) + [
"p", "pre", "span", "h1", "h2", "h3", "h4", "h5", "h6", "table", "thead", "tbody", "tr", "th", "td", "blockquote", "code"
]
ALLOWED_ATTRIBUTES = {**bleach.sanitizer.ALLOWED_ATTRIBUTES, "span": ["class"], "code": ["class"], "th": ["align"], "td": ["align"]}
def render_markdown(value: str) -> str:
"""Convert markdown text to sanitized HTML."""
if not isinstance(value, str):
value = str(value)
html = md.markdown(
value,
extensions=["fenced_code", "tables", "codehilite", "toc", "sane_lists"],
output_format="html5"
)
cleaned = bleach.clean(html, tags=ALLOWED_TAGS, attributes=ALLOWED_ATTRIBUTES, strip=True)
return cleaned
jinja_env.filters['markdown'] = render_markdown
def update_execution_state(state: Dict[str, Any]):
"""Callback function to update the app_state based on LangGraph's state."""
print(f"📡 Callback received state keys: {list(state.keys())}")

View File

@ -398,6 +398,45 @@ body {
word-wrap: break-word;
}
/* Markdown content styling */
.markdown-body {
line-height: 1.6;
font-size: 0.95rem;
}
.markdown-body h1, .markdown-body h2, .markdown-body h3 {
border-bottom: 1px solid var(--border-color);
padding-bottom: 0.3em;
margin-top: 1.2em;
}
.markdown-body code {
background: var(--bg-secondary);
padding: 2px 5px;
border-radius: 4px;
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
}
.markdown-body pre code {
display: block;
padding: 12px;
overflow-x: auto;
}
.markdown-body table {
width: 100%;
border-collapse: collapse;
margin: 1em 0;
}
.markdown-body th, .markdown-body td {
border: 1px solid var(--border-color);
padding: 6px 10px;
text-align: left;
}
.markdown-body blockquote {
border-left: 4px solid var(--accent-color);
margin: 1em 0;
padding: 0.5em 1em;
background: var(--bg-secondary);
border-radius: 4px;
}
#right-panel h3 {
color: var(--text-primary);
margin-bottom: 15px;

View File

@ -1,4 +1,4 @@
<div>
<h3>Content Details</h3>
<pre>{{ content }}</pre>
<div class="markdown-body">{{ content | markdown | safe }}</div>
</div>