Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -732,6 +732,53 @@ def render_error(msg: str) -> str:
|
|
| 732 |
"""
|
| 733 |
|
| 734 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 735 |
# ============================================================
|
| 736 |
# Gradio app
|
| 737 |
# ============================================================
|
|
@@ -747,9 +794,9 @@ EXAMPLES = [
|
|
| 747 |
]
|
| 748 |
|
| 749 |
|
| 750 |
-
def analyze(query: str) -> tuple[str, str]:
|
| 751 |
if not query or not query.strip():
|
| 752 |
-
return render_error("Please enter a compliance query."), ""
|
| 753 |
|
| 754 |
query = query.strip()
|
| 755 |
print("\n" + "=" * 72, flush=True)
|
|
@@ -768,12 +815,12 @@ def analyze(query: str) -> tuple[str, str]:
|
|
| 768 |
)
|
| 769 |
|
| 770 |
if not results:
|
| 771 |
-
return render_error("No relevant regulatory chunks found. Try rephrasing your query."), ""
|
| 772 |
|
| 773 |
context = format_context(results)
|
| 774 |
report = call_claude(query, context)
|
| 775 |
if not report:
|
| 776 |
-
return render_error("Could not reach Claude API. Check that ANTHROPIC_API_KEY is set as a Space Secret."), context
|
| 777 |
|
| 778 |
report = canonicalize_report(report, results)
|
| 779 |
print(
|
|
@@ -783,7 +830,7 @@ def analyze(query: str) -> tuple[str, str]:
|
|
| 783 |
flush=True,
|
| 784 |
)
|
| 785 |
|
| 786 |
-
return render_report(report, query, results), context
|
| 787 |
|
| 788 |
|
| 789 |
CSS = """
|
|
@@ -802,7 +849,7 @@ body { background: #0a0f1e !important; }
|
|
| 802 |
.query-box textarea:focus { border-color: #38bdf8 !important; box-shadow: 0 0 0 2px rgba(56,189,248,0.15) !important; }
|
| 803 |
.analyze-btn { background: #0369a1 !important; color: #fff !important; font-family: 'IBM Plex Mono', monospace !important; font-weight: 600 !important; letter-spacing: 0.05em !important; border: none !important; border-radius: 8px !important; height: 44px !important; font-size: 0.85rem !important; transition: background 0.2s !important; }
|
| 804 |
.analyze-btn:hover { background: #0284c7 !important; }
|
| 805 |
-
.context-box textarea { background: #0f172a !important; border: 1px solid #1e293b !important; color: #
|
| 806 |
label { color: #94a3b8 !important; font-size: 0.8rem !important; }
|
| 807 |
"""
|
| 808 |
|
|
@@ -839,7 +886,9 @@ with gr.Blocks(css=CSS, title="RegTech BR") as demo:
|
|
| 839 |
|
| 840 |
report_html = gr.HTML(label="Compliance Assessment")
|
| 841 |
|
| 842 |
-
|
|
|
|
|
|
|
| 843 |
context_box = gr.Textbox(
|
| 844 |
label="Raw chunks retrieved by RAG",
|
| 845 |
lines=10,
|
|
@@ -856,8 +905,8 @@ with gr.Blocks(css=CSS, title="RegTech BR") as demo:
|
|
| 856 |
""")
|
| 857 |
|
| 858 |
use_example_btn.click(fn=lambda example: example or "", inputs=[example_dropdown], outputs=[query_box])
|
| 859 |
-
analyze_btn.click(fn=analyze, inputs=[query_box], outputs=[report_html, context_box])
|
| 860 |
-
query_box.submit(fn=analyze, inputs=[query_box], outputs=[report_html, context_box])
|
| 861 |
|
| 862 |
if __name__ == "__main__":
|
| 863 |
port = int(os.environ.get("PORT", 7860))
|
|
|
|
| 732 |
"""
|
| 733 |
|
| 734 |
|
| 735 |
+
|
| 736 |
+
def render_evidence_summary(results: list[dict]) -> str:
|
| 737 |
+
"""Render a compact, visible summary of the retrieved RAG evidence."""
|
| 738 |
+
if not results:
|
| 739 |
+
return """
|
| 740 |
+
<div style="font-family:'IBM Plex Mono',monospace;background:#0f172a;color:#94a3b8;
|
| 741 |
+
padding:1rem;border-radius:10px;border:1px solid #1e3a5f;margin-top:0.8rem">
|
| 742 |
+
No retrieved evidence to display.
|
| 743 |
+
</div>
|
| 744 |
+
"""
|
| 745 |
+
|
| 746 |
+
rows = []
|
| 747 |
+
for i, r in enumerate(results, 1):
|
| 748 |
+
source_id = esc(r.get("source_id", "unknown"))
|
| 749 |
+
authority = esc(r.get("authority", "?"))
|
| 750 |
+
article = esc(r.get("article_hint", "—") or "—")
|
| 751 |
+
norm = esc(r.get("normative_reference_hint", "") or "")
|
| 752 |
+
score = f"{float(r.get('_final', 0.0)):.3f}"
|
| 753 |
+
text = esc(str(r.get("text", ""))[:260].strip())
|
| 754 |
+
norm_line = f" · {norm}" if norm else ""
|
| 755 |
+
rows.append(f"""
|
| 756 |
+
<div style="background:#111827;border:1px solid #26364f;border-radius:8px;
|
| 757 |
+
padding:0.85rem;margin:0.55rem 0">
|
| 758 |
+
<div style="display:flex;gap:0.5rem;align-items:center;flex-wrap:wrap;margin-bottom:0.35rem">
|
| 759 |
+
<span style="color:#e2e8f0;font-weight:700">SOURCE {i}</span>
|
| 760 |
+
<span style="background:#1e3a5f;color:#93c5fd;padding:2px 8px;border-radius:4px;font-size:0.72rem">{source_id}</span>
|
| 761 |
+
<span style="color:#94a3b8;font-size:0.72rem">{authority} · {article}{norm_line} · score {score}</span>
|
| 762 |
+
</div>
|
| 763 |
+
<div style="color:#cbd5e1;font-size:0.78rem;line-height:1.55">{text}...</div>
|
| 764 |
+
</div>
|
| 765 |
+
""")
|
| 766 |
+
|
| 767 |
+
return f"""
|
| 768 |
+
<div style="font-family:'IBM Plex Mono',monospace;background:#0f172a;color:#e2e8f0;
|
| 769 |
+
padding:1rem;border-radius:12px;border:1px solid #1e3a5f;margin:1rem 0">
|
| 770 |
+
<div style="color:#94a3b8;font-size:0.75rem;text-transform:uppercase;
|
| 771 |
+
letter-spacing:0.1em;margin-bottom:0.4rem">
|
| 772 |
+
Evidence retrieved from RAG
|
| 773 |
+
</div>
|
| 774 |
+
<div style="color:#64748b;font-size:0.74rem;margin-bottom:0.5rem">
|
| 775 |
+
Compact view of the chunks used as context for Claude. The full raw context remains available below.
|
| 776 |
+
</div>
|
| 777 |
+
{''.join(rows)}
|
| 778 |
+
</div>
|
| 779 |
+
"""
|
| 780 |
+
|
| 781 |
+
|
| 782 |
# ============================================================
|
| 783 |
# Gradio app
|
| 784 |
# ============================================================
|
|
|
|
| 794 |
]
|
| 795 |
|
| 796 |
|
| 797 |
+
def analyze(query: str) -> tuple[str, str, str]:
|
| 798 |
if not query or not query.strip():
|
| 799 |
+
return render_error("Please enter a compliance query."), "", ""
|
| 800 |
|
| 801 |
query = query.strip()
|
| 802 |
print("\n" + "=" * 72, flush=True)
|
|
|
|
| 815 |
)
|
| 816 |
|
| 817 |
if not results:
|
| 818 |
+
return render_error("No relevant regulatory chunks found. Try rephrasing your query."), "", ""
|
| 819 |
|
| 820 |
context = format_context(results)
|
| 821 |
report = call_claude(query, context)
|
| 822 |
if not report:
|
| 823 |
+
return render_error("Could not reach Claude API. Check that ANTHROPIC_API_KEY is set as a Space Secret."), render_evidence_summary(results), context
|
| 824 |
|
| 825 |
report = canonicalize_report(report, results)
|
| 826 |
print(
|
|
|
|
| 830 |
flush=True,
|
| 831 |
)
|
| 832 |
|
| 833 |
+
return render_report(report, query, results), render_evidence_summary(results), context
|
| 834 |
|
| 835 |
|
| 836 |
CSS = """
|
|
|
|
| 849 |
.query-box textarea:focus { border-color: #38bdf8 !important; box-shadow: 0 0 0 2px rgba(56,189,248,0.15) !important; }
|
| 850 |
.analyze-btn { background: #0369a1 !important; color: #fff !important; font-family: 'IBM Plex Mono', monospace !important; font-weight: 600 !important; letter-spacing: 0.05em !important; border: none !important; border-radius: 8px !important; height: 44px !important; font-size: 0.85rem !important; transition: background 0.2s !important; }
|
| 851 |
.analyze-btn:hover { background: #0284c7 !important; }
|
| 852 |
+
.context-box textarea { background: #0f172a !important; border: 1px solid #1e293b !important; color: #cbd5e1 !important; font-family: 'IBM Plex Mono', monospace !important; font-size: 0.75rem !important; }
|
| 853 |
label { color: #94a3b8 !important; font-size: 0.8rem !important; }
|
| 854 |
"""
|
| 855 |
|
|
|
|
| 886 |
|
| 887 |
report_html = gr.HTML(label="Compliance Assessment")
|
| 888 |
|
| 889 |
+
evidence_html = gr.HTML(label="Evidence retrieved from RAG")
|
| 890 |
+
|
| 891 |
+
with gr.Accordion("Full raw retrieved regulatory context", open=False):
|
| 892 |
context_box = gr.Textbox(
|
| 893 |
label="Raw chunks retrieved by RAG",
|
| 894 |
lines=10,
|
|
|
|
| 905 |
""")
|
| 906 |
|
| 907 |
use_example_btn.click(fn=lambda example: example or "", inputs=[example_dropdown], outputs=[query_box])
|
| 908 |
+
analyze_btn.click(fn=analyze, inputs=[query_box], outputs=[report_html, evidence_html, context_box])
|
| 909 |
+
query_box.submit(fn=analyze, inputs=[query_box], outputs=[report_html, evidence_html, context_box])
|
| 910 |
|
| 911 |
if __name__ == "__main__":
|
| 912 |
port = int(os.environ.get("PORT", 7860))
|