dylanglenister commited on
Commit
e17b0a7
·
1 Parent(s): 8ca39cb

Removed depreciated code.

Browse files

Uses context manager instead of events which are depreciated.

Files changed (1) hide show
  1. app.py +454 -449
app.py CHANGED
@@ -1,76 +1,128 @@
1
  # app.py
2
- import os
3
  import time
4
- import logging
 
5
  import uvicorn
6
- from google import genai
7
- from fastapi import FastAPI, Request, HTTPException
8
- from fastapi.responses import HTMLResponse, JSONResponse
9
- from fastapi.staticfiles import StaticFiles
10
  from fastapi.middleware.cors import CORSMiddleware
 
 
 
11
  from pydantic import BaseModel
12
- from typing import Optional, Dict, Any
13
- import json
14
 
15
  # Load environment variables from .env file
16
  try:
17
- from dotenv import load_dotenv
18
- load_dotenv()
19
- print("✅ Environment variables loaded from .env file")
20
  except ImportError:
21
- print("⚠️ python-dotenv not available, using system environment variables")
22
  except Exception as e:
23
- print(f"⚠️ Error loading .env file: {e}")
24
 
 
25
  # Import our custom modules
26
  from memo.memory import MemoryLRU
27
- from memo.history import MedicalHistoryManager
28
- from utils.rotator import APIKeyRotator
29
  from utils.embeddings import create_embedding_client
30
  from utils.logger import get_logger
 
31
 
32
  # Configure logging
33
  logger = get_logger("MEDICAL_APP", __name__)
34
 
35
  # Pydantic models for API requests/responses
36
  class ChatRequest(BaseModel):
37
- user_id: str
38
- session_id: str
39
- message: str
40
- user_role: Optional[str] = "Medical Professional"
41
- user_specialty: Optional[str] = ""
42
- title: Optional[str] = "New Chat" # Added missing title field
43
 
44
  class ChatResponse(BaseModel):
45
- response: str
46
- session_id: str
47
- timestamp: str
48
- medical_context: Optional[str] = None
49
 
50
  class UserProfileRequest(BaseModel):
51
- user_id: str
52
- name: str
53
- role: str
54
- specialty: Optional[str] = ""
55
 
56
  class SessionRequest(BaseModel):
57
- user_id: str
58
- title: Optional[str] = "New Chat"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
  # Initialize FastAPI app
61
  app = FastAPI(
62
- title="Medical AI Assistant",
63
- description="AI-powered medical chatbot with memory and context awareness",
64
- version="1.0.0"
 
65
  )
66
 
67
  # Add CORS middleware
68
  app.add_middleware(
69
- CORSMiddleware,
70
- allow_origins=["*"],
71
- allow_credentials=True,
72
- allow_methods=["*"],
73
- allow_headers=["*"],
74
  )
75
 
76
  # Initialize core components
@@ -84,70 +136,70 @@ nvidia_rotator = APIKeyRotator("NVIDIA_API_", max_slots=5)
84
 
85
  # Mock medical knowledge base for demo purposes
86
  MEDICAL_KB = {
87
- "symptoms": {
88
- "fever": "Fever is a temporary increase in body temperature, often due to illness. Normal body temperature is around 98.6°F (37°C).",
89
- "headache": "Headache is pain in the head or upper neck. Common types include tension headaches, migraines, and cluster headaches.",
90
- "cough": "Cough is a sudden expulsion of air from the lungs. It can be dry or productive (bringing up mucus).",
91
- "fatigue": "Fatigue is extreme tiredness that doesn't improve with rest. It can be caused by various medical conditions.",
92
- "nausea": "Nausea is a feeling of sickness with an inclination to vomit. It can be caused by various conditions including infections, medications, and pregnancy.",
93
- "dizziness": "Dizziness is a sensation of lightheadedness or unsteadiness. It can be caused by inner ear problems, low blood pressure, or neurological conditions.",
94
- "chest pain": "Chest pain can have many causes, from muscle strain to serious heart conditions. Any unexplained chest pain should be evaluated by a healthcare provider.",
95
- "shortness of breath": "Shortness of breath, or dyspnea, is difficulty breathing. It can be caused by respiratory, cardiac, or other medical conditions."
96
- },
97
- "conditions": {
98
- "hypertension": "Hypertension (high blood pressure) is a common condition that affects the arteries. It's often called the 'silent killer' because it usually has no symptoms.",
99
- "diabetes": "Diabetes is a chronic disease that affects how your body turns food into energy. There are two main types: Type 1 and Type 2.",
100
- "asthma": "Asthma is a condition that affects the airways in the lungs. It can cause wheezing, shortness of breath, chest tightness, and coughing.",
101
- "pneumonia": "Pneumonia is an infection that inflames the air sacs in one or both lungs. It can be caused by bacteria, viruses, or fungi.",
102
- "heart disease": "Heart disease refers to various conditions affecting the heart, including coronary artery disease, heart failure, and arrhythmias.",
103
- "arthritis": "Arthritis is inflammation of the joints, causing pain and stiffness. The most common types are osteoarthritis and rheumatoid arthritis.",
104
- "depression": "Depression is a mental health disorder characterized by persistently depressed mood or loss of interest in activities.",
105
- "anxiety": "Anxiety disorders involve excessive fear or worry that can interfere with daily activities and relationships."
106
- },
107
- "medications": {
108
- "aspirin": "Aspirin is a common medication used to treat pain, fever, and inflammation. It's also used to prevent heart attacks and strokes.",
109
- "ibuprofen": "Ibuprofen is a nonsteroidal anti-inflammatory drug (NSAID) used to reduce fever and treat pain or inflammation.",
110
- "acetaminophen": "Acetaminophen is used to treat pain and reduce fever. It's generally safe when used as directed.",
111
- "antibiotics": "Antibiotics are medications that fight bacterial infections. They don't work against viral infections like colds or flu.",
112
- "insulin": "Insulin is a hormone that helps control blood sugar levels. It's essential for people with Type 1 diabetes and some with Type 2.",
113
- "statins": "Statins are medications that help lower cholesterol levels in the blood, reducing the risk of heart disease and stroke."
114
- },
115
- "procedures": {
116
- "blood test": "Blood tests can check for various conditions, monitor organ function, and assess overall health. Common types include CBC, metabolic panels, and lipid profiles.",
117
- "x-ray": "X-rays use radiation to create images of bones and some soft tissues. They're commonly used to diagnose fractures, pneumonia, and other conditions.",
118
- "mri": "MRI (Magnetic Resonance Imaging) uses magnetic fields and radio waves to create detailed images of organs and tissues.",
119
- "ct scan": "CT scans use X-rays and computer technology to create cross-sectional images of the body, useful for diagnosing various conditions."
120
- }
121
  }
