Spaces:
Sleeping
Sleeping
File size: 4,185 Bytes
e3518f9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | """
ScamSentry — Gradio UI
Thin presentation layer over the same orchestrator engine cli.py uses —
both call agents.orchestrator.run_pipeline() directly, no duplicated
pipeline logic. The "Agent Trajectory" panel just dumps the same state
dict run_pipeline() already returns (intake_result, reputation_signals,
linguistic_signal, injection_guard_result, draft_verdict,
semantic_check_result, final_verdict, report_text) — no new
instrumentation needed.
Note on types: some agents (url_reputation_agent) have no output_schema,
so their state value is the model's raw JSON text, not a parsed dict —
same reason risk_synthesis_gate.py defensively does
`json.loads(x) if isinstance(x, str) else x` before touching intake_result
or semantic_check_result. This file does the same before rendering.
"""
import json
import mimetypes
import gradio as gr
from agents.orchestrator import run_pipeline
TRAJECTORY_KEYS = [
"intake_result",
"reputation_signals",
"linguistic_signal",
"injection_guard_result",
"draft_verdict",
"semantic_check_result",
"final_verdict",
]
def _maybe_parse(value):
"""Parse a value as JSON if it's a string; return it unchanged
otherwise (or if it's a string but not valid JSON, e.g. report_text,
which is deliberately plain prose)."""
if isinstance(value, str):
try:
return json.loads(value)
except (json.JSONDecodeError, TypeError):
return value
return value
def format_verdict_panel(result: dict) -> str:
verdict = _maybe_parse(result.get("final_verdict")) or {}
report = result.get("report_text") or "(no report generated)"
risk = str(verdict.get("risk_level", "unknown")).upper()
confidence = verdict.get("confidence", "unknown")
injection = verdict.get("injection_attempt_detected", False)
lines = [f"### Risk level: {risk}", f"**Confidence:** {confidence}"]
if injection:
lines.append("**⚠️ Prompt-injection attempt detected in the analyzed content.**")
lines.append("")
lines.append(report)
return "\n".join(lines)
def format_trajectory(result: dict) -> str:
trajectory = {k: _maybe_parse(result.get(k)) for k in TRAJECTORY_KEYS if k in result}
return json.dumps(trajectory, indent=2, default=str)
async def scan(raw_text, image_path):
if not raw_text and not image_path:
return "Please paste a message or upload a screenshot.", "{}"
image_bytes = None
image_mime = "image/png"
if image_path:
with open(image_path, "rb") as f:
image_bytes = f.read()
guessed, _ = mimetypes.guess_type(image_path)
if guessed:
image_mime = guessed
try:
result = await run_pipeline(
text=raw_text or None,
image_bytes=image_bytes,
image_mime=image_mime,
verbose=False,
)
except Exception as e:
return f"⚠️ Scan failed: {e}", "{}"
if not result:
return "⚠️ Pipeline returned no result — check the logs.", "{}"
return format_verdict_panel(result), format_trajectory(result)
with gr.Blocks(title="ScamSentry") as demo:
gr.Markdown(
"# ScamSentry\n"
"Paste a suspicious message or upload a screenshot to check it for scam/phishing signals."
)
with gr.Row():
with gr.Column():
text_input = gr.Textbox(
label="Message text",
placeholder="Paste the email, SMS, or WhatsApp message here...",
lines=8,
)
image_input = gr.Image(label="Or upload a screenshot", type="filepath")
scan_button = gr.Button("Scan", variant="primary")
with gr.Column():
verdict_output = gr.Markdown(label="Verdict")
with gr.Accordion("Agent Trajectory (debug view)", open=False):
trajectory_output = gr.Code(label="Raw agent state", language="json")
scan_button.click(
fn=scan,
inputs=[text_input, image_input],
outputs=[verdict_output, trajectory_output],
)
if __name__ == "__main__":
demo.launch(server_name="0.0.0.0", server_port=7860) |