Spaces:
Sleeping
Sleeping
| import sys | |
| import types | |
| if 'audioop' not in sys.modules: | |
| sys.modules['audioop'] = types.ModuleType('audioop') | |
| from flask import Flask, request, jsonify, render_template_string | |
| import anthropic | |
| import os | |
| app = Flask(__name__) | |
| client = anthropic.Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY")) | |
| SYSTEM_PROMPT = """You are a medical AI assistant specializing in disease identification and health analysis. | |
| When given symptoms, images, or descriptions, you: | |
| 1. Analyze the provided information carefully | |
| 2. List the most likely conditions/diseases (ranked by likelihood) | |
| 3. Explain key symptoms that match each condition | |
| 4. Recommend appropriate next steps (see a doctor, urgency level, etc.) | |
| 5. Provide general information about each condition | |
| IMPORTANT DISCLAIMERS: | |
| - Always remind users this is for educational purposes only | |
| - Always recommend consulting a qualified healthcare professional | |
| - Never provide definitive diagnoses | |
| - Be clear about limitations | |
| Format your response clearly with sections: | |
| ## Possible Conditions | |
| ## Symptom Analysis | |
| ## Recommended Actions | |
| ## Important Disclaimer | |
| """ | |
| HTML = """<!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>DxAI — Disease Identification</title> | |
| <link href="https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&family=DM+Sans:wght@300;400;500;600&display=swap" rel="stylesheet"> | |
| <style> | |
| *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } | |
| :root { | |
| --navy: #0a0e1a; --navy-mid: #111827; --navy-light: #1e2d40; | |
| --green: #00ff87; --green-dim: #00cc6a; --green-glow: rgba(0,255,135,0.15); | |
| --cyan: #00d4ff; --text: #e8f4f8; --muted: #7a9bb5; | |
| --border: rgba(0,255,135,0.2); --red: rgba(255,68,68,0.1); --red-b: rgba(255,68,68,0.3); | |
| } | |
| body { background: var(--navy); color: var(--text); font-family: 'DM Sans', sans-serif; min-height: 100vh; padding: 32px 16px; } | |
| .container { max-width: 960px; margin: 0 auto; } | |
| .tag { font-family: 'Space Mono', monospace; font-size: 0.7rem; color: var(--cyan); letter-spacing: 0.2em; text-transform: uppercase; margin-bottom: 8px; } | |
| h1 { font-family: 'Space Mono', monospace; font-size: 2rem; color: var(--green); text-shadow: 0 0 30px var(--green-glow); line-height: 1.2; margin-bottom: 8px; } | |
| h1 span { color: var(--muted); font-size: 1.1rem; display: block; margin-top: 4px; } | |
| .subtitle { color: var(--muted); font-size: 0.9rem; line-height: 1.6; max-width: 560px; margin-bottom: 24px; } | |
| .disclaimer { background: var(--red); border: 1px solid var(--red-b); border-radius: 6px; padding: 10px 16px; color: #ff8888; font-size: 0.82rem; margin-bottom: 28px; } | |
| .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; } | |
| @media (max-width: 640px) { .grid { grid-template-columns: 1fr; } } | |
| .panel { background: var(--navy-mid); border: 1px solid var(--border); border-radius: 8px; padding: 20px; } | |
| .panel-label { font-family: 'Space Mono', monospace; font-size: 0.7rem; color: var(--cyan); letter-spacing: 0.15em; margin-bottom: 12px; } | |
| label { display: block; color: var(--muted); font-size: 0.78rem; font-weight: 500; letter-spacing: 0.05em; text-transform: uppercase; margin-bottom: 6px; margin-top: 14px; } | |
| label:first-of-type { margin-top: 0; } | |
| textarea, input[type=file] { width: 100%; background: var(--navy-light); border: 1px solid var(--border); border-radius: 6px; color: var(--text); font-family: 'DM Sans', sans-serif; font-size: 0.9rem; padding: 10px 12px; resize: vertical; transition: border-color 0.2s; } | |
| textarea:focus { outline: none; border-color: var(--green); box-shadow: 0 0 0 2px var(--green-glow); } | |
| input[type=file] { padding: 8px; cursor: pointer; } | |
| button { margin-top: 16px; width: 100%; background: var(--green); color: var(--navy); font-family: 'Space Mono', monospace; font-weight: 700; font-size: 0.82rem; letter-spacing: 0.05em; border: none; border-radius: 6px; padding: 13px; cursor: pointer; transition: all 0.2s; } | |
| button:hover { background: var(--green-dim); box-shadow: 0 0 20px var(--green-glow); } | |
| button:disabled { opacity: 0.5; cursor: not-allowed; } | |
| .tips { margin-top: 16px; padding: 12px; background: rgba(0,255,135,0.05); border: 1px solid rgba(0,255,135,0.15); border-radius: 6px; font-size: 0.78rem; color: var(--muted); line-height: 1.7; } | |
| .tips strong { color: var(--green); } | |
| #output { min-height: 400px; font-size: 0.9rem; line-height: 1.75; color: var(--text); } | |
| #output .placeholder { color: var(--muted); font-style: italic; } | |
| #output h2 { font-family: 'Space Mono', monospace; font-size: 0.85rem; color: var(--green); border-bottom: 1px solid var(--border); padding-bottom: 4px; margin: 18px 0 8px; } | |
| .spin { display: inline-block; width: 16px; height: 16px; border: 2px solid var(--navy); border-top-color: transparent; border-radius: 50%; animation: spin 0.7s linear infinite; vertical-align: middle; } | |
| @keyframes spin { to { transform: rotate(360deg); } } | |
| .examples { display: flex; gap: 14px; margin-top: 24px; flex-wrap: wrap; } | |
| .ex { flex: 1; min-width: 180px; padding: 14px; border-radius: 8px; font-size: 0.8rem; color: var(--muted); line-height: 1.5; } | |
| .ex-label { font-family: 'Space Mono', monospace; font-size: 0.65rem; letter-spacing: 0.1em; margin-bottom: 5px; } | |
| .ex:nth-child(1) { background: rgba(0,212,255,0.05); border: 1px solid rgba(0,212,255,0.2); } | |
| .ex:nth-child(1) .ex-label { color: var(--cyan); } | |
| .ex:nth-child(2) { background: rgba(0,255,135,0.05); border: 1px solid rgba(0,255,135,0.2); } | |
| .ex:nth-child(2) .ex-label { color: var(--green); } | |
| .ex:nth-child(3) { background: var(--red); border: 1px solid var(--red-b); } | |
| .ex:nth-child(3) .ex-label { color: #ff8888; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <div class="tag">◈ DxAI Diagnostic System v1.0</div> | |
| <h1>Disease Identification<span>AI-Powered Symptom Analysis</span></h1> | |
| <p class="subtitle">Describe your symptoms or upload an image for AI-assisted disease identification. Powered by Claude — for educational purposes only.</p> | |
| <div class="disclaimer">⚠ <strong>Medical Disclaimer:</strong> This tool is for educational and informational purposes only. It does not constitute medical advice, diagnosis, or treatment. Always consult a qualified healthcare professional.</div> | |
| <div class="grid"> | |
| <div class="panel"> | |
| <div class="panel-label">INPUT</div> | |
| <label for="symptoms">Describe Your Symptoms</label> | |
| <textarea id="symptoms" rows="7" placeholder="e.g., Persistent cough for 2 weeks, mild fever (99.5°F), fatigue, and occasional chest tightness. No known allergies. Age 34, non-smoker."></textarea> | |
| <label for="image">Upload Image (Optional)</label> | |
| <input type="file" id="image" accept="image/*"> | |
| <button id="btn" onclick="analyze()">▶ Run Diagnostic Analysis</button> | |
| <div class="tips"><strong>Tips for better results:</strong><br> | |
| • Include duration, severity (1–10), and progression<br> | |
| • Mention age, sex, and relevant medical history<br> | |
| • For images: rashes, skin conditions, wounds work best<br> | |
| • Note any medications currently being taken | |
| </div> | |
| </div> | |
| <div class="panel"> | |
| <div class="panel-label">ANALYSIS OUTPUT</div> | |
| <div id="output"><span class="placeholder">Analysis results will appear here after you submit your symptoms...</span></div> | |
| </div> | |
| </div> | |
| <div class="examples"> | |
| <div class="ex"><div class="ex-label">◈ EXAMPLE: RESPIRATORY</div>Dry cough 10 days, 100°F fever, shortness of breath, no appetite, slight chest pain on deep breathing.</div> | |
| <div class="ex"><div class="ex-label">◈ EXAMPLE: GASTROINTESTINAL</div>Nausea after eating, bloating, sharp abdominal pain (upper right), yellow tinge in eyes, fatigue for 3 days.</div> | |
| <div class="ex"><div class="ex-label">◈ EXAMPLE: DERMATOLOGY</div>Red itchy rash spreading from arms to torso, small blisters, started after using new detergent, 5 days ago.</div> | |
| </div> | |
| </div> | |
| <script> | |
| async function analyze() { | |
| const symptoms = document.getElementById('symptoms').value.trim(); | |
| const imageFile = document.getElementById('image').files[0]; | |
| const btn = document.getElementById('btn'); | |
| const output = document.getElementById('output'); | |
| if (!symptoms && !imageFile) { output.innerHTML = '<span style="color:#ff8888">⚠ Please enter symptoms or upload an image.</span>'; return; } | |
| btn.disabled = true; | |
| btn.innerHTML = '<span class="spin"></span> Analyzing...'; | |
| output.innerHTML = '<span style="color:var(--muted)">Running diagnostic analysis...</span>'; | |
| try { | |
| let imageData = null; | |
| if (imageFile) { | |
| imageData = await new Promise((res, rej) => { const r = new FileReader(); r.onload = () => res(r.result.split(',')[1]); r.onerror = rej; r.readAsDataURL(imageFile); }); | |
| } | |
| const resp = await fetch('/analyze', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({symptoms, image: imageData}) }); | |
| const data = await resp.json(); | |
| if (data.error) { output.innerHTML = `<span style="color:#ff8888">Error: ${data.error}</span>`; } | |
| } else { | |
| let html = data.result | |
| .replace(/&/g,'&') | |
| .replace(/</g,'<') | |
| .replace(/>/g,'>'); | |
| html = html.split('\n').map(line => { | |
| if (line.startsWith('## ')) return '<h2>' + line.slice(3) + '</h2>'; | |
| return line; | |
| }).join('<br>'); | |
| output.innerHTML = html; | |
| } | |
| } catch(e) { output.innerHTML = '<span style="color:#ff8888">Something went wrong. Please try again.</span>'; } | |
| btn.disabled = false; | |
| btn.innerHTML = '▶ Run Diagnostic Analysis'; | |
| } | |
| </script> | |
| </body> | |
| </html>""" | |
| def index(): | |
| return render_template_string(HTML) | |
| def analyze(): | |
| data = request.json | |
| symptoms = data.get("symptoms", "").strip() | |
| image_b64 = data.get("image") | |
| if not symptoms and not image_b64: | |
| return jsonify({"error": "No input provided."}) | |
| content = [] | |
| if image_b64: | |
| content.append({"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": image_b64}}) | |
| prompt = "" | |
| if symptoms: | |
| prompt += f"**Patient Symptoms:**\n{symptoms}\n\n" | |
| if image_b64: | |
| prompt += "An image has also been provided for visual analysis.\n\n" | |
| prompt += "Please analyze and identify possible diseases or conditions." | |
| content.append({"type": "text", "text": prompt}) | |
| try: | |
| response = client.messages.create( | |
| model="claude-opus-4-5", max_tokens=1500, system=SYSTEM_PROMPT, | |
| messages=[{"role": "user", "content": content}] | |
| ) | |
| return jsonify({"result": response.content[0].text}) | |
| except Exception as e: | |
| return jsonify({"error": str(e)}) | |
| if __name__ == "__main__": | |
| app.run(host="0.0.0.0", port=7860) | |