122
 
123
  def search_medical_kb(query: str) -> str:
124
- """Search the medical knowledge base for relevant information"""
125
- query_lower = query.lower()
126
- relevant_info = []
127
-
128
- for category, items in MEDICAL_KB.items():
129
- for key, value in items.items():
130
- if query_lower in key.lower() or query_lower in value.lower():
131
- relevant_info.append(f"{key.title()}: {value}")
132
-
133
- if relevant_info:
134
- return "\n\n".join(relevant_info[:3]) # Limit to 3 most relevant
135
- return ""
136
 
137
  async def generate_medical_response_with_gemini(user_message: str, user_role: str, user_specialty: str, medical_context: str = "", rotator=None) -> str:
138
- """Generate a medical response using Gemini AI for intelligent, contextual responses"""
139
- try:
140
- # Get API key from rotator
141
- api_key = rotator.get_key() if rotator else None
142
- if not api_key:
143
- logger.warning("No Gemini API key available, using fallback response")
144
- return generate_medical_response_fallback(user_message, user_role, user_specialty, medical_context)
145
-
146
- # Configure Gemini
147
- client = genai.Client(api_key=api_key)
148
-
149
- # Build context-aware prompt
150
- prompt = f"""You are a knowledgeable medical AI assistant. Provide a comprehensive, accurate, and helpful response to this medical question.
151
 
152
  **User Role:** {user_role}
153
  **User Specialty:** {user_specialty if user_specialty else 'General'}
@@ -172,390 +224,343 @@ async def generate_medical_response_with_gemini(user_message: str, user_role: st
172
 
173
  Remember: This is for educational purposes only. Always emphasize consulting healthcare professionals for medical advice."""
174
 
175
- # Generate response
176
- response = client.models.generate_content(model="gemini-2.5-flash", contents=prompt)
177
-
178
- if response.text:
179
- # Add medical disclaimer if not already present
180
- if "disclaimer" not in response.text.lower() and "consult" not in response.text.lower():
181
- response.text += "\n\n⚠️ **Important Disclaimer:** This information is for educational purposes only and should not replace professional medical advice, diagnosis, or treatment. Always consult with qualified healthcare professionals."
182
-
183
- return response.text
184
-
185
- # Fallback if Gemini fails
186
- logger.warning("Gemini response generation failed, using fallback")
187
- return generate_medical_response_fallback(user_message, user_role, user_specialty, medical_context)
188
-
189
- except Exception as e:
190
- logger.warning(f"Gemini medical response generation failed: {e}, using fallback")
191
- return generate_medical_response_fallback(user_message, user_role, user_specialty, medical_context)
192
 
193
  def generate_medical_response_fallback(user_message: str, user_role: str, user_specialty: str, medical_context: str = "") -> str:
194
- """Fallback medical response generator using local knowledge base"""
195
-
196
- # Search medical knowledge base
197
- kb_info = search_medical_kb(user_message)
198
-
199
- # Build response based on available information
200
- response_parts = []
201
-
202
- # Analyze the question to provide more specific responses
203
- question_lower = user_message.lower()
204
-
205
- if kb_info:
206
- response_parts.append(f"Based on your question about medical topics, here's what I found:\n\n{kb_info}")
207
-
208
- # Add specific guidance based on the medical topic
209
- if any(word in question_lower for word in ["fever", "temperature", "hot"]):
210
- response_parts.append("\n\n**Key Points about Fever:**")
211
- response_parts.append("• Normal body temperature is around 98.6°F (37°C)")
212
- response_parts.append("• Fever is often a sign of infection or inflammation")
213
- response_parts.append("• Monitor for other symptoms that accompany fever")
214
- response_parts.append("• Seek medical attention for high fevers (>103°F/39.4°C) or persistent fevers")
215
-
216
- elif any(word in question_lower for word in ["headache", "head pain", "migraine"]):
217
- response_parts.append("\n\n**Key Points about Headaches:**")
218
- response_parts.append("• Tension headaches are the most common type")
219
- response_parts.append("• Migraines often have specific triggers and symptoms")
220
- response_parts.append("• Sudden, severe headaches require immediate medical attention")
221
- response_parts.append("• Keep a headache diary to identify patterns")
222
-
223
- elif any(word in question_lower for word in ["cough", "cold", "respiratory"]):
224
- response_parts.append("\n\n**Key Points about Respiratory Symptoms:**")
225
- response_parts.append("• Dry vs. productive cough have different implications")
226
- response_parts.append("• Most colds resolve within 7-10 days")
227
- response_parts.append("• Persistent cough may indicate underlying conditions")
228
- response_parts.append("• Monitor for difficulty breathing or chest pain")
229
-
230
- elif any(word in question_lower for word in ["hypertension", "blood pressure", "high bp"]):
231
- response_parts.append("\n\n**Key Points about Hypertension:**")
232
- response_parts.append("• Often called the 'silent killer' due to lack of symptoms")
233
- response_parts.append("• Regular monitoring is essential")
234
- response_parts.append("• Lifestyle modifications can help control blood pressure")
235
- response_parts.append("• Medication may be necessary for some individuals")
236
-
237
- elif any(word in question_lower for word in ["diabetes", "blood sugar", "glucose"]):
238
- response_parts.append("\n\n**Key Points about Diabetes:**")
239
- response_parts.append("• Type 1: Autoimmune, requires insulin")
240
- response_parts.append("• Type 2: Often lifestyle-related, may be managed with diet/exercise")
241
- response_parts.append("• Regular blood sugar monitoring is crucial")
242
- response_parts.append("• Complications can affect multiple organ systems")
243
-
244
- else:
245
- # Provide more helpful response for general questions
246
- if "what is" in question_lower or "define" in question_lower:
247
- response_parts.append("I understand you're asking about a medical topic. While I don't have specific information about this particular condition or symptom, I can provide some general guidance.")
248
- elif "how to" in question_lower or "treatment" in question_lower:
249
- response_parts.append("I understand you're asking about treatment or management of a medical condition. This is an area where professional medical advice is particularly important.")
250
- elif "symptom" in question_lower or "sign" in question_lower:
251
- response_parts.append("I understand you're asking about symptoms or signs of a medical condition. Remember that symptoms can vary between individuals and may indicate different conditions.")
252
- else:
253
- response_parts.append("Thank you for your medical question. While I can provide general information, it's important to consult with healthcare professionals for personalized medical advice.")
254
-
255
- # Add role-specific guidance
256
- if user_role.lower() in ["physician", "doctor", "nurse"]:
257
- response_parts.append("\n\n**Professional Context:** As a healthcare professional, you're likely familiar with these concepts. Remember to always follow your institution's protocols and guidelines, and consider the latest clinical evidence in your practice.")
258
- elif user_role.lower() in ["medical student", "student"]:
259
- response_parts.append("\n\n**Educational Context:** As a medical student, this information can help with your studies. Always verify information with your professors and clinical supervisors, and use this as a starting point for further research.")
260
- elif user_role.lower() in ["patient"]:
261
- response_parts.append("\n\n**Patient Context:** As a patient, this information is for educational purposes only. Please discuss any concerns with your healthcare provider, and don't make treatment decisions based solely on this information.")
262
- else:
263
- response_parts.append("\n\n**General Context:** This information is provided for educational purposes. Always consult with qualified healthcare professionals for medical advice.")
264
-
265
- # Add specialty-specific information if available
266
- if user_specialty and user_specialty.lower() in ["cardiology", "cardiac"]:
267
- response_parts.append("\n\n**Cardiology Perspective:** Given your interest in cardiology, consider how this information relates to cardiovascular health and patient care. Many conditions can have cardiac implications.")
268
- elif user_specialty and user_specialty.lower() in ["pediatrics", "pediatric"]:
269
- response_parts.append("\n\n**Pediatric Perspective:** In pediatric care, remember that children may present differently than adults and may require specialized approaches. Consider age-appropriate considerations.")
270
- elif user_specialty and user_specialty.lower() in ["emergency", "er"]:
271
- response_parts.append("\n\n**Emergency Medicine Perspective:** In emergency settings, rapid assessment and intervention are crucial. Consider the urgency and severity of presenting symptoms.")
272
-
273
- # Add medical disclaimer
274
- response_parts.append("\n\n⚠️ **Important Disclaimer:** This information is for educational purposes only and should not replace professional medical advice, diagnosis, or treatment. Always consult with qualified healthcare professionals.")
275
-
276
- return "\n".join(response_parts)
277
 
