Spaces:
Sleeping
Sleeping
Aissugur Kurganbekov
Claude Opus 4.8
Add hybrid retrieval (TF-IDF + embeddings), OCR, A/B eval, and answer trust/UX
f65a6b3 Download frontend/src/components/EvidenceReport.jsx from financeflow/ai: direct link, hf CLI and curl.
- Browser
- Download file 5.29 kB
-
https://huggingface.co/spaces/financeflow/ai/resolve/main/frontend/src/components/EvidenceReport.jsx
- Command line
-
hf download hf://spaces/financeflow/ai/frontend/src/components/EvidenceReport.jsx
-
curl -L -o EvidenceReport.jsx https://huggingface.co/spaces/financeflow/ai/resolve/main/frontend/src/components/EvidenceReport.jsx
5.29 kB
| // EvidenceReport β a clean, printable one-page summary of an answer and the | |
| // evidence behind it. Rendered off-screen until the user triggers a print; we | |
| // flip a class on <html> so a print-only stylesheet shows just the report. | |
| // No new deps β window.print() does the work. | |
| import { useEffect } from "react"; | |
| import { highlightMatch } from "../lib/highlight"; | |
| function fmtConfidence(c) { | |
| if (typeof c !== "number") return "β"; | |
| return `${Math.round(c * 100)}%`; | |
| } | |
| // One printable evidence row. Mirrors EvidencePanel's metadata line + excerpt, | |
| // but always expanded (a report shows everything) and ink-on-paper styled. | |
| function ReportEvidence({ citation, rank }) { | |
| const meta = [ | |
| citation.page != null ? `page ${citation.page}` : null, | |
| `chunk #${citation.chunk_index}`, | |
| citation.score != null ? `relevance ${citation.score}` : null, | |
| ] | |
| .filter(Boolean) | |
| .join(" Β· "); | |
| return ( | |
| <div className="report-evidence"> | |
| <div className="report-evidence-head"> | |
| <span className="report-rank">{rank}</span> | |
| <span className="report-doc">{citation.document_name}</span> | |
| <span className="report-meta">{meta}</span> | |
| </div> | |
| <div className="report-excerpt"> | |
| {highlightMatch(citation.excerpt, citation.match_text)} | |
| </div> | |
| </div> | |
| ); | |
| } | |
| export default function EvidenceReport({ result, question, onClose }) { | |
| // Trigger the browser print dialog once mounted, then notify the parent so it | |
| // can drop the report from the tree. afterprint covers both "Print" and | |
| // "Cancel" so we never get stuck in the print-only view. | |
| useEffect(() => { | |
| if (!result) return; | |
| const root = document.documentElement; | |
| root.classList.add("printing-report"); | |
| let finished = false; | |
| let fallback; | |
| // Runs exactly once: drop the print-only class and notify the parent so it | |
| // can unmount the report. Must ALWAYS fire (even if printing fails), or the | |
| // parent is left with a stuck report and a later re-export can't reopen. | |
| const done = () => { | |
| if (finished) return; | |
| finished = true; | |
| clearTimeout(fallback); | |
| window.removeEventListener("afterprint", done); | |
| root.classList.remove("printing-report"); | |
| onClose?.(); | |
| }; | |
| window.addEventListener("afterprint", done); | |
| // Let the report paint before opening the dialog. | |
| const t = setTimeout(() => { | |
| try { | |
| window.print(); | |
| } catch { | |
| done(); // print blocked/threw β don't leave the report stuck | |
| return; | |
| } | |
| // Safety net for environments where `afterprint` never fires (sandboxed | |
| // iframes / print-blocking browsers): clean up anyway. | |
| fallback = setTimeout(done, 3000); | |
| }, 60); | |
| return () => { | |
| clearTimeout(t); | |
| clearTimeout(fallback); | |
| window.removeEventListener("afterprint", done); | |
| root.classList.remove("printing-report"); | |
| }; | |
| // eslint-disable-next-line react-hooks/exhaustive-deps | |
| }, []); | |
| if (!result) return null; | |
| const citations = result.citations || []; | |
| const meta = result.metadata || {}; | |
| const supported = meta.evidence_status === "supported" && !result.abstained; | |
| return ( | |
| <div className="evidence-report" role="document" aria-label="Evidence report"> | |
| <header className="report-header"> | |
| <div> | |
| <div className="report-brand">FinanceFlow AI Β· Evidence Report</div> | |
| <div className="report-date">{new Date().toLocaleString()}</div> | |
| </div> | |
| <div className={`report-status ${supported ? "ok" : "weak"}`}> | |
| {supported ? "Grounded" : "Not enough info"} | |
| </div> | |
| </header> | |
| <section className="report-block"> | |
| <h2 className="report-label">Question</h2> | |
| <p className="report-question">{question}</p> | |
| </section> | |
| <section className="report-block"> | |
| <div className="report-answer-head"> | |
| <h2 className="report-label">Answer</h2> | |
| <div className="report-conf"> | |
| <span className="report-conf-num">{fmtConfidence(meta.confidence)}</span> | |
| <span className="report-conf-lbl">confidence</span> | |
| </div> | |
| </div> | |
| <p className="report-answer">{result.answer}</p> | |
| <div className="report-facts"> | |
| <span>Mode: {result.mode || "β"}</span> | |
| <span>Sources cited: {citations.length}</span> | |
| {meta.question_type && <span>Type: {meta.question_type}</span>} | |
| {typeof meta.top_score === "number" && ( | |
| <span>Top match: {meta.top_score}</span> | |
| )} | |
| </div> | |
| </section> | |
| <section className="report-block"> | |
| <h2 className="report-label"> | |
| Evidence{citations.length > 0 ? ` (${citations.length})` : ""} | |
| </h2> | |
| {citations.length > 0 ? ( | |
| citations.map((c, i) => ( | |
| <ReportEvidence key={i} citation={c} rank={i + 1} /> | |
| )) | |
| ) : ( | |
| <p className="report-noevidence"> | |
| No supporting passages β the assistant abstained rather than guess. | |
| </p> | |
| )} | |
| </section> | |
| <footer className="report-footer"> | |
| Generated by FinanceFlow AI β answers are grounded in the cited source | |
| passages above. | |
| </footer> | |
| </div> | |
| ); | |
| } | |