Spaces:
Running
Running
| """ | |
| MediGuard AI — Gradio Web UI | |
| Provides a simple chat interface and biomarker analysis panel. | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import logging | |
| import os | |
| import httpx | |
| logger = logging.getLogger(__name__) | |
| API_BASE = os.getenv("MEDIGUARD_API_URL", "http://localhost:8000") | |
| def ask_stream(question: str, history: list, model: str): | |
| """Call the /ask/stream endpoint.""" | |
| history = history or [] | |
| if not question.strip(): | |
| yield "", history | |
| return | |
| history.append((question, "")) | |
| try: | |
| with httpx.stream("POST", f"{API_BASE}/ask/stream", json={"question": question}, timeout=60.0) as resp: | |
| resp.raise_for_status() | |
| for line in resp.iter_lines(): | |
| if line.startswith("data: "): | |
| content = line[6:] | |
| if content == "[DONE]": | |
| break | |
| try: | |
| data = json.loads(content) | |
| current_bot_msg = history[-1][1] + data.get("text", "") | |
| history[-1] = (question, current_bot_msg) | |
| yield "", history | |
| except Exception as trace_exc: | |
| logger.debug("Failed to parse streaming chunk: %s", trace_exc) | |
| except Exception as exc: | |
| history[-1] = (question, f"Error: {exc}") | |
| yield "", history | |
| def _call_analyze(biomarkers_json: str) -> str: | |
| """Call the /analyze/structured endpoint.""" | |
| try: | |
| biomarkers = json.loads(biomarkers_json) | |
| with httpx.Client(timeout=60.0) as client: | |
| resp = client.post( | |
| f"{API_BASE}/analyze/structured", | |
| json={"biomarkers": biomarkers}, | |
| ) | |
| resp.raise_for_status() | |
| data = resp.json() | |
| summary = data.get("conversational_summary") or json.dumps(data, indent=2) | |
| return summary | |
| except json.JSONDecodeError: | |
| return 'Invalid JSON. Please enter biomarkers as: {"Glucose": 185, "HbA1c": 8.2}' | |
| except Exception as exc: | |
| return f"Error: {exc}" | |
| def launch_gradio(share: bool = False, server_port: int = 7860) -> None: | |
| """Launch the Gradio interface.""" | |
| try: | |
| import gradio as gr | |
| except ImportError: | |
| raise ImportError("gradio is required. Install: pip install gradio") | |
| with gr.Blocks(title="MediGuard AI", theme=gr.themes.Soft()) as demo: | |
| gr.Markdown("# 🏥 MediGuard AI — Medical Analysis") | |
| gr.Markdown( | |
| "**Disclaimer**: This tool is for informational purposes only and does not " | |
| "replace professional medical advice." | |
| ) | |
| with gr.Tab("Ask a Question"): | |
| with gr.Row(): | |
| with gr.Column(scale=3): | |
| chatbot = gr.Chatbot(label="Medical Q&A History", height=400) | |
| question_input = gr.Textbox( | |
| label="Medical Question", | |
| placeholder="e.g., What does a high HbA1c level indicate?", | |
| lines=2, | |
| ) | |
| with gr.Row(): | |
| ask_btn = gr.Button("Ask (Streaming)", variant="primary") | |
| clear_btn = gr.Button("Clear History") | |
| with gr.Column(scale=1): | |
| model_selector = gr.Dropdown( | |
| choices=["llama-3.3-70b-versatile", "gemini-2.0-flash", "llama3.1:8b"], | |
| value="llama-3.3-70b-versatile", | |
| label="LLM Provider/Model", | |
| ) | |
| ask_btn.click( | |
| fn=ask_stream, inputs=[question_input, chatbot, model_selector], outputs=[question_input, chatbot] | |
| ) | |
| clear_btn.click(fn=lambda: ([], ""), outputs=[chatbot, question_input]) | |
| with gr.Tab("Analyze Biomarkers"): | |
| bio_input = gr.Textbox( | |
| label="Biomarkers (JSON)", | |
| placeholder='{"Glucose": 185, "HbA1c": 8.2, "Cholesterol": 210}', | |
| lines=5, | |
| ) | |
| analyze_btn = gr.Button("Analyze", variant="primary") | |
| analysis_output = gr.Textbox(label="Analysis", lines=20, interactive=False) | |
| analyze_btn.click(fn=_call_analyze, inputs=bio_input, outputs=analysis_output) | |
| with gr.Tab("Search Knowledge Base"): | |
| with gr.Row(): | |
| search_input = gr.Textbox( | |
| label="Search Query", placeholder="e.g., diabetes management guidelines", lines=2, scale=3 | |
| ) | |
| search_mode = gr.Radio( | |
| choices=["hybrid", "bm25", "vector"], value="hybrid", label="Search Strategy", scale=1 | |
| ) | |
| search_btn = gr.Button("Search", variant="primary") | |
| search_output = gr.Textbox(label="Results", lines=15, interactive=False) | |
| def _call_search(query: str, mode: str) -> str: | |
| try: | |
| with httpx.Client(timeout=30.0) as client: | |
| resp = client.post( | |
| f"{API_BASE}/search", | |
| json={"query": query, "top_k": 5, "mode": mode}, | |
| ) | |
| resp.raise_for_status() | |
| data = resp.json() | |
| results = data.get("results", []) | |
| if not results: | |
| return "No results found." | |
| parts = [] | |
| for i, r in enumerate(results, 1): | |
| parts.append( | |
| f"**[{i}] {r.get('title', 'Untitled')}** (score: {r.get('score', 0):.3f})\n" | |
| f"{r.get('text', '')}\n" | |
| ) | |
| return "\n---\n".join(parts) | |
| except Exception as exc: | |
| return f"Error: {exc}" | |
| search_btn.click(fn=_call_search, inputs=[search_input, search_mode], outputs=search_output) | |
| demo.launch(server_name="0.0.0.0", server_port=server_port, share=share) | |
| if __name__ == "__main__": | |
| port = int(os.environ.get("GRADIO_PORT", 7860)) | |
| launch_gradio(server_port=port) | |