import gradio as gr import requests import json # ==================== API Functions ==================== def check_api_health(api_url): """Check if the backend API is healthy""" try: response = requests.get(f"{api_url}/api/v1/health", timeout=10) response.raise_for_status() health_data = response.json() return f"✅ API is healthy and running!\n\nService: {health_data.get('service', 'ICD-CPT Coding API')}" except requests.exceptions.Timeout: return "❌ API Health Check Failed: Request timed out" except requests.exceptions.RequestException as e: return f"❌ API Health Check Failed: {str(e)}" except Exception as e: return f"❌ Unexpected Error: {str(e)}" def analyze_provider_notes(provider_notes, api_url, progress=gr.Progress()): """Send provider notes to backend API for analysis""" if not provider_notes or not provider_notes.strip(): return generate_empty_response() progress(0, desc="Starting analysis...") try: payload = {"provider_notes": provider_notes} progress(0.3, desc="Sending request to API...") response = requests.post( f"{api_url}/api/v1/analyze", json=payload, headers={"Content-Type": "application/json"}, timeout=60 ) progress(0.6, desc="Processing response...") response.raise_for_status() result = response.json() progress(0.9, desc="Formatting results...") # Format complete response formatted_response = format_complete_response(result) progress(1.0, desc="Complete!") return formatted_response except requests.exceptions.Timeout: return "❌ **Request Timeout**\n\nThe API is taking too long to respond. Please try again." except requests.exceptions.HTTPError as e: return f"❌ **HTTP Error {e.response.status_code}**\n\n{e.response.text}" except requests.exceptions.RequestException as e: return f"❌ **Request Error**\n\n{str(e)}" except Exception as e: return f"❌ **Unexpected Error**\n\n{str(e)}" def generate_empty_response(): """Generate empty response message""" return """ ### 💬 Welcome to ICD-10 & CPT Coding Assistant Please enter provider notes in the chat box and click **Send** to analyze. **Tips:** - Provide detailed clinical documentation - Include symptoms, diagnoses, and procedures - Be specific about treatments and prescriptions """ def format_complete_response(result): """Format complete analysis response""" output = "---\n\n" # Overall Summary if result.get("overall_summary"): output += f"### 📋 Overall Summary\n\n{result['overall_summary']}\n\n---\n\n" # ICD-10 Codes output += "### 🏥 ICD-10 Diagnostic Codes\n\n" icd_codes = result.get("icd_codes", []) if icd_codes: for idx, icd in enumerate(icd_codes, 1): output += f"**{idx}. {icd.get('code', 'N/A')}** - {icd.get('description', 'N/A')}\n\n" output += f"*Explanation:* {icd.get('explanation', 'N/A')}\n\n" else: output += "*No ICD-10 codes identified*\n\n" output += "---\n\n" # CPT Codes output += "### 💼 CPT Procedure Codes\n\n" cpt_codes = result.get("cpt_codes", []) if cpt_codes: for idx, cpt in enumerate(cpt_codes, 1): output += f"**{idx}. {cpt.get('code', 'N/A')}** - {cpt.get('description', 'N/A')}\n\n" output += f"*Explanation:* {cpt.get('explanation', 'N/A')}\n\n" else: output += "*No CPT codes identified*\n\n" return output # ==================== Example Notes ==================== EXAMPLES = { "Acute Bronchitis": """Patient presents with acute bronchitis. Cough for 5 days, productive with yellow sputum. Lung exam reveals diffuse wheezing. Prescribed azithromycin 500mg.""", "Type 2 Diabetes": """Patient with type 2 diabetes mellitus, uncontrolled. HbA1c 9.2%. Discussed diet and medication compliance. Adjusted insulin dosing. Referred to diabetes educator.""", "Hypertension Follow-up": """Follow-up visit for hypertension. Blood pressure 145/92. Patient reports good medication compliance. Continue current antihypertensive regimen. Return in 3 months.""", "Annual Physical": """Annual physical examination for 45-year-old patient. Comprehensive metabolic panel ordered. Discussed preventive health measures. No acute concerns. Patient in good health.""", "Acute Pharyngitis": """Patient with sore throat for 3 days, fever 101.5°F. Physical exam shows erythematous pharynx with exudate. Rapid strep test positive. Prescribed amoxicillin 500mg TID for 10 days.""", } def load_example(example_name): """Load example provider notes""" return EXAMPLES.get(example_name, "") # ==================== Custom CSS ==================== custom_css = """ /* Global Styles */ .gradio-container { max-width: 100% !important; padding: 0 !important; background: linear-gradient(135deg, #0d1117 0%, #1a1f2e 100%) !important; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important; } /* Header Styling */ .header-container { background: linear-gradient(90deg, #00d4aa 0%, #00a896 100%); padding: 25px; text-align: center; border-radius: 0; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3); margin-bottom: 0; } .header-container h1 { color: white; font-size: 2rem; font-weight: 700; margin: 0; text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2); } .header-container p { color: #e0f7f4; font-size: 1rem; margin: 8px 0 0 0; } /* Sidebar Styling */ .sidebar { background: #1a1f2e !important; border-right: 1px solid #00d4aa !important; padding: 20px !important; height: 100vh !important; overflow-y: auto !important; } .sidebar-title { color: #00d4aa !important; font-size: 1.3rem !important; font-weight: 600 !important; margin-bottom: 20px !important; padding-bottom: 10px !important; border-bottom: 2px solid #00d4aa !important; } /* Example Buttons */ .example-btn { background: linear-gradient(135deg, #00d4aa 0%, #00a896 100%) !important; color: white !important; border: none !important; border-radius: 8px !important; padding: 12px 16px !important; margin: 8px 0 !important; font-weight: 500 !important; cursor: pointer !important; transition: all 0.3s ease !important; width: 100% !important; text-align: left !important; } .example-btn:hover { background: linear-gradient(135deg, #00ffcc 0%, #00d4aa 100%) !important; transform: translateX(5px) !important; box-shadow: 0 4px 12px rgba(0, 212, 170, 0.4) !important; } /* Chat Container */ .chat-container { background: #0d1117 !important; border-radius: 12px !important; padding: 20px !important; height: calc(100vh - 200px) !important; display: flex !important; flex-direction: column !important; } /* Input Area */ .input-row { background: #1a1f2e !important; border: 2px solid #00d4aa !important; border-radius: 12px !important; padding: 15px !important; margin-top: 10px !important; } .input-row textarea { background: #0d1117 !important; color: #e6e6e6 !important; border: 1px solid #2d3748 !important; border-radius: 8px !important; font-size: 1rem !important; padding: 12px !important; } .input-row textarea:focus { border-color: #00d4aa !important; outline: none !important; box-shadow: 0 0 0 3px rgba(0, 212, 170, 0.1) !important; } /* Send Button */ .send-button { background: linear-gradient(135deg, #00d4aa 0%, #00a896 100%) !important; color: white !important; border: none !important; border-radius: 8px !important; padding: 12px 32px !important; font-size: 1rem !important; font-weight: 600 !important; cursor: pointer !important; transition: all 0.3s ease !important; margin-top: 10px !important; } .send-button:hover { background: linear-gradient(135deg, #00ffcc 0%, #00d4aa 100%) !important; box-shadow: 0 6px 20px rgba(0, 212, 170, 0.4) !important; transform: translateY(-2px) !important; } /* Clear Button */ .clear-button { background: linear-gradient(135deg, #2d3748 0%, #1a202c 100%) !important; color: white !important; border: 1px solid #4a5568 !important; border-radius: 8px !important; padding: 12px 32px !important; font-size: 1rem !important; font-weight: 600 !important; cursor: pointer !important; transition: all 0.3s ease !important; margin-top: 10px !important; } .clear-button:hover { background: linear-gradient(135deg, #4a5568 0%, #2d3748 100%) !important; border-color: #718096 !important; transform: translateY(-2px) !important; } /* Output Area */ .output-container { background: #1a1f2e !important; border: 1px solid #2d3748 !important; border-radius: 12px !important; padding: 20px !important; color: #e6e6e6 !important; overflow-y: auto !important; flex-grow: 1 !important; margin-bottom: 15px !important; } .output-container h3 { color: #00d4aa !important; border-bottom: 2px solid #00d4aa !important; padding-bottom: 8px !important; margin-top: 20px !important; } .output-container strong { color: #00ffcc !important; } .output-container hr { border: none !important; border-top: 1px solid #2d3748 !important; margin: 20px 0 !important; } /* API Config Section */ .api-config { background: #1a1f2e !important; border: 1px solid #2d3748 !important; border-radius: 8px !important; padding: 15px !important; margin-bottom: 15px !important; } .api-config input { background: #0d1117 !important; color: #e6e6e6 !important; border: 1px solid #2d3748 !important; border-radius: 6px !important; padding: 10px !important; } .api-config input:focus { border-color: #00d4aa !important; outline: none !important; } /* Check API Button */ .check-api-button { background: linear-gradient(135deg, #00a896 0%, #008577 100%) !important; color: white !important; border: none !important; border-radius: 6px !important; padding: 10px 20px !important; font-weight: 500 !important; cursor: pointer !important; transition: all 0.3s ease !important; } .check-api-button:hover { background: linear-gradient(135deg, #00d4aa 0%, #00a896 100%) !important; box-shadow: 0 4px 12px rgba(0, 168, 150, 0.4) !important; } /* Footer */ .footer { text-align: center; padding: 20px; color: #718096; font-size: 0.9rem; background: #0d1117; border-top: 1px solid #2d3748; margin-top: 20px; } .footer strong { color: #00d4aa; } /* Scrollbar Styling */ ::-webkit-scrollbar { width: 8px; } ::-webkit-scrollbar-track { background: #0d1117; } ::-webkit-scrollbar-thumb { background: #00d4aa; border-radius: 4px; } ::-webkit-scrollbar-thumb:hover { background: #00ffcc; } /* Accordion Styling */ .accordion { background: #1a1f2e !important; border: 1px solid #2d3748 !important; border-radius: 8px !important; } .accordion summary { color: #00d4aa !important; font-weight: 600 !important; padding: 12px !important; cursor: pointer !important; } /* Responsive */ @media (max-width: 768px) { .header-container h1 { font-size: 1.5rem; } .sidebar { height: auto !important; border-right: none !important; border-bottom: 1px solid #00d4aa !important; } } """ # ==================== Gradio Interface ==================== with gr.Blocks(css=custom_css, theme=gr.themes.Soft(), title="ICD-10 & CPT Coding Assistant") as demo: # Hidden state for API URL api_url_state = gr.State(value="https://Distopia22-icd-cpt-coding-api.hf.space") # Header gr.HTML("""
AI-Powered Medical Coding Analysis using Groq LLaMA 3.3 70B