"""Step 5 — Gradio chat UI for the Medical RAG Chatbot. Run locally: python app.py Then open the printed local URL in your browser. This same file is the entry point HuggingFace Spaces looks for, so it deploys with no changes. """ from __future__ import annotations import gradio as gr from src.rag_pipeline import RAGChatbot DISCLAIMER = ( "⚠️ **Research demo only — not medical advice.** " "Answers are generated from a small set of documents and may be incomplete " "or wrong. Always consult a qualified ophthalmologist." ) EXAMPLE_QUESTIONS = [ "What is diabetic retinopathy?", "How is the severity of diabetic retinopathy graded?", "What deep learning methods are used to detect diabetic retinopathy?", "Why is early screening for diabetic retinopathy important?", ] def ensure_vector_store() -> None: """Build the ChromaDB index on first run if it doesn't exist yet. This makes the app self-contained on HuggingFace Spaces: if the vector store is missing, it downloads the source PDFs (when needed) and ingests them automatically, so the Space works without a manual build step. """ from src.config import CHROMA_DIR, DATA_DIR if CHROMA_DIR.exists(): return # already built — nothing to do print("No vector store found — building it now (first-run setup) ...") # If there are no source PDFs yet, try to fetch the defaults. if not list(DATA_DIR.glob("*.pdf")): try: from src.download_sources import main as download_main download_main() except Exception as exc: # noqa: BLE001 print(f" (could not auto-download sources: {exc})") # Build the index from whatever PDFs are present. from src.ingest import main as ingest_main ingest_main() # Load the pipeline once at startup. If something is missing (no docs or no key), # show the error in the UI instead of crashing silently. _load_error: str | None = None bot: RAGChatbot | None = None try: ensure_vector_store() bot = RAGChatbot() except Exception as exc: # noqa: BLE001 _load_error = str(exc) def _format_sources(sources) -> str: if not sources: return "" seen = [] for d in sources: src = d.metadata.get("source", "unknown") page = d.metadata.get("page", "?") label = f"{src} (p.{page})" if label not in seen: seen.append(label) return "\n\n**Sources used:** " + "; ".join(seen) def respond(message: str, history): """Gradio chat callback.""" if _load_error: return f"⚠️ Setup incomplete: {_load_error}" if not message.strip(): return "Please type a question about diabetic retinopathy." resp = bot.ask(message) return resp.answer + _format_sources(resp.sources) with gr.Blocks(title="Medical RAG Chatbot") as demo: gr.Markdown("# 🩺 Medical RAG Chatbot — Diabetic Retinopathy") gr.Markdown( "Ask questions about diabetic retinopathy. Answers are grounded in " "open-access research papers using Retrieval-Augmented Generation " "(LangChain · ChromaDB · sentence-transformers · Llama 3.3 70B on Groq)." ) gr.Markdown(DISCLAIMER) gr.ChatInterface( fn=respond, examples=EXAMPLE_QUESTIONS, ) if __name__ == "__main__": demo.launch()