278
  def generate_medical_response(user_message: str, user_role: str, user_specialty: str, medical_context: str = "") -> str:
279
- """Legacy function - now calls the fallback generator"""
280
- return generate_medical_response_fallback(user_message, user_role, user_specialty, medical_context)
281
 
282
  @app.get("/", response_class=HTMLResponse)
283
  async def get_medical_chatbot():
284
- """Serve the medical chatbot UI"""
285
- try:
286
- with open("static/index.html", "r", encoding="utf-8") as f:
287
- html_content = f.read()
288
- return HTMLResponse(content=html_content)
289
- except FileNotFoundError:
290
- raise HTTPException(status_code=404, detail="Medical chatbot UI not found")
291
 
292
  @app.post("/chat", response_model=ChatResponse)
293
  async def chat_endpoint(request: ChatRequest):
294
- """Handle chat messages and generate medical responses"""
295
- start_time = time.time()
296
-
297
- try:
298
- logger.info(f"Chat request from user {request.user_id} in session {request.session_id}")
299
- logger.info(f"Message: {request.message[:100]}...") # Log first 100 chars of message
300
-
301
- # Get or create user profile
302
- user_profile = memory_system.get_user(request.user_id)
303
- if not user_profile:
304
- memory_system.create_user(request.user_id, request.user_role or "Anonymous")
305
- if request.user_specialty:
306
- memory_system.get_user(request.user_id).set_preference("specialty", request.user_specialty)
307
-
308
- # Get or create session
309
- session = memory_system.get_session(request.session_id)
310
- if not session:
311
- session_id = memory_system.create_session(request.user_id, request.title or "New Chat")
312
- session = memory_system.get_session(session_id)
313
- logger.info(f"Created new session: {session_id}")
314
-
315
- # Get medical context from memory
316
- medical_context = history_manager.get_conversation_context(
317
- request.user_id,
318
- request.session_id,
319
- request.message
320
- )
321
-
322
- # Generate response using Gemini AI
323
- logger.info(f"Generating medical response using Gemini AI for user {request.user_id}")
324
- response = await generate_medical_response_with_gemini(
325
- request.message,
326
- request.user_role or "Medical Professional",
327
- request.user_specialty or "",
328
- medical_context,
329
- gemini_rotator
330
- )
331
- logger.info(f"Gemini response generated successfully, length: {len(response)} characters")
332
-
333
- # Process and store the exchange
334
- try:
335
- await history_manager.process_medical_exchange(
336
- request.user_id,
337
- request.session_id,
338
- request.message,
339
- response,
340
- gemini_rotator,
341
- nvidia_rotator
342
- )
343
- except Exception as e:
344
- logger.warning(f"Failed to process medical exchange: {e}")
345
- # Continue without storing if there's an error
346
-
347
- # Calculate response time
348
- response_time = time.time() - start_time
349
-
350
- logger.info(f"Generated response in {response_time:.2f}s for user {request.user_id}")
351
- logger.info(f"Response length: {len(response)} characters")
352
-
353
- return ChatResponse(
354
- response=response,
355
- session_id=request.session_id,
356
- timestamp=time.strftime("%Y-%m-%d %H:%M:%S"),
357
- medical_context=medical_context if medical_context else None
358
- )
359
-
360
- except Exception as e:
361
- logger.error(f"Error in chat endpoint: {e}")
362
- logger.error(f"Request data: {request.dict()}")
363
- raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}")
364
 
365
  @app.post("/users")
366
  async def create_user_profile(request: UserProfileRequest):
367
- """Create or update user profile"""
368
- try:
369
- user = memory_system.create_user(request.user_id, request.name)
370
- user.set_preference("role", request.role)
371
- if request.specialty:
372
- user.set_preference("specialty", request.specialty)
373
-
374
- return {"message": "User profile created successfully", "user_id": request.user_id}
375
- except Exception as e:
376
- logger.error(f"Error creating user profile: {e}")
377
- raise HTTPException(status_code=500, detail=str(e))
378
 
379
  @app.get("/users/{user_id}")
380
  async def get_user_profile(user_id: str):
