Skip to content

Report

to_html

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.

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)