| from fastapi import FastAPI, HTTPException |
| from fastapi.middleware.cors import CORSMiddleware |
| from fastapi.responses import FileResponse |
| from pydantic import BaseModel |
| from typing import List, Optional, Dict |
| import google.generativeai as genai |
| import os |
| from datetime import datetime |
| import uuid |
| import json |
| from pathlib import Path |
| from reportlab.lib.pagesizes import letter, A4 |
| from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle |
| from reportlab.lib.units import inch |
| from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, PageBreak, Table, TableStyle |
| from reportlab.lib import colors |
| from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY |
| from reportlab.pdfgen import canvas |
|
|
| |
| os.environ["GOOGLE_API_KEY"] = GOOGLE_API_KEY |
| genai.configure(api_key=os.getenv("GOOGLE_API_KEY")) |
|
|
| MODEL_ID = "gemini-2.0-flash-exp" |
|
|
| |
| STORAGE_DIR = Path("consultation_storage") |
| STORAGE_DIR.mkdir(exist_ok=True) |
|
|
| PDF_DIR = Path("consultation_pdfs") |
| PDF_DIR.mkdir(exist_ok=True) |
|
|
| |
| DOCTOR_SYSTEM_PROMPT = """ |
| You are Dr. HealBot, a calm, knowledgeable, and empathetic virtual doctor. |
| |
| GOAL: |
| Hold a natural, focused conversation with the patient to understand their health issue and offer helpful preliminary medical guidance. |
| |
| CONVERSATION LOGIC: |
| - Ask only relevant and concise medical questions necessary for diagnosing the illness. |
| - Each question should help clarify symptoms or narrow possible causes. |
| - Stop asking once enough information is collected for a basic assessment. |
| - Then, provide a structured, friendly, and visually clear medical response using headings, emojis, and bullet points. |
| |
| FINAL RESPONSE FORMAT: |
| When giving your full assessment, use this markdown-styled format: |
| |
| π©Ί Based on what you've told me... |
| Brief summary of what the patient described. |
| |
| π‘ Possible Causes (Preliminary) |
| - List 1β2 possible conditions using phrases like "It could be" or "This sounds like". |
| - Include a disclaimer that this is not a confirmed diagnosis. |
| |
| π Suggested Over-the-Counter Medicines |
| - Generic medicine names only (e.g., "Paracetamol 500mg every 6 hours if fever or pain") |
| - Mention to check packaging or consult a pharmacist for dosage confirmation. |
| |
| π₯ Lifestyle & Home Care Tips |
| - 2β3 practical suggestions (rest, hydration, warm compress, balanced diet, etc.) |
| |
| β When to See a Real Doctor |
| - 2β3 warning signs or conditions when urgent medical care is needed. |
| |
| π
Follow-Up Advice |
| - Brief recommendation for self-care or follow-up timing (e.g., "If not improving in 3 days, visit a clinic.") |
| |
| TONE & STYLE: |
| - Speak like a real, caring doctor β short, clear, and empathetic (1β2 sentences per reply). |
| - Use plain language, no jargon. |
| - Only one question per turn unless clarification is essential. |
| - Keep tone warm, calm, and professional. |
| - Early messages: short questions only. |
| - Final message: structured output with emojis and headings. |
| |
| IMPORTANT: |
| - Always emphasize that this is preliminary guidance and not a substitute for professional care. |
| - Never make definitive diagnoses; use phrases like "it sounds like" or "it could be". |
| - If symptoms seem serious, always recommend urgent medical attention. |
| |
| CONVERSATION FLOW: |
| 1. Ask about the main symptom. |
| 2. Ask about its duration, severity, and any triggers. |
| 3. Ask about accompanying symptoms. |
| 4. Ask about medical history, allergies, or medications. |
| 5. Then, provide your structured assessment as described above. |
| """ |
|
|
| |
| |
| |
|
|
| def generate_pdf_summary(session_id: str, summary_text: str, patient_data: Dict, history: List[Dict]) -> str: |
| """Generate a professional PDF summary of the consultation""" |
| |
| pdf_filename = f"{session_id}_summary.pdf" |
| pdf_path = PDF_DIR / pdf_filename |
| |
| |
| doc = SimpleDocTemplate(str(pdf_path), pagesize=letter, |
| rightMargin=72, leftMargin=72, |
| topMargin=72, bottomMargin=18) |
| |
| |
| elements = [] |
| |
| |
| styles = getSampleStyleSheet() |
| |
| |
| title_style = ParagraphStyle( |
| 'CustomTitle', |
| parent=styles['Heading1'], |
| fontSize=24, |
| textColor=colors.HexColor('#667eea'), |
| spaceAfter=30, |
| alignment=TA_CENTER, |
| fontName='Helvetica-Bold' |
| ) |
| |
| heading_style = ParagraphStyle( |
| 'CustomHeading', |
| parent=styles['Heading2'], |
| fontSize=16, |
| textColor=colors.HexColor('#667eea'), |
| spaceAfter=12, |
| spaceBefore=12, |
| fontName='Helvetica-Bold' |
| ) |
| |
| normal_style = ParagraphStyle( |
| 'CustomNormal', |
| parent=styles['Normal'], |
| fontSize=11, |
| spaceAfter=12, |
| alignment=TA_JUSTIFY, |
| leading=14 |
| ) |
| |
| |
| elements.append(Paragraph("π©Ί AI DOCTOR CONSULTATION SUMMARY", title_style)) |
| elements.append(Spacer(1, 0.3*inch)) |
| |
| |
| elements.append(Spacer(1, 0.1*inch)) |
| |
| |
| patient_info_data = [ |
| ['Patient Name:', patient_data.get('name', 'N/A')], |
| ['Age:', patient_data.get('age', 'N/A')], |
| ['Session ID:', session_id[:20] + '...'], |
| ['Consultation Date:', datetime.now().strftime('%B %d, %Y at %I:%M %p')], |
| ['Total Messages:', str(len(history))] |
| ] |
| |
| patient_table = Table(patient_info_data, colWidths=[2*inch, 4*inch]) |
| patient_table.setStyle(TableStyle([ |
| ('BACKGROUND', (0, 0), (0, -1), colors.HexColor('#f0f0f0')), |
| ('TEXTCOLOR', (0, 0), (-1, -1), colors.black), |
| ('ALIGN', (0, 0), (-1, -1), 'LEFT'), |
| ('FONTNAME', (0, 0), (0, -1), 'Helvetica-Bold'), |
| ('FONTNAME', (1, 0), (1, -1), 'Helvetica'), |
| ('FONTSIZE', (0, 0), (-1, -1), 10), |
| ('BOTTOMPADDING', (0, 0), (-1, -1), 8), |
| ('TOPPADDING', (0, 0), (-1, -1), 8), |
| ('GRID', (0, 0), (-1, -1), 1, colors.grey) |
| ])) |
| |
| elements.append(patient_table) |
| elements.append(Spacer(1, 0.3*inch)) |
| |
| |
| elements.append(Paragraph("CONSULTATION SUMMARY", heading_style)) |
| |
| |
| summary_lines = summary_text.split('\n') |
| for line in summary_lines: |
| if line.strip(): |
| |
| line = line.replace('π©Ί', '[Medical] ') |
| line = line.replace('π‘', '[Insight] ') |
| line = line.replace('π', '[Medicine] ') |
| line = line.replace('π₯', '[Lifestyle] ') |
| line = line.replace('β οΈ', '[Warning] ') |
| line = line.replace('β ', '[Warning] ') |
| line = line.replace('π
', '[Follow-up] ') |
| line = line.replace('β', '-') |
| |
| |
| if line.strip().startswith('**') and line.strip().endswith('**'): |
| elements.append(Paragraph(line.strip('*'), heading_style)) |
| else: |
| elements.append(Paragraph(line, normal_style)) |
| |
| elements.append(Spacer(1, 0.3*inch)) |
| |
| |
| elements.append(PageBreak()) |
| elements.append(Paragraph("CONVERSATION HISTORY", heading_style)) |
| elements.append(Spacer(1, 0.2*inch)) |
| |
| for i, msg in enumerate(history, 1): |
| role = "DOCTOR" if msg['role'] == 'assistant' else "PATIENT" |
| timestamp = msg.get('timestamp', 'N/A') |
| |
| role_style = ParagraphStyle( |
| f'Role{i}', |
| parent=styles['Normal'], |
| fontSize=10, |
| textColor=colors.HexColor('#667eea') if role == "DOCTOR" else colors.HexColor('#28a745'), |
| fontName='Helvetica-Bold', |
| spaceAfter=4 |
| ) |
| |
| elements.append(Paragraph(f"{role} ({timestamp}):", role_style)) |
| |
| content = msg['content'].replace('π©Ί', '').replace('π‘', '').replace('π', '') |
| content = content.replace('π₯', '').replace('β οΈ', '').replace('β ', '').replace('π
', '') |
| elements.append(Paragraph(content, normal_style)) |
| elements.append(Spacer(1, 0.15*inch)) |
| |
| |
| elements.append(Spacer(1, 0.3*inch)) |
| |
| disclaimer_style = ParagraphStyle( |
| 'Disclaimer', |
| parent=styles['Normal'], |
| fontSize=9, |
| textColor=colors.red, |
| alignment=TA_CENTER, |
| fontName='Helvetica-Bold', |
| borderColor=colors.red, |
| borderWidth=1, |
| borderPadding=10, |
| spaceAfter=12 |
| ) |
| |
| elements.append(Paragraph( |
| "β IMPORTANT DISCLAIMER β <br/>" + |
| "This is a preliminary AI-generated consultation for informational purposes only.<br/>" + |
| "It is NOT a substitute for professional medical advice, diagnosis, or treatment.<br/>" + |
| "Always seek the advice of a qualified healthcare provider with any questions regarding a medical condition.", |
| disclaimer_style |
| )) |
| |
| |
| doc.build(elements) |
| |
| return pdf_filename |
|
|
| |
| |
| |
|
|
| def save_session_to_json(session_id: str, memory: 'ConversationMemory'): |
| """Save session data to JSON file""" |
| file_path = STORAGE_DIR / f"{session_id}.json" |
| |
| session_data = { |
| "session_id": session_id, |
| "created_at": memory.created_at.isoformat(), |
| "last_updated": datetime.now().isoformat(), |
| "patient_data": memory.patient_data, |
| "questions_asked": memory.questions_asked, |
| "history": memory.history, |
| "message_count": len(memory.history), |
| "pdf_filename": getattr(memory, 'pdf_filename', None) |
| } |
| |
| with open(file_path, 'w', encoding='utf-8') as f: |
| json.dump(session_data, f, indent=2, ensure_ascii=False) |
|
|
| def load_session_from_json(session_id: str) -> Optional[Dict]: |
| """Load session data from JSON file""" |
| file_path = STORAGE_DIR / f"{session_id}.json" |
| |
| if not file_path.exists(): |
| return None |
| |
| with open(file_path, 'r', encoding='utf-8') as f: |
| return json.load(f) |
|
|
| def list_all_sessions() -> List[Dict]: |
| """List all stored sessions""" |
| sessions_list = [] |
| |
| for file_path in STORAGE_DIR.glob("*.json"): |
| try: |
| with open(file_path, 'r', encoding='utf-8') as f: |
| data = json.load(f) |
| sessions_list.append({ |
| "session_id": data["session_id"], |
| "created_at": data["created_at"], |
| "last_updated": data.get("last_updated", data["created_at"]), |
| "patient_name": data["patient_data"].get("name", "Unknown"), |
| "message_count": data["message_count"], |
| "has_pdf": data.get("pdf_filename") is not None |
| }) |
| except Exception as e: |
| print(f"Error reading {file_path}: {e}") |
| |
| return sorted(sessions_list, key=lambda x: x["last_updated"], reverse=True) |
|
|
| |
| |
| |
|
|
| class ConversationMemory: |
| """Manages short-term memory for each session""" |
| def __init__(self, max_messages: int = 20, session_id: str = None): |
| self.max_messages = max_messages |
| self.history = [] |
| self.patient_data = {} |
| self.created_at = datetime.now() |
| self.questions_asked = 0 |
| self.session_id = session_id |
| self.pdf_filename = None |
| |
| def add_message(self, role: str, content: str): |
| """Add message to history with memory management""" |
| self.history.append({ |
| "role": role, |
| "content": content, |
| "timestamp": datetime.now().isoformat() |
| }) |
| |
| if role == "assistant" and "?" in content: |
| self.questions_asked += 1 |
| |
| if len(self.history) > self.max_messages: |
| self.history = [self.history[0]] + self.history[-(self.max_messages-1):] |
| |
| if self.session_id: |
| save_session_to_json(self.session_id, self) |
| |
| def extract_patient_info(self, message: str): |
| """Extract and store patient information from conversation""" |
| message_lower = message.lower() |
| |
| if any(word in message_lower for word in ["name is", "i'm", "i am", "im"]): |
| words = message.split() |
| for i, word in enumerate(words): |
| if word.lower() in ["is", "i'm", "am", "im"] and i + 1 < len(words): |
| self.patient_data["name"] = words[i + 1].strip(".,!?") |
| |
| if "year" in message_lower or "age" in message_lower: |
| import re |
| age_match = re.search(r'\b(\d{1,3})\b', message) |
| if age_match: |
| self.patient_data["age"] = age_match.group(1) |
| |
| if "fever" in message_lower or "pain" in message_lower or "sick" in message_lower: |
| self.patient_data["has_symptoms"] = True |
| |
| def should_give_recommendations(self) -> bool: |
| """Check if we should provide recommendations now""" |
| return ( |
| self.questions_asked >= 7 or |
| self.patient_data.get("has_symptoms", False) |
| ) |
| |
| def get_context_summary(self) -> str: |
| """Generate a brief context summary for the AI""" |
| summary = "\n[Session Context: " |
| if "name" in self.patient_data: |
| summary += f"Name: {self.patient_data['name']}, " |
| if "age" in self.patient_data: |
| summary += f"Age: {self.patient_data['age']}, " |
| summary += f"Questions asked: {self.questions_asked}/7, " |
| |
| if self.questions_asked >= 5: |
| summary += "β οΈ IMPORTANT: You've asked enough questions. After the next 1-2 answers, IMMEDIATELY provide comprehensive medical recommendations.]" |
| elif self.questions_asked >= 7: |
| summary += "β οΈ CRITICAL: You MUST provide comprehensive medical recommendations NOW. Do not ask more questions!]" |
| else: |
| summary += f"Ask {7 - self.questions_asked} more essential questions then give recommendations.]" |
| |
| return summary |
| |
| def get_gemini_history(self) -> List[Dict]: |
| """Convert history to Gemini format""" |
| gemini_history = [] |
| for msg in self.history: |
| gemini_history.append({ |
| "role": "user" if msg["role"] == "user" else "model", |
| "parts": [msg["content"]] |
| }) |
| return gemini_history |
| |
| @classmethod |
| def from_json(cls, session_data: Dict) -> 'ConversationMemory': |
| """Create ConversationMemory from JSON data""" |
| memory = cls(session_id=session_data["session_id"]) |
| memory.history = session_data["history"] |
| memory.patient_data = session_data["patient_data"] |
| memory.questions_asked = session_data["questions_asked"] |
| memory.created_at = datetime.fromisoformat(session_data["created_at"]) |
| memory.pdf_filename = session_data.get("pdf_filename") |
| return memory |
|
|
| sessions: Dict[str, ConversationMemory] = {} |
|
|
| def cleanup_old_sessions(): |
| """Remove sessions older than 1 hour from memory""" |
| current_time = datetime.now() |
| expired_sessions = [] |
| |
| for session_id, memory in sessions.items(): |
| age = (current_time - memory.created_at).total_seconds() |
| if age > 3600: |
| expired_sessions.append(session_id) |
| |
| for session_id in expired_sessions: |
| del sessions[session_id] |
|
|
| |
| |
| |
|
|
| app = FastAPI( |
| title="AI Doctor Consultation API with PDF Generation", |
| description="Professional medical consultation API with PDF summary generation", |
| version="3.0.0" |
| ) |
|
|
| app.add_middleware( |
| CORSMiddleware, |
| allow_origins=["*"], |
| allow_credentials=True, |
| allow_methods=["*"], |
| allow_headers=["*"], |
| ) |
|
|
| |
| class ChatRequest(BaseModel): |
| session_id: Optional[str] = None |
| message: str |
|
|
| class ChatResponse(BaseModel): |
| session_id: str |
| response: str |
| timestamp: str |
| patient_data: Dict |
|
|
| class SessionRequest(BaseModel): |
| session_id: str |
|
|
| class SummaryResponse(BaseModel): |
| summary: str |
| session_id: str |
| pdf_filename: str |
| pdf_url: str |
|
|
| class HealthCheck(BaseModel): |
| status: str |
| timestamp: str |
| active_sessions: int |
| stored_sessions: int |
| stored_pdfs: int |
|
|
| |
| |
| |
|
|
| @app.get("/", response_model=HealthCheck) |
| async def root(): |
| """Health check endpoint""" |
| cleanup_old_sessions() |
| stored_count = len(list(STORAGE_DIR.glob("*.json"))) |
| pdf_count = len(list(PDF_DIR.glob("*.pdf"))) |
| return { |
| "status": "healthy", |
| "timestamp": datetime.now().isoformat(), |
| "active_sessions": len(sessions), |
| "stored_sessions": stored_count, |
| "stored_pdfs": pdf_count |
| } |
|
|
| @app.post("/start-session") |
| async def start_session(): |
| """Start a new consultation session""" |
| session_id = str(uuid.uuid4()) |
| sessions[session_id] = ConversationMemory(max_messages=20, session_id=session_id) |
| |
| initial_message = "Hello! I'm Dr. AI Assistant. I'm here to help you today.\n\nπ€ May I have your name, please?" |
| |
| sessions[session_id].add_message("assistant", initial_message) |
| |
| return { |
| "session_id": session_id, |
| "message": initial_message, |
| "timestamp": datetime.now().isoformat() |
| } |
|
|
| @app.post("/chat", response_model=ChatResponse) |
| async def chat(request: ChatRequest): |
| """Send a message and get doctor's response""" |
| try: |
| if not request.session_id or request.session_id not in sessions: |
| session_id = str(uuid.uuid4()) |
| sessions[session_id] = ConversationMemory(max_messages=20, session_id=session_id) |
| else: |
| session_id = request.session_id |
| |
| memory = sessions[session_id] |
| memory.extract_patient_info(request.message) |
| memory.add_message("user", request.message) |
| |
| context = memory.get_context_summary() |
| system_prompt = DOCTOR_SYSTEM_PROMPT + context |
| |
| model = genai.GenerativeModel( |
| model_name=MODEL_ID, |
| system_instruction=system_prompt |
| ) |
| |
| chat = model.start_chat(history=memory.get_gemini_history()[:-1]) |
| response = chat.send_message(request.message) |
| doctor_response = response.text |
| |
| memory.add_message("assistant", doctor_response) |
| |
| return { |
| "session_id": session_id, |
| "response": doctor_response, |
| "timestamp": datetime.now().isoformat(), |
| "patient_data": memory.patient_data |
| } |
| |
| except Exception as e: |
| raise HTTPException(status_code=500, detail=f"Error: {str(e)}") |
|
|
| @app.post("/summary", response_model=SummaryResponse) |
| async def generate_summary(request: SessionRequest): |
| """Generate consultation summary and PDF""" |
| if request.session_id not in sessions: |
| session_data = load_session_from_json(request.session_id) |
| if not session_data: |
| raise HTTPException(status_code=404, detail="Session not found") |
| memory = ConversationMemory.from_json(session_data) |
| sessions[request.session_id] = memory |
| else: |
| memory = sessions[request.session_id] |
| |
| summary_request = """Please generate a COMPREHENSIVE and DETAILED medical consultation summary based on our entire conversation. Make it thorough and professional: |
| |
| π **COMPREHENSIVE MEDICAL CONSULTATION SUMMARY** |
| βββββββββββββββββββββββββββββββββββββββββββββ |
| |
| **PATIENT INFORMATION:** |
| - Full Name: [Patient's name] |
| - Age: [Patient's age if mentioned] |
| - Gender: [If mentioned] |
| - Consultation Date: [Current date and time] |
| - Session Duration: [Approximate] |
| - Current Medications: [List all mentioned] |
| - Known Allergies: [If mentioned] |
| |
| **CHIEF COMPLAINTS & SYMPTOMS:** |
| [Provide a detailed description of ALL symptoms mentioned, including:] |
| - Primary symptom and severity |
| - Duration of each symptom |
| - Onset and progression |
| - Associated symptoms |
| - Aggravating and relieving factors |
| - Impact on daily activities |
| |
| **DETAILED MEDICAL HISTORY:** |
| [Include everything discussed:] |
| - Current medications and dosages |
| - Past medical conditions |
| - Recent illnesses or infections |
| - Family medical history (if mentioned) |
| - Lifestyle factors (sleep, stress, diet) |
| - Recent travel or exposures |
| |
| **CLINICAL ASSESSMENT:** |
| [Provide detailed analysis:] |
| - Most likely diagnosis with explanation |
| - Differential diagnoses (2-3 possibilities) |
| - Reasoning behind each possibility |
| - Risk factors present |
| - Severity assessment |
| |
| **COMPREHENSIVE TREATMENT PLAN:** |
| |
| 1. **IMMEDIATE CARE RECOMMENDATIONS:** |
| - What to do in the next 24-48 hours |
| - Symptom management strategies |
| - Warning signs to watch for |
| |
| 2. **MEDICATION RECOMMENDATIONS:** |
| - Primary medications (generic names, dosages, frequency, duration) |
| - Alternative options if first choice unavailable |
| - Potential side effects to monitor |
| - Drug interactions to avoid |
| - When to take each medication (with/without food) |
| - Important: Check with pharmacist for exact dosing |
| |
| 3. **DETAILED DIETARY RECOMMENDATIONS:** |
| - Foods to eat (specific examples and portions) |
| - Foods to avoid completely |
| - Meal timing and frequency |
| - Hydration guidelines (specific amounts) |
| - Nutritional supplements if needed |
| - Sample meal plan for recovery |
| |
| 4. **LIFESTYLE MODIFICATIONS:** |
| - Sleep recommendations (hours, timing, environment) |
| - Rest and activity balance |
| - Stress management techniques |
| - Environmental modifications |
| - Work/school attendance guidance |
| - Specific activities to avoid |
| |
| 5. **HOME CARE REMEDIES:** |
| - Natural remedies that may help |
| - Temperature management techniques |
| - Pain relief methods |
| - Steam inhalation or other therapies |
| - Specific home treatments for symptoms |
| |
| 6. **EXERCISE & PHYSICAL ACTIVITY:** |
| - Current activity restrictions |
| - Safe exercises during recovery |
| - When to resume normal activities |
| - Gradual activity progression plan |
| - Post-recovery exercise recommendations |
| |
| 7. **PREVENTIVE MEASURES:** |
| - How to prevent recurrence |
| - Hygiene practices |
| - Vaccination recommendations |
| - Family/household precautions |
| - Long-term health maintenance |
| |
| 8. **MONITORING PLAN:** |
| - Symptoms to track daily |
| - How to measure improvement |
| - When improvement should be expected |
| - What to document for doctor visit |
| |
| **CRITICAL WARNING SIGNS - SEEK IMMEDIATE MEDICAL ATTENTION IF:** |
| [List 5-7 specific warning signs that require emergency care:] |
| - [Specific symptom with threshold] |
| - [Specific symptom with threshold] |
| - [Continue with detailed warnings] |
| |
| **FOLLOW-UP CARE PLAN:** |
| - Timeline for self-care (e.g., "Monitor for 48 hours") |
| - When to schedule doctor appointment (specific timeframe) |
| - What information to bring to doctor |
| - Specialist referral recommendations if needed |
| - Follow-up testing that may be needed |
| |
| **PROGNOSIS & EXPECTED RECOVERY:** |
| - Expected recovery timeline |
| - What to expect during recovery process |
| - Signs of improvement to look for |
| - Long-term outlook |
| |
| **ADDITIONAL RESOURCES:** |
| - Reputable health information sources |
| - Support resources if applicable |
| - Emergency contact information reminder |
| |
| **PATIENT EDUCATION:** |
| - Understanding your condition |
| - How the body fights this illness |
| - Why specific recommendations are important |
| - Common misconceptions about this condition |
| |
| βββββββββββββββββββββββββββββββββββββββββββββ |
| β οΈ **CRITICAL DISCLAIMER** β οΈ |
| This is a preliminary AI-generated consultation for informational and educational purposes ONLY. |
| This is NOT a substitute for professional medical advice, diagnosis, or treatment. |
| This AI cannot examine you physically, run laboratory tests, or make definitive diagnoses. |
| ALWAYS seek the advice of a qualified, licensed healthcare provider with any questions regarding a medical condition. |
| Never disregard professional medical advice or delay seeking it because of this AI consultation. |
| In case of emergency, call your local emergency services immediately. |
| βββββββββββββββββββββββββββββββββββββββββββββ |
| |
| Please make this summary as detailed, professional, and helpful as possible. Include specific, actionable advice.""" |
| |
| try: |
| model = genai.GenerativeModel( |
| model_name=MODEL_ID, |
| system_instruction=DOCTOR_SYSTEM_PROMPT |
| ) |
| |
| chat = model.start_chat(history=memory.get_gemini_history()) |
| response = chat.send_message(summary_request) |
| summary_text = response.text |
| |
| |
| pdf_filename = generate_pdf_summary( |
| request.session_id, |
| summary_text, |
| memory.patient_data, |
| memory.history |
| ) |
| |
| |
| memory.pdf_filename = pdf_filename |
| save_session_to_json(request.session_id, memory) |
| |
| return { |
| "summary": summary_text, |
| "session_id": request.session_id, |
| "pdf_filename": pdf_filename, |
| "pdf_url": f"/download-pdf/{request.session_id}" |
| } |
| |
| except Exception as e: |
| raise HTTPException(status_code=500, detail=f"Error generating summary: {str(e)}") |
|
|
| @app.get("/download-pdf/{session_id}") |
| async def download_pdf(session_id: str): |
| """Download PDF summary for a session""" |
| |
| if session_id in sessions: |
| memory = sessions[session_id] |
| else: |
| session_data = load_session_from_json(session_id) |
| if not session_data: |
| raise HTTPException(status_code=404, detail="Session not found") |
| memory = ConversationMemory.from_json(session_data) |
| |
| if not memory.pdf_filename: |
| raise HTTPException(status_code=404, detail="PDF not generated yet. Please generate summary first.") |
| |
| pdf_path = PDF_DIR / memory.pdf_filename |
| |
| if not pdf_path.exists(): |
| raise HTTPException(status_code=404, detail="PDF file not found") |
| |
| patient_name = memory.patient_data.get('name', 'Patient') |
| download_filename = f"Consultation_Summary_{patient_name}_{datetime.now().strftime('%Y%m%d')}.pdf" |
| |
| return FileResponse( |
| path=str(pdf_path), |
| media_type='application/pdf', |
| filename=download_filename |
| ) |
|
|
| @app.get("/load-session/{session_id}") |
| async def load_session(session_id: str): |
| """Load a previous consultation session by ID""" |
| if session_id in sessions: |
| memory = sessions[session_id] |
| return { |
| "session_id": session_id, |
| "loaded": True, |
| "from_cache": True, |
| "history": memory.history, |
| "patient_data": memory.patient_data, |
| "created_at": memory.created_at.isoformat(), |
| "questions_asked": memory.questions_asked, |
| "has_pdf": memory.pdf_filename is not None, |
| "pdf_url": f"/download-pdf/{session_id}" if memory.pdf_filename else None |
| } |
| |
| session_data = load_session_from_json(session_id) |
| |
| if not session_data: |
| raise HTTPException(status_code=404, detail=f"Session {session_id} not found") |
| |
| memory = ConversationMemory.from_json(session_data) |
| sessions[session_id] = memory |
| |
| return { |
| "session_id": session_id, |
| "loaded": True, |
| "from_cache": False, |
| "history": memory.history, |
| "patient_data": memory.patient_data, |
| "created_at": memory.created_at.isoformat(), |
| "questions_asked": memory.questions_asked, |
| "has_pdf": memory.pdf_filename is not None, |
| "pdf_url": f"/download-pdf/{session_id}" if memory.pdf_filename else None, |
| "message": "Session loaded successfully. You can continue the conversation." |
| } |
|
|
| @app.get("/all-sessions") |
| async def get_all_sessions(): |
| """Get list of all stored consultation sessions""" |
| return { |
| "total_sessions": len(list(STORAGE_DIR.glob("*.json"))), |
| "sessions": list_all_sessions() |
| } |
|
|
| @app.post("/restart-session") |
| async def restart_session(request: SessionRequest): |
| """Restart a consultation session""" |
| if request.session_id in sessions: |
| del sessions[request.session_id] |
| |
| sessions[request.session_id] = ConversationMemory(max_messages=20, session_id=request.session_id) |
| |
| initial_message = "Consultation restarted. Hello! I'm Dr. AI Assistant. May I have your name please?" |
| sessions[request.session_id].add_message("assistant", initial_message) |
| |
| return { |
| "session_id": request.session_id, |
| "message": initial_message, |
| "timestamp": datetime.now().isoformat() |
| } |
|
|
| @app.delete("/session/{session_id}") |
| async def delete_session(session_id: str): |
| """Delete a consultation session (from memory, JSON, and PDF)""" |
| if session_id in sessions: |
| memory = sessions[session_id] |
| pdf_filename = memory.pdf_filename |
| del sessions[session_id] |
| else: |
| session_data = load_session_from_json(session_id) |
| pdf_filename = session_data.get('pdf_filename') if session_data else None |
| |
| |
| file_path = STORAGE_DIR / f"{session_id}.json" |
| if file_path.exists(): |
| file_path.unlink() |
| |
| |
| if pdf_filename: |
| pdf_path = PDF_DIR / pdf_filename |
| if pdf_path.exists(): |
| pdf_path.unlink() |
| |
| return {"message": "Session and associated files deleted successfully"} |
|
|
| @app.get("/session/{session_id}/history") |
| async def get_session_history(session_id: str): |
| """Get conversation history for a session""" |
| if session_id in sessions: |
| memory = sessions[session_id] |
| else: |
| session_data = load_session_from_json(session_id) |
| if not session_data: |
| raise HTTPException(status_code=404, detail="Session not found") |
| memory = ConversationMemory.from_json(session_data) |
| |
| return { |
| "session_id": session_id, |
| "history": memory.history, |
| "patient_data": memory.patient_data, |
| "created_at": memory.created_at.isoformat(), |
| "questions_asked": memory.questions_asked, |
| "has_pdf": memory.pdf_filename is not None |
| } |
|
|
| @app.get("/active-sessions") |
| async def get_active_sessions(): |
| """Get list of all active sessions in memory""" |
| cleanup_old_sessions() |
| return { |
| "active_sessions": len(sessions), |
| "sessions": [ |
| { |
| "session_id": sid, |
| "created_at": mem.created_at.isoformat(), |
| "message_count": len(mem.history), |
| "questions_asked": mem.questions_asked, |
| "patient_data": mem.patient_data, |
| "has_pdf": mem.pdf_filename is not None |
| } |
| for sid, mem in sessions.items() |
| ] |
| } |
|
|
| if __name__ == "__main__": |
| import uvicorn |
| uvicorn.run(app, host="0.0.0.0", port=8000) |