""" app.py — GharScan HuggingFace Space Uses gr.Blocks (ZeroGPU-compatible). Custom CSS for Off-Brand badge. """ import os import spaces import gradio as gr from PIL import Image from pathlib import Path from inference import run_gharscan_pipeline from agent_trace import AgentTraceLogger trace_logger = AgentTraceLogger() # ── ZeroGPU-compatible inference ────────────────────────────────────────────── @spaces.GPU def analyze_image(image: Image.Image, language: str) -> dict: if image is None: return {} session = trace_logger.start_trace() report = run_gharscan_pipeline(image, language=language, trace_session=session) trace_logger.save_trace(session) return report def analyze_and_render(image, language): if image is None: return "

Please upload or take a photo first.

" r = analyze_image(image, language) if not r.get("analysis_ok"): return f"

⚠️ {r.get('description','Analysis failed.')}

" color = r.get("severity_color", "#6b7280") sev = r.get("severity", 0) pct = sev * 20 struct_html = "" if r.get("is_structural"): struct_html = f"""
⚠️ STRUCTURAL RISK — {r.get("structural_reasoning","")}
""" else: struct_html = "
✅ Not Structural — No immediate safety risk
" liability = "" if r.get("show_liability_banner"): liability = f"
⚠️ {r.get('liability_text','')}
" disclaimer = "" if r.get("disclaimer"): disclaimer = f"
{r['disclaimer']}
" monsoon = "" if r.get("monsoon_risk"): monsoon = "
🌧️ Monsoon Risk: This defect worsens during heavy rainfall. Address before June.
" return f"""
{struct_html}
{r.get("defect_display","")}
{r.get("defect_type","").replace("_"," ").upper()}
{sev}
{r.get("severity_label","").upper()}
CosmeticModerateCritical
WHAT IT IS
{r.get("description","")}
WHY IT HAPPENS
{r.get("primary_cause","")}
WHAT TO DO
{r.get("immediate_action","")}
WHEN TO ACT
{r.get("urgency_display","")}
{r.get("cost_range_inr","")}
👷 {r.get("professional_display","")}
{monsoon} {liability} {disclaimer}
""" # ── Custom CSS ───────────────────────────────────────────────────────────────── CSS = """ body, .gradio-container { background: #0f1114 !important; color: #e8eaed !important; } .gradio-container { max-width: 700px !important; margin: 0 auto !important; } .gr-button-primary { background: linear-gradient(135deg,#2563eb,#1d4ed8) !important; border: none !important; } .gr-button-primary:hover { opacity: 0.9 !important; } footer { display: none !important; } #component-0 { padding: 20px !important; } .dark { --background-fill-primary: #181b20; --background-fill-secondary: #20242b; --border-color-primary: #2a2f38; --color-text-body: #e8eaed; } """ # ── UI ────────────────────────────────────────────────────────────────────────── with gr.Blocks( css=CSS, title="GharScan — Building Defect Inspector", theme=gr.themes.Base( primary_hue="blue", neutral_hue="slate", ) ) as demo: gr.HTML("""
🏗️
GharScan
AI Building Defect Inspector · India
● Qwen2-VL-2B · 2.07B
""") with gr.Row(): image_input = gr.Image( sources=["upload", "webcam"], type="pil", label="📸 Take Photo or Upload", height=300, ) language = gr.Radio( choices=["en", "hi"], value="en", label="Output language", ) analyze_btn = gr.Button("🔍 Analyse Defect", variant="primary", size="lg") report_output = gr.HTML(label="Inspection Report") analyze_btn.click( fn=analyze_and_render, inputs=[image_input, language], outputs=report_output, api_name="analyze" ) gr.HTML("""

Qwen2-VL-2B fine-tuned on Indian building defects · No cloud APIs

🏗️ Built for Build Small Hackathon 2026 · Backyard AI Track

GharScan is a triage aid, not a substitute for professional structural assessment.

""") demo.queue() demo.launch()