381
- """Get user profile and sessions"""
382
- try:
383
- user = memory_system.get_user(user_id)
384
- if not user:
385
- raise HTTPException(status_code=404, detail="User not found")
386
-
387
- sessions = memory_system.get_user_sessions(user_id)
388
-
389
- return {
390
- "user": {
391
- "id": user.user_id,
392
- "name": user.name,
393
- "role": user.preferences.get("role", "Unknown"),
394
- "specialty": user.preferences.get("specialty", ""),
395
- "created_at": user.created_at,
396
- "last_seen": user.last_seen
397
- },
398
- "sessions": [
399
- {
400
- "id": session.session_id,
401
- "title": session.title,
402
- "created_at": session.created_at,
403
- "last_activity": session.last_activity,
404
- "message_count": len(session.messages)
405
- }
406
- for session in sessions
407
- ]
408
- }
409
- except HTTPException:
410
- raise
411
- except Exception as e:
412
- logger.error(f"Error getting user profile: {e}")
413
- raise HTTPException(status_code=500, detail=str(e))
414
 
415
  @app.post("/sessions")
416
  async def create_chat_session(request: SessionRequest):
417
- """Create a new chat session"""
418
- try:
419
- session_id = memory_system.create_session(request.user_id, request.title or "New Chat")
420
- return {"session_id": session_id, "message": "Session created successfully"}
421
- except Exception as e:
422
- logger.error(f"Error creating session: {e}")
423
- raise HTTPException(status_code=500, detail=str(e))
424
 
425
  @app.get("/sessions/{session_id}")
426
  async def get_chat_session(session_id: str):
427
- """Get chat session details and messages"""
428
- try:
429
- session = memory_system.get_session(session_id)
430
- if not session:
431
- raise HTTPException(status_code=404, detail="Session not found")
432
-
433
- return {
434
- "session_id": session.session_id,
435
- "user_id": session.user_id,
436
- "title": session.title,
437
- "created_at": session.created_at,
438
- "last_activity": session.last_activity,
439
- "messages": session.messages
440
- }
441
- except HTTPException:
442
- raise
443
- except Exception as e:
444
- logger.error(f"Error getting session: {e}")
445
- raise HTTPException(status_code=500, detail=str(e))
446
 
447
  @app.delete("/sessions/{session_id}")
448
  async def delete_chat_session(session_id: str):
449
- """Delete a chat session"""
450
- try:
451
- memory_system.delete_session(session_id)
452
- return {"message": "Session deleted successfully"}
453
- except Exception as e:
454
- logger.error(f"Error deleting session: {e}")
455
- raise HTTPException(status_code=500, detail=str(e))
456
 
457
  @app.get("/health")
458
  async def health_check():
459
- """Health check endpoint"""
460
- return {
461
- "status": "healthy",
462
- "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
463
- "components": {
464
- "memory_system": "operational",
465
- "embedding_client": "operational" if embedding_client.is_available() else "fallback_mode",
466
- "api_rotator": "operational",
467
- "gemini_keys_available": len([k for k in gemini_rotator.keys if k]) > 0,
468
- "nvidia_keys_available": len([k for k in nvidia_rotator.keys if k]) > 0
469
- }
470
- }
471
 
472
  @app.get("/api/info")
473
  async def get_api_info():
474
- """Get API information and capabilities"""
475
- return {
476
- "name": "Medical AI Assistant",
477
- "version": "1.0.0",
478
- "description": "AI-powered medical chatbot with memory and context awareness",
479
- "features": [
480
- "Multi-user support with profiles",
481
- "Chat session management",
482
- "Medical context memory",
483
- "API key rotation",
484
- "Embedding-based similarity search",
485
- "Medical knowledge base integration"
486
- ],
487
- "endpoints": [
488
- "POST /chat - Send chat message",
489
- "POST /users - Create user profile",
490
- "GET /users/{user_id} - Get user profile and sessions",
491
- "POST /sessions - Create chat session",
492
- "GET /sessions/{session_id} - Get session details",
493
- "DELETE /sessions/{session_id} - Delete session",
494
- "GET /health - Health check",
495
- "GET /api/info - API information"
496
- ]
497
- }
498
 
499
  # Mount static files
500
  app.mount("/static", StaticFiles(directory="static"), name="static")
501
 
502
- # Startup event
503
- @app.on_event("startup")
504
- async def startup_event():
505
- """Initialize application on startup"""
506
- logger.info("🚀 Starting Medical AI Assistant...")
507
-
508
- # Check system resources
509
- try:
510
- import psutil
511
- memory = psutil.virtual_memory()
512
- cpu = psutil.cpu_percent(interval=1)
513
- logger.info(f"System Resources - RAM: {memory.percent}%, CPU: {cpu}%")
514
-
515
- if memory.percent > 85:
516
- logger.warning("⚠️ High RAM usage detected!")
517
- if cpu > 90:
518
- logger.warning("⚠️ High CPU usage detected!")
519
- except ImportError:
520
- logger.info("psutil not available, skipping system resource check")
521
-
522
- # Check API keys
523
- gemini_keys = len([k for k in gemini_rotator.keys if k])
524
- if gemini_keys == 0:
525
- logger.warning("⚠️ No Gemini API keys found! Set GEMINI_API_1, GEMINI_API_2, etc. environment variables.")
526
- else:
527
- logger.info(f"✅ {gemini_keys} Gemini API keys available")
528
-
529
- nvidia_keys = len([k for k in nvidia_rotator.keys if k])
530
- if nvidia_keys == 0:
531
- logger.warning("⚠️ No NVIDIA API keys found! Set NVIDIA_API_1, NVIDIA_API_2, etc. environment variables.")
532
- else:
533
- logger.info(f"✅ {nvidia_keys} NVIDIA API keys available")
534
-
535
- # Check embedding client
536
- if embedding_client.is_available():
537
- logger.info("✅ Embedding model loaded successfully")
538
- else:
539
- logger.info("⚠️ Using fallback embedding mode")
540
-
541
- logger.info("✅ Medical AI Assistant startup complete")
542
-
543
- # Shutdown event
544
- @app.on_event("shutdown")
545
- async def shutdown_event():
546
- """Cleanup on shutdown"""
547
- logger.info("🛑 Shutting down Medical AI Assistant...")
548
-
549
  if __name__ == "__main__":
550
- logger.info("Starting Medical AI Assistant server...")
551
- try:
552
- uvicorn.run(
553
- app,
554
- host="0.0.0.0",
555
- port=8000,
556
- log_level="info",
557
- reload=True
558
- )
559
- except Exception as e:
560
- logger.error(f"❌ Server startup failed: {e}")
561
- exit(1)
 
1
  # app.py
 
2
  import time
3
+ from contextlib import asynccontextmanager
4
+
5
  import uvicorn
6
+ from fastapi import FastAPI, HTTPException
 
 
 
7
  from fastapi.middleware.cors import CORSMiddleware
8
+ from fastapi.responses import HTMLResponse
9
+ from fastapi.staticfiles import StaticFiles
10
+ from google import genai
11
  from pydantic import BaseModel
 
 
12
 
13
  # Load environment variables from .env file
14
  try:
15
+ from dotenv import load_dotenv
16
+ load_dotenv()
17
+ print("✅ Environment variables loaded from .env file")
18
  except ImportError:
19
+ print("⚠️ python-dotenv not available, using system environment variables")
20
  except Exception as e:
21
+ print(f"⚠️ Error loading .env file: {e}")
22
 
23
+ from memo.history import MedicalHistoryManager
24
  # Import our custom modules
25
  from memo.memory import MemoryLRU
 
 
26
  from utils.embeddings import create_embedding_client
27
  from utils.logger import get_logger
28
+ from utils.rotator import APIKeyRotator
29
 
30
  # Configure logging
31
  logger = get_logger("MEDICAL_APP", __name__)
32
 
33
  # Pydantic models for API requests/responses
34
  class ChatRequest(BaseModel):
35
+ user_id: str
36
+ session_id: str
37
+ message: str
38
+ user_role: str | None = "Medical Professional"
39
+ user_specialty: str | None = ""
40
+ title: str | None = "New Chat" # Added missing title field
41
 
42
  class ChatResponse(BaseModel):
43
+ response: str
44
+ session_id: str
45
+ timestamp: str
46
+ medical_context: str | None = None
47
 
48
  class UserProfileRequest(BaseModel):
49
+ user_id: str
50
+ name: str
51
+ role: str
52
+ specialty: str | None = ""
53
 
54
  class SessionRequest(BaseModel):
55
+ user_id: str
56
+ title: str | None = "New Chat"
57
+
58
+ # Startup event
59
+ def startup_event():
60
+ """Initialize application on startup"""
61
+ logger.info("🚀 Starting Medical AI Assistant...")
62
+
63
+ # Check system resources
64
+ try:
65
+ import psutil
66
+ memory = psutil.virtual_memory()
67
+ cpu = psutil.cpu_percent(interval=1)
68
+ logger.info(f"System Resources - RAM: {memory.percent}%, CPU: {cpu}%")
69
+
70
+ if memory.percent > 85:
71
+ logger.warning("⚠️ High RAM usage detected!")
72
+ if cpu > 90:
73
+ logger.warning("⚠️ High CPU usage detected!")
74
+ except ImportError:
75
+ logger.info("psutil not available, skipping system resource check")
76
+
77
+ # Check API keys
78
+ gemini_keys = len([k for k in gemini_rotator.keys if k])
79
+ if gemini_keys == 0:
80
+ logger.warning("⚠️ No Gemini API keys found! Set GEMINI_API_1, GEMINI_API_2, etc. environment variables.")
81
+ else:
82
+ logger.info(f"✅ {gemini_keys} Gemini API keys available")
83
+
84
+ nvidia_keys = len([k for k in nvidia_rotator.keys if k])
85
+ if nvidia_keys == 0:
86
+ logger.warning("⚠️ No NVIDIA API keys found! Set NVIDIA_API_1, NVIDIA_API_2, etc. environment variables.")
87
+ else:
88
+ logger.info(f"✅ {nvidia_keys} NVIDIA API keys available")
89
+
90
+ # Check embedding client
91
+ if embedding_client.is_available():
92
+ logger.info("✅ Embedding model loaded successfully")
93
+ else:
94
+ logger.info("⚠️ Using fallback embedding mode")
95
+
96
+ logger.info("✅ Medical AI Assistant startup complete")
97
+
98
+ # Shutdown event
99
+ def shutdown_event():
100
+ """Cleanup on shutdown"""
101
+ logger.info("🛑 Shutting down Medical AI Assistant...")
102
+
103
+ @asynccontextmanager
104
+ async def lifespan(app: FastAPI):
105
+ # Startup code here
106
+ startup_event()
107
+ yield
108
+ # Shutdown code here
109
+ shutdown_event()
110
 
111
  # Initialize FastAPI app
112
  app = FastAPI(
113
+ lifespan=lifespan,
114
+ title="Medical AI Assistant",
115
+ description="AI-powered medical chatbot with memory and context awareness",
116
+ version="1.0.0"
117
  )
118
 
119
  # Add CORS middleware
120
  app.add_middleware(
121
+ CORSMiddleware,
122
+ allow_origins=["*"],
123
+ allow_credentials=True,
124
+ allow_methods=["*"],
125
+ allow_headers=["*"],
126
  )
127
 
128
  # Initialize core components
 
136
 
137
  # Mock medical knowledge base for demo purposes
138
  MEDICAL_KB = {
139
+ "symptoms": {
140
+ "fever": "Fever is a temporary increase in body temperature, often due to illness. Normal body temperature is around 98.6°F (37°C).",
141
+ "headache": "Headache is pain in the head or upper neck. Common types include tension headaches, migraines, and cluster headaches.",
142
+ "cough": "Cough is a sudden expulsion of air from the lungs. It can be dry or productive (bringing up mucus).",
143
+ "fatigue": "Fatigue is extreme tiredness that doesn't improve with rest. It can be caused by various medical conditions.",
144
+ "nausea": "Nausea is a feeling of sickness with an inclination to vomit. It can be caused by various conditions including infections, medications, and pregnancy.",
145
+ "dizziness": "Dizziness is a sensation of lightheadedness or unsteadiness. It can be caused by inner ear problems, low blood pressure, or neurological conditions.",
146
+ "chest pain": "Chest pain can have many causes, from muscle strain to serious heart conditions. Any unexplained chest pain should be evaluated by a healthcare provider.",
147
+ "shortness of breath": "Shortness of breath, or dyspnea, is difficulty breathing. It can be caused by respiratory, cardiac, or other medical conditions."
148
+ },
149
+ "conditions": {
150
+ "hypertension": "Hypertension (high blood pressure) is a common condition that affects the arteries. It's often called the 'silent killer' because it usually has no symptoms.",
151
+ "diabetes": "Diabetes is a chronic disease that affects how your body turns food into energy. There are two main types: Type 1 and Type 2.",
152
+ "asthma": "Asthma is a condition that affects the airways in the lungs. It can cause wheezing, shortness of breath, chest tightness, and coughing.",
153
+ "pneumonia": "Pneumonia is an infection that inflames the air sacs in one or both lungs. It can be caused by bacteria, viruses, or fungi.",
154
+ "heart disease": "Heart disease refers to various conditions affecting the heart, including coronary artery disease, heart failure, and arrhythmias.",
155
+ "arthritis": "Arthritis is inflammation of the joints, causing pain and stiffness. The most common types are osteoarthritis and rheumatoid arthritis.",
156
+ "depression": "Depression is a mental health disorder characterized by persistently depressed mood or loss of interest in activities.",
157
+ "anxiety": "Anxiety disorders involve excessive fear or worry that can interfere with daily activities and relationships."
158
+ },
159
+ "medications": {
160
+ "aspirin": "Aspirin is a common medication used to treat pain, fever, and inflammation. It's also used to prevent heart attacks and strokes.",
161
+ "ibuprofen": "Ibuprofen is a nonsteroidal anti-inflammatory drug (NSAID) used to reduce fever and treat pain or inflammation.",
162
+ "acetaminophen": "Acetaminophen is used to treat pain and reduce fever. It's generally safe when used as directed.",
163
+ "antibiotics": "Antibiotics are medications that fight bacterial infections. They don't work against viral infections like colds or flu.",
164
+ "insulin": "Insulin is a hormone that helps control blood sugar levels. It's essential for people with Type 1 diabetes and some with Type 2.",
165
+ "statins": "Statins are medications that help lower cholesterol levels in the blood, reducing the risk of heart disease and stroke."
166
+ },
167
+ "procedures": {
168
+ "blood test": "Blood tests can check for various conditions, monitor organ function, and assess overall health. Common types include CBC, metabolic panels, and lipid profiles.",
169
+ "x-ray": "X-rays use radiation to create images of bones and some soft tissues. They're commonly used to diagnose fractures, pneumonia, and other conditions.",
170
+ "mri": "MRI (Magnetic Resonance Imaging) uses magnetic fields and radio waves to create detailed images of organs and tissues.",
171
+ "ct scan": "CT scans use X-rays and computer technology to create cross-sectional images of the body, useful for diagnosing various conditions."
172
+ }
173
  }
174
 
175
  def search_medical_kb(query: str) -> str:
176
+ """Search the medical knowledge base for relevant information"""
177
+ query_lower = query.lower()
178
+ relevant_info = []
179
+
180
+ for category, items in MEDICAL_KB.items():
181
+ for key, value in items.items():
182
+ if query_lower in key.lower() or query_lower in value.lower():
183
+ relevant_info.append(f"{key.title()}: {value}")
184
+
185
+ if relevant_info:
186
+ return "\n\n".join(relevant_info[:3]) # Limit to 3 most relevant
187
+ return ""
188
 
189
  async def generate_medical_response_with_gemini(user_message: str, user_role: str, user_specialty: str, medical_context: str = "", rotator=None) -> str:
190
+ """Generate a medical response using Gemini AI for intelligent, contextual responses"""
191
+ try:
192
+ # Get API key from rotator
193
+ api_key = rotator.get_key() if rotator else None
194
+ if not api_key:
195
+ logger.warning("No Gemini API key available, using fallback response")
196
+ return generate_medical_response_fallback(user_message, user_role, user_specialty, medical_context)
197
+
198
+ # Configure Gemini
199
+ client = genai.Client(api_key=api_key)
200
+
201
+ # Build context-aware prompt
202
+ prompt = f"""You are a knowledgeable medical AI assistant. Provide a comprehensive, accurate, and helpful response to this medical question.
203
 
204
  **User Role:** {user_role}
205
  **User Specialty:** {user_specialty if user_specialty else 'General'}
 
224
 
225
  Remember: This is for educational purposes only. Always emphasize consulting healthcare professionals for medical advice."""
226
 
227
+ # Generate response
228
+ response = client.models.generate_content(model="gemini-2.5-flash", contents=prompt)
229
+
230
+ if response.text:
231
+ # Add medical disclaimer if not already present
232
+ if "disclaimer" not in response.text.lower() and "consult" not in response.text.lower():
233
+ response.text += "\n\n⚠️ **Important Disclaimer:** This information is for educational purposes only and should not replace professional medical advice, diagnosis, or treatment. Always consult with qualified healthcare professionals."
234
+
235
+ return response.text
236
+
237
+ # Fallback if Gemini fails
238
+ logger.warning("Gemini response generation failed, using fallback")
239
+ return generate_medical_response_fallback(user_message, user_role, user_specialty, medical_context)
240
+
241
+ except Exception as e:
242
+ logger.warning(f"Gemini medical response generation failed: {e}, using fallback")
243
+ return generate_medical_response_fallback(user_message, user_role, user_specialty, medical_context)
244
 
245
  def generate_medical_response_fallback(user_message: str, user_role: str, user_specialty: str, medical_context: str = "") -> str:
246
+ """Fallback medical response generator using local knowledge base"""
247
+
248
+ # Search medical knowledge base
249
+ kb_info = search_medical_kb(user_message)
250
+
251
+ # Build response based on available information
252
+ response_parts = []
253
+
254
+ # Analyze the question to provide more specific responses
255
+ question_lower = user_message.lower()
256
+
257
+ if kb_info:
258
+ response_parts.append(f"Based on your question about medical topics, here's what I found:\n\n{kb_info}")
259
+
260
+ # Add specific guidance based on the medical topic
261
+ if any(word in question_lower for word in ["fever", "temperature", "hot"]):
262
+ response_parts.append("\n\n**Key Points about Fever:**")
263
+ response_parts.append("• Normal body temperature is around 98.6°F (37°C)")
264
+ response_parts.append("• Fever is often a sign of infection or inflammation")
265
+ response_parts.append("• Monitor for other symptoms that accompany fever")
266
+ response_parts.append("• Seek medical attention for high fevers (>103°F/39.4°C) or persistent fevers")
267
+
268
+ elif any(word in question_lower for word in ["headache", "head pain", "migraine"]):
269
+ response_parts.append("\n\n**Key Points about Headaches:**")
270
+ response_parts.append("• Tension headaches are the most common type")
271
+ response_parts.append("• Migraines often have specific triggers and symptoms")
272
+ response_parts.append("• Sudden, severe headaches require immediate medical attention")
273
+ response_parts.append("• Keep a headache diary to identify patterns")
274
+
275
+ elif any(word in question_lower for word in ["cough", "cold", "respiratory"]):
276
+ response_parts.append("\n\n**Key Points about Respiratory Symptoms:**")
277
+ response_parts.append("• Dry vs. productive cough have different implications")
278
+ response_parts.append("• Most colds resolve within 7-10 days")
279
+ response_parts.append("• Persistent cough may indicate underlying conditions")
280
+ response_parts.append("• Monitor for difficulty breathing or chest pain")
281
+
282
+ elif any(word in question_lower for word in ["hypertension", "blood pressure", "high bp"]):
283
+ response_parts.append("\n\n**Key Points about Hypertension:**")
284
+ response_parts.append("• Often called the 'silent killer' due to lack of symptoms")
285
+ response_parts.append("• Regular monitoring is essential")
286
+ response_parts.append("• Lifestyle modifications can help control blood pressure")
287
+ response_parts.append("• Medication may be necessary for some individuals")
288
+
289
+ elif any(word in question_lower for word in ["diabetes", "blood sugar", "glucose"]):
290
+ response_parts.append("\n\n**Key Points about Diabetes:**")
291
+ response_parts.append("• Type 1: Autoimmune, requires insulin")
292
+ response_parts.append("• Type 2: Often lifestyle-related, may be managed with diet/exercise")
293
+ response_parts.append("• Regular blood sugar monitoring is crucial")
294
+ response_parts.append("• Complications can affect multiple organ systems")
295
+
296
+ else:
297
+ # Provide more helpful response for general questions
298
+ if "what is" in question_lower or "define" in question_lower:
299
+ response_parts.append("I understand you're asking about a medical topic. While I don't have specific information about this particular condition or symptom, I can provide some general guidance.")
300
+ elif "how to" in question_lower or "treatment" in question_lower:
301
+ response_parts.append("I understand you're asking about treatment or management of a medical condition. This is an area where professional medical advice is particularly important.")
302
+ elif "symptom" in question_lower or "sign" in question_lower:
303
+ response_parts.append("I understand you're asking about symptoms or signs of a medical condition. Remember that symptoms can vary between individuals and may indicate different conditions.")
304
+ else:
305
+ response_parts.append("Thank you for your medical question. While I can provide general information, it's important to consult with healthcare professionals for personalized medical advice.")
306
+
307
+ # Add role-specific guidance
308
+ if user_role.lower() in ["physician", "doctor", "nurse"]:
309
+ response_parts.append("\n\n**Professional Context:** As a healthcare professional, you're likely familiar with these concepts. Remember to always follow your institution's protocols and guidelines, and consider the latest clinical evidence in your practice.")
310
+ elif user_role.lower() in ["medical student", "student"]:
311
+ response_parts.append("\n\n**Educational Context:** As a medical student, this information can help with your studies. Always verify information with your professors and clinical supervisors, and use this as a starting point for further research.")
312
+ elif user_role.lower() in ["patient"]:
313
+ response_parts.append("\n\n**Patient Context:** As a patient, this information is for educational purposes only. Please discuss any concerns with your healthcare provider, and don't make treatment decisions based solely on this information.")
314
+ else:
315
+ response_parts.append("\n\n**General Context:** This information is provided for educational purposes. Always consult with qualified healthcare professionals for medical advice.")
316
+
317
+ # Add specialty-specific information if available
318
+ if user_specialty and user_specialty.lower() in ["cardiology", "cardiac"]:
319
+ response_parts.append("\n\n**Cardiology Perspective:** Given your interest in cardiology, consider how this information relates to cardiovascular health and patient care. Many conditions can have cardiac implications.")
320
+ elif user_specialty and user_specialty.lower() in ["pediatrics", "pediatric"]:
321
+ response_parts.append("\n\n**Pediatric Perspective:** In pediatric care, remember that children may present differently than adults and may require specialized approaches. Consider age-appropriate considerations.")
322
+ elif user_specialty and user_specialty.lower() in ["emergency", "er"]:
323
+ response_parts.append("\n\n**Emergency Medicine Perspective:** In emergency settings, rapid assessment and intervention are crucial. Consider the urgency and severity of presenting symptoms.")
324
+
325
+ # Add medical disclaimer
326
+ response_parts.append("\n\n⚠️ **Important Disclaimer:** This information is for educational purposes only and should not replace professional medical advice, diagnosis, or treatment. Always consult with qualified healthcare professionals.")
327
+
328
+ return "\n".join(response_parts)
329
 
330
  def generate_medical_response(user_message: str, user_role: str, user_specialty: str, medical_context: str = "") -> str:
331
+ """Legacy function - now calls the fallback generator"""
332
+ return generate_medical_response_fallback(user_message, user_role, user_specialty, medical_context)
333
 
334
  @app.get("/", response_class=HTMLResponse)
335
  async def get_medical_chatbot():
336
+ """Serve the medical chatbot UI"""
337
+ try:
338
+ with open("static/index.html", "r", encoding="utf-8") as f:
339
+ html_content = f.read()
340
+ return HTMLResponse(content=html_content)
341
+ except FileNotFoundError:
342
+ raise HTTPException(status_code=404, detail="Medical chatbot UI not found")
343
 
344
  @app.post("/chat", response_model=ChatResponse)
345
  async def chat_endpoint(request: ChatRequest):
346
+ """Handle chat messages and generate medical responses"""
347
+ start_time = time.time()
348
+
349
+ try:
350
+ logger.info(f"Chat request from user {request.user_id} in session {request.session_id}")
351
+ logger.info(f"Message: {request.message[:100]}...") # Log first 100 chars of message
352
+
353
+ # Get or create user profile
354
+ user_profile = memory_system.get_user(request.user_id)
355
+ if not user_profile:
356
+ memory_system.create_user(request.user_id, request.user_role or "Anonymous")
357
+ if request.user_specialty:
358
+ memory_system.get_user(request.user_id).set_preference("specialty", request.user_specialty)
359
+
360
+ # Get or create session
361
+ session = memory_system.get_session(request.session_id)
362
+ if not session:
363
+ session_id = memory_system.create_session(request.user_id, request.title or "New Chat")
364
+ session = memory_system.get_session(session_id)
365
+ logger.info(f"Created new session: {session_id}")
366
+
367
+ # Get medical context from memory
368
+ medical_context = history_manager.get_conversation_context(
369
+ request.user_id,
370
+ request.session_id,
371
+ request.message
372
+ )
373
+
374
+ # Generate response using Gemini AI
375
+ logger.info(f"Generating medical response using Gemini AI for user {request.user_id}")
376
+ response = await generate_medical_response_with_gemini(
377
+ request.message,
378
+ request.user_role or "Medical Professional",
379
+ request.user_specialty or "",
380
+ medical_context,
381
+ gemini_rotator
382
+ )
383
+ logger.info(f"Gemini response generated successfully, length: {len(response)} characters")
384
+
385
+ # Process and store the exchange
386
+ try:
387
+ await history_manager.process_medical_exchange(
388
+ request.user_id,
389
+ request.session_id,
390
+ request.message,
391
+ response,
392
+ gemini_rotator,
393
+ nvidia_rotator
394
+ )
395
+ except Exception as e:
396
+ logger.warning(f"Failed to process medical exchange: {e}")
397
+ # Continue without storing if there's an error
398
+
399
+ # Calculate response time
400
+ response_time = time.time() - start_time
401
+
402
+ logger.info(f"Generated response in {response_time:.2f}s for user {request.user_id}")
403
+ logger.info(f"Response length: {len(response)} characters")
404
+
405
+ return ChatResponse(
406
+ response=response,
407
+ session_id=request.session_id,
408
+ timestamp=time.strftime("%Y-%m-%d %H:%M:%S"),
409
+ medical_context=medical_context if medical_context else None
410
+ )
411
+
412
+ except Exception as e:
413
+ logger.error(f"Error in chat endpoint: {e}")
414
+ logger.error(f"Request data: {request.dict()}")
415
+ raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}")
416
 
417
  @app.post("/users")
418
  async def create_user_profile(request: UserProfileRequest):
419
+ """Create or update user profile"""
420
+ try:
421
+ user = memory_system.create_user(request.user_id, request.name)
422
+ user.set_preference("role", request.role)
423
+ if request.specialty:
424
+ user.set_preference("specialty", request.specialty)
425
+
426
+ return {"message": "User profile created successfully", "user_id": request.user_id}
427
+ except Exception as e:
428
+ logger.error(f"Error creating user profile: {e}")
429
+ raise HTTPException(status_code=500, detail=str(e))
430
 
431
  @app.get("/users/{user_id}")
432
  async def get_user_profile(user_id: str):
433
+ """Get user profile and sessions"""
434
+ try:
435
+ user = memory_system.get_user(user_id)
436
+ if not user:
437
+ raise HTTPException(status_code=404, detail="User not found")
438
+
439
+ sessions = memory_system.get_user_sessions(user_id)
440
+
441
+ return {
442
+ "user": {
443
+ "id": user.user_id,
444
+ "name": user.name,
445
+ "role": user.preferences.get("role", "Unknown"),
446
+ "specialty": user.preferences.get("specialty", ""),
447
+ "created_at": user.created_at,
448
+ "last_seen": user.last_seen
449
+ },
450
+ "sessions": [
451
+ {
452
+ "id": session.session_id,
453
+ "title": session.title,
454
+ "created_at": session.created_at,
455
+ "last_activity": session.last_activity,
456
+ "message_count": len(session.messages)
457
+ }
458
+ for session in sessions
459
+ ]
460
+ }
461
+ except HTTPException:
462
+ raise
463
+ except Exception as e:
464
+ logger.error(f"Error getting user profile: {e}")
465
+ raise HTTPException(status_code=500, detail=str(e))
466
 
467
  @app.post("/sessions")
468
  async def create_chat_session(request: SessionRequest):
469
+ """Create a new chat session"""
470
+ try:
471
+ session_id = memory_system.create_session(request.user_id, request.title or "New Chat")
472
+ return {"session_id": session_id, "message": "Session created successfully"}
473
+ except Exception as e:
474
+ logger.error(f"Error creating session: {e}")
475
+ raise HTTPException(status_code=500, detail=str(e))
476
 
477
  @app.get("/sessions/{session_id}")
478
  async def get_chat_session(session_id: str):
479
+ """Get chat session details and messages"""
480
+ try:
481
+ session = memory_system.get_session(session_id)
482
+ if not session:
483
+ raise HTTPException(status_code=404, detail="Session not found")
484
+
485
+ return {
486
+ "session_id": session.session_id,
487
+ "user_id": session.user_id,
488
+ "title": session.title,
489
+ "created_at": session.created_at,
490
+ "last_activity": session.last_activity,
491
+ "messages": session.messages
492
+ }
493
+ except HTTPException:
494
+ raise
495
+ except Exception as e:
496
+ logger.error(f"Error getting session: {e}")
497
+ raise HTTPException(status_code=500, detail=str(e))
498
 
499
  @app.delete("/sessions/{session_id}")
500
  async def delete_chat_session(session_id: str):
501
+ """Delete a chat session"""
502
+ try:
503
+ memory_system.delete_session(session_id)
504
+ return {"message": "Session deleted successfully"}
505
+ except Exception as e:
506
+ logger.error(f"Error deleting session: {e}")
507
+ raise HTTPException(status_code=500, detail=str(e))
508
 
509
  @app.get("/health")
510
  async def health_check():
511
+ """Health check endpoint"""
512
+ return {
513
+ "status": "healthy",
514
+ "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
515
+ "components": {
516
+ "memory_system": "operational",
517
+ "embedding_client": "operational" if embedding_client.is_available() else "fallback_mode",
518
+ "api_rotator": "operational",
519
+ "gemini_keys_available": len([k for k in gemini_rotator.keys if k]) > 0,
520
+ "nvidia_keys_available": len([k for k in nvidia_rotator.keys if k]) > 0
521
+ }
522
+ }
523
 
524
  @app.get("/api/info")
525
  async def get_api_info():
526
+ """Get API information and capabilities"""
527
+ return {
528
+ "name": "Medical AI Assistant",
529
+ "version": "1.0.0",
530
+ "description": "AI-powered medical chatbot with memory and context awareness",
531
+ "features": [
532
+ "Multi-user support with profiles",
533
+ "Chat session management",
534
+ "Medical context memory",
535
+ "API key rotation",
536
+ "Embedding-based similarity search",
537
+ "Medical knowledge base integration"
538
+ ],
539
+ "endpoints": [
540
+ "POST /chat - Send chat message",
541
+ "POST /users - Create user profile",
542
+ "GET /users/{user_id} - Get user profile and sessions",
543
+ "POST /sessions - Create chat session",
544
+ "GET /sessions/{session_id} - Get session details",
545
+ "DELETE /sessions/{session_id} - Delete session",
546
+ "GET /health - Health check",
547
+ "GET /api/info - API information"
548
+ ]
549
+ }
550
 
551
  # Mount static files
552
  app.mount("/static", StaticFiles(directory="static"), name="static")
553
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
554
  if __name__ == "__main__":
555
+ logger.info("Starting Medical AI Assistant server...")
556
+ try:
557
+ uvicorn.run(
558
+ app,
559
+ host="0.0.0.0",
560
+ port=8000,
561
+ log_level="info",
562
+ reload=True
563
+ )
564
+ except Exception as e:
565
+ logger.error(f"❌ Server startup failed: {e}")
566
+ exit(1)