Render answer as a self-contained HTML page string.
Pure-Python, no Jinja or external template dep. Returns the full HTML
document; caller writes to a file or serves it as needed.
Source code in src/verifiable_rag/report.py
| def to_html(answer: "Answer", title: str = "verifiable-rag report") -> str:
"""Render *answer* as a self-contained HTML page string.
Pure-Python, no Jinja or external template dep. Returns the full HTML
document; caller writes to a file or serves it as needed.
"""
parts: list[str] = []
parts.append(
"<!DOCTYPE html>\n<html lang='en'>\n<head>\n"
f"<meta charset='utf-8'><title>{html.escape(title)}</title>\n"
f"<style>{_CSS}</style>\n</head>\n<body>\n"
)
parts.append(f"<header><h1>{html.escape(title)}</h1>")
verifier_ran = bool(answer.verification_results)
faithfulness_label = (
f"faithfulness={answer.faithfulness_score:.3f}"
if verifier_ran
else "no verifier configured"
)
parts.append(
f"<div class='meta'>strictness={html.escape(str(answer.strictness))} · "
f"refused={'yes' if answer.was_refused else 'no'} · "
f"{faithfulness_label}</div></header>\n"
)
# Query
parts.append("<h2>Query</h2>")
parts.append(f"<div class='query'>{html.escape(answer.query)}</div>")
# Refusal banner (when applicable)
if answer.was_refused:
parts.append(
f"<div class='refused'><strong>Refused.</strong> "
f"{html.escape(answer.refusal_reason or 'no reason given')}</div>"
)
# Answer with inline citations + verification color coding
parts.append("<h2>Answer</h2>")
parts.append(_render_answer_body(answer))
# Faithfulness card row — only meaningful when a verifier ran. Skip the
# section entirely when no verifier was configured to avoid surfacing
# an uninterpretable 1.0 default + a raw retrieval scalar.
if verifier_ran:
parts.append("<h2>Faithfulness</h2>")
parts.append(_render_faithfulness(answer))
# Per-sentence verification table
if answer.verification_results:
parts.append("<h2>Per-sentence verification</h2>")
parts.append(_render_verification_table(answer))
# Reranked passages the generator saw
if answer.retrieved_chunks:
parts.append(
f"<h2>Reranked passages ({len(answer.retrieved_chunks)})</h2>"
)
parts.append(_render_passages(answer))
parts.append(
"<footer>Generated by "
"<a href='https://github.com/firish/rag-rack'>verifiable-rag</a></footer>"
)
parts.append("</body></html>")
return "".join(parts